61 Commits

Author SHA1 Message Date
19ed45ac25 Also only build release in for_unity build 2017-12-01 14:41:20 -08:00
be7fda7c50 Update build script for a more targeted set of stuff to build for unity 2017-12-01 14:41:20 -08:00
50ea4e61c6 Fix unity build for osx 2017-12-01 14:41:20 -08:00
82439911c6 wip update unity example 2017-12-01 14:41:20 -08:00
0d24fabdf4 Fix param type 2017-12-01 14:41:20 -08:00
79eea99d19 Update Unreal example (register more callbacks, still mostly debug prints) 2017-12-01 14:41:20 -08:00
c053b72f58 Update Unity example (also, remove dll, need to do that a better way) 2017-12-01 14:41:20 -08:00
1a278528a2 Add discriminator to join request 2017-12-01 14:41:20 -08:00
c1a8899d02 Build script cleanup 2017-12-01 14:41:20 -08:00
b01d0a8af3 Move the signing stuff out of cmake 2017-11-29 14:00:37 -08:00
8cae35ea46 Added community wrapper/implementation table 2017-11-29 11:24:39 -08:00
8307a1ad83 Update readme 2017-11-29 10:36:14 -08:00
8af28e46be Disable code formatting on CI builds. 2017-11-29 09:50:30 -08:00
2af5adca3d Use simpler version of GetModuleFileNameW
As per
https://msdn.microsoft.com/en-us/library/windows/desktop/ms683198(v=vs.85).aspx,
the advised use for getting the file name of a module from `<current
process>` is to use the simplified version. Additionally, this clears
the confusion introduced in Win7's PSAPI which moved
`GetModuleFileNameExW` into the kernel, changing its signature to
`K32GetModuleFileNameExW`, effectively breaking pre-Win7 uses unless
actively mitigated with `#define PSAPI_VERSION 1`
2017-11-29 08:08:47 -08:00
7300d1caa8 wip build script, playing with buildkite logging 2017-11-28 10:58:10 -08:00
7c3e28870e wip more build script, add signing 2017-11-28 10:27:40 -08:00
9130707086 Wrap std::thread in a holder that joins it on destruction. 2017-11-28 09:16:00 -08:00
49b23040c6 wip updating build script 2017-11-22 16:12:37 -08:00
522c304b32 Change appveyor artifacts 2017-11-22 08:51:37 -08:00
Kia
5d5bc82e26 Moved generated.h include to last position as it is required by UE4. 2017-11-19 17:26:33 -08:00
49b7703334 Skip formatting on Travis 2017-11-17 10:53:59 -08:00
706847dd47 Add Xcode 2017-11-17 10:53:59 -08:00
2c566b208b Add clang 5 2017-11-17 10:53:59 -08:00
95be02d4ce Add Travis CI support 2017-11-17 10:53:59 -08:00
c834a2e6d9 Thanks, paranoia 2017-11-17 09:15:24 -08:00
6a963456ed Thanks, Danny. 2017-11-17 09:13:22 -08:00
c5d3481c47 Add GNUInstallDirs include to top-level CMakeLists 2017-11-16 09:03:17 -08:00
2b248a6cef Don't use hardcoded paths for installation targets 2017-11-16 09:03:17 -08:00
c9cf6b3f41 Move /MT directive to MSVC only 2017-11-16 09:03:17 -08:00
704c56d13f Add option for building with /MT 2017-11-16 09:03:17 -08:00
5085d23dd1 Improve cmakelists for vcpkg building
- Remove forced /MT directive.
- Allow building for debug.
- Add option to prevent building of example apps.
2017-11-16 09:03:17 -08:00
1675d5d2dc Provide link to info about BUILD_SHARED_LIBS 2017-11-15 11:35:55 -08:00
bf6e0fddd5 Update appveyor.yml 2017-11-15 09:31:21 -08:00
18f6d878e0 Fix appveyor build 2017-11-15 09:31:21 -08:00
364606f7e9 Create Appveyor config for automated CI testing 2017-11-15 09:31:21 -08:00
b206dd44f0 Use -fPIC option when building shared libs 2017-11-15 09:22:07 -08:00
d121bbe709 Use cmake-standard flag BUILD_SHARED_LIBS
This removes custom `BUILD_DYNAMIC_LIB` option and replaces it with a
standard BUILD_SHARED_LIBS:
https://cmake.org/cmake/help/v3.7/variable/BUILD_SHARED_LIBS.html

Although not mentioned in the documentation there, this flag is
implicitly available.
2017-11-15 09:22:07 -08:00
cfd6470946 Add a comment explaining WINVER macros for MinGW 2017-11-15 08:20:48 -08:00
f22f299330 Fix mingw builds 2017-11-15 08:20:48 -08:00
767b15184f What's build.py for 2017-11-14 20:40:32 -08:00
6e744d228f Update to clang-format 6, specify all the options, turn off sort includes. 2017-11-13 10:50:37 -08:00
a1ab6c96f2 Fix unused result 2017-11-13 10:38:18 -08:00
0ea7ddbd5a Fix CMake issue when ENABLE_IO_THREAD=OFF (#28)
This was probably just a typo.
2017-11-13 08:01:58 -08:00
2f54e62c23 fix include order 2017-11-10 13:14:00 -08:00
54abef2624 Fix unused parameter warning triggering 2017-11-10 09:59:51 -08:00
c5d70514ac Use steam://rungameid/ 2017-11-10 09:59:51 -08:00
57316cbaee clang-format I suppose 2017-11-10 09:59:51 -08:00
Kia
9d4d145e7f Add entry for include folder where you have to place discord-rpc.h 2017-11-10 08:13:01 -08:00
7dc663a170 Static link crt 2017-11-09 14:24:30 -08:00
f872b4e49c fix event names and add ACTIVITY_JOIN_REQUEST (#10)
* fix event names and add ACTIVITY_JOIN_REQUEST

* Update hard-mode.md

* fix typo
2017-11-09 13:32:17 -08:00
ca5d70a5f9 Add more -Wflags 2017-11-09 13:08:05 -08:00
ee9c504d1c Change -Weverything to -Wall for more compilers 2017-11-09 13:08:05 -08:00
127eadcb89 Added VS2015 C++ redist dependency info 2017-11-07 09:59:04 -08:00
a7808a20ed Fix some sizes on join request strings. 2017-11-03 13:40:30 -07:00
3bdb88d918 Unity ajt fix 2017-11-02 11:59:45 -07:00
aa79c70bf9 Adding MIT license. 2017-11-01 15:25:14 -07:00
a089aab53e Update unity. 2017-10-17 13:34:03 -07:00
dafd85c39f Rename avatarUrl -> avatar for API consistency 2017-10-17 13:34:03 -07:00
b1d6a7c0fc Normalize white-space 2017-10-17 09:37:56 -07:00
e4b3ef63b7 Added request to join functionality 2017-10-17 09:37:56 -07:00
86ca320cb9 These should have been extern C. 2017-10-13 10:02:12 -07:00
39 changed files with 833 additions and 255 deletions

View File

@ -1,52 +1,71 @@
--- ---
Language: Cpp
AccessModifierOffset: -4 AccessModifierOffset: -4
AlignAfterOpenBracket: true AlignAfterOpenBracket: true
AlignEscapedNewlinesLeft: true AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: false AlignOperands: false
AlignTrailingComments: true AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: false AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: InlineOnly
AllowShortIfStatementsOnASingleLine: false AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false AllowShortLoopsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline AlwaysBreakAfterReturnType: None
AlwaysBreakAfterDefinitionReturnType: false
AlwaysBreakTemplateDeclarations: true
AlwaysBreakBeforeMultilineStrings: false AlwaysBreakBeforeMultilineStrings: false
BreakBeforeBraces: Stroustrup AlwaysBreakTemplateDeclarations: true
BreakBeforeBinaryOperators: None
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: true
BinPackParameters: false
BinPackArguments: false BinPackArguments: false
BinPackParameters: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Stroustrup
BreakBeforeInheritanceComma: true
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeComma
BreakStringLiterals: true
ColumnLimit: 100 ColumnLimit: 100
CommentPragmas: '' CommentPragmas: ''
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 2 ConstructorInitializerIndentWidth: 2
ContinuationIndentWidth: 2 ContinuationIndentWidth: 2
Cpp11BracedListStyle: true Cpp11BracedListStyle: true
DerivePointerAlignment: false DerivePointerAlignment: false
DisableFormat: false DisableFormat: false
ExperimentalAutoDetectBinPacking: false FixNamespaceComments: true
ForEachMacros: [ foreach ] ForEachMacros: []
IndentCaseLabels: false
IncludeCategories:
- Regex: '^("|<)stdafx\.h(pp)?("|>)'
Priority: -1
- Regex: '^<(W|w)indows.h>'
Priority: 1
- Regex: '^<'
Priority: 2
- Regex: '.*'
Priority: 3
IncludeIsMainRegex: '(_test|_win|_linux|_mac|_ios|_osx|_null)?$'
IndentCaseLabels: false IndentCaseLabels: false
IndentWidth: 4 IndentWidth: 4
IndentWrappedFunctionNames: false IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1 MaxEmptyLinesToKeep: 1
NamespaceIndentation: None NamespaceIndentation: None
ObjCBlockIndentWidth: 4 PenaltyBreakAssignment: 0
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
PenaltyBreakBeforeFirstCallParameter: 1 PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300 PenaltyBreakComment: 300
PenaltyBreakString: 1000
PenaltyBreakFirstLessLess: 120 PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000 PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 9999999 PenaltyReturnTypeOnItsOwnLine: 9999999
PointerAlignment: Left PointerAlignment: Left
ReflowComments: true
SortIncludes: false
SortUsingDeclarations: true
SpaceAfterCStyleCast: false SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false SpaceInEmptyParentheses: false
@ -59,4 +78,15 @@ SpacesInSquareBrackets: false
Standard: Cpp11 Standard: Cpp11
TabWidth: 4 TabWidth: 4
UseTab: Never UseTab: Never
---
Language: Cpp
---
Language: ObjC
ObjCBlockIndentWidth: 4
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: false
---
Language: Java
BasedOnStyle: Google
BreakAfterJavaFieldAnnotations: true
... ...

47
.travis.yml Normal file
View File

@ -0,0 +1,47 @@
language: cpp
env:
global:
- CLANG_FORMAT_SUFFIX="-dummy" # don't use formatting on Travis, this is
# needed not to use default 3.5 version
# which is too old.
matrix:
include:
- os: linux
env: MATRIX_EVAL="CC=gcc-5 && CXX=g++-5"
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-5
- os: linux
env: MATRIX_EVAL="CC=clang-4.0 && CXX=clang++-4.0"
addons:
apt:
sources:
- llvm-toolchain-trusty-4.0
packages:
- clang-4.0
- os: linux
env: MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0"
addons:
apt:
sources:
- llvm-toolchain-trusty-5.0
packages:
- clang-5.0
- os: osx
osx_image: xcode9
# prevent Travis from overwriting our CXX variables
before_install:
- eval "${MATRIX_EVAL}"
- echo $CXX
script:
- mkdir build
- cd build
- cmake -DCLANG_FORMAT_SUFFIX=$CLANG_FORMAT_SUFFIX --config Release ..
- cmake --build . -- -j2

View File

@ -1,6 +1,10 @@
cmake_minimum_required (VERSION 3.7.0) cmake_minimum_required (VERSION 3.2.0)
project (DiscordRPC) project (DiscordRPC)
include(GNUInstallDirs)
option(BUILD_EXAMPLES "Build example apps" ON)
# format # format
file(GLOB_RECURSE ALL_SOURCE_FILES file(GLOB_RECURSE ALL_SOURCE_FILES
examples/*.cpp examples/*.h examples/*.c examples/*.cpp examples/*.h examples/*.c
@ -8,22 +12,18 @@ file(GLOB_RECURSE ALL_SOURCE_FILES
src/*.cpp src/*.h src/*.c src/*.cpp src/*.h src/*.c
) )
find_program(CLANG_FORMAT_CMD clang-format) # Set CLANG_FORMAT_SUFFIX if you are using custom clang-format, e.g. clang-format-5.0
find_program(CLANG_FORMAT_CMD clang-format${CLANG_FORMAT_SUFFIX})
if (CLANG_FORMAT_CMD) if (CLANG_FORMAT_CMD)
add_custom_target( add_custom_target(
clangformat clangformat
COMMAND clang-format COMMAND ${CLANG_FORMAT_CMD}
-i -style=file -fallback-style=none -i -style=file -fallback-style=none
${ALL_SOURCE_FILES} ${ALL_SOURCE_FILES}
DEPENDS DEPENDS
${ALL_SOURCE_FILES} ${ALL_SOURCE_FILES}
) )
else(CLANG_FORMAT_CMD)
add_custom_target(
clangformat
COMMENT "no clang format"
)
endif(CLANG_FORMAT_CMD) endif(CLANG_FORMAT_CMD)
# thirdparty stuff # thirdparty stuff
@ -32,7 +32,7 @@ execute_process(
ERROR_QUIET ERROR_QUIET
) )
find_file(RAPIDJSONTEST NAMES rapidjson rapidjson-1.1.0 PATHS ${CMAKE_SOURCE_DIR}/thirdparty) find_file(RAPIDJSONTEST NAMES rapidjson rapidjson-1.1.0 PATHS ${CMAKE_SOURCE_DIR}/thirdparty CMAKE_FIND_ROOT_PATH_BOTH)
if (NOT RAPIDJSONTEST) if (NOT RAPIDJSONTEST)
message("no rapidjson, download") message("no rapidjson, download")
set(RJ_TAR_FILE ${CMAKE_SOURCE_DIR}/thirdparty/v1.1.0.tar.gz) set(RJ_TAR_FILE ${CMAKE_SOURCE_DIR}/thirdparty/v1.1.0.tar.gz)
@ -44,11 +44,13 @@ if (NOT RAPIDJSONTEST)
file(REMOVE ${RJ_TAR_FILE}) file(REMOVE ${RJ_TAR_FILE})
endif(NOT RAPIDJSONTEST) endif(NOT RAPIDJSONTEST)
find_file(RAPIDJSON NAMES rapidjson rapidjson-1.1.0 PATHS ${CMAKE_SOURCE_DIR}/thirdparty) find_file(RAPIDJSON NAMES rapidjson rapidjson-1.1.0 PATHS ${CMAKE_SOURCE_DIR}/thirdparty CMAKE_FIND_ROOT_PATH_BOTH)
add_library(rapidjson STATIC IMPORTED ${RAPIDJSON}) add_library(rapidjson STATIC IMPORTED ${RAPIDJSON})
# add subdirs # add subdirs
add_subdirectory(src) add_subdirectory(src)
add_subdirectory(examples/send-presence) if (BUILD_EXAMPLES)
add_subdirectory(examples/send-presence)
endif(BUILD_EXAMPLES)

19
LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright 2017 Discord, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,33 +1,27 @@
# Discord RPC # Discord RPC
This is a lib and quick demos that implement the very minimal subset to show current status, and This is a library for interfacing your game with a locally running Discord desktop client. It's known to work on Windows, macOS, and Linux. You can use the lib directly if you like, or use it as a guide to writing your own if it doesn't suit your game as is. PRs/feedback welcome if you have an improvement everyone might want, or can describe how this doesn't meet your needs.
have callbacks for where a more complete game would do more things. You can use the lib directly
if you like, or use it as a guide to writing your own if it doesn't suit your game as is.
PRs/feedback welcome if you have an improvement everyone might want, or can describe how this Included here are some quick demos that implement the very minimal subset to show current status, and
doesn't meet your needs. have callbacks for where a more complete game would do more things (joining, spectating, etc).
## Documentation ## Documentation
The most up to date documentation for Rich Presence can always be found in our [developer site](https://discordapp.com/developers/docs/topics/rich-presence)! The most up to date documentation for Rich Presence can always be found in our [developer site](https://discordapp.com/developers/docs/topics/rich-presence)!
## Usage ## Basic Usage
Zeroith, you should be set up to build things because you are a game developer, right? Zeroith, you should be set up to build things because you are a game developer, right?
First, head on over to the [Discord developers site](https://discordapp.com/developers/applications/me) First, head on over to the [Discord developers site](https://discordapp.com/developers/applications/me) and make yourself an app. Keep track of `Client ID` -- you'll need it here to pass to the init function.
and make yourself an app. Keep track of `Client ID` -- you'll need it here to pass to the init
function.
### From package ### From package
Download a release package, extract it, add `/include` to your compile includes, `/lib` to your Download a release package for your platform(s) -- they have subdirs with various prebuilt options, select the one you need add `/include` to your compile includes, `/lib` to your linker paths, and link with `discord-rpc`. For the dynamically linked builds, you'll need to ship the associated file along with your game.
linker paths, and link with `discord-rpc`.
### From repo ### From repo
There's a [CMake](https://cmake.org/download/) file that should be able to generate the lib for There's a [CMake](https://cmake.org/download/) file that should be able to generate the lib for you; Sometimes I use it like this:
you; I use it like this:
```sh ```sh
cd <path to discord-rpc> cd <path to discord-rpc>
mkdir build mkdir build
@ -35,25 +29,50 @@ you; I use it like this:
cmake .. -DCMAKE_INSTALL_PREFIX=<path to install discord-rpc to> cmake .. -DCMAKE_INSTALL_PREFIX=<path to install discord-rpc to>
cmake --build . --config Release --target install cmake --build . --config Release --target install
``` ```
Sometimes I use the generated project files. There are a couple of CMake options you might care about: There is a wrapper build script `build.py` that runs `cmake` with a few different options.
Usually, I run `build.py` to get things started, then use the generated project files as I work on things.
There are some CMake options you might care about:
| flag | default | does | | flag | default | does |
|------|---------|------| |------|---------|------|
| `ENABLE_IO_THREAD` | `ON` | When enabled, we start up a thread to do io processing, if disabled you should call `Discord_UpdateConnection` yourself. | `ENABLE_IO_THREAD` | `ON` | When enabled, we start up a thread to do io processing, if disabled you should call `Discord_UpdateConnection` yourself.
| `BUILD_DYNAMIC_LIB` | `OFF` | Build library as a DLL | `USE_STATIC_CRT` | `OFF` | (Windows) Enable to statically link the CRT, avoiding requiring users install the redistributable package. (The prebuilt binaries enable this option)
| [`BUILD_SHARED_LIBS`](https://cmake.org/cmake/help/v3.7/variable/BUILD_SHARED_LIBS.html) | `OFF` | Build library as a DLL
## Continuous Builds
Why do we have three of these? Three times the fun!
| CI | badge |
|----|-------|
| TravisCI | [![Build status](https://travis-ci.org/discordapp/discord-rpc.svg?branch=master)](https://travis-ci.org/discordapp/discord-rpc)
| AppVeyor | [![Build status](https://ci.appveyor.com/api/projects/status/qvkoc0w1c4f4b8tj?svg=true)](https://ci.appveyor.com/project/crmarsh/discord-rpc)
| Buildkite (internal) | [![Build status](https://badge.buildkite.com/e103d79d247f6776605a15246352a04b8fd83d69211b836111.svg)](https://buildkite.com/discord/discord-rpc)
## Sample: send-presence ## Sample: send-presence
This is a text adventure "game" that inits/deinits the connection to Discord, and sends a presence This is a text adventure "game" that inits/deinits the connection to Discord, and sends a presence update on each command.
update on each command.
## Sample: button-clicker ## Sample: button-clicker
This is a sample [Unity](https://unity3d.com/) project that wraps a DLL version of the library, and This is a sample [Unity](https://unity3d.com/) project that wraps a DLL version of the library, and sends presence updates when you click on a button.
sends presence updates when you click on a button.
## Sample: unrealstatus ## Sample: unrealstatus
This is a sample [Unreal](https://www.unrealengine.com) project that wraps the DLL version of the This is a sample [Unreal](https://www.unrealengine.com) project that wraps the DLL version of the library with an Unreal plugin, exposes a blueprint class for interacting with it, and uses that to make a very simple UI.
library with an Unreal plugin, exposes a blueprint class for interacting with it, and uses that to
make a very simple UI. ## Wrappers and Implementations
Below is a table of unofficial, community-developed wrappers for and implementations of Rich Presence in various languages. If you would like to have yours added, please make a pull request adding your repository to the table. The repository should include:
- The code
- A brief ReadMe of how to use it
- A working example
###### Rich Presence Wrappers and Implementations
| Name | Language |
|------|----------|
| Be the first! | |

17
appveyor.yml Normal file
View File

@ -0,0 +1,17 @@
version: '{build}'
install:
- python -m pip install click
build_script:
- mkdir examples\unrealstatus\Plugins\discordrpc\Binaries\ThirdParty\discordrpcLibrary\Win64
- python build.py
artifacts:
- path: builds\install\win32-dynamic
name: win32-dynamic
- path: builds\install\win32-static
name: win32-static
- path: builds\install\win64-dynamic
name: win64-dynamic
- path: builds\install\win64-static
name: win64-static

208
build.py
View File

@ -1,15 +1,39 @@
#!/usr/bin/env python #!/usr/bin/env python
import click
import os import os
import subprocess import subprocess
import sys import sys
import shutil import shutil
import zipfile import zipfile
from contextlib import contextmanager from contextlib import contextmanager
import click
def get_platform():
""" a name for the platform """
if sys.platform.startswith('win'):
return 'win'
elif sys.platform == 'darwin':
return 'osx'
elif sys.platform.startswith('linux'):
return 'linux'
raise Exception('Unsupported platform ' + sys.platform)
SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__)) SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__))
# we use Buildkite which sets this env variable by default
IS_BUILD_MACHINE = os.environ.get('CI', '') == 'true'
PLATFORM = get_platform()
INSTALL_ROOT = os.path.join(SCRIPT_PATH, 'builds', 'install')
def get_signtool():
""" get path to code signing tool """
if PLATFORM == 'win':
sdk_dir = os.environ['WindowsSdkDir']
return os.path.join(sdk_dir, 'bin', 'x86', 'signtool.exe')
elif PLATFORM == 'osx':
return '/usr/bin/codesign'
@contextmanager @contextmanager
@ -26,69 +50,185 @@ def cd(new_dir):
def mkdir_p(path): def mkdir_p(path):
""" mkdir -p """ """ mkdir -p """
if not os.path.isdir(path): if not os.path.isdir(path):
click.secho('Making ' + path, fg='yellow')
os.makedirs(path) os.makedirs(path)
def build_lib(build_name, generator, options): @click.group(invoke_without_command=True)
@click.pass_context
@click.option('--clean', is_flag=True)
def cli(ctx, clean):
""" click wrapper for command line stuff """
if ctx.invoked_subcommand is None:
ctx.invoke(libs, clean=clean)
if IS_BUILD_MACHINE:
ctx.invoke(sign)
ctx.invoke(archive)
@cli.command()
def unity():
""" todo: build unity project """
pass
@cli.command()
@click.pass_context
def for_unity(ctx):
""" build just dynamic libs for use in unity project """
ctx.invoke(
libs,
clean=False,
static=False,
shared=True,
skip_formatter=True,
just_release=True
)
@cli.command()
def unreal():
""" todo: build unreal project """
pass
def build_lib(build_name, generator, options, just_release):
""" Create a dir under builds, run build and install in it """
build_path = os.path.join(SCRIPT_PATH, 'builds', build_name) build_path = os.path.join(SCRIPT_PATH, 'builds', build_name)
install_path = os.path.join(SCRIPT_PATH, 'builds', 'install', build_name) install_path = os.path.join(INSTALL_ROOT, build_name)
mkdir_p(build_path) mkdir_p(build_path)
mkdir_p(install_path) mkdir_p(install_path)
with cd(build_path): with cd(build_path):
initial_cmake = ['cmake', SCRIPT_PATH, '-DCMAKE_INSTALL_PREFIX=%s' % os.path.join('..', 'install', build_name)] initial_cmake = [
'cmake',
SCRIPT_PATH,
'-DCMAKE_INSTALL_PREFIX=%s' % os.path.join('..', 'install', build_name)
]
if generator: if generator:
initial_cmake.extend(['-G', generator]) initial_cmake.extend(['-G', generator])
for key in options: for key in options:
val = 'ON' if options[key] else 'OFF' val = options[key]
initial_cmake.append('-D%s=%s' %(key, val)) if type(val) is bool:
val = 'ON' if val else 'OFF'
initial_cmake.append('-D%s=%s' % (key, val))
click.echo('--- Building ' + build_name)
subprocess.check_call(initial_cmake) subprocess.check_call(initial_cmake)
if not just_release:
subprocess.check_call(['cmake', '--build', '.', '--config', 'Debug']) subprocess.check_call(['cmake', '--build', '.', '--config', 'Debug'])
subprocess.check_call(['cmake', '--build', '.', '--config', 'Release', '--target', 'install']) subprocess.check_call(['cmake', '--build', '.', '--config', 'Release', '--target', 'install'])
def create_archive(): @cli.command()
archive_file_path = os.path.join(SCRIPT_PATH, 'builds', 'discord-rpc-%s.zip' % sys.platform) def archive():
""" create zip of install dir """
click.echo('--- Archiving')
archive_file_path = os.path.join(SCRIPT_PATH, 'builds', 'discord-rpc-%s.zip' % get_platform())
archive_file = zipfile.ZipFile(archive_file_path, 'w', zipfile.ZIP_DEFLATED) archive_file = zipfile.ZipFile(archive_file_path, 'w', zipfile.ZIP_DEFLATED)
archive_src_base_path = os.path.join(SCRIPT_PATH, 'builds', 'install') archive_src_base_path = INSTALL_ROOT
archive_dst_base_path = 'discord-rpc' archive_dst_base_path = 'discord-rpc'
with cd(archive_src_base_path): with cd(archive_src_base_path):
for path, subdirs, filenames in os.walk('.'): for path, _, filenames in os.walk('.'):
for fname in filenames: for fname in filenames:
fpath = os.path.join(path, fname) fpath = os.path.join(path, fname)
archive_file.write(fpath, os.path.normpath(os.path.join(archive_dst_base_path, fpath))) dst_path = os.path.normpath(os.path.join(archive_dst_base_path, fpath))
click.echo('Adding ' + dst_path)
archive_file.write(fpath, dst_path)
@click.command() @cli.command()
def sign():
""" Do code signing within install directory using our cert """
tool = get_signtool()
signable_extensions = set()
if PLATFORM == 'win':
signable_extensions.add('.dll')
sign_command_base = [
tool,
'sign',
'/n', 'Hammer & Chisel Inc.',
'/a',
'/tr', 'http://timestamp.digicert.com/rfc3161',
'/as',
'/td', 'sha256',
'/fd', 'sha256',
]
elif PLATFORM == 'osx':
signable_extensions.add('.dylib')
sign_command_base = [
tool,
'--keychain', os.path.expanduser('~/Library/Keychains/login.keychain'),
'-vvvv',
'--deep',
'--force',
'--sign', 'Developer ID Application: Hammer & Chisel Inc. (53Q6R32WPB)',
]
else:
click.secho('Not signing things on this platform yet', fg='red')
return
click.echo('--- Signing')
for path, _, filenames in os.walk(INSTALL_ROOT):
for fname in filenames:
ext = os.path.splitext(fname)[1]
if ext not in signable_extensions:
continue
fpath = os.path.join(path, fname)
click.echo('Sign ' + fpath)
sign_command = sign_command_base + [fpath]
subprocess.check_call(sign_command)
@cli.command()
@click.option('--clean', is_flag=True) @click.option('--clean', is_flag=True)
def main(clean): @click.option('--static', is_flag=True)
os.chdir(SCRIPT_PATH) @click.option('--shared', is_flag=True)
@click.option('--skip_formatter', is_flag=True)
@click.option('--just_release', is_flag=True)
def libs(clean, static, shared, skip_formatter, just_release):
""" Do all the builds for this platform """
if clean: if clean:
shutil.rmtree('builds', ignore_errors=True) shutil.rmtree('builds', ignore_errors=True)
mkdir_p('builds') mkdir_p('builds')
if sys.platform.startswith('win'): if not (static or shared):
static = True
shared = True
static_options = {}
dynamic_options = {
'BUILD_SHARED_LIBS': True,
'USE_STATIC_CRT': True,
}
if skip_formatter or IS_BUILD_MACHINE:
static_options['CLANG_FORMAT_SUFFIX'] = 'none'
dynamic_options['CLANG_FORMAT_SUFFIX'] = 'none'
if IS_BUILD_MACHINE:
just_release = True
if PLATFORM == 'win':
generator32 = 'Visual Studio 14 2015' generator32 = 'Visual Studio 14 2015'
generator64 = 'Visual Studio 14 2015 Win64' generator64 = 'Visual Studio 14 2015 Win64'
if static:
build_lib('win32-static', generator32, {}) build_lib('win32-static', generator32, static_options, just_release)
build_lib('win32-dynamic', generator32, {'BUILD_DYNAMIC_LIB': True}) build_lib('win64-static', generator64, static_options, just_release)
build_lib('win64-static', generator64, {}) if shared:
build_lib('win64-dynamic', generator64, {'BUILD_DYNAMIC_LIB': True}) build_lib('win32-dynamic', generator32, dynamic_options, just_release)
build_lib('win64-dynamic', generator64, dynamic_options, just_release)
# todo: this in some better way elif PLATFORM == 'osx':
src_dll = os.path.join(SCRIPT_PATH, 'builds', 'win64-dynamic', 'src', 'Release', 'discord-rpc.dll') if static:
dst_dll = os.path.join(SCRIPT_PATH, 'examples', 'button-clicker', 'Assets', 'Resources', 'discord-rpc.dll') build_lib('osx-static', None, static_options, just_release)
shutil.copy(src_dll, dst_dll) if shared:
dst_dll = os.path.join(SCRIPT_PATH, 'examples', 'unrealstatus', 'Plugins', 'discordrpc', 'Binaries', 'ThirdParty', 'discordrpcLibrary', 'Win64', 'discord-rpc.dll') build_lib('osx-dynamic', None, dynamic_options, just_release)
shutil.copy(src_dll, dst_dll) elif PLATFORM == 'linux':
elif sys.platform == 'darwin': if static:
build_lib('osx-static', None, {}) build_lib('linux-static', None, static_options, just_release)
build_lib('osx-dynamic', None, {'BUILD_DYNAMIC_LIB': True}) if shared:
build_lib('linux-dynamic', None, dynamic_options, just_release)
create_archive()
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main()) os.chdir(SCRIPT_PATH)
sys.exit(cli())

View File

@ -56,7 +56,9 @@ Below is a full example of a `SET_ACTIVITY` command. Field restrictions like siz
## New RPC Events ## New RPC Events
The two new RPC events for Rich Presence power the ability to join and spectate your friends' games. First is the `GAME_JOIN` event: The three new RPC events for Rich Presence power the ability to join and spectate your friends' games.
First is the `ACTIVITY_JOIN` event:
```json ```json
{ {
@ -64,11 +66,11 @@ The two new RPC events for Rich Presence power the ability to join and spectate
"data": { "data": {
"secret": "025ed05c71f639de8bfaa0d679d7c94b2fdce12f" "secret": "025ed05c71f639de8bfaa0d679d7c94b2fdce12f"
}, },
"evnt": "GAME_JOIN" "evnt": "ACTIVITY_JOIN"
} }
``` ```
And second is the `GAME_SPECTATE` event: Second is the `ACTIVITY_SPECTATE` event:
```json ```json
{ {
@ -76,7 +78,25 @@ And second is the `GAME_SPECTATE` event:
"data": { "data": {
"secret": "e7eb30d2ee025ed05c71ea495f770b76454ee4e0" "secret": "e7eb30d2ee025ed05c71ea495f770b76454ee4e0"
}, },
"evnt": "GAME_SPECTATE" "evnt": "ACTIVITY_SPECTATE"
}
```
And third is the `ACTIVITY_JOIN_REQUEST` event:
```json
{
"cmd": "DISPATCH",
"data": {
"user": {
"id": "53908232506183680",
"username": "Mason",
"discriminator": "1337",
"avatar": "a_bab14f271d565501444b2ca3be944b25"
},
"secret": "e459ca99273f59909dd16ed97865f3ad"
},
"evnt": "ACTIVITY_JOIN_REQUEST"
} }
``` ```
@ -85,7 +105,7 @@ In order to receive these events, you need to [subscribe](https://discordapp.com
```json ```json
{ {
"nonce": "be9a6de3-31d0-4767-a8e9-4818c5690015", "nonce": "be9a6de3-31d0-4767-a8e9-4818c5690015",
"evt": "GAME_JOIN", "evt": "ACTIVITY_JOIN",
"cmd": "SUBSCRIBE" "cmd": "SUBSCRIBE"
} }
``` ```
@ -93,7 +113,15 @@ In order to receive these events, you need to [subscribe](https://discordapp.com
```json ```json
{ {
"nonce": "ae9qdde3-31d0-8989-a8e9-dnakwy174he", "nonce": "ae9qdde3-31d0-8989-a8e9-dnakwy174he",
"evt": "GAME_SPECTATE", "evt": "ACTIVITY_SPECTATE",
"cmd": "SUBSCRIBE"
}
```
```json
{
"nonce": "5dc0c062-98c6-47a0-8922-bbb52e9d6afa",
"evt": "ACTIVITY_JOIN_REQUEST",
"cmd": "SUBSCRIBE" "cmd": "SUBSCRIBE"
} }
``` ```

View File

@ -1,6 +1,8 @@
/Library/ /Library/
/Temp/ /Temp/
/obj/ /obj/
/Assets/Plugins/
/Assets/Plugins.meta
*.sln *.sln
*.csproj *.csproj
*.userprefs *.userprefs

View File

@ -0,0 +1,101 @@
using UnityEditor;
using System.Diagnostics;
using System.IO;
[InitializeOnLoad]
public class ScriptBatch
{
static ScriptBatch()
{
EnsureDLL();
}
public static bool FileExists(string filename)
{
return new FileInfo(filename).Exists;
}
public static bool RunRpcBuildScript()
{
UnityEngine.Debug.Log("Try to run build script");
Process proc = new Process();
#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX
proc.StartInfo.UseShellExecute = false;
// brew installs cmake in /usr/local/bin, which Unity seems to strip from PATH?
string newPath = proc.StartInfo.EnvironmentVariables["PATH"] + ":/usr/local/bin";
proc.StartInfo.EnvironmentVariables["PATH"] = newPath;
#endif
proc.StartInfo.FileName = "python";
proc.StartInfo.Arguments = "build.py for_unity";
proc.StartInfo.WorkingDirectory = "../..";
proc.Start();
proc.WaitForExit();
return proc.ExitCode == 0;
}
public static void EnsureDLL()
{
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
string[] dstDirs = { "Assets/Plugins", "Assets/Plugins/x86", "Assets/Plugins/x86_64" };
string[] dstDlls = { "Assets/Plugins/x86/discord-rpc.dll", "Assets/Plugins/x86_64/discord-rpc.dll" };
string[] srcDlls = { "../../builds/install/win64-dynamic/bin/discord-rpc.dll", "../../builds/install/win64-dynamic/bin/discord-rpc.dll" };
#elif UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX
string[] dstDirs = { "Assets/Plugins" };
string[] dstDlls = { "Assets/Plugins/discord-rpc.bundle" };
string[] srcDlls = { "../../builds/install/osx-dynamic/lib/libdiscord-rpc.dylib" };
#else
string[] dstDirs = { "Assets/Plugins", "Assets/Plugins/x86", "Assets/Plugins/x86_64" };
string[] dstDlls = { "Assets/Plugins/x86/discord-rpc.so", "Assets/Plugins/x86_64/discord-rpc.so" };
string[] srcDlls = { "../../builds/install/linux-dynamic/bin/discord-rpc.dll", "../../builds/install/win64-dynamic/bin/discord-rpc.dll" };
#endif
Debug.Assert(dstDlls.Length == srcDlls.Length);
bool exists = true;
foreach (string fname in dstDlls)
{
if (!FileExists(fname))
{
exists = false;
break;
}
}
if (exists)
{
return;
}
exists = true;
foreach (string fname in srcDlls)
{
if (!FileExists(fname))
{
exists = false;
break;
}
}
if (!exists)
{
if (!RunRpcBuildScript())
{
UnityEngine.Debug.LogError("Build failed");
return;
}
}
// make sure the dirs exist
foreach (string dirname in dstDirs)
{
Directory.CreateDirectory(dirname);
}
// Copy dlls
for (int i = 0; i < dstDlls.Length; ++i)
{
FileUtil.CopyFileOrDirectory(srcDlls[i], dstDlls[i]);
}
}
}

View File

@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: e5aecc4633e5f594b85eaa39f49bb402
timeCreated: 1512071254
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,6 +1,16 @@
using UnityEngine; using UnityEngine;
public class DiscordController : MonoBehaviour { [System.Serializable]
public class DiscordJoinEvent : UnityEngine.Events.UnityEvent<string> { }
[System.Serializable]
public class DiscordSpectateEvent : UnityEngine.Events.UnityEvent<string> { }
[System.Serializable]
public class DiscordJoinRequestEvent : UnityEngine.Events.UnityEvent<DiscordRpc.JoinRequest> { }
public class DiscordController : MonoBehaviour
{
public DiscordRpc.RichPresence presence; public DiscordRpc.RichPresence presence;
public string applicationId; public string applicationId;
public string optionalSteamId; public string optionalSteamId;
@ -8,6 +18,9 @@ public class DiscordController : MonoBehaviour {
public int clickCounter; public int clickCounter;
public UnityEngine.Events.UnityEvent onConnect; public UnityEngine.Events.UnityEvent onConnect;
public UnityEngine.Events.UnityEvent onDisconnect; public UnityEngine.Events.UnityEvent onDisconnect;
public DiscordJoinEvent onJoin;
public DiscordJoinEvent onSpectate;
public DiscordJoinRequestEvent onJoinRequest;
DiscordRpc.EventHandlers handlers; DiscordRpc.EventHandlers handlers;
@ -45,18 +58,29 @@ public class DiscordController : MonoBehaviour {
{ {
++callbackCalls; ++callbackCalls;
Debug.Log(string.Format("Discord: join ({0})", secret)); Debug.Log(string.Format("Discord: join ({0})", secret));
onJoin.Invoke(secret);
} }
public void SpectateCallback(string secret) public void SpectateCallback(string secret)
{ {
++callbackCalls; ++callbackCalls;
Debug.Log(string.Format("Discord: spectate ({0})", secret)); Debug.Log(string.Format("Discord: spectate ({0})", secret));
onSpectate.Invoke(secret);
} }
void Start () { public void RequestCallback(ref DiscordRpc.JoinRequest request)
{
++callbackCalls;
Debug.Log(string.Format("Discord: join request {0}#{1}: {2}", request.username, request.discriminator, request.userId));
onJoinRequest.Invoke(request);
} }
void Update () { void Start()
{
}
void Update()
{
DiscordRpc.RunCallbacks(); DiscordRpc.RunCallbacks();
} }
@ -71,6 +95,7 @@ public class DiscordController : MonoBehaviour {
handlers.errorCallback += ErrorCallback; handlers.errorCallback += ErrorCallback;
handlers.joinCallback += JoinCallback; handlers.joinCallback += JoinCallback;
handlers.spectateCallback += SpectateCallback; handlers.spectateCallback += SpectateCallback;
handlers.requestCallback += RequestCallback;
DiscordRpc.Initialize(applicationId, ref handlers, true, optionalSteamId); DiscordRpc.Initialize(applicationId, ref handlers, true, optionalSteamId);
} }

View File

@ -17,6 +17,9 @@ public class DiscordRpc
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SpectateCallback(string secret); public delegate void SpectateCallback(string secret);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void RequestCallback(ref JoinRequest request);
public struct EventHandlers public struct EventHandlers
{ {
public ReadyCallback readyCallback; public ReadyCallback readyCallback;
@ -24,6 +27,7 @@ public class DiscordRpc
public ErrorCallback errorCallback; public ErrorCallback errorCallback;
public JoinCallback joinCallback; public JoinCallback joinCallback;
public SpectateCallback spectateCallback; public SpectateCallback spectateCallback;
public RequestCallback requestCallback;
} }
[System.Serializable] [System.Serializable]
@ -46,6 +50,22 @@ public class DiscordRpc
public bool instance; public bool instance;
} }
[System.Serializable]
public struct JoinRequest
{
public string userId;
public string username;
public string discriminator;
public string avatar;
}
public enum Reply
{
No = 0,
Yes = 1,
Ignore = 2
}
[DllImport("discord-rpc", EntryPoint = "Discord_Initialize", CallingConvention = CallingConvention.Cdecl)] [DllImport("discord-rpc", EntryPoint = "Discord_Initialize", CallingConvention = CallingConvention.Cdecl)]
public static extern void Initialize(string applicationId, ref EventHandlers handlers, bool autoRegister, string optionalSteamId); public static extern void Initialize(string applicationId, ref EventHandlers handlers, bool autoRegister, string optionalSteamId);
@ -57,5 +77,8 @@ public class DiscordRpc
[DllImport("discord-rpc", EntryPoint = "Discord_UpdatePresence", CallingConvention = CallingConvention.Cdecl)] [DllImport("discord-rpc", EntryPoint = "Discord_UpdatePresence", CallingConvention = CallingConvention.Cdecl)]
public static extern void UpdatePresence(ref RichPresence presence); public static extern void UpdatePresence(ref RichPresence presence);
[DllImport("discord-rpc", EntryPoint = "Discord_Respond", CallingConvention = CallingConvention.Cdecl)]
public static extern void Respond(string userId, Reply reply);
} }

View File

@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: 4ddcc1759a3a2394fa1fa376963639e0
folderAsset: yes
timeCreated: 1501697278
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,27 +0,0 @@
fileFormatVersion: 2
guid: 2aadd6305b09fa94dab94261a8bb8caf
timeCreated: 1501697340
licenseType: Free
PluginImporter:
serializedVersion: 2
iconMap: {}
executionOrder: {}
isPreloaded: 0
isOverridable: 0
platformData:
data:
first:
Any:
second:
enabled: 1
settings: {}
data:
first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:

View File

@ -653,7 +653,7 @@ MonoBehaviour:
m_EditorClassIdentifier: m_EditorClassIdentifier:
presence: presence:
state: Button clicking state: Button clicking
details: details: Best game ever
startTimestamp: 0 startTimestamp: 0
endTimestamp: 0 endTimestamp: 0
largeImageKey: stable-large largeImageKey: stable-large
@ -663,10 +663,10 @@ MonoBehaviour:
partyId: abcdefg partyId: abcdefg
partySize: 1 partySize: 1
partyMax: 10 partyMax: 10
matchSecret: matchSecret: match_secret
joinSecret: joinSecret: join_secret
spectateSecret: spectateSecret: spectate_secret
instance: 0 instance: 1
applicationId: 345229890980937739 applicationId: 345229890980937739
optionalSteamId: optionalSteamId:
callbackCalls: 0 callbackCalls: 0
@ -703,6 +703,21 @@ MonoBehaviour:
m_CallState: 2 m_CallState: 2
m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral, m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral,
PublicKeyToken=null PublicKeyToken=null
onJoin:
m_PersistentCalls:
m_Calls: []
m_TypeName: DiscordJoinEvent, Assembly-CSharp, Version=0.0.0.0, Culture=neutral,
PublicKeyToken=null
onSpectate:
m_PersistentCalls:
m_Calls: []
m_TypeName: DiscordJoinEvent, Assembly-CSharp, Version=0.0.0.0, Culture=neutral,
PublicKeyToken=null
onJoinRequest:
m_PersistentCalls:
m_Calls: []
m_TypeName: DiscordJoinRequestEvent, Assembly-CSharp, Version=0.0.0.0, Culture=neutral,
PublicKeyToken=null
--- !u!4 &1929635630 --- !u!4 &1929635630
Transform: Transform:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0

View File

@ -17,3 +17,4 @@ PhysicsManager:
m_EnablePCM: 1 m_EnablePCM: 1
m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
m_AutoSimulation: 1 m_AutoSimulation: 1
m_AutoSyncTransforms: 1

View File

@ -4,4 +4,7 @@
EditorBuildSettings: EditorBuildSettings:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
serializedVersion: 2 serializedVersion: 2
m_Scenes: [] m_Scenes:
- enabled: 1
path: Assets/main.unity
guid: 3b03d21bb25fa494e8694cd6e4b6d769

View File

@ -24,6 +24,7 @@ Physics2DSettings:
m_QueriesStartInColliders: 1 m_QueriesStartInColliders: 1
m_ChangeStopsCallbacks: 0 m_ChangeStopsCallbacks: 0
m_CallbacksOnDisable: 1 m_CallbacksOnDisable: 1
m_AutoSyncTransforms: 1
m_AlwaysShowColliders: 0 m_AlwaysShowColliders: 0
m_ShowColliderSleep: 1 m_ShowColliderSleep: 1
m_ShowColliderContacts: 0 m_ShowColliderContacts: 0

View File

@ -3,14 +3,15 @@
--- !u!129 &1 --- !u!129 &1
PlayerSettings: PlayerSettings:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
serializedVersion: 12 serializedVersion: 13
productGUID: 5eccc60d3e382a346a65f512d6b81b84 productGUID: 5eccc60d3e382a346a65f512d6b81b84
AndroidProfiler: 0 AndroidProfiler: 0
AndroidFilterTouchesWhenObscured: 0
defaultScreenOrientation: 4 defaultScreenOrientation: 4
targetDevice: 2 targetDevice: 2
useOnDemandResources: 0 useOnDemandResources: 0
accelerometerFrequency: 60 accelerometerFrequency: 60
companyName: DefaultCompany companyName: Discord Inc.
productName: button-clicker productName: button-clicker
defaultCursor: {fileID: 0} defaultCursor: {fileID: 0}
cursorHotspot: {x: 0, y: 0} cursorHotspot: {x: 0, y: 0}
@ -38,8 +39,6 @@ PlayerSettings:
width: 1 width: 1
height: 1 height: 1
m_SplashScreenLogos: [] m_SplashScreenLogos: []
m_SplashScreenBackgroundLandscape: {fileID: 0}
m_SplashScreenBackgroundPortrait: {fileID: 0}
m_VirtualRealitySplashScreen: {fileID: 0} m_VirtualRealitySplashScreen: {fileID: 0}
m_HolographicTrackingLossScreen: {fileID: 0} m_HolographicTrackingLossScreen: {fileID: 0}
defaultScreenWidth: 1024 defaultScreenWidth: 1024
@ -49,7 +48,6 @@ PlayerSettings:
m_StereoRenderingPath: 0 m_StereoRenderingPath: 0
m_ActiveColorSpace: 0 m_ActiveColorSpace: 0
m_MTRendering: 1 m_MTRendering: 1
m_MobileMTRendering: 0
m_StackTraceTypes: 010000000100000001000000010000000100000001000000 m_StackTraceTypes: 010000000100000001000000010000000100000001000000
iosShowActivityIndicatorOnLoading: -1 iosShowActivityIndicatorOnLoading: -1
androidShowActivityIndicatorOnLoading: -1 androidShowActivityIndicatorOnLoading: -1
@ -64,8 +62,10 @@ PlayerSettings:
useOSAutorotation: 1 useOSAutorotation: 1
use32BitDisplayBuffer: 1 use32BitDisplayBuffer: 1
disableDepthAndStencilBuffers: 0 disableDepthAndStencilBuffers: 0
androidBlitType: 0
defaultIsFullScreen: 1 defaultIsFullScreen: 1
defaultIsNativeResolution: 1 defaultIsNativeResolution: 1
macRetinaSupport: 1
runInBackground: 0 runInBackground: 0
captureSingleScreen: 0 captureSingleScreen: 0
muteOtherAudioSources: 0 muteOtherAudioSources: 0
@ -95,6 +95,7 @@ PlayerSettings:
xboxEnableHeadOrientation: 0 xboxEnableHeadOrientation: 0
xboxEnableGuest: 0 xboxEnableGuest: 0
xboxEnablePIXSampling: 0 xboxEnablePIXSampling: 0
metalFramebufferOnly: 0
n3dsDisableStereoscopicView: 0 n3dsDisableStereoscopicView: 0
n3dsEnableSharedListOpt: 1 n3dsEnableSharedListOpt: 1
n3dsEnableVSync: 0 n3dsEnableVSync: 0
@ -103,6 +104,7 @@ PlayerSettings:
xboxOneMonoLoggingLevel: 0 xboxOneMonoLoggingLevel: 0
xboxOneLoggingLevel: 1 xboxOneLoggingLevel: 1
xboxOneDisableEsram: 0 xboxOneDisableEsram: 0
xboxOnePresentImmediateThreshold: 0
videoMemoryForVertexBuffers: 0 videoMemoryForVertexBuffers: 0
psp2PowerMode: 0 psp2PowerMode: 0
psp2AcquireBGM: 1 psp2AcquireBGM: 1
@ -134,12 +136,17 @@ PlayerSettings:
daydream: daydream:
depthFormat: 0 depthFormat: 0
useSustainedPerformanceMode: 0 useSustainedPerformanceMode: 0
enableVideoLayer: 0
useProtectedVideoMemory: 0
hololens: hololens:
depthFormat: 1 depthFormat: 1
protectGraphicsMemory: 0 protectGraphicsMemory: 0
useHDRDisplay: 0 useHDRDisplay: 0
m_ColorGamuts: 00000000
targetPixelDensity: 0 targetPixelDensity: 0
resolutionScalingMode: 0 resolutionScalingMode: 0
androidSupportedAspectRatio: 1
androidMaxAspectRatio: 2.1
applicationIdentifier: {} applicationIdentifier: {}
buildNumber: {} buildNumber: {}
AndroidBundleVersionCode: 1 AndroidBundleVersionCode: 1
@ -160,10 +167,10 @@ PlayerSettings:
serializedVersion: 2 serializedVersion: 2
m_Bits: 238 m_Bits: 238
iPhoneSdkVersion: 988 iPhoneSdkVersion: 988
iOSTargetOSVersionString: iOSTargetOSVersionString: 7.0
tvOSSdkVersion: 0 tvOSSdkVersion: 0
tvOSRequireExtendedGameController: 0 tvOSRequireExtendedGameController: 0
tvOSTargetOSVersionString: tvOSTargetOSVersionString: 9.0
uIPrerenderedIcon: 0 uIPrerenderedIcon: 0
uIRequiresPersistentWiFi: 0 uIRequiresPersistentWiFi: 0
uIRequiresFullScreen: 1 uIRequiresFullScreen: 1
@ -220,7 +227,9 @@ PlayerSettings:
AndroidKeyaliasName: AndroidKeyaliasName:
AndroidTVCompatibility: 1 AndroidTVCompatibility: 1
AndroidIsGame: 1 AndroidIsGame: 1
AndroidEnableTango: 0
androidEnableBanner: 1 androidEnableBanner: 1
androidUseLowAccuracyLocation: 0
m_AndroidBanners: m_AndroidBanners:
- width: 320 - width: 320
height: 180 height: 180
@ -231,10 +240,14 @@ PlayerSettings:
m_BuildTargetBatching: [] m_BuildTargetBatching: []
m_BuildTargetGraphicsAPIs: [] m_BuildTargetGraphicsAPIs: []
m_BuildTargetVRSettings: [] m_BuildTargetVRSettings: []
m_BuildTargetEnableVuforiaSettings: []
openGLRequireES31: 0 openGLRequireES31: 0
openGLRequireES31AEP: 0 openGLRequireES31AEP: 0
webPlayerTemplate: APPLICATION:Default
m_TemplateCustomTags: {} m_TemplateCustomTags: {}
mobileMTRendering:
Android: 1
iPhone: 1
tvOS: 1
wiiUTitleID: 0005000011000000 wiiUTitleID: 0005000011000000
wiiUGroupID: 00010000 wiiUGroupID: 00010000
wiiUCommonSaveSize: 4096 wiiUCommonSaveSize: 4096
@ -368,6 +381,9 @@ PlayerSettings:
switchUdpSendBufferSize: 9 switchUdpSendBufferSize: 9
switchUdpReceiveBufferSize: 42 switchUdpReceiveBufferSize: 42
switchSocketBufferEfficiency: 4 switchSocketBufferEfficiency: 4
switchSocketInitializeEnabled: 1
switchNetworkInterfaceManagerInitializeEnabled: 1
switchPlayerConnectionEnabled: 1
ps4NPAgeRating: 12 ps4NPAgeRating: 12
ps4NPTitleSecret: ps4NPTitleSecret:
ps4NPTrophyPackPath: ps4NPTrophyPackPath:

View File

@ -1 +1 @@
m_EditorVersion: 2017.1.0f3 m_EditorVersion: 2017.2.0f3

View File

@ -0,0 +1,4 @@
{
"dependencies": {
}
}

View File

@ -83,7 +83,7 @@ static void handleDiscordJoinRequest(const DiscordJoinRequest* request)
char yn[4]; char yn[4];
printf("\nDiscord: join request from %s - %s - %s\n", printf("\nDiscord: join request from %s - %s - %s\n",
request->username, request->username,
request->avatarUrl, request->avatar,
request->userId); request->userId);
do { do {
printf("Accept? (y/n)"); printf("Accept? (y/n)");

View File

@ -12,6 +12,7 @@ public class discordrpcLibrary : ModuleRules
if (Target.Platform == UnrealTargetPlatform.Win64) if (Target.Platform == UnrealTargetPlatform.Win64)
{ {
// Add the import library // Add the import library
PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Include"));
PublicLibraryPaths.Add(Path.Combine(ModuleDirectory, "x64", "Release")); PublicLibraryPaths.Add(Path.Combine(ModuleDirectory, "x64", "Release"));
PublicAdditionalLibraries.Add("discord-rpc.lib"); PublicAdditionalLibraries.Add("discord-rpc.lib");

View File

@ -7,6 +7,7 @@
DEFINE_LOG_CATEGORY(Discord) DEFINE_LOG_CATEGORY(Discord)
static UDiscordRpc* self = nullptr; static UDiscordRpc* self = nullptr;
static void ReadyHandler() static void ReadyHandler()
{ {
UE_LOG(Discord, Log, TEXT("Discord connected")); UE_LOG(Discord, Log, TEXT("Discord connected"));
@ -53,6 +54,19 @@ static void SpectateGameHandler(const char* spectateSecret)
} }
} }
static void JoinRequestHandler(const DiscordJoinRequest* request)
{
FDiscordJoinRequestData jr;
jr.userId = ANSI_TO_TCHAR(request->userId);
jr.username = ANSI_TO_TCHAR(request->username);
jr.discriminator = ANSI_TO_TCHAR(request->discriminator);
jr.avatar = ANSI_TO_TCHAR(request->avatar);
UE_LOG(Discord, Log, TEXT("Discord join request from %s#%s"), *jr.username, *jr.discriminator);
if (self) {
self->OnJoinRequest.Broadcast(jr);
}
}
void UDiscordRpc::Initialize(const FString& applicationId, void UDiscordRpc::Initialize(const FString& applicationId,
bool autoRegister, bool autoRegister,
const FString& optionalSteamId) const FString& optionalSteamId)
@ -69,6 +83,9 @@ void UDiscordRpc::Initialize(const FString& applicationId,
if (OnSpectate.IsBound()) { if (OnSpectate.IsBound()) {
handlers.spectateGame = SpectateGameHandler; handlers.spectateGame = SpectateGameHandler;
} }
if (OnJoinRequest.IsBound()) {
handlers.joinRequest = JoinRequestHandler;
}
auto appId = StringCast<ANSICHAR>(*applicationId); auto appId = StringCast<ANSICHAR>(*applicationId);
auto steamId = StringCast<ANSICHAR>(*optionalSteamId); auto steamId = StringCast<ANSICHAR>(*optionalSteamId);
Discord_Initialize( Discord_Initialize(

View File

@ -2,8 +2,8 @@
#include "discordrpc.h" #include "discordrpc.h"
#include "Core.h" #include "Core.h"
#include "ModuleManager.h"
#include "IPluginManager.h" #include "IPluginManager.h"
#include "ModuleManager.h"
#define LOCTEXT_NAMESPACE "FdiscordrpcModule" #define LOCTEXT_NAMESPACE "FdiscordrpcModule"

View File

@ -2,13 +2,31 @@
#pragma once #pragma once
#include "Engine.h"
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "Engine.h"
#include "DiscordRpcBlueprint.generated.h" #include "DiscordRpcBlueprint.generated.h"
// unreal's header tool hates clang-format // unreal's header tool hates clang-format
// clang-format off // clang-format off
/**
* Ask to join callback data
*/
USTRUCT(BlueprintType)
struct FDiscordJoinRequestData {
GENERATED_USTRUCT_BODY()
UPROPERTY(BlueprintReadOnly)
FString userId;
UPROPERTY(BlueprintReadOnly)
FString username;
UPROPERTY(BlueprintReadOnly)
FString discriminator;
UPROPERTY(BlueprintReadOnly)
FString avatar;
};
DECLARE_LOG_CATEGORY_EXTERN(Discord, Log, All); DECLARE_LOG_CATEGORY_EXTERN(Discord, Log, All);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FDiscordConnected); DECLARE_DYNAMIC_MULTICAST_DELEGATE(FDiscordConnected);
@ -16,6 +34,7 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FDiscordDisconnected, int, errorCod
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FDiscordErrored, int, errorCode, const FString&, errorMessage); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FDiscordErrored, int, errorCode, const FString&, errorMessage);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDiscordJoin, const FString&, joinSecret); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDiscordJoin, const FString&, joinSecret);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDiscordSpectate, const FString&, spectateSecret); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDiscordSpectate, const FString&, spectateSecret);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDiscordJoinRequest, const FDiscordJoinRequestData&, joinRequest);
// clang-format on // clang-format on
@ -119,6 +138,12 @@ public:
Category = "Discord") Category = "Discord")
FDiscordSpectate OnSpectate; FDiscordSpectate OnSpectate;
UPROPERTY(BlueprintAssignable,
meta = (DisplayName = "When Discord another user sends a join request",
Keywords = "Discord rpc"),
Category = "Discord")
FDiscordJoinRequest OnJoinRequest;
UPROPERTY(BlueprintReadWrite, UPROPERTY(BlueprintReadWrite,
meta = (DisplayName = "Rich presence info", Keywords = "Discord rpc"), meta = (DisplayName = "Rich presence info", Keywords = "Discord rpc"),
Category = "Discord") Category = "Discord")

View File

@ -1,6 +1,6 @@
{ {
"FileVersion": 3, "FileVersion": 3,
"EngineAssociation": "4.16", "EngineAssociation": "4.18",
"Category": "", "Category": "",
"Description": "", "Description": "",
"Modules": [ "Modules": [

View File

@ -42,9 +42,10 @@ typedef struct DiscordRichPresence {
} DiscordRichPresence; } DiscordRichPresence;
typedef struct DiscordJoinRequest { typedef struct DiscordJoinRequest {
char userId[24]; const char* userId;
char username[48]; const char* username;
char avatarUrl[128]; const char* discriminator;
const char* avatar;
} DiscordJoinRequest; } DiscordJoinRequest;
typedef struct DiscordEventHandlers { typedef struct DiscordEventHandlers {

View File

@ -1,7 +1,9 @@
include_directories(${PROJECT_SOURCE_DIR}/include) include_directories(${PROJECT_SOURCE_DIR}/include)
option(ENABLE_IO_THREAD "Start up a separate I/O thread, otherwise I'd need to call an update function" ON) option(ENABLE_IO_THREAD "Start up a separate I/O thread, otherwise I'd need to call an update function" ON)
option(BUILD_DYNAMIC_LIB "Build library as a DLL" OFF) option(USE_STATIC_CRT "Use /MT[d] for dynamic library" OFF)
set(CMAKE_CXX_STANDARD 14)
set(BASE_RPC_SRC set(BASE_RPC_SRC
${PROJECT_SOURCE_DIR}/include/discord-rpc.h ${PROJECT_SOURCE_DIR}/include/discord-rpc.h
@ -16,20 +18,27 @@ set(BASE_RPC_SRC
msg_queue.h msg_queue.h
) )
if (${BUILD_DYNAMIC_LIB}) if (${BUILD_SHARED_LIBS})
set(RPC_LIBRARY_TYPE SHARED)
if(WIN32) if(WIN32)
set(BASE_RPC_SRC ${BASE_RPC_SRC} dllmain.cpp) set(BASE_RPC_SRC ${BASE_RPC_SRC} dllmain.cpp)
endif(WIN32) endif(WIN32)
else(${BUILD_DYNAMIC_LIB}) endif(${BUILD_SHARED_LIBS})
set(RPC_LIBRARY_TYPE STATIC)
endif(${BUILD_DYNAMIC_LIB})
if(WIN32) if(WIN32)
add_definitions(-DDISCORD_WINDOWS) add_definitions(-DDISCORD_WINDOWS)
set(BASE_RPC_SRC ${BASE_RPC_SRC} connection_win.cpp discord_register_win.cpp) set(BASE_RPC_SRC ${BASE_RPC_SRC} connection_win.cpp discord_register_win.cpp)
add_library(discord-rpc ${RPC_LIBRARY_TYPE} ${BASE_RPC_SRC}) add_library(discord-rpc ${BASE_RPC_SRC})
if (MSVC)
set(CRT_FLAGS)
if(USE_STATIC_CRT)
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CRT_FLAGS /MTd)
else()
set(CRT_FLAGS /MT)
endif()
endif(USE_STATIC_CRT)
target_compile_options(discord-rpc PRIVATE /EHsc target_compile_options(discord-rpc PRIVATE /EHsc
${CRT_FLAGS}
/Wall /Wall
/wd4100 # unreferenced formal parameter /wd4100 # unreferenced formal parameter
/wd4514 # unreferenced inline /wd4514 # unreferenced inline
@ -43,6 +52,8 @@ if(WIN32)
/wd4946 # reinterpret_cast used between related classes /wd4946 # reinterpret_cast used between related classes
/wd5027 # move assignment operator was implicitly defined as deleted /wd5027 # move assignment operator was implicitly defined as deleted
) )
endif(MSVC)
target_link_libraries(discord-rpc PRIVATE psapi)
endif(WIN32) endif(WIN32)
if(UNIX) if(UNIX)
@ -56,11 +67,14 @@ if(UNIX)
set(BASE_RPC_SRC ${BASE_RPC_SRC} discord_register_linux.cpp) set(BASE_RPC_SRC ${BASE_RPC_SRC} discord_register_linux.cpp)
endif(APPLE) endif(APPLE)
add_library(discord-rpc ${RPC_LIBRARY_TYPE} ${BASE_RPC_SRC}) add_library(discord-rpc ${BASE_RPC_SRC})
target_link_libraries(discord-rpc PUBLIC pthread) target_link_libraries(discord-rpc PUBLIC pthread)
target_compile_options(discord-rpc PRIVATE target_compile_options(discord-rpc PRIVATE
-g -g
-Weverything -Wall
-Wextra
-Wpedantic
-Werror
-Wno-unknown-pragmas # pragma push thing doesn't work on clang -Wno-unknown-pragmas # pragma push thing doesn't work on clang
-Wno-old-style-cast # it's fine -Wno-old-style-cast # it's fine
-Wno-c++98-compat # that was almost 2 decades ago -Wno-c++98-compat # that was almost 2 decades ago
@ -71,7 +85,10 @@ if(UNIX)
-Wno-exit-time-destructors # not sure about these -Wno-exit-time-destructors # not sure about these
-Wno-global-constructors -Wno-global-constructors
) )
target_compile_options(discord-rpc PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-std=c++14>)
if (${BUILD_SHARED_LIBS})
target_compile_options(discord-rpc PRIVATE -fPIC)
endif (${BUILD_SHARED_LIBS})
if (APPLE) if (APPLE)
target_link_libraries(discord-rpc PRIVATE "-framework AppKit") target_link_libraries(discord-rpc PRIVATE "-framework AppKit")
@ -81,15 +98,17 @@ endif(UNIX)
target_include_directories(discord-rpc PRIVATE ${RAPIDJSON}/include) target_include_directories(discord-rpc PRIVATE ${RAPIDJSON}/include)
if (NOT ${ENABLE_IO_THREAD}) if (NOT ${ENABLE_IO_THREAD})
add_definitions(discord-rpc PUBLIC -DDISCORD_DISABLE_IO_THREAD) target_compile_definitions(discord-rpc PUBLIC -DDISCORD_DISABLE_IO_THREAD)
endif (NOT ${ENABLE_IO_THREAD}) endif (NOT ${ENABLE_IO_THREAD})
if (${BUILD_DYNAMIC_LIB}) if (${BUILD_SHARED_LIBS})
target_compile_definitions(discord-rpc PUBLIC -DDISCORD_DYNAMIC_LIB) target_compile_definitions(discord-rpc PUBLIC -DDISCORD_DYNAMIC_LIB)
target_compile_definitions(discord-rpc PRIVATE -DDISCORD_BUILDING_SDK) target_compile_definitions(discord-rpc PRIVATE -DDISCORD_BUILDING_SDK)
endif(${BUILD_DYNAMIC_LIB}) endif(${BUILD_SHARED_LIBS})
add_dependencies(discord-rpc clangformat) if (CLANG_FORMAT_CMD)
add_dependencies(discord-rpc clangformat)
endif(CLANG_FORMAT_CMD)
# install # install
@ -97,16 +116,13 @@ install(
TARGETS discord-rpc TARGETS discord-rpc
EXPORT "discord-rpc" EXPORT "discord-rpc"
RUNTIME RUNTIME
DESTINATION "bin" DESTINATION "${CMAKE_INSTALL_BINDIR}"
CONFIGURATIONS Release
LIBRARY LIBRARY
DESTINATION "lib" DESTINATION "${CMAKE_INSTALL_LIBDIR}"
CONFIGURATIONS Release
ARCHIVE ARCHIVE
DESTINATION "lib" DESTINATION "${CMAKE_INSTALL_LIBDIR}"
CONFIGURATIONS Release
INCLUDES INCLUDES
DESTINATION "include" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
) )
install( install(

View File

@ -1,8 +1,8 @@
#pragma once #pragma once
#include <stdint.h>
#include <algorithm> #include <algorithm>
#include <random> #include <random>
#include <stdint.h>
#include <time.h> #include <time.h>
struct Backoff { struct Backoff {

View File

@ -4,8 +4,8 @@
#define NOMCX #define NOMCX
#define NOSERVICE #define NOSERVICE
#define NOIME #define NOIME
#include <windows.h>
#include <assert.h> #include <assert.h>
#include <windows.h>
int GetProcessId() int GetProcessId()
{ {

View File

@ -2,9 +2,9 @@
#include "backoff.h" #include "backoff.h"
#include "discord_register.h" #include "discord_register.h"
#include "msg_queue.h"
#include "rpc_connection.h" #include "rpc_connection.h"
#include "serialization.h" #include "serialization.h"
#include "msg_queue.h"
#include <atomic> #include <atomic>
#include <chrono> #include <chrono>
@ -32,6 +32,20 @@ struct QueuedMessage {
} }
}; };
struct JoinRequest {
// snowflake (64bit int), turned into a ascii decimal string, at most 20 chars +1 null
// terminator = 21
char userId[32];
// 32 unicode glyphs is max name size => 4 bytes per glyph in the worst case, +1 for null
// terminator = 129
char username[344];
// 4 decimal digits + 1 null terminator = 5
char discriminator[8];
// optional 'a_' + md5 hex digest (32 bytes) + null terminator = 35
char avatar[128];
// Rounded way up because I'm paranoid about games breaking from future changes in these sizes
};
static RpcConnection* Connection{nullptr}; static RpcConnection* Connection{nullptr};
static DiscordEventHandlers Handlers{}; static DiscordEventHandlers Handlers{};
static std::atomic_bool WasJustConnected{false}; static std::atomic_bool WasJustConnected{false};
@ -48,21 +62,60 @@ static char LastDisconnectErrorMessage[256];
static std::mutex PresenceMutex; static std::mutex PresenceMutex;
static QueuedMessage QueuedPresence{}; static QueuedMessage QueuedPresence{};
static MsgQueue<QueuedMessage, MessageQueueSize> SendQueue; static MsgQueue<QueuedMessage, MessageQueueSize> SendQueue;
static MsgQueue<DiscordJoinRequest, JoinQueueSize> JoinAskQueue; static MsgQueue<JoinRequest, JoinQueueSize> JoinAskQueue;
// We want to auto connect, and retry on failure, but not as fast as possible. This does expoential // We want to auto connect, and retry on failure, but not as fast as possible. This does expoential
// backoff from 0.5 seconds to 1 minute // backoff from 0.5 seconds to 1 minute
static Backoff ReconnectTimeMs(500, 60 * 1000); static Backoff ReconnectTimeMs(500, 60 * 1000);
static auto NextConnect{std::chrono::system_clock::now()}; static auto NextConnect = std::chrono::system_clock::now();
static int Pid{0}; static int Pid{0};
static int Nonce{1}; static int Nonce{1};
#ifndef DISCORD_DISABLE_IO_THREAD #ifndef DISCORD_DISABLE_IO_THREAD
static std::atomic_bool KeepRunning{true}; static void Discord_UpdateConnection(void);
static std::mutex WaitForIOMutex; class IoThreadHolder {
static std::condition_variable WaitForIOActivity; private:
static std::thread IoThread; std::atomic_bool keepRunning{true};
std::mutex waitForIOMutex;
std::condition_variable waitForIOActivity;
std::thread ioThread;
public:
void Start()
{
keepRunning.store(true);
ioThread = std::thread([&]() {
const std::chrono::duration<int64_t, std::milli> maxWait{500LL};
while (keepRunning.load()) {
Discord_UpdateConnection();
std::unique_lock<std::mutex> lock(waitForIOMutex);
waitForIOActivity.wait_for(lock, maxWait);
}
});
}
void Notify() { waitForIOActivity.notify_all(); }
void Stop()
{
keepRunning.exchange(false);
Notify();
if (ioThread.joinable()) {
ioThread.join();
}
}
~IoThreadHolder() { Stop(); }
};
#else
class IoThreadHolder {
public:
void Start() {}
void Stop() {}
void Notify() {}
};
#endif // DISCORD_DISABLE_IO_THREAD #endif // DISCORD_DISABLE_IO_THREAD
static IoThreadHolder IoThread;
static void UpdateReconnectTime() static void UpdateReconnectTime()
{ {
@ -71,7 +124,7 @@ static void UpdateReconnectTime()
} }
#ifdef DISCORD_DISABLE_IO_THREAD #ifdef DISCORD_DISABLE_IO_THREAD
DISCORD_EXPORT void Discord_UpdateConnection(void) extern "C" DISCORD_EXPORT void Discord_UpdateConnection(void)
#else #else
static void Discord_UpdateConnection(void) static void Discord_UpdateConnection(void)
#endif #endif
@ -115,8 +168,9 @@ static void Discord_UpdateConnection(void)
continue; continue;
} }
if (strcmp(evtName, "ACTIVITY_JOIN") == 0) {
auto data = GetObjMember(&message, "data"); auto data = GetObjMember(&message, "data");
if (strcmp(evtName, "ACTIVITY_JOIN") == 0) {
auto secret = GetStrMember(data, "secret"); auto secret = GetStrMember(data, "secret");
if (secret) { if (secret) {
StringCopy(JoinGameSecret, secret); StringCopy(JoinGameSecret, secret);
@ -124,7 +178,6 @@ static void Discord_UpdateConnection(void)
} }
} }
else if (strcmp(evtName, "ACTIVITY_SPECTATE") == 0) { else if (strcmp(evtName, "ACTIVITY_SPECTATE") == 0) {
auto data = GetObjMember(&message, "data");
auto secret = GetStrMember(data, "secret"); auto secret = GetStrMember(data, "secret");
if (secret) { if (secret) {
StringCopy(SpectateGameSecret, secret); StringCopy(SpectateGameSecret, secret);
@ -132,20 +185,23 @@ static void Discord_UpdateConnection(void)
} }
} }
else if (strcmp(evtName, "ACTIVITY_JOIN_REQUEST") == 0) { else if (strcmp(evtName, "ACTIVITY_JOIN_REQUEST") == 0) {
auto data = GetObjMember(&message, "data");
auto user = GetObjMember(data, "user"); auto user = GetObjMember(data, "user");
auto userId = GetStrMember(user, "id"); auto userId = GetStrMember(user, "id");
auto username = GetStrMember(user, "username"); auto username = GetStrMember(user, "username");
auto avatarUrl = GetStrMember(user, "avatar"); auto avatar = GetStrMember(user, "avatar");
auto joinReq = JoinAskQueue.GetNextAddMessage(); auto joinReq = JoinAskQueue.GetNextAddMessage();
if (userId && username && joinReq) { if (userId && username && joinReq) {
StringCopy(joinReq->userId, userId); StringCopy(joinReq->userId, userId);
StringCopy(joinReq->username, username); StringCopy(joinReq->username, username);
if (avatarUrl) { auto discriminator = GetStrMember(user, "discriminator");
StringCopy(joinReq->avatarUrl, avatarUrl); if (discriminator) {
StringCopy(joinReq->discriminator, discriminator);
}
if (avatar) {
StringCopy(joinReq->avatar, avatar);
} }
else { else {
joinReq->avatarUrl[0] = 0; joinReq->avatar[0] = 0;
} }
JoinAskQueue.CommitAdd(); JoinAskQueue.CommitAdd();
} }
@ -176,25 +232,9 @@ static void Discord_UpdateConnection(void)
} }
} }
#ifndef DISCORD_DISABLE_IO_THREAD
static void DiscordRpcIo(void)
{
const std::chrono::duration<int64_t, std::milli> maxWait{500LL};
while (KeepRunning.load()) {
Discord_UpdateConnection();
std::unique_lock<std::mutex> lock(WaitForIOMutex);
WaitForIOActivity.wait_for(lock, maxWait);
}
}
#endif
static void SignalIOActivity() static void SignalIOActivity()
{ {
#ifndef DISCORD_DISABLE_IO_THREAD IoThread.Notify();
WaitForIOActivity.notify_all();
#endif
} }
static bool RegisterForEvent(const char* evtName) static bool RegisterForEvent(const char* evtName)
@ -210,7 +250,7 @@ static bool RegisterForEvent(const char* evtName)
return false; return false;
} }
DISCORD_EXPORT void Discord_Initialize(const char* applicationId, extern "C" DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
DiscordEventHandlers* handlers, DiscordEventHandlers* handlers,
int autoRegister, int autoRegister,
const char* optionalSteamId) const char* optionalSteamId)
@ -261,13 +301,10 @@ DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
UpdateReconnectTime(); UpdateReconnectTime();
}; };
#ifndef DISCORD_DISABLE_IO_THREAD IoThread.Start();
KeepRunning.store(true);
IoThread = std::thread(DiscordRpcIo);
#endif
} }
DISCORD_EXPORT void Discord_Shutdown() extern "C" DISCORD_EXPORT void Discord_Shutdown()
{ {
if (!Connection) { if (!Connection) {
return; return;
@ -275,17 +312,11 @@ DISCORD_EXPORT void Discord_Shutdown()
Connection->onConnect = nullptr; Connection->onConnect = nullptr;
Connection->onDisconnect = nullptr; Connection->onDisconnect = nullptr;
Handlers = {}; Handlers = {};
#ifndef DISCORD_DISABLE_IO_THREAD IoThread.Stop();
KeepRunning.exchange(false);
SignalIOActivity();
if (IoThread.joinable()) {
IoThread.join();
}
#endif
RpcConnection::Destroy(Connection); RpcConnection::Destroy(Connection);
} }
DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence) extern "C" DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence)
{ {
PresenceMutex.lock(); PresenceMutex.lock();
QueuedPresence.length = JsonWriteRichPresenceObj( QueuedPresence.length = JsonWriteRichPresenceObj(
@ -294,7 +325,7 @@ DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence)
SignalIOActivity(); SignalIOActivity();
} }
DISCORD_EXPORT void Discord_Respond(const char* userId, /* DISCORD_REPLY_ */ int reply) extern "C" DISCORD_EXPORT void Discord_Respond(const char* userId, /* DISCORD_REPLY_ */ int reply)
{ {
// if we are not connected, let's not batch up stale messages for later // if we are not connected, let's not batch up stale messages for later
if (!Connection || !Connection->IsOpen()) { if (!Connection || !Connection->IsOpen()) {
@ -309,7 +340,7 @@ DISCORD_EXPORT void Discord_Respond(const char* userId, /* DISCORD_REPLY_ */ int
} }
} }
DISCORD_EXPORT void Discord_RunCallbacks() extern "C" DISCORD_EXPORT void Discord_RunCallbacks()
{ {
// Note on some weirdness: internally we might connect, get other signals, disconnect any number // Note on some weirdness: internally we might connect, get other signals, disconnect any number
// of times inbetween calls here. Externally, we want the sequence to seem sane, so any other // of times inbetween calls here. Externally, we want the sequence to seem sane, so any other
@ -353,7 +384,8 @@ DISCORD_EXPORT void Discord_RunCallbacks()
while (JoinAskQueue.HavePendingSends()) { while (JoinAskQueue.HavePendingSends()) {
auto req = JoinAskQueue.GetNextSendMessage(); auto req = JoinAskQueue.GetNextSendMessage();
if (Handlers.joinRequest) { if (Handlers.joinRequest) {
Handlers.joinRequest(req); DiscordJoinRequest djr{req->userId, req->username, req->discriminator, req->avatar};
Handlers.joinRequest(&djr);
} }
JoinAskQueue.CommitSend(); JoinAskQueue.CommitSend();
} }

View File

@ -85,12 +85,14 @@ extern "C" void Discord_Register(const char* applicationId, const char* command)
"xdg-mime default discord-%s.desktop x-scheme-handler/discord-%s", "xdg-mime default discord-%s.desktop x-scheme-handler/discord-%s",
applicationId, applicationId,
applicationId); applicationId);
system(xdgMimeCommand); if (system(xdgMimeCommand) < 0) {
fprintf(stderr, "Failed to register mime handler\n");
}
} }
extern "C" void Discord_RegisterSteamGame(const char* applicationId, const char* steamId) extern "C" void Discord_RegisterSteamGame(const char* applicationId, const char* steamId)
{ {
char command[256]; char command[256];
sprintf(command, "xdg-open steam://run/%s", steamId); sprintf(command, "xdg-open steam://rungameid/%s", steamId);
Discord_Register(applicationId, command); Discord_Register(applicationId, command);
} }

View File

@ -92,6 +92,6 @@ void Discord_Register(const char* applicationId, const char* command)
void Discord_RegisterSteamGame(const char* applicationId, const char* steamId) void Discord_RegisterSteamGame(const char* applicationId, const char* steamId)
{ {
char command[256]; char command[256];
sprintf(command, "steam://run/%s", steamId); sprintf(command, "steam://rungameid/%s", steamId);
Discord_Register(applicationId, command); Discord_Register(applicationId, command);
} }

View File

@ -1,3 +1,15 @@
/*
* MinGW defaults to WINNT 5.1 (aka XP), however some of functions used here
* require WINNT >= 6.0 APIs, which are only visible when WINVER and
* _WIN32_WINNT defines are set properly before including any system headers.
* Such API is e.g. RegSetKeyValueW.
*/
#ifdef __MINGW32__
// 0x0600 == vista
#define WINVER 0x0600
#define _WIN32_WINNT 0x0600
#endif // __MINGW32__
#include "discord-rpc.h" #include "discord-rpc.h"
#include <stdio.h> #include <stdio.h>
@ -6,9 +18,8 @@
#define NOSERVICE #define NOSERVICE
#define NOIME #define NOIME
#include <windows.h> #include <windows.h>
#include <Psapi.h> #include <psapi.h>
#include <Strsafe.h> #include <strsafe.h>
#pragma comment(lib, "Psapi.lib")
void Discord_RegisterW(const wchar_t* applicationId, const wchar_t* command) void Discord_RegisterW(const wchar_t* applicationId, const wchar_t* command)
{ {
@ -17,14 +28,14 @@ void Discord_RegisterW(const wchar_t* applicationId, const wchar_t* command)
// Update the HKEY_CURRENT_USER, because it doesn't seem to require special permissions. // Update the HKEY_CURRENT_USER, because it doesn't seem to require special permissions.
wchar_t exeFilePath[MAX_PATH]; wchar_t exeFilePath[MAX_PATH];
DWORD exeLen = GetModuleFileNameExW(GetCurrentProcess(), nullptr, exeFilePath, MAX_PATH); DWORD exeLen = GetModuleFileNameW(nullptr, exeFilePath, MAX_PATH);
wchar_t openCommand[1024]; wchar_t openCommand[1024];
if (command && command[0]) { if (command && command[0]) {
StringCbPrintfW(openCommand, sizeof(openCommand), L"%s", command); StringCbPrintfW(openCommand, sizeof(openCommand), L"%s", command);
} }
else { else {
lstrcpyW(openCommand, exeFilePath); StringCbCopyW(openCommand, sizeof(openCommand), exeFilePath);
} }
wchar_t protocolName[64]; wchar_t protocolName[64];
@ -121,7 +132,7 @@ extern "C" void Discord_RegisterSteamGame(const char* applicationId, const char*
} }
wchar_t command[1024]; wchar_t command[1024];
StringCbPrintfW(command, sizeof(command), L"\"%s\" steam://run/%s", steamPath, wSteamId); StringCbPrintfW(command, sizeof(command), L"\"%s\" steam://rungameid/%s", steamPath, wSteamId);
Discord_RegisterW(appId, command); Discord_RegisterW(appId, command);
} }

View File

@ -11,8 +11,8 @@
#pragma warning(disable : 6313) // Incorrect operator #pragma warning(disable : 6313) // Incorrect operator
#include "rapidjson/document.h" #include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h" #include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
#pragma warning(pop) #pragma warning(pop)
@ -79,6 +79,9 @@ public:
} }
// allocate how much you need in the first place // allocate how much you need in the first place
assert(!originalPtr && !originalSize); assert(!originalPtr && !originalSize);
// unused parameter warning
(void)(originalPtr);
(void)(originalSize);
return Malloc(newSize); return Malloc(newSize);
} }
static void Free(void* ptr) static void Free(void* ptr)