Merge branch 'main' into android

This commit is contained in:
SSimco
2025-08-30 21:19:07 +03:00
221 changed files with 2363 additions and 9172 deletions
+8 -3
View File
@@ -95,8 +95,8 @@ jobs:
- uses: actions/download-artifact@v4
with:
name: cemu-bin-macos-x64
path: cemu-bin-macos-x64
pattern: cemu-bin-macos*
path: cemu-macos
- name: Initialize
run: |
@@ -137,7 +137,12 @@ jobs:
rm -r ./${{ env.CEMU_FOLDER_NAME }}
- name: Create release from macos-bin
run: cp cemu-bin-macos-x64/Cemu.dmg upload/cemu-${{ env.CEMU_VERSION }}-macos-12-x64.dmg
run: |
cd cemu-macos
for bin_dir in cemu-bin-macos-*; do
arch="${bin_dir##cemu-bin-macos-}"
cp $bin_dir/Cemu.dmg ../upload/cemu-${{ env.CEMU_VERSION }}-macos-12-$arch.dmg
done
- name: Create release from android-bin
run: cp cemu-bin-android/*.apk upload/cemu-${{ env.CEMU_VERSION }}.apk
+30
View File
@@ -21,6 +21,9 @@
- [Installing Library Dependencies](#installing-library-dependencies)
- [Build Cemu using CMake](#build-cemu-using-cmake)
- [Android](#android)
- [FreeBSD](#freebsd)
- [Installing Dependencies](#installing-dependencies)
- [Build Cemu on BSD with CMake](#build-cemu-on-bsd-with-cmake)
- [Updating Cemu and source code](#updating-cemu-and-source-code)
## Windows
@@ -196,6 +199,33 @@ Instructions:
2. Open the project located in `src/android` in Android Studio
3. Click Build > Make Project
## FreeBSD
The following instructions to build Cemu on FreeBSD are experimental. Some features available on other platforms are not available on FreeBSD (discord rich presence, bluetooth/support for actual Wii U controllers, auto-updates, etc.)
To compile Cemu, a recent enough compiler and STL with C++20 support is required! Clang-15 or higher is what we recommend. Any version of FreeBSD 13.3-RELEASE or higher comes bundled with LLVM > version 15 as part of the base system. However, if for whatever reason your system lacks a recent version of LLVM you can install one by executing:
`sudo pkg install llvm15`
Or a higher version as desired.
### Installing Dependencies
`sudo pkg install boost-libs cmake-core curl glslang gtk3 libzip ninja png pkgconf pugixml rapidjson sdl2 wayland wayland-protocols wx32-gtk3 xorg zstd`
### Build Cemu on BSD with CMake
```
git clone --recursive https://github.com/cemu-project/Cemu
cd Cemu
cmake -B build -DCMAKE_BUILD_TYPE=release -DENABLE_BLUEZ=OFF -DENABLE_DISCORD_RPC=OFF -DENABLE_FERAL_GAMEMODE=OFF -DENABLE_HIDAPI=OFF -DENABLE_VCPKG=OFF -G Ninja
cmake --build build
cd build && ninja install
```
You should now have a Cemu executable file in the /bin folder, which you can run using `./bin/Cemu_release`.
## Updating Cemu and source code
1. To update your Cemu local repository, use the command `git pull --recurse-submodules` (run this command on the Cemu root).
- This should update your local copy of Cemu and all of its dependencies.
+6 -8
View File
@@ -38,11 +38,11 @@ if (ENABLE_VCPKG)
if(UNIX AND NOT APPLE AND NOT VCPKG_TARGET_ANDROID)
set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports_linux")
set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports_linux;${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports")
elseif(APPLE)
set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports_mac")
set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports_mac;${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports")
else()
set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports")
set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports_win;${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports")
endif()
if(VCPKG_TARGET_ANDROID)
set(ENV{ANDROID_NDK_HOME} ${ANDROID_NDK})
@@ -172,7 +172,7 @@ if (UNIX AND NOT APPLE AND NOT ANDROID)
BASENAME viewporter)
add_library(CemuWaylandProtocols STATIC ${WAYLAND_PROTOCOL_SRCS})
target_include_directories(CemuWaylandProtocols PUBLIC "${CMAKE_CURRENT_BINARY_DIR}")
target_include_directories(CemuWaylandProtocols PRIVATE ${Wayland_INCLUDE_DIRS})
add_compile_definitions(HAS_WAYLAND)
endif()
find_package(GTK3 REQUIRED)
@@ -195,8 +195,6 @@ endif()
if (ENABLE_DISCORD_RPC)
add_compile_definitions(ENABLE_DISCORD_RPC)
add_subdirectory(dependencies/discord-rpc EXCLUDE_FROM_ALL)
target_include_directories(discord-rpc INTERFACE ./dependencies/discord-rpc/include)
endif()
if (ENABLE_HIDAPI)
@@ -214,7 +212,7 @@ if(UNIX AND NOT APPLE AND NOT ANDROID)
endif()
if (ENABLE_WXWIDGETS)
find_package(wxWidgets 3.2 REQUIRED COMPONENTS base core gl propgrid xrc)
find_package(wxWidgets 3.3 REQUIRED COMPONENTS base core gl propgrid xrc)
endif()
if (ENABLE_CUBEB)
@@ -257,4 +255,4 @@ if(ANDROID)
add_subdirectory("dependencies/stb" EXCLUDE_FROM_ALL)
endif()
add_subdirectory(src)
add_subdirectory(src)
-92
View File
@@ -1,92 +0,0 @@
---
AccessModifierOffset: -4
AlignAfterOpenBracket: true
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: false
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: InlineOnly
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: false
BinPackParameters: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Stroustrup
BreakBeforeInheritanceComma: true
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeComma
BreakStringLiterals: true
ColumnLimit: 100
CommentPragmas: ''
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 2
ContinuationIndentWidth: 2
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
FixNamespaceComments: true
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
IndentWidth: 4
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
PenaltyBreakAssignment: 0
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 9999999
PointerAlignment: Left
ReflowComments: true
SortIncludes: false
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: true
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 4
UseTab: Never
---
Language: Cpp
---
Language: ObjC
ObjCBlockIndentWidth: 4
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: false
---
Language: Java
BasedOnStyle: Google
BreakAfterJavaFieldAnnotations: true
...
-5
View File
@@ -1,5 +0,0 @@
/build*/
/.vscode/
/thirdparty/
.vs/
.DS_Store
-47
View File
@@ -1,47 +0,0 @@
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 -DWARNINGS_AS_ERRORS=On --config Release ..
- cmake --build . -- -j2
-42
View File
@@ -1,42 +0,0 @@
cmake_minimum_required (VERSION 3.2.0)
project (DiscordRPC)
include(GNUInstallDirs)
option(BUILD_EXAMPLES "Build example apps" OFF)
# format
file(GLOB_RECURSE ALL_SOURCE_FILES
examples/*.cpp examples/*.h examples/*.c
include/*.h
src/*.cpp src/*.h src/*.c
)
# thirdparty stuff
execute_process(
COMMAND mkdir ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty
ERROR_QUIET
)
#find_file(RAPIDJSONTEST NAMES rapidjson rapidjson-1.1.0 PATHS ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty CMAKE_FIND_ROOT_PATH_BOTH)
#if (NOT RAPIDJSONTEST)
# message("no rapidjson, download")
# set(RJ_TAR_FILE ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/v1.1.0.tar.gz)
# file(DOWNLOAD https://github.com/miloyip/rapidjson/archive/v1.1.0.tar.gz ${RJ_TAR_FILE})
# execute_process(
# COMMAND ${CMAKE_COMMAND} -E tar xzf ${RJ_TAR_FILE}
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty
# )
# file(REMOVE ${RJ_TAR_FILE})
#endif(NOT RAPIDJSONTEST)
#find_file(RAPIDJSON NAMES rapidjson rapidjson-1.1.0 PATHS ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty CMAKE_FIND_ROOT_PATH_BOTH)
#add_library(rapidjson STATIC IMPORTED ${RAPIDJSON})
# add subdirs
add_subdirectory(src)
if (BUILD_EXAMPLES)
add_subdirectory(examples/send-presence)
endif(BUILD_EXAMPLES)
-19
View File
@@ -1,19 +0,0 @@
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.
-158
View File
@@ -1,158 +0,0 @@
# Discord RPC
## Deprecation Notice
This library has been deprecated in favor of Discord's GameSDK. [Learn more here](https://discordapp.com/developers/docs/game-sdk/sdk-starter-guide)
---
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.
Included here are some quick demos that implement the very minimal subset to show current status, and
have callbacks for where a more complete game would do more things (joining, spectating, etc).
## Documentation
The most up to date documentation for Rich Presence can always be found on our [developer site](https://discordapp.com/developers/docs/rich-presence/how-to)! If you're interested in rolling your own native implementation of Rich Presence via IPC sockets instead of using our SDK—hey, you've got free time, right?—check out the ["Hard Mode" documentation](https://github.com/discordapp/discord-rpc/blob/master/documentation/hard-mode.md).
## Basic Usage
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) and make yourself an app. Keep track of `Client ID` -- you'll need it here to pass to the init function.
### Unreal Engine 4 Setup
To use the Rich Presense plugin with Unreal Engine Projects:
1. Download the latest [release](https://github.com/discordapp/discord-rpc/releases) for each operating system you are targeting and the zipped source code
2. In the source code zip, copy the UE plugin—`examples/unrealstatus/Plugins/discordrpc`—to your project's plugin directory
3. At `[YOUR_UE_PROJECT]/Plugins/discordrpc/source/ThirdParty/DiscordRpcLibrary/`, create an `Include` folder and copy `discord_rpc.h` and `discord_register.h` to it from the zip
4. Follow the steps below for each OS
5. Build your UE4 project
6. Launch the editor, and enable the Discord plugin.
#### Windows
- At `[YOUR_UE_PROJECT]/Plugins/discordrpc/source/ThirdParty/DiscordRpcLibrary/`, create a `Win64` folder
- Copy `lib/discord-rpc.lib` and `bin/discord-rpc.dll` from `[RELEASE_ZIP]/win64-dynamic` to the `Win64` folder
#### Mac
- At `[YOUR_UE_PROJECT]/Plugins/discordrpc/source/ThirdParty/DiscordRpcLibrary/`, create a `Mac` folder
- Copy `libdiscord-rpc.dylib` from `[RELEASE_ZIP]/osx-dynamic/lib` to the `Mac` folder
#### Linux
- At `[YOUR_UE_PROJECT]/Plugins/discordrpc/source/ThirdParty/DiscordRpcLibrary/`, create a `Linux` folder
- Inside, create another folder `x86_64-unknown-linux-gnu`
- Copy `libdiscord-rpc.so` from `[RELEASE_ZIP]/linux-dynamic/lib` to `Linux/x86_64-unknown-linux-gnu`
### Unity Setup
If you're a Unity developer looking to integrate Rich Presence into your game, follow this simple guide to get started towards success:
1. Download the DLLs for any platform that you need from [our releases](https://github.com/discordapp/discord-rpc/releases)
2. In your Unity project, create a `Plugins` folder inside your `Assets` folder if you don't already have one
3. Copy the file `DiscordRpc.cs` from [here](https://github.com/discordapp/discord-rpc/blob/master/examples/button-clicker/Assets/DiscordRpc.cs) into your `Assets` folder. This is basically your header file for the SDK
We've got our `Plugins` folder ready, so let's get platform-specific!
#### Windows
4. Create `x86` and `x86_64` folders inside `Assets/Plugins/`
5. Copy `discord-rpc-win/win64-dynamic/bin/discord-rpc.dll` to `Assets/Plugins/x86_64/`
6. Copy `discord-rpc-win/win32-dynamic/bin/discord-rpc.dll` to `Assets/Plugins/x86/`
7. Click on both DLLs and make sure they are targetting the correct architectures in the Unity editor properties pane
8. Done!
#### MacOS
4. Copy `discord-rpc-osx/osx-dynamic/lib/libdiscord-rpc.dylib` to `Assets/Plugins/`
5. Rename `libdiscord-rpc.dylib` to `discord-rpc.bundle`
6. Done!
#### Linux
4. Copy `discord-rpc-linux/linux-dynamic-lib/libdiscord-rpc.so` to `Assets/Plugins/`
5. Done!
You're ready to roll! For code examples on how to interact with the SDK using the `DiscordRpc.cs` header file, check out [our example](https://github.com/discordapp/discord-rpc/blob/master/examples/button-clicker/Assets/DiscordController.cs)
### From package
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.
### From repo
First-eth, you'll want `CMake`. There's a few different ways to install it on your system, and you should refer to [their website](https://cmake.org/install/). Many package managers provide ways of installing CMake as well.
To make sure it's installed correctly, type `cmake --version` into your flavor of terminal/cmd. If you get a response with a version number, you're good to go!
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:
```sh
cd <path to discord-rpc>
mkdir build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX=<path to install discord-rpc to>
cmake --build . --config Release --target install
```
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. It does depend on `click` library, so do a quick `pip install click` to make sure you have it if you want to run `build.py`.
There are some CMake options you might care about:
| 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. |
| `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 |
| `WARNINGS_AS_ERRORS` | `OFF` | When enabled, compiles with `-Werror` (on \*nix platforms). |
## 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
This is a text adventure "game" that inits/deinits the connection to Discord, and sends a presence update on each command.
## Sample: button-clicker
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. Run `python build.py unity` in the root directory to build the correct library files and place them in their respective folders.
## Sample: unrealstatus
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. Run `python build.py unreal` in the root directory to build the correct library files and place them in their respective folders.
## 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 |
| ------------------------------------------------------------------------- | --------------------------------- |
| [Discord RPC C#](https://github.com/Lachee/discord-rpc-csharp) | C# |
| [Discord RPC D](https://github.com/voidblaster/discord-rpc-d) | [D](https://dlang.org/) |
| [discord-rpc.jar](https://github.com/Vatuu/discord-rpc 'Discord-RPC.jar') | Java |
| [java-discord-rpc](https://github.com/MinnDevelopment/java-discord-rpc) | Java |
| [Discord-IPC](https://github.com/jagrosh/DiscordIPC) | Java |
| [Discord Rich Presence](https://npmjs.org/discord-rich-presence) | JavaScript |
| [drpc4k](https://github.com/Bluexin/drpc4k) | [Kotlin](https://kotlinlang.org/) |
| [lua-discordRPC](https://github.com/pfirsich/lua-discordRPC) | LuaJIT (FFI) |
| [pypresence](https://github.com/qwertyquerty/pypresence) | [Python](https://python.org/) |
| [SwordRPC](https://github.com/Azoy/SwordRPC) | [Swift](https://swift.org) |
-17
View File
@@ -1,17 +0,0 @@
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
-304
View File
@@ -1,304 +0,0 @@
#!/usr/bin/env python
import os
import subprocess
import sys
import shutil
import zipfile
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__))
# 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 = 'c:\\Program Files (x86)\\Windows Kits\\10' # os.environ['WindowsSdkDir']
return os.path.join(sdk_dir, 'bin', 'x86', 'signtool.exe')
elif PLATFORM == 'osx':
return '/usr/bin/codesign'
@contextmanager
def cd(new_dir):
""" Temporarily change current directory """
if new_dir:
old_dir = os.getcwd()
os.chdir(new_dir)
yield
if new_dir:
os.chdir(old_dir)
def mkdir_p(path):
""" mkdir -p """
if not os.path.isdir(path):
click.secho('Making ' + path, fg='yellow')
os.makedirs(path)
@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()
@click.pass_context
def 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)
BUILDS = []
click.echo('--- Copying libs and header into unity example')
UNITY_PROJECT_PATH = os.path.join(SCRIPT_PATH, 'examples', 'button-clicker', 'Assets', 'Plugins')
if sys.platform.startswith('win'):
LIBRARY_NAME = 'discord-rpc.dll'
BUILD_64_BASE_PATH = os.path.join(SCRIPT_PATH, 'builds', 'win64-dynamic', 'src', 'Release')
UNITY_64_DLL_PATH = os.path.join(UNITY_PROJECT_PATH, 'x86_64')
BUILDS.append({BUILD_64_BASE_PATH: UNITY_64_DLL_PATH})
BUILD_32_BASE_PATH = os.path.join(SCRIPT_PATH, 'builds', 'win32-dynamic', 'src', 'Release')
UNITY_32_DLL_PATH = os.path.join(UNITY_PROJECT_PATH, 'x86')
BUILDS.append({BUILD_32_BASE_PATH: UNITY_32_DLL_PATH})
elif sys.platform == 'darwin':
LIBRARY_NAME = 'discord-rpc.bundle'
BUILD_BASE_PATH = os.path.join(SCRIPT_PATH, 'builds', 'osx-dynamic', 'src')
UNITY_DLL_PATH = UNITY_PROJECT_PATH
os.rename(
os.path.join(BUILD_BASE_PATH, 'libdiscord-rpc.dylib'), os.path.join(BUILD_BASE_PATH, 'discord-rpc.bundle'))
BUILDS.append({BUILD_BASE_PATH: UNITY_DLL_PATH})
elif sys.platform.startswith('linux'):
LIBRARY_NAME = 'discord-rpc.so'
BUILD_BASE_PATH = os.path.join(SCRIPT_PATH, 'builds', 'linux-dynamic', 'src')
UNITY_DLL_PATH = os.path.join(UNITY_PROJECT_PATH, 'x86')
os.rename(os.path.join(BUILD_BASE_PATH, 'libdiscord-rpc.so'), os.path.join(BUILD_BASE_PATH, 'discord-rpc.so'))
BUILDS.append({BUILD_BASE_PATH: UNITY_DLL_PATH})
else:
raise Exception('Unsupported platform ' + sys.platform)
for build in BUILDS:
for i in build:
mkdir_p(build[i])
shutil.copy(os.path.join(i, LIBRARY_NAME), build[i])
@cli.command()
@click.pass_context
def unreal(ctx):
""" build libs and copy them into the unreal project """
ctx.invoke(libs, clean=False, static=False, shared=True, skip_formatter=True, just_release=True)
BUILDS = []
click.echo('--- Copying libs and header into unreal example')
UNREAL_PROJECT_PATH = os.path.join(SCRIPT_PATH, 'examples', 'unrealstatus', 'Plugins', 'discordrpc')
UNREAL_INCLUDE_PATH = os.path.join(UNREAL_PROJECT_PATH, 'Source', 'ThirdParty', 'DiscordRpcLibrary', 'Include')
mkdir_p(UNREAL_INCLUDE_PATH)
shutil.copy(os.path.join(SCRIPT_PATH, 'include', 'discord_rpc.h'), UNREAL_INCLUDE_PATH)
if sys.platform.startswith('win'):
LIBRARY_NAME = 'discord-rpc.lib'
BUILD_64_BASE_PATH = os.path.join(SCRIPT_PATH, 'builds', 'win64-dynamic', 'src', 'Release')
UNREAL_64_DLL_PATH = os.path.join(UNREAL_PROJECT_PATH, 'Source', 'ThirdParty', 'DiscordRpcLibrary', 'Win64')
BUILDS.append({BUILD_64_BASE_PATH: UNREAL_64_DLL_PATH})
BUILD_32_BASE_PATH = os.path.join(SCRIPT_PATH, 'builds', 'win32-dynamic', 'src', 'Release')
UNREAL_32_DLL_PATH = os.path.join(UNREAL_PROJECT_PATH, 'Source', 'ThirdParty', 'DiscordRpcLibrary', 'Win32')
BUILDS.append({BUILD_32_BASE_PATH: UNREAL_32_DLL_PATH})
elif sys.platform == 'darwin':
LIBRARY_NAME = 'libdiscord-rpc.dylib'
BUILD_BASE_PATH = os.path.join(SCRIPT_PATH, 'builds', 'osx-dynamic', 'src')
UNREAL_DLL_PATH = os.path.join(UNREAL_PROJECT_PATH, 'Source', 'ThirdParty', 'DiscordRpcLibrary', 'Mac')
BUILDS.append({BUILD_BASE_PATH: UNREAL_DLL_PATH})
elif sys.platform.startswith('linux'):
LIBRARY_NAME = 'libdiscord-rpc.so'
BUILD_BASE_PATH = os.path.join(SCRIPT_PATH, 'builds', 'linux-dynamic', 'src')
UNREAL_DLL_PATH = os.path.join(UNREAL_PROJECT_PATH, 'Source', 'ThirdParty', 'DiscordRpcLibrary', 'Linux')
BUILDS.append({BUILD_BASE_PATH: UNREAL_DLL_PATH})
else:
raise Exception('Unsupported platform ' + sys.platform)
for build in BUILDS:
for i in build:
mkdir_p(build[i])
shutil.copy(os.path.join(i, LIBRARY_NAME), build[i])
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)
install_path = os.path.join(INSTALL_ROOT, build_name)
mkdir_p(build_path)
mkdir_p(install_path)
with cd(build_path):
initial_cmake = ['cmake', SCRIPT_PATH, '-DCMAKE_INSTALL_PREFIX=%s' % os.path.join('..', 'install', build_name)]
if generator:
initial_cmake.extend(['-G', generator])
for key in options:
val = options[key]
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)
if not just_release:
subprocess.check_call(['cmake', '--build', '.', '--config', 'Debug'])
subprocess.check_call(['cmake', '--build', '.', '--config', 'Release', '--target', 'install'])
@cli.command()
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_src_base_path = INSTALL_ROOT
archive_dst_base_path = 'discord-rpc'
with cd(archive_src_base_path):
for path, _, filenames in os.walk('.'):
for fname in filenames:
fpath = os.path.join(path, fname)
dst_path = os.path.normpath(os.path.join(archive_dst_base_path, fpath))
click.echo('Adding ' + dst_path)
archive_file.write(fpath, dst_path)
@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',
'Discord 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('--static', is_flag=True)
@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:
shutil.rmtree('builds', ignore_errors=True)
mkdir_p('builds')
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
static_options['WARNINGS_AS_ERRORS'] = True
dynamic_options['WARNINGS_AS_ERRORS'] = True
if PLATFORM == 'win':
generator32 = 'Visual Studio 14 2015'
generator64 = 'Visual Studio 14 2015 Win64'
if static:
build_lib('win32-static', generator32, static_options, just_release)
build_lib('win64-static', generator64, static_options, just_release)
if shared:
build_lib('win32-dynamic', generator32, dynamic_options, just_release)
build_lib('win64-dynamic', generator64, dynamic_options, just_release)
elif PLATFORM == 'osx':
if static:
build_lib('osx-static', None, static_options, just_release)
if shared:
build_lib('osx-dynamic', None, dynamic_options, just_release)
elif PLATFORM == 'linux':
if static:
build_lib('linux-static', None, static_options, just_release)
if shared:
build_lib('linux-dynamic', None, dynamic_options, just_release)
if __name__ == '__main__':
os.chdir(SCRIPT_PATH)
sys.exit(cli())
-164
View File
@@ -1,164 +0,0 @@
# Hard Mode: Roll Your Own Client
Discord's Rich Presence feature is designed as an obfuscated addition to our existing [RPC infrastructure](https://discordapp.com/developers/docs/topics/rpc). The standalone library and header files make it easy for any dev to drop it into their game.
Our library communicates with Discord over the local Discord RPC socket. We've already done the work in connecting properly, handling disconnects and reconnects, and other RPC intracacies, but those who have done this implementation for our private alpha Voice and Chat SDK can simply make use of the new RPC commands and events to implement Rich Presence.
## Hark! A warning!
By committing to an RPC-only integration, you decide to forego the work our library and header file have done for you in the way of error handling, state storage, disconnecting and reconnecting, and other quality of life abstractions. While simply implementing the new RPC command and events will enable Rich Presence for your game, we highly suggest that you do your best to mimic the functionality of the SDK the most that you can. It ensure not only code quality on your part, but also an excellent experience on the part of your players.
## Application Protocol Registration
One thing that cannot be explicitly done over RPC is registering an application protocol for your game. If you choose to do an RPC-only implementation, you will have to register your application protocol yourself in the format of `discord-[your_app_id]://`. You can use `Discord_Register()` as a good(?) example of how to properly register an application protocol for use with Discord. For OSX and Linux it is probably simpler to handle the protocol registration as part of your install/packaging.
## New RPC Command
The new RPC command for Rich Presence is `SET_ACTIVITY`. The fields are similar to what is outlined in the SDK; we've combined similar fields into objects for the sake of less data on the wire.
The one major difference is the `party.size` field. It is an array with a size of two. The first element is the current party size, `partySize` from the main documentation. The second element is the maximum party size, `partyMax` from the main documentation.
Below is a full example of a `SET_ACTIVITY` command. Field restrictions like size are the same as outlined in the main documentation.
```
{
"cmd": "SET_ACTIVITY",
"args": {
"pid": 9999, // Your application's process id - required field
"activity": {
"state": "In a Group",
"details": "Competitive | In a Match",
"timestamps": {
"start": time(nullptr),
"end": time(nullptr) + ((60 * 5) + 23)
},
"assets": {
"large_image": "numbani_map",
"large_text": "Numbani",
"small_image": "pharah_profile",
"small_text": "Pharah"
},
"party": {
"id": GameEngine.GetPartyId(),
"size": [3, 6]
},
"secrets": {
"join": "025ed05c71f639de8bfaa0d679d7c94b2fdce12f",
"spectate": "e7eb30d2ee025ed05c71ea495f770b76454ee4e0",
"match": "4b2fdce12f639de8bfa7e3591b71a0d679d7c93f"
},
"instance": true
}
},
"nonce": "647d814a-4cf8-4fbb-948f-898abd24f55b"
}
```
## New RPC Events
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
{
"cmd": "DISPATCH",
"data": {
"secret": "025ed05c71f639de8bfaa0d679d7c94b2fdce12f"
},
"evt": "ACTIVITY_JOIN"
}
```
Second is the `ACTIVITY_SPECTATE` event:
```json
{
"cmd": "DISPATCH",
"data": {
"secret": "e7eb30d2ee025ed05c71ea495f770b76454ee4e0"
},
"evt": "ACTIVITY_SPECTATE"
}
```
And third is the `ACTIVITY_JOIN_REQUEST` event:
```json
{
"cmd": "DISPATCH",
"data": {
"user": {
"id": "53908232506183680",
"username": "Mason",
"discriminator": "1337",
"avatar": "a_bab14f271d565501444b2ca3be944b25"
}
},
"evt": "ACTIVITY_JOIN_REQUEST"
}
```
In order to receive these events, you need to [subscribe](https://discordapp.com/developers/docs/topics/rpc#subscribe) to them like so:
```json
{
"nonce": "be9a6de3-31d0-4767-a8e9-4818c5690015",
"evt": "ACTIVITY_JOIN",
"cmd": "SUBSCRIBE"
}
```
```json
{
"nonce": "ae9qdde3-31d0-8989-a8e9-dnakwy174he",
"evt": "ACTIVITY_SPECTATE",
"cmd": "SUBSCRIBE"
}
```
```json
{
"nonce": "5dc0c062-98c6-47a0-8922-bbb52e9d6afa",
"evt": "ACTIVITY_JOIN_REQUEST",
"cmd": "SUBSCRIBE"
}
```
To unsubscribe from these events, resend with the command `UNSUBSCRIBE`
## Responding
A discord user will request access to the game. If the ACTIVITY_JOIN_REQUEST has been subscribed too, the ACTIVITY_JOIN_REQUEST event will be sent to the host's game. Accept it with following model:
```json
{
"nonce": "5dc0c062-98c6-47a0-8922-15aerg126",
"cmd": "SEND_ACTIVITY_JOIN_INVITE",
"args":
{
"user_id": "53908232506183680"
}
}
```
To reject the request, use `CLOSE_ACTIVITY_REQUEST`:
```json
{
"nonce": "5dc0c062-98c6-47a0-8922-dasg256eafg",
"cmd": "CLOSE_ACTIVITY_REQUEST",
"args":
{
"user_id": "53908232506183680"
}
}
```
## Notes
Here are just some quick notes to help with some common troubleshooting problems.
* IPC will echo back every command you send as a response. Use this as a lock-step feature to avoid flooding messages. Can be used to validate messages such as the Presence or Subscribes.
* The pipe expects for frames to be written in a single byte array. You cannot do multiple `stream.Write(opcode);` `stream.Write(length);` as it will break the pipe. Instead create a buffer, write the data to the buffer, then send the entire buffer to the stream.
* Discord can be on any pipe ranging from `discord-ipc-0` to `discord-ipc-9`. It is a good idea to try and connect to each one and keeping the first one you connect too. For multiple clients (eg Discord and Canary), you might want to add a feature to manually select the pipe so you can more easily debug the application.
* All enums are `lower_snake_case`.
* The opcode and length in the header are `Little Endian Unsigned Integers (32bits)`. In some languages, you must convert them as they can be architecture specific.
* [Discord Rich Presence How-To](https://discordapp.com/developers/docs/rich-presence/how-to) contains a lot of the information this document doesn't. For example, it will tell you about the response payload.
* In the documentation, DISCORD_REPLY_IGNORE is just implemented the same as DISCORD_REPLY_NO.
* You can test the Join / Spectate feature by enabling them in your profile and whitelisting a test account. Use Canary to run 2 accounts on the same machine.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

@@ -1,8 +0,0 @@
/Library/
/Temp/
/obj/
/Assets/Plugins/
/Assets/Plugins.meta
*.sln
*.csproj
*.userprefs
@@ -1,125 +0,0 @@
using UnityEngine;
[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.DiscordUser> { }
public class DiscordController : MonoBehaviour
{
public DiscordRpc.RichPresence presence = new DiscordRpc.RichPresence();
public string applicationId;
public string optionalSteamId;
public int clickCounter;
public DiscordRpc.DiscordUser joinRequest;
public UnityEngine.Events.UnityEvent onConnect;
public UnityEngine.Events.UnityEvent onDisconnect;
public UnityEngine.Events.UnityEvent hasResponded;
public DiscordJoinEvent onJoin;
public DiscordJoinEvent onSpectate;
public DiscordJoinRequestEvent onJoinRequest;
DiscordRpc.EventHandlers handlers;
public void OnClick()
{
Debug.Log("Discord: on click!");
clickCounter++;
presence.details = string.Format("Button clicked {0} times", clickCounter);
presence.joinSecret = "aSecret";
presence.partyId = "aPartyId";
presence.partySize = 1;
presence.partyMax = 3;
presence.partyPrivacy = DiscordRpc.PartyPrivacy.Public;
DiscordRpc.UpdatePresence(presence);
}
public void RequestRespondYes()
{
Debug.Log("Discord: responding yes to Ask to Join request");
DiscordRpc.Respond(joinRequest.userId, DiscordRpc.Reply.Yes);
hasResponded.Invoke();
}
public void RequestRespondNo()
{
Debug.Log("Discord: responding no to Ask to Join request");
DiscordRpc.Respond(joinRequest.userId, DiscordRpc.Reply.No);
hasResponded.Invoke();
}
public void ReadyCallback(ref DiscordRpc.DiscordUser connectedUser)
{
Debug.Log(string.Format("Discord: connected to {0}#{1}: {2}", connectedUser.username, connectedUser.discriminator, connectedUser.userId));
onConnect.Invoke();
}
public void DisconnectedCallback(int errorCode, string message)
{
Debug.Log(string.Format("Discord: disconnect {0}: {1}", errorCode, message));
onDisconnect.Invoke();
}
public void ErrorCallback(int errorCode, string message)
{
Debug.Log(string.Format("Discord: error {0}: {1}", errorCode, message));
}
public void JoinCallback(string secret)
{
Debug.Log(string.Format("Discord: join ({0})", secret));
onJoin.Invoke(secret);
}
public void SpectateCallback(string secret)
{
Debug.Log(string.Format("Discord: spectate ({0})", secret));
onSpectate.Invoke(secret);
}
public void RequestCallback(ref DiscordRpc.DiscordUser request)
{
Debug.Log(string.Format("Discord: join request {0}#{1}: {2}", request.username, request.discriminator, request.userId));
joinRequest = request;
onJoinRequest.Invoke(request);
}
void Start()
{
}
void Update()
{
DiscordRpc.RunCallbacks();
}
void OnEnable()
{
Debug.Log("Discord: init");
handlers = new DiscordRpc.EventHandlers();
handlers.readyCallback += ReadyCallback;
handlers.disconnectedCallback += DisconnectedCallback;
handlers.errorCallback += ErrorCallback;
handlers.joinCallback += JoinCallback;
handlers.spectateCallback += SpectateCallback;
handlers.requestCallback += RequestCallback;
DiscordRpc.Initialize(applicationId, ref handlers, true, optionalSteamId);
}
void OnDisable()
{
Debug.Log("Discord: shutdown");
DiscordRpc.Shutdown();
}
void OnDestroy()
{
}
}
@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 27f0a5f59ffffa84c86547736e2e730a
timeCreated: 1501697692
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,231 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using AOT;
public class DiscordRpc
{
[MonoPInvokeCallback(typeof(OnReadyInfo))]
public static void ReadyCallback(ref DiscordUser connectedUser) { Callbacks.readyCallback(ref connectedUser); }
public delegate void OnReadyInfo(ref DiscordUser connectedUser);
[MonoPInvokeCallback(typeof(OnDisconnectedInfo))]
public static void DisconnectedCallback(int errorCode, string message) { Callbacks.disconnectedCallback(errorCode, message); }
public delegate void OnDisconnectedInfo(int errorCode, string message);
[MonoPInvokeCallback(typeof(OnErrorInfo))]
public static void ErrorCallback(int errorCode, string message) { Callbacks.errorCallback(errorCode, message); }
public delegate void OnErrorInfo(int errorCode, string message);
[MonoPInvokeCallback(typeof(OnJoinInfo))]
public static void JoinCallback(string secret) { Callbacks.joinCallback(secret); }
public delegate void OnJoinInfo(string secret);
[MonoPInvokeCallback(typeof(OnSpectateInfo))]
public static void SpectateCallback(string secret) { Callbacks.spectateCallback(secret); }
public delegate void OnSpectateInfo(string secret);
[MonoPInvokeCallback(typeof(OnRequestInfo))]
public static void RequestCallback(ref DiscordUser request) { Callbacks.requestCallback(ref request); }
public delegate void OnRequestInfo(ref DiscordUser request);
static EventHandlers Callbacks { get; set; }
public struct EventHandlers
{
public OnReadyInfo readyCallback;
public OnDisconnectedInfo disconnectedCallback;
public OnErrorInfo errorCallback;
public OnJoinInfo joinCallback;
public OnSpectateInfo spectateCallback;
public OnRequestInfo requestCallback;
}
[Serializable, StructLayout(LayoutKind.Sequential)]
public struct RichPresenceStruct
{
public IntPtr state; /* max 128 bytes */
public IntPtr details; /* max 128 bytes */
public long startTimestamp;
public long endTimestamp;
public IntPtr largeImageKey; /* max 32 bytes */
public IntPtr largeImageText; /* max 128 bytes */
public IntPtr smallImageKey; /* max 32 bytes */
public IntPtr smallImageText; /* max 128 bytes */
public IntPtr partyId; /* max 128 bytes */
public int partySize;
public int partyMax;
public int partyPrivacy;
public IntPtr matchSecret; /* max 128 bytes */
public IntPtr joinSecret; /* max 128 bytes */
public IntPtr spectateSecret; /* max 128 bytes */
public bool instance;
}
[Serializable]
public struct DiscordUser
{
public string userId;
public string username;
public string discriminator;
public string avatar;
}
public enum Reply
{
No = 0,
Yes = 1,
Ignore = 2
}
public enum PartyPrivacy
{
Private = 0,
Public = 1
}
public static void Initialize(string applicationId, ref EventHandlers handlers, bool autoRegister, string optionalSteamId)
{
Callbacks = handlers;
EventHandlers staticEventHandlers = new EventHandlers();
staticEventHandlers.readyCallback += DiscordRpc.ReadyCallback;
staticEventHandlers.disconnectedCallback += DiscordRpc.DisconnectedCallback;
staticEventHandlers.errorCallback += DiscordRpc.ErrorCallback;
staticEventHandlers.joinCallback += DiscordRpc.JoinCallback;
staticEventHandlers.spectateCallback += DiscordRpc.SpectateCallback;
staticEventHandlers.requestCallback += DiscordRpc.RequestCallback;
InitializeInternal(applicationId, ref staticEventHandlers, autoRegister, optionalSteamId);
}
[DllImport("discord-rpc", EntryPoint = "Discord_Initialize", CallingConvention = CallingConvention.Cdecl)]
static extern void InitializeInternal(string applicationId, ref EventHandlers handlers, bool autoRegister, string optionalSteamId);
[DllImport("discord-rpc", EntryPoint = "Discord_Shutdown", CallingConvention = CallingConvention.Cdecl)]
public static extern void Shutdown();
[DllImport("discord-rpc", EntryPoint = "Discord_RunCallbacks", CallingConvention = CallingConvention.Cdecl)]
public static extern void RunCallbacks();
[DllImport("discord-rpc", EntryPoint = "Discord_UpdatePresence", CallingConvention = CallingConvention.Cdecl)]
private static extern void UpdatePresenceNative(ref RichPresenceStruct presence);
[DllImport("discord-rpc", EntryPoint = "Discord_ClearPresence", CallingConvention = CallingConvention.Cdecl)]
public static extern void ClearPresence();
[DllImport("discord-rpc", EntryPoint = "Discord_Respond", CallingConvention = CallingConvention.Cdecl)]
public static extern void Respond(string userId, Reply reply);
[DllImport("discord-rpc", EntryPoint = "Discord_UpdateHandlers", CallingConvention = CallingConvention.Cdecl)]
public static extern void UpdateHandlers(ref EventHandlers handlers);
public static void UpdatePresence(RichPresence presence)
{
var presencestruct = presence.GetStruct();
UpdatePresenceNative(ref presencestruct);
presence.FreeMem();
}
public class RichPresence
{
private RichPresenceStruct _presence;
private readonly List<IntPtr> _buffers = new List<IntPtr>(10);
public string state; /* max 128 bytes */
public string details; /* max 128 bytes */
public long startTimestamp;
public long endTimestamp;
public string largeImageKey; /* max 32 bytes */
public string largeImageText; /* max 128 bytes */
public string smallImageKey; /* max 32 bytes */
public string smallImageText; /* max 128 bytes */
public string partyId; /* max 128 bytes */
public int partySize;
public int partyMax;
public PartyPrivacy partyPrivacy;
public string matchSecret; /* max 128 bytes */
public string joinSecret; /* max 128 bytes */
public string spectateSecret; /* max 128 bytes */
public bool instance;
/// <summary>
/// Get the <see cref="RichPresenceStruct"/> reprensentation of this instance
/// </summary>
/// <returns><see cref="RichPresenceStruct"/> reprensentation of this instance</returns>
internal RichPresenceStruct GetStruct()
{
if (_buffers.Count > 0)
{
FreeMem();
}
_presence.state = StrToPtr(state);
_presence.details = StrToPtr(details);
_presence.startTimestamp = startTimestamp;
_presence.endTimestamp = endTimestamp;
_presence.largeImageKey = StrToPtr(largeImageKey);
_presence.largeImageText = StrToPtr(largeImageText);
_presence.smallImageKey = StrToPtr(smallImageKey);
_presence.smallImageText = StrToPtr(smallImageText);
_presence.partyId = StrToPtr(partyId);
_presence.partySize = partySize;
_presence.partyMax = partyMax;
_presence.partyPrivacy = (int)partyPrivacy;
_presence.matchSecret = StrToPtr(matchSecret);
_presence.joinSecret = StrToPtr(joinSecret);
_presence.spectateSecret = StrToPtr(spectateSecret);
_presence.instance = instance;
return _presence;
}
/// <summary>
/// Returns a pointer to a representation of the given string with a size of maxbytes
/// </summary>
/// <param name="input">String to convert</param>
/// <returns>Pointer to the UTF-8 representation of <see cref="input"/></returns>
private IntPtr StrToPtr(string input)
{
if (string.IsNullOrEmpty(input)) return IntPtr.Zero;
var convbytecnt = Encoding.UTF8.GetByteCount(input);
var buffer = Marshal.AllocHGlobal(convbytecnt + 1);
for (int i = 0; i < convbytecnt + 1; i++)
{
Marshal.WriteByte(buffer, i, 0);
}
_buffers.Add(buffer);
Marshal.Copy(Encoding.UTF8.GetBytes(input), 0, buffer, convbytecnt);
return buffer;
}
/// <summary>
/// Convert string to UTF-8 and add null termination
/// </summary>
/// <param name="toconv">string to convert</param>
/// <returns>UTF-8 representation of <see cref="toconv"/> with added null termination</returns>
private static string StrToUtf8NullTerm(string toconv)
{
var str = toconv.Trim();
var bytes = Encoding.Default.GetBytes(str);
if (bytes.Length > 0 && bytes[bytes.Length - 1] != 0)
{
str += "\0\0";
}
return Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(str));
}
/// <summary>
/// Free the allocated memory for conversion to <see cref="RichPresenceStruct"/>
/// </summary>
internal void FreeMem()
{
for (var i = _buffers.Count - 1; i >= 0; i--)
{
Marshal.FreeHGlobal(_buffers[i]);
_buffers.RemoveAt(i);
}
}
}
}
@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: b4474a677de9d80409e98c5393ec5b1e
timeCreated: 1501697692
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More