11 Commits

Author SHA1 Message Date
e8091f5137 Changing kebab case filenames in source to snake case for consistency 2018-02-27 13:33:00 -08:00
4055565147 Update C# wrapper with visual C# compatible version (#126)
- Custom serializer to fix utf-8 strings in C#
2018-02-22 11:47:18 -08:00
578eb6de7c Provide fake DllMain declaration to fix missing-declarations warning (#130) 2018-02-15 14:36:31 -08:00
4e61b9c82c Fix mingw compilation with -Werror=missing-declarations (#128) 2018-02-14 13:33:02 -08:00
8ec10dc011 Fix compilation with -Werror=missing-declarations (#127) 2018-02-14 11:42:29 -08:00
f5f2d69a72 Update Unreal Example to include Ask to Join (#125) 2018-02-12 13:47:38 -08:00
453222075b partyMax is mandatory if partySize is included (#122) 2018-02-12 13:44:49 -08:00
c4201806cf Update build.py to properly build and copy libraries for Unity and Unreal (#120) 2018-02-12 13:40:41 -08:00
ccf04d21f5 Moving buildhelper to editor folder (#118) 2018-02-02 16:02:11 -08:00
c7b4e6b2fc Fix segfaults in Discord_RegisterW in MinGW builds (#105)
* Add MinGW and WinXP support, remove Win SDK dep when using MinGW

* Remove Win SDK dependency when compiled with MinGW

* Remap the Win SDK-depended functions to string.h substitutes

* Remap missing WinAPI call RegSetKeyValueW to a substitute function

* Remove warnings by pragma when using MinGW

* Fix segfaults in Discord_RegisterW in MinGW builds
2018-02-02 09:40:33 -08:00
eee5085e9b Exported Discord_Register and Discord_RegisterSteam (#109)
* Update CMakeLists.txt

* Update and rename src/discord_register.h to include/discord_register.h

* Update CMakeLists.txt

* Update discord_register_win.cpp

* Update discord_register_linux.cpp
2018-02-01 16:42:17 -08:00
20 changed files with 311 additions and 94 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@
/.vscode/ /.vscode/
/thirdparty/ /thirdparty/
.vs/ .vs/
.DS_Store

View File

@ -57,11 +57,11 @@ This is a text adventure "game" that inits/deinits the connection to Discord, an
## Sample: button-clicker ## 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. 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 ## 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. 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 ## Wrappers and Implementations

View File

@ -66,15 +66,9 @@ def cli(ctx, clean):
ctx.invoke(archive) ctx.invoke(archive)
@cli.command()
def unity():
""" todo: build unity project """
pass
@cli.command() @cli.command()
@click.pass_context @click.pass_context
def for_unity(ctx): def unity(ctx):
""" build just dynamic libs for use in unity project """ """ build just dynamic libs for use in unity project """
ctx.invoke( ctx.invoke(
libs, libs,
@ -84,6 +78,44 @@ def for_unity(ctx):
skip_formatter=True, skip_formatter=True,
just_release=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() @cli.command()
@ -98,23 +130,45 @@ def unreal(ctx):
skip_formatter=True, skip_formatter=True,
just_release=True just_release=True
) )
BUILDS = []
click.echo('--- Copying libs and header into unreal example') click.echo('--- Copying libs and header into unreal example')
UNREAL_PROJECT_PATH = os.path.join(SCRIPT_PATH, 'examples', 'unrealstatus', 'Plugins', 'discordrpc') UNREAL_PROJECT_PATH = os.path.join(SCRIPT_PATH, 'examples', 'unrealstatus', 'Plugins', 'discordrpc')
BUILD_BASE_PATH = os.path.join(SCRIPT_PATH, 'builds', 'win64-dynamic', 'src', 'Release')
UNREAL_DLL_PATH = os.path.join(UNREAL_PROJECT_PATH, 'Source', 'ThirdParty', 'DiscordRpcLibrary', 'Win64')
mkdir_p(UNREAL_DLL_PATH)
shutil.copy(os.path.join(BUILD_BASE_PATH, 'discord-rpc.dll'), UNREAL_DLL_PATH)
UNREAL_INCLUDE_PATH = os.path.join(UNREAL_PROJECT_PATH, 'Source', 'ThirdParty', 'DiscordRpcLibrary', 'Include') UNREAL_INCLUDE_PATH = os.path.join(UNREAL_PROJECT_PATH, 'Source', 'ThirdParty', 'DiscordRpcLibrary', 'Include')
mkdir_p(UNREAL_INCLUDE_PATH) mkdir_p(UNREAL_INCLUDE_PATH)
shutil.copy(os.path.join(SCRIPT_PATH, 'include', 'discord-rpc.h'), UNREAL_INCLUDE_PATH) shutil.copy(os.path.join(SCRIPT_PATH, 'include', 'discord_rpc.h'), UNREAL_INCLUDE_PATH)
UNREAL_LIB_PATH = os.path.join(UNREAL_PROJECT_PATH, 'Source', 'ThirdParty', 'DiscordRpcLibrary', 'Win64') if sys.platform.startswith('win'):
mkdir_p(UNREAL_LIB_PATH) LIBRARY_NAME = 'discord-rpc.lib'
shutil.copy(os.path.join(BUILD_BASE_PATH, 'discord-rpc.lib'), UNREAL_LIB_PATH) 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): def build_lib(build_name, generator, options, just_release):

View File

@ -11,7 +11,7 @@ public class DiscordJoinRequestEvent : UnityEngine.Events.UnityEvent<DiscordRpc.
public class DiscordController : MonoBehaviour public class DiscordController : MonoBehaviour
{ {
public DiscordRpc.RichPresence presence; public DiscordRpc.RichPresence presence = new DiscordRpc.RichPresence();
public string applicationId; public string applicationId;
public string optionalSteamId; public string optionalSteamId;
public int callbackCalls; public int callbackCalls;
@ -33,7 +33,7 @@ public class DiscordController : MonoBehaviour
presence.details = string.Format("Button clicked {0} times", clickCounter); presence.details = string.Format("Button clicked {0} times", clickCounter);
DiscordRpc.UpdatePresence(ref presence); DiscordRpc.UpdatePresence(presence);
} }
public void RequestRespondYes() public void RequestRespondYes()

View File

@ -1,4 +1,7 @@
using System.Runtime.InteropServices; using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
public class DiscordRpc public class DiscordRpc
{ {
@ -30,27 +33,27 @@ public class DiscordRpc
public RequestCallback requestCallback; public RequestCallback requestCallback;
} }
[System.Serializable] [Serializable, StructLayout(LayoutKind.Sequential)]
public struct RichPresence public struct RichPresenceStruct
{ {
public string state; /* max 128 bytes */ public IntPtr state; /* max 128 bytes */
public string details; /* max 128 bytes */ public IntPtr details; /* max 128 bytes */
public long startTimestamp; public long startTimestamp;
public long endTimestamp; public long endTimestamp;
public string largeImageKey; /* max 32 bytes */ public IntPtr largeImageKey; /* max 32 bytes */
public string largeImageText; /* max 128 bytes */ public IntPtr largeImageText; /* max 128 bytes */
public string smallImageKey; /* max 32 bytes */ public IntPtr smallImageKey; /* max 32 bytes */
public string smallImageText; /* max 128 bytes */ public IntPtr smallImageText; /* max 128 bytes */
public string partyId; /* max 128 bytes */ public IntPtr partyId; /* max 128 bytes */
public int partySize; public int partySize;
public int partyMax; public int partyMax;
public string matchSecret; /* max 128 bytes */ public IntPtr matchSecret; /* max 128 bytes */
public string joinSecret; /* max 128 bytes */ public IntPtr joinSecret; /* max 128 bytes */
public string spectateSecret; /* max 128 bytes */ public IntPtr spectateSecret; /* max 128 bytes */
public bool instance; public bool instance;
} }
[System.Serializable] [Serializable]
public struct JoinRequest public struct JoinRequest
{ {
public string userId; public string userId;
@ -76,12 +79,139 @@ public class DiscordRpc
public static extern void RunCallbacks(); public static extern void RunCallbacks();
[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); private static extern void UpdatePresenceNative(ref RichPresenceStruct presence);
[DllImport("discord-rpc", EntryPoint = "Discord_ClearPresence", CallingConvention = CallingConvention.Cdecl)] [DllImport("discord-rpc", EntryPoint = "Discord_ClearPresence", CallingConvention = CallingConvention.Cdecl)]
public static extern void ClearPresence(); public static extern void ClearPresence();
[DllImport("discord-rpc", EntryPoint = "Discord_Respond", CallingConvention = CallingConvention.Cdecl)] [DllImport("discord-rpc", EntryPoint = "Discord_Respond", CallingConvention = CallingConvention.Cdecl)]
public static extern void Respond(string userId, Reply reply); public static extern void Respond(string userId, Reply reply);
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 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, 128);
_presence.details = StrToPtr(details, 128);
_presence.startTimestamp = startTimestamp;
_presence.endTimestamp = endTimestamp;
_presence.largeImageKey = StrToPtr(largeImageKey, 32);
_presence.largeImageText = StrToPtr(largeImageText, 128);
_presence.smallImageKey = StrToPtr(smallImageKey, 32);
_presence.smallImageText = StrToPtr(smallImageText, 128);
_presence.partyId = StrToPtr(partyId, 128);
_presence.partySize = partySize;
_presence.partyMax = partyMax;
_presence.matchSecret = StrToPtr(matchSecret, 128);
_presence.joinSecret = StrToPtr(joinSecret, 128);
_presence.spectateSecret = StrToPtr(spectateSecret, 128);
_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>
/// <param name="maxbytes">Max number of bytes to use</param>
/// <returns>Pointer to the UTF-8 representation of <see cref="input"/></returns>
private IntPtr StrToPtr(string input, int maxbytes)
{
if (string.IsNullOrEmpty(input)) return IntPtr.Zero;
var convstr = StrClampBytes(input, maxbytes);
var convbytecnt = Encoding.UTF8.GetByteCount(convstr);
var buffer = Marshal.AllocHGlobal(convbytecnt);
_buffers.Add(buffer);
Marshal.Copy(Encoding.UTF8.GetBytes(convstr), 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>
/// Clamp the string to the given byte length preserving null termination
/// </summary>
/// <param name="toclamp">string to clamp</param>
/// <param name="maxbytes">max bytes the resulting string should have (including null termination)</param>
/// <returns>null terminated string with a byte length less or equal to <see cref="maxbytes"/></returns>
private static string StrClampBytes(string toclamp, int maxbytes)
{
var str = StrToUtf8NullTerm(toclamp);
var strbytes = Encoding.UTF8.GetBytes(str);
if (strbytes.Length <= maxbytes)
{
return str;
}
var newstrbytes = new byte[] { };
Array.Copy(strbytes, 0, newstrbytes, 0, maxbytes - 1);
newstrbytes[newstrbytes.Length - 1] = 0;
newstrbytes[newstrbytes.Length - 2] = 0;
return Encoding.UTF8.GetString(newstrbytes);
}
/// <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);
}
}
}
}

View File

@ -27,7 +27,7 @@ public class ScriptBatch
proc.StartInfo.EnvironmentVariables["PATH"] = newPath; proc.StartInfo.EnvironmentVariables["PATH"] = newPath;
#endif #endif
proc.StartInfo.FileName = "python"; proc.StartInfo.FileName = "python";
proc.StartInfo.Arguments = "build.py for_unity"; proc.StartInfo.Arguments = "build.py unity";
proc.StartInfo.WorkingDirectory = "../.."; proc.StartInfo.WorkingDirectory = "../..";
proc.Start(); proc.Start();
proc.WaitForExit(); proc.WaitForExit();

View File

@ -9,7 +9,7 @@
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include "discord-rpc.h" #include "discord_rpc.h"
static const char* APPLICATION_ID = "345229890980937739"; static const char* APPLICATION_ID = "345229890980937739";
static int FrustrationLevel = 0; static int FrustrationLevel = 0;

View File

@ -1,6 +1,6 @@
#include "DiscordRpcPrivatePCH.h" #include "DiscordRpcPrivatePCH.h"
#include "DiscordRpcBlueprint.h" #include "DiscordRpcBlueprint.h"
#include "discord-rpc.h" #include "discord_rpc.h"
DEFINE_LOG_CATEGORY(Discord) DEFINE_LOG_CATEGORY(Discord)
@ -59,7 +59,7 @@ static void JoinRequestHandler(const DiscordJoinRequest* request)
jr.username = ANSI_TO_TCHAR(request->username); jr.username = ANSI_TO_TCHAR(request->username);
jr.discriminator = ANSI_TO_TCHAR(request->discriminator); jr.discriminator = ANSI_TO_TCHAR(request->discriminator);
jr.avatar = ANSI_TO_TCHAR(request->avatar); jr.avatar = ANSI_TO_TCHAR(request->avatar);
UE_LOG(Discord, Log, TEXT("Discord join request from %s#%s"), *jr.username, *jr.discriminator); UE_LOG(Discord, Log, TEXT("Discord join request from %s - %s#%s"), *jr.userId, *jr.username, *jr.discriminator);
if (self) { if (self) {
self->OnJoinRequest.Broadcast(jr); self->OnJoinRequest.Broadcast(jr);
} }
@ -134,9 +134,6 @@ void UDiscordRpc::UpdatePresence()
auto spectateSecret = StringCast<ANSICHAR>(*RichPresence.spectateSecret); auto spectateSecret = StringCast<ANSICHAR>(*RichPresence.spectateSecret);
rp.spectateSecret = spectateSecret.Get(); rp.spectateSecret = spectateSecret.Get();
rp.startTimestamp = RichPresence.startTimestamp;
rp.endTimestamp = RichPresence.endTimestamp;
rp.partySize = RichPresence.partySize; rp.partySize = RichPresence.partySize;
rp.partyMax = RichPresence.partyMax; rp.partyMax = RichPresence.partyMax;
rp.instance = RichPresence.instance; rp.instance = RichPresence.instance;
@ -148,3 +145,10 @@ void UDiscordRpc::ClearPresence()
{ {
Discord_ClearPresence(); Discord_ClearPresence();
} }
void UDiscordRpc::Respond(const FString& userId, int reply)
{
UE_LOG(Discord, Log, TEXT("Responding %d to join request from %s"), reply, *userId);
FTCHARToUTF8 utf8_userid(*userId);
Discord_Respond(utf8_userid.Get(), reply);
}

View File

@ -111,6 +111,11 @@ public:
Category = "Discord") Category = "Discord")
void ClearPresence(); void ClearPresence();
UFUNCTION(BlueprintCallable,
meta = (DisplayName = "Respond to join request", Keywords = "Discord rpc"),
Category = "Discord")
void Respond(const FString& userId, int reply);
UPROPERTY(BlueprintReadOnly, UPROPERTY(BlueprintReadOnly,
meta = (DisplayName = "Is Discord connected", Keywords = "Discord rpc"), meta = (DisplayName = "Is Discord connected", Keywords = "Discord rpc"),
Category = "Discord") Category = "Discord")

View File

@ -0,0 +1,26 @@
#pragma once
#if defined(DISCORD_DYNAMIC_LIB)
# if defined(_WIN32)
# if defined(DISCORD_BUILDING_SDK)
# define DISCORD_EXPORT __declspec(dllexport)
# else
# define DISCORD_EXPORT __declspec(dllimport)
# endif
# else
# define DISCORD_EXPORT __attribute__((visibility("default")))
# endif
#else
# define DISCORD_EXPORT
#endif
#ifdef __cplusplus
extern "C" {
#endif
DISCORD_EXPORT void Discord_Register(const char* applicationId, const char* command);
DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId, const char* steamId);
#ifdef __cplusplus
}
#endif

View File

@ -6,9 +6,9 @@ option(USE_STATIC_CRT "Use /MT[d] for dynamic library" OFF)
set(CMAKE_CXX_STANDARD 14) 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
discord-rpc.cpp discord_rpc.cpp
discord_register.h ${PROJECT_SOURCE_DIR}/include/discord_register.h
rpc_connection.h rpc_connection.h
rpc_connection.cpp rpc_connection.cpp
serialization.h serialization.h
@ -129,6 +129,7 @@ install(
install( install(
FILES FILES
"../include/discord-rpc.h" "../include/discord_rpc.h"
"../include/discord_register.h"
DESTINATION "include" DESTINATION "include"
) )

View File

@ -1,12 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void Discord_Register(const char* applicationId, const char* command);
void Discord_RegisterSteamGame(const char* applicationId, const char* steamId);
#ifdef __cplusplus
}
#endif

View File

@ -1,4 +1,5 @@
#include "discord-rpc.h" #include "discord_rpc.h"
#include "discord_register.h"
#include <stdio.h> #include <stdio.h>
#include <errno.h> #include <errno.h>
@ -8,7 +9,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
bool Mkdir(const char* path) static bool Mkdir(const char* path)
{ {
int result = mkdir(path, 0755); int result = mkdir(path, 0755);
if (result == 0) { if (result == 0) {
@ -21,7 +22,7 @@ bool Mkdir(const char* path)
} }
// we want to register games so we can run them from Discord client as discord-<appid>:// // we want to register games so we can run them from Discord client as discord-<appid>://
extern "C" void Discord_Register(const char* applicationId, const char* command) extern "C" DISCORD_EXPORT void Discord_Register(const char* applicationId, const char* command)
{ {
// Add a desktop file and update some mime handlers so that xdg-open does the right thing. // Add a desktop file and update some mime handlers so that xdg-open does the right thing.
@ -90,7 +91,7 @@ extern "C" void Discord_Register(const char* applicationId, const char* command)
} }
} }
extern "C" void Discord_RegisterSteamGame(const char* applicationId, const char* steamId) extern "C" DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId, const char* steamId)
{ {
char command[256]; char command[256];
sprintf(command, "xdg-open steam://rungameid/%s", steamId); sprintf(command, "xdg-open steam://rungameid/%s", steamId);

View File

@ -1,4 +1,5 @@
#include "discord-rpc.h" #include "discord_rpc.h"
#include "discord_register.h"
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#define NOMCX #define NOMCX
@ -7,7 +8,7 @@
#include <windows.h> #include <windows.h>
#include <psapi.h> #include <psapi.h>
#include <cwchar> #include <cwchar>
#include <stdio.h> #include <cstdio>
/** /**
* Updated fixes for MinGW and WinXP * Updated fixes for MinGW and WinXP
@ -20,11 +21,16 @@
*/ */
#ifdef __MINGW32__ #ifdef __MINGW32__
/// strsafe.h fixes /// strsafe.h fixes
#define StringCbPrintfW snwprintf static HRESULT StringCbPrintfW(LPWSTR pszDest, size_t cbDest, LPCWSTR pszFormat, ...)
LPWSTR StringCbCopyW(LPWSTR a, size_t l, LPCWSTR b)
{ {
a[l-1] = 0; HRESULT ret;
return wcsncpy(a, b, l - 1); // does not set the last byte to 0 on overflow, so it's set to 0 above va_list va;
va_start(va, pszFormat);
cbDest /= 2; // Size is divided by 2 to convert from bytes to wide characters - causes segfault othervise
ret = vsnwprintf(pszDest, cbDest, pszFormat, va);
pszDest[cbDest - 1] = 0; // Terminate the string in case a buffer overflow; -1 will be returned
va_end(va);
return ret;
} }
#else #else
#include <strsafe.h> #include <strsafe.h>
@ -38,21 +44,21 @@ LPWSTR StringCbCopyW(LPWSTR a, size_t l, LPCWSTR b)
#undefine RegSetKeyValueW #undefine RegSetKeyValueW
#endif #endif
#define RegSetKeyValueW regset #define RegSetKeyValueW regset
LSTATUS regset(HKEY hkey, LPCWSTR subkey, LPCWSTR name, DWORD type, const void *data, DWORD len) static LSTATUS regset(HKEY hkey, LPCWSTR subkey, LPCWSTR name, DWORD type, const void *data, DWORD len)
{ {
HKEY hsubkey = NULL; HKEY htkey = hkey, hsubkey = nullptr;
LSTATUS ret; LSTATUS ret;
if (subkey && subkey[0]) /* need to create the subkey */ if (subkey && subkey[0])
{ {
if ((ret = RegCreateKeyW( hkey, subkey, &hsubkey )) != ERROR_SUCCESS) return ret; if((ret = RegCreateKeyExW(hkey, subkey, 0, 0, 0, KEY_ALL_ACCESS, 0, &hsubkey, 0)) != ERROR_SUCCESS) return ret;
hkey = hsubkey; htkey = hsubkey;
} }
ret = RegSetValueExW( hkey, name, 0, type, (const BYTE*)data, len ); ret = RegSetValueExW(htkey, name, 0, type, (const BYTE*)data, len);
if (hsubkey) RegCloseKey( hsubkey ); if (hsubkey && hsubkey != hkey) RegCloseKey(hsubkey);
return ret; return ret;
} }
void Discord_RegisterW(const wchar_t* applicationId, const wchar_t* command) static void Discord_RegisterW(const wchar_t* applicationId, const wchar_t* command)
{ {
// https://msdn.microsoft.com/en-us/library/aa767914(v=vs.85).aspx // https://msdn.microsoft.com/en-us/library/aa767914(v=vs.85).aspx
// we want to register games so we can run them as discord-<appid>:// // we want to register games so we can run them as discord-<appid>://
@ -66,7 +72,8 @@ void Discord_RegisterW(const wchar_t* applicationId, const wchar_t* command)
StringCbPrintfW(openCommand, sizeof(openCommand), L"%s", command); StringCbPrintfW(openCommand, sizeof(openCommand), L"%s", command);
} }
else { else {
StringCbCopyW(openCommand, sizeof(openCommand), exeFilePath); //StringCbCopyW(openCommand, sizeof(openCommand), exeFilePath);
StringCbPrintfW(openCommand, sizeof(openCommand), L"%s", exeFilePath);
} }
wchar_t protocolName[64]; wchar_t protocolName[64];
@ -115,7 +122,7 @@ void Discord_RegisterW(const wchar_t* applicationId, const wchar_t* command)
RegCloseKey(key); RegCloseKey(key);
} }
extern "C" void Discord_Register(const char* applicationId, const char* command) extern "C" DISCORD_EXPORT void Discord_Register(const char* applicationId, const char* command)
{ {
wchar_t appId[32]; wchar_t appId[32];
MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32); MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32);
@ -131,7 +138,7 @@ extern "C" void Discord_Register(const char* applicationId, const char* command)
Discord_RegisterW(appId, wcommand); Discord_RegisterW(appId, wcommand);
} }
extern "C" void Discord_RegisterSteamGame(const char* applicationId, const char* steamId) extern "C" DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId, const char* steamId)
{ {
wchar_t appId[32]; wchar_t appId[32];
MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32); MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32);

View File

@ -1,4 +1,4 @@
#include "discord-rpc.h" #include "discord_rpc.h"
#include "backoff.h" #include "backoff.h"
#include "discord_register.h" #include "discord_register.h"

View File

@ -1,5 +1,7 @@
#include <windows.h> #include <windows.h>
// outsmart GCC's missing-declarations warning
BOOL WINAPI DllMain(HMODULE, DWORD, LPVOID);
BOOL WINAPI DllMain(HMODULE, DWORD, LPVOID) BOOL WINAPI DllMain(HMODULE, DWORD, LPVOID)
{ {
return TRUE; return TRUE;

View File

@ -1,6 +1,6 @@
#include "serialization.h" #include "serialization.h"
#include "connection.h" #include "connection.h"
#include "discord-rpc.h" #include "discord_rpc.h"
template <typename T> template <typename T>
void NumberToString(char* dest, T number) void NumberToString(char* dest, T number)
@ -138,14 +138,12 @@ size_t JsonWriteRichPresenceObj(char* dest,
presence->partyMax) { presence->partyMax) {
WriteObject party(writer, "party"); WriteObject party(writer, "party");
WriteOptionalString(writer, "id", presence->partyId); WriteOptionalString(writer, "id", presence->partyId);
if (presence->partySize) { if (presence->partySize && presence->partyMax) {
WriteArray size(writer, "size"); WriteArray size(writer, "size");
writer.Int(presence->partySize); writer.Int(presence->partySize);
if (0 < presence->partyMax) {
writer.Int(presence->partyMax); writer.Int(presence->partyMax);
} }
} }
}
if ((presence->matchSecret && presence->matchSecret[0]) || if ((presence->matchSecret && presence->matchSecret[0]) ||
(presence->joinSecret && presence->joinSecret[0]) || (presence->joinSecret && presence->joinSecret[0]) ||