修discordRPC加载失败会崩溃
This commit is contained in:
@@ -1,343 +1,283 @@
|
|||||||
|
local appId='1288557386700951554'
|
||||||
|
|
||||||
local ffi=require"ffi"
|
local ffi=require"ffi"
|
||||||
|
|
||||||
|
local RPC_C
|
||||||
-- Get the host os to load correct lib
|
if SYSTEM=='Windows' then
|
||||||
local osname=love.system.getOS()
|
local suc
|
||||||
local discordRPClib=nil
|
suc,RPC_C=pcall(ffi.load,"discord-rpc")
|
||||||
|
if not (suc and RPC_C) then
|
||||||
-- FFI requires the libraries really be files just sitting in the filesystem. It
|
print("Failed to load Discord-RPC lib",RPC_C)
|
||||||
-- can't load libraries from a .love archive, nor a fused executable on Windows.
|
MES.new('error',"Failed to load Discord-RPC lib")
|
||||||
-- Merely using love.filesystem.getSource() only works when running LOVE with
|
RPC_C=nil
|
||||||
-- the game unarchived from command line, like "love .".
|
end
|
||||||
--
|
|
||||||
-- The code here setting "source" will set the directory where the game was run
|
|
||||||
-- from, so FFI can load discordRPC. We assume that the discordRPC library's
|
|
||||||
-- libs directory is in the same directory as the .love archive; if it's
|
|
||||||
-- missing, it just won't load.
|
|
||||||
local source=love.filesystem.getSource()
|
|
||||||
if string.sub(source, -5)==".love" or love.filesystem.isFused() then
|
|
||||||
source=love.filesystem.getSourceBaseDirectory()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if osname=="Linux" then
|
local RPC
|
||||||
discordRPClib=ffi.load(source.."/libs/discord-rpc.so")
|
if RPC_C then
|
||||||
elseif osname=="OS X" then
|
RPC={}
|
||||||
discordRPClib=ffi.load(source.."/libs/discord-rpc.dylib")
|
|
||||||
elseif osname=="Windows" then
|
|
||||||
-- I would strongly advise never touching this. It was not trivial to get correct. -nightmareci
|
|
||||||
|
|
||||||
ffi.cdef[[
|
ffi.cdef[[
|
||||||
typedef uint32_t DWORD;
|
typedef struct DiscordRichPresence {
|
||||||
typedef char CHAR;
|
const char* state; /* max 128 bytes */
|
||||||
typedef CHAR *LPSTR;
|
const char* details; /* max 128 bytes */
|
||||||
typedef const CHAR *LPCSTR;
|
int64_t startTimestamp;
|
||||||
typedef wchar_t WCHAR;
|
int64_t endTimestamp;
|
||||||
typedef WCHAR *LPWSTR;
|
const char* largeImageKey; /* max 32 bytes */
|
||||||
typedef LPWSTR PWSTR;
|
const char* largeImageText; /* max 128 bytes */
|
||||||
typedef const WCHAR *LPCWSTR;
|
const char* smallImageKey; /* max 32 bytes */
|
||||||
|
const char* smallImageText; /* max 128 bytes */
|
||||||
|
const char* partyId; /* max 128 bytes */
|
||||||
|
int partySize;
|
||||||
|
int partyMax;
|
||||||
|
const char* matchSecret; /* max 128 bytes */
|
||||||
|
const char* joinSecret; /* max 128 bytes */
|
||||||
|
const char* spectateSecret; /* max 128 bytes */
|
||||||
|
int8_t instance;
|
||||||
|
} DiscordRichPresence;
|
||||||
|
|
||||||
static const DWORD CP_UTF8 = 65001;
|
typedef struct DiscordUser {
|
||||||
int32_t MultiByteToWideChar(
|
const char* userId;
|
||||||
DWORD CodePage,
|
const char* username;
|
||||||
DWORD dwFlags,
|
const char* discriminator;
|
||||||
LPCSTR lpMultiByteStr,
|
const char* avatar;
|
||||||
int32_t cbMultiByte,
|
} DiscordUser;
|
||||||
LPWSTR lpWideCharStr,
|
|
||||||
int32_t cchWideChar
|
|
||||||
);
|
|
||||||
|
|
||||||
int32_t WideCharToMultiByte(
|
typedef void (*readyPtr)(const DiscordUser* request);
|
||||||
DWORD CodePage,
|
typedef void (*disconnectedPtr)(int errorCode, const char* message);
|
||||||
DWORD dwFlags,
|
typedef void (*erroredPtr)(int errorCode, const char* message);
|
||||||
LPCWSTR lpWideCharStr,
|
typedef void (*joinGamePtr)(const char* joinSecret);
|
||||||
int32_t cchWideChar,
|
typedef void (*spectateGamePtr)(const char* spectateSecret);
|
||||||
LPSTR lpMultiByteStr,
|
typedef void (*joinRequestPtr)(const DiscordUser* request);
|
||||||
int32_t cbMultiByte,
|
|
||||||
void* lpDefaultChar,
|
|
||||||
void* lpUsedDefaultChar
|
|
||||||
);
|
|
||||||
|
|
||||||
DWORD GetShortPathNameW(
|
typedef struct DiscordEventHandlers {
|
||||||
LPCWSTR lpszLongPath,
|
readyPtr ready;
|
||||||
LPWSTR lpszShortPath,
|
disconnectedPtr disconnected;
|
||||||
DWORD cchBuffer
|
erroredPtr errored;
|
||||||
);
|
joinGamePtr joinGame;
|
||||||
|
spectateGamePtr spectateGame;
|
||||||
|
joinRequestPtr joinRequest;
|
||||||
|
} DiscordEventHandlers;
|
||||||
|
|
||||||
|
void Discord_Initialize(const char* applicationId,
|
||||||
|
DiscordEventHandlers* handlers,
|
||||||
|
int autoRegister,
|
||||||
|
const char* optionalSteamId);
|
||||||
|
void Discord_Shutdown(void);
|
||||||
|
void Discord_RunCallbacks(void);
|
||||||
|
void Discord_UpdatePresence(const DiscordRichPresence* presence);
|
||||||
|
void Discord_ClearPresence(void);
|
||||||
|
void Discord_Respond(const char* userid, int reply);
|
||||||
|
void Discord_UpdateHandlers(DiscordEventHandlers* handlers);
|
||||||
]]
|
]]
|
||||||
|
|
||||||
local originalWideSize=ffi.C.MultiByteToWideChar(ffi.C.CP_UTF8, 0, source, -1, nil, 0)
|
local function unpackDiscordUser(request)
|
||||||
local originalWide=ffi.new('WCHAR[?]', originalWideSize)
|
return ffi.string(request.userId),ffi.string(request.username),
|
||||||
ffi.C.MultiByteToWideChar(ffi.C.CP_UTF8, 0, source, -1, originalWide, originalWideSize)
|
ffi.string(request.discriminator),ffi.string(request.avatar)
|
||||||
|
|
||||||
local sourceSize=ffi.C.GetShortPathNameW(originalWide, nil, 0)
|
|
||||||
local sourceWide=ffi.new('WCHAR[?]', sourceSize)
|
|
||||||
ffi.C.GetShortPathNameW(originalWide, sourceWide, sourceSize)
|
|
||||||
|
|
||||||
local sourceChar=ffi.new('char[?]', sourceSize)
|
|
||||||
ffi.C.WideCharToMultiByte(ffi.C.CP_UTF8, 0, sourceWide, sourceSize, sourceChar, sourceSize, nil, nil)
|
|
||||||
|
|
||||||
discordRPClib=ffi.load("discord-rpc.dll")
|
|
||||||
-- source = ffi.string(sourceChar)
|
|
||||||
-- if jit.arch == "x86" then
|
|
||||||
-- discordRPClib = ffi.load(source.."/libs/discord-rpc_x86.dll")
|
|
||||||
-- elseif jit.arch == "x64" then
|
|
||||||
-- discordRPClib = ffi.load(source.."/libs/discord-rpc_x64.dll")
|
|
||||||
-- end
|
|
||||||
else
|
|
||||||
-- Else it crashes later on
|
|
||||||
error(string.format("Discord rpc not supported on platform (%s)", osname))
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
ffi.cdef[[
|
|
||||||
typedef struct DiscordRichPresence {
|
|
||||||
const char* state; /* max 128 bytes */
|
|
||||||
const char* details; /* max 128 bytes */
|
|
||||||
int64_t startTimestamp;
|
|
||||||
int64_t endTimestamp;
|
|
||||||
const char* largeImageKey; /* max 32 bytes */
|
|
||||||
const char* largeImageText; /* max 128 bytes */
|
|
||||||
const char* smallImageKey; /* max 32 bytes */
|
|
||||||
const char* smallImageText; /* max 128 bytes */
|
|
||||||
const char* partyId; /* max 128 bytes */
|
|
||||||
int partySize;
|
|
||||||
int partyMax;
|
|
||||||
const char* matchSecret; /* max 128 bytes */
|
|
||||||
const char* joinSecret; /* max 128 bytes */
|
|
||||||
const char* spectateSecret; /* max 128 bytes */
|
|
||||||
int8_t instance;
|
|
||||||
} DiscordRichPresence;
|
|
||||||
|
|
||||||
typedef struct DiscordUser {
|
|
||||||
const char* userId;
|
|
||||||
const char* username;
|
|
||||||
const char* discriminator;
|
|
||||||
const char* avatar;
|
|
||||||
} DiscordUser;
|
|
||||||
|
|
||||||
typedef void (*readyPtr)(const DiscordUser* request);
|
|
||||||
typedef void (*disconnectedPtr)(int errorCode, const char* message);
|
|
||||||
typedef void (*erroredPtr)(int errorCode, const char* message);
|
|
||||||
typedef void (*joinGamePtr)(const char* joinSecret);
|
|
||||||
typedef void (*spectateGamePtr)(const char* spectateSecret);
|
|
||||||
typedef void (*joinRequestPtr)(const DiscordUser* request);
|
|
||||||
|
|
||||||
typedef struct DiscordEventHandlers {
|
|
||||||
readyPtr ready;
|
|
||||||
disconnectedPtr disconnected;
|
|
||||||
erroredPtr errored;
|
|
||||||
joinGamePtr joinGame;
|
|
||||||
spectateGamePtr spectateGame;
|
|
||||||
joinRequestPtr joinRequest;
|
|
||||||
} DiscordEventHandlers;
|
|
||||||
|
|
||||||
void Discord_Initialize(const char* applicationId,
|
|
||||||
DiscordEventHandlers* handlers,
|
|
||||||
int autoRegister,
|
|
||||||
const char* optionalSteamId);
|
|
||||||
|
|
||||||
void Discord_Shutdown(void);
|
|
||||||
|
|
||||||
void Discord_RunCallbacks(void);
|
|
||||||
|
|
||||||
void Discord_UpdatePresence(const DiscordRichPresence* presence);
|
|
||||||
|
|
||||||
void Discord_ClearPresence(void);
|
|
||||||
|
|
||||||
void Discord_Respond(const char* userid, int reply);
|
|
||||||
|
|
||||||
void Discord_UpdateHandlers(DiscordEventHandlers* handlers);
|
|
||||||
]]
|
|
||||||
|
|
||||||
local RPC={} -- module table
|
|
||||||
|
|
||||||
-- proxy to detect garbage collection of the module
|
|
||||||
RPC.gcDummy=newproxy(true)
|
|
||||||
|
|
||||||
local function unpackDiscordUser(request)
|
|
||||||
return ffi.string(request.userId),ffi.string(request.username),
|
|
||||||
ffi.string(request.discriminator),ffi.string(request.avatar)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- callback proxies
|
|
||||||
-- note: callbacks are not JIT compiled (= SLOW), try to avoid doing performance critical tasks in them
|
|
||||||
-- luajit.org/ext_ffi_semantics.html
|
|
||||||
local ready_proxy=ffi.cast("readyPtr", function(request)
|
|
||||||
if RPC.ready then
|
|
||||||
RPC.ready(unpackDiscordUser(request))
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
local disconnected_proxy=ffi.cast("disconnectedPtr", function(errorCode,message)
|
|
||||||
if RPC.disconnected then
|
|
||||||
RPC.disconnected(errorCode, ffi.string(message))
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
local errored_proxy=ffi.cast("erroredPtr", function(errorCode,message)
|
|
||||||
if RPC.errored then
|
|
||||||
RPC.errored(errorCode, ffi.string(message))
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
local joinGame_proxy=ffi.cast("joinGamePtr", function(joinSecret)
|
|
||||||
if RPC.joinGame then
|
|
||||||
RPC.joinGame(ffi.string(joinSecret))
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
local spectateGame_proxy=ffi.cast("spectateGamePtr", function(spectateSecret)
|
|
||||||
if RPC.spectateGame then
|
|
||||||
RPC.spectateGame(ffi.string(spectateSecret))
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
local joinRequest_proxy=ffi.cast("joinRequestPtr", function(request)
|
|
||||||
if RPC.joinRequest then
|
|
||||||
RPC.joinRequest(unpackDiscordUser(request))
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- helpers
|
|
||||||
local function checkArg(arg,argType,argName,func,maybeNil)
|
|
||||||
assert(type(arg)==argType or (maybeNil and arg==nil),
|
|
||||||
string.format("Argument \"%s\" to function \"%s\" has to be of type \"%s\"",
|
|
||||||
argName, func, argType))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function checkStrArg(arg,maxLen,argName,func,maybeNil)
|
|
||||||
if maxLen then
|
|
||||||
assert(type(arg)=="string" and arg:len()<=maxLen or (maybeNil and arg==nil),
|
|
||||||
string.format("Argument \"%s\" of function \"%s\" has to be of type string with maximum length %d",
|
|
||||||
argName, func, maxLen))
|
|
||||||
else
|
|
||||||
checkArg(arg, "string", argName, func, true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function checkIntArg(arg,maxBits,argName,func,maybeNil)
|
|
||||||
maxBits=math.min(maxBits or 32, 52) -- lua number (double) can only store integers < 2^53
|
|
||||||
local maxVal=2^(maxBits-1) -- assuming signed integers, which, for now, are the only ones in use
|
|
||||||
assert(type(arg)=="number" and math.floor(arg)==arg
|
|
||||||
and arg<maxVal and arg>=-maxVal
|
|
||||||
or (maybeNil and arg==nil),
|
|
||||||
string.format("Argument \"%s\" of function \"%s\" has to be a whole number <= %d",
|
|
||||||
argName, func, maxVal))
|
|
||||||
end
|
|
||||||
|
|
||||||
-- function wrappers
|
|
||||||
function RPC.initialize(applicationId,autoRegister,optionalSteamId)
|
|
||||||
local func="discordRPC.Initialize"
|
|
||||||
checkStrArg(applicationId, nil, "applicationId", func)
|
|
||||||
checkArg(autoRegister, "boolean", "autoRegister", func)
|
|
||||||
if optionalSteamId~=nil then
|
|
||||||
checkStrArg(optionalSteamId, nil, "optionalSteamId", func)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local eventHandlers=ffi.new("struct DiscordEventHandlers")
|
-- callback proxies
|
||||||
eventHandlers.ready=ready_proxy
|
-- note: callbacks are not JIT compiled (= SLOW), try to avoid doing performance critical tasks in them
|
||||||
eventHandlers.disconnected=disconnected_proxy
|
-- luajit.org/ext_ffi_semantics.html
|
||||||
eventHandlers.errored=errored_proxy
|
local ready_proxy=ffi.cast("readyPtr",function(request)
|
||||||
eventHandlers.joinGame=joinGame_proxy
|
if RPC.ready then
|
||||||
eventHandlers.spectateGame=spectateGame_proxy
|
RPC.ready(unpackDiscordUser(request))
|
||||||
eventHandlers.joinRequest=joinRequest_proxy
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
discordRPClib.Discord_Initialize(applicationId, eventHandlers,
|
local disconnected_proxy=ffi.cast("disconnectedPtr",function(errorCode,message)
|
||||||
autoRegister and 1 or 0, optionalSteamId)
|
if RPC.disconnected then
|
||||||
end
|
RPC.disconnected(errorCode,ffi.string(message))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
function RPC.shutdown()
|
local errored_proxy=ffi.cast("erroredPtr",function(errorCode,message)
|
||||||
discordRPClib.Discord_Shutdown()
|
if RPC.errored then
|
||||||
end
|
RPC.errored(errorCode,ffi.string(message))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
function RPC.runCallbacks()
|
local joinGame_proxy=ffi.cast("joinGamePtr",function(joinSecret)
|
||||||
discordRPClib.Discord_RunCallbacks()
|
if RPC.joinGame then
|
||||||
end
|
RPC.joinGame(ffi.string(joinSecret))
|
||||||
-- http://luajit.org/ext_ffi_semantics.html#callback :
|
end
|
||||||
-- It is not allowed, to let an FFI call into a C function (runCallbacks)
|
end)
|
||||||
-- get JIT-compiled, which in turn calls a callback, calling into Lua again (e.g. discordRPC.ready).
|
|
||||||
-- Usually this attempt is caught by the interpreter first and the C function
|
|
||||||
-- is blacklisted for compilation.
|
|
||||||
-- solution:
|
|
||||||
-- "Then you'll need to manually turn off JIT-compilation with jit.off() for
|
|
||||||
-- the surrounding Lua function that invokes such a message polling function."
|
|
||||||
jit.off(RPC.runCallbacks)
|
|
||||||
|
|
||||||
function RPC.updatePresence(presence)
|
local spectateGame_proxy=ffi.cast("spectateGamePtr",function(spectateSecret)
|
||||||
local func="discordRPC.updatePresence"
|
if RPC.spectateGame then
|
||||||
checkArg(presence, "table", "presence", func)
|
RPC.spectateGame(ffi.string(spectateSecret))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
-- -1 for string length because of 0-termination
|
local joinRequest_proxy=ffi.cast("joinRequestPtr",function(request)
|
||||||
checkStrArg(presence.state, 127, "presence.state", func, true)
|
if RPC.joinRequest then
|
||||||
checkStrArg(presence.details, 127, "presence.details", func, true)
|
RPC.joinRequest(unpackDiscordUser(request))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
checkIntArg(presence.startTimestamp, 64, "presence.startTimestamp", func, true)
|
-- helpers
|
||||||
checkIntArg(presence.endTimestamp, 64, "presence.endTimestamp", func, true)
|
local function checkArg(arg,argType,argName,func,maybeNil)
|
||||||
|
assert(type(arg)==argType or (maybeNil and arg==nil),
|
||||||
|
string.format("Argument \"%s\" to function \"%s\" has to be of type \"%s\"",
|
||||||
|
argName,func,argType))
|
||||||
|
end
|
||||||
|
|
||||||
checkStrArg(presence.largeImageKey, 31, "presence.largeImageKey", func, true)
|
local function checkStrArg(arg,maxLen,argName,func,maybeNil)
|
||||||
checkStrArg(presence.largeImageText, 127, "presence.largeImageText", func, true)
|
if maxLen then
|
||||||
checkStrArg(presence.smallImageKey, 31, "presence.smallImageKey", func, true)
|
assert(type(arg)=="string" and arg:len()<=maxLen or (maybeNil and arg==nil),
|
||||||
checkStrArg(presence.smallImageText, 127, "presence.smallImageText", func, true)
|
string.format("Argument \"%s\" of function \"%s\" has to be of type string with maximum length %d",
|
||||||
checkStrArg(presence.partyId, 127, "presence.partyId", func, true)
|
argName,func,maxLen))
|
||||||
|
else
|
||||||
|
checkArg(arg,"string",argName,func,true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
checkIntArg(presence.partySize, 32, "presence.partySize", func, true)
|
local function checkIntArg(arg,maxBits,argName,func,maybeNil)
|
||||||
checkIntArg(presence.partyMax, 32, "presence.partyMax", func, true)
|
maxBits=math.min(maxBits or 32,52) -- lua number (double) can only store integers < 2^53
|
||||||
|
local maxVal=2^(maxBits-1) -- assuming signed integers, which, for now, are the only ones in use
|
||||||
|
assert(type(arg)=="number" and math.floor(arg)==arg
|
||||||
|
and arg<maxVal and arg>=-maxVal
|
||||||
|
or (maybeNil and arg==nil),
|
||||||
|
string.format("Argument \"%s\" of function \"%s\" has to be a whole number <= %d",
|
||||||
|
argName,func,maxVal))
|
||||||
|
end
|
||||||
|
|
||||||
checkStrArg(presence.matchSecret, 127, "presence.matchSecret", func, true)
|
-- function wrappers
|
||||||
checkStrArg(presence.joinSecret, 127, "presence.joinSecret", func, true)
|
function RPC.initialize(applicationId,autoRegister,optionalSteamId)
|
||||||
checkStrArg(presence.spectateSecret, 127, "presence.spectateSecret", func, true)
|
local func="discordRPC.Initialize"
|
||||||
|
checkStrArg(applicationId,nil,"applicationId",func)
|
||||||
|
checkArg(autoRegister,"boolean","autoRegister",func)
|
||||||
|
if optionalSteamId~=nil then
|
||||||
|
checkStrArg(optionalSteamId,nil,"optionalSteamId",func)
|
||||||
|
end
|
||||||
|
|
||||||
checkIntArg(presence.instance, 8, "presence.instance", func, true)
|
local eventHandlers=ffi.new("struct DiscordEventHandlers")
|
||||||
|
eventHandlers.ready=ready_proxy
|
||||||
|
eventHandlers.disconnected=disconnected_proxy
|
||||||
|
eventHandlers.errored=errored_proxy
|
||||||
|
eventHandlers.joinGame=joinGame_proxy
|
||||||
|
eventHandlers.spectateGame=spectateGame_proxy
|
||||||
|
eventHandlers.joinRequest=joinRequest_proxy
|
||||||
|
|
||||||
local cpresence=ffi.new("struct DiscordRichPresence")
|
RPC_C.Discord_Initialize(applicationId,eventHandlers,
|
||||||
cpresence.state=presence.state
|
autoRegister and 1 or 0,optionalSteamId)
|
||||||
cpresence.details=presence.details
|
end
|
||||||
cpresence.startTimestamp=presence.startTimestamp or 0
|
|
||||||
cpresence.endTimestamp=presence.endTimestamp or 0
|
|
||||||
cpresence.largeImageKey=presence.largeImageKey
|
|
||||||
cpresence.largeImageText=presence.largeImageText
|
|
||||||
cpresence.smallImageKey=presence.smallImageKey
|
|
||||||
cpresence.smallImageText=presence.smallImageText
|
|
||||||
cpresence.partyId=presence.partyId
|
|
||||||
cpresence.partySize=presence.partySize or 0
|
|
||||||
cpresence.partyMax=presence.partyMax or 0
|
|
||||||
cpresence.matchSecret=presence.matchSecret
|
|
||||||
cpresence.joinSecret=presence.joinSecret
|
|
||||||
cpresence.spectateSecret=presence.spectateSecret
|
|
||||||
cpresence.instance=presence.instance or 0
|
|
||||||
|
|
||||||
discordRPClib.Discord_UpdatePresence(cpresence)
|
function RPC.shutdown()
|
||||||
end
|
RPC_C.Discord_Shutdown()
|
||||||
|
end
|
||||||
|
|
||||||
function RPC.clearPresence()
|
function RPC.runCallbacks()
|
||||||
discordRPClib.Discord_ClearPresence()
|
RPC_C.Discord_RunCallbacks()
|
||||||
end
|
end
|
||||||
|
-- http://luajit.org/ext_ffi_semantics.html#callback :
|
||||||
|
-- It is not allowed, to let an FFI call into a C function (runCallbacks)
|
||||||
|
-- get JIT-compiled, which in turn calls a callback, calling into Lua again (e.g. discordRPC.ready).
|
||||||
|
-- Usually this attempt is caught by the interpreter first and the C function
|
||||||
|
-- is blacklisted for compilation.
|
||||||
|
-- solution:
|
||||||
|
-- "Then you'll need to manually turn off JIT-compilation with jit.off() for
|
||||||
|
-- the surrounding Lua function that invokes such a message polling function."
|
||||||
|
jit.off(RPC.runCallbacks)
|
||||||
|
|
||||||
local replyMap={
|
function RPC.updatePresence(presence)
|
||||||
no=0,
|
local func="discordRPC.updatePresence"
|
||||||
yes=1,
|
checkArg(presence,"table","presence",func)
|
||||||
ignore=2,
|
|
||||||
}
|
|
||||||
|
|
||||||
-- maybe let reply take ints too (0, 1, 2) and add constants to the module
|
-- -1 for string length because of 0-termination
|
||||||
function RPC.respond(userId,reply)
|
checkStrArg(presence.state,127,"presence.state",func,true)
|
||||||
checkStrArg(userId, nil, "userId", "discordRPC.respond")
|
checkStrArg(presence.details,127,"presence.details",func,true)
|
||||||
assert(replyMap[reply], "Argument 'reply' to discordRPC.respond has to be one of \"yes\", \"no\" or \"ignore\"")
|
|
||||||
discordRPClib.Discord_Respond(userId, replyMap[reply])
|
|
||||||
end
|
|
||||||
|
|
||||||
-- garbage collection callback
|
checkIntArg(presence.startTimestamp,64,"presence.startTimestamp",func,true)
|
||||||
getmetatable(RPC.gcDummy).__gc=function()
|
checkIntArg(presence.endTimestamp,64,"presence.endTimestamp",func,true)
|
||||||
RPC.shutdown()
|
|
||||||
ready_proxy:free()
|
checkStrArg(presence.largeImageKey,31,"presence.largeImageKey",func,true)
|
||||||
disconnected_proxy:free()
|
checkStrArg(presence.largeImageText,127,"presence.largeImageText",func,true)
|
||||||
errored_proxy:free()
|
checkStrArg(presence.smallImageKey,31,"presence.smallImageKey",func,true)
|
||||||
joinGame_proxy:free()
|
checkStrArg(presence.smallImageText,127,"presence.smallImageText",func,true)
|
||||||
spectateGame_proxy:free()
|
checkStrArg(presence.partyId,127,"presence.partyId",func,true)
|
||||||
joinRequest_proxy:free()
|
|
||||||
|
checkIntArg(presence.partySize,32,"presence.partySize",func,true)
|
||||||
|
checkIntArg(presence.partyMax,32,"presence.partyMax",func,true)
|
||||||
|
|
||||||
|
checkStrArg(presence.matchSecret,127,"presence.matchSecret",func,true)
|
||||||
|
checkStrArg(presence.joinSecret,127,"presence.joinSecret",func,true)
|
||||||
|
checkStrArg(presence.spectateSecret,127,"presence.spectateSecret",func,true)
|
||||||
|
|
||||||
|
checkIntArg(presence.instance,8,"presence.instance",func,true)
|
||||||
|
|
||||||
|
local cpresence=ffi.new("struct DiscordRichPresence")
|
||||||
|
cpresence.state=presence.state
|
||||||
|
cpresence.details=presence.details
|
||||||
|
cpresence.startTimestamp=presence.startTimestamp or 0
|
||||||
|
cpresence.endTimestamp=presence.endTimestamp or 0
|
||||||
|
cpresence.largeImageKey=presence.largeImageKey
|
||||||
|
cpresence.largeImageText=presence.largeImageText
|
||||||
|
cpresence.smallImageKey=presence.smallImageKey
|
||||||
|
cpresence.smallImageText=presence.smallImageText
|
||||||
|
cpresence.partyId=presence.partyId
|
||||||
|
cpresence.partySize=presence.partySize or 0
|
||||||
|
cpresence.partyMax=presence.partyMax or 0
|
||||||
|
cpresence.matchSecret=presence.matchSecret
|
||||||
|
cpresence.joinSecret=presence.joinSecret
|
||||||
|
cpresence.spectateSecret=presence.spectateSecret
|
||||||
|
cpresence.instance=presence.instance or 0
|
||||||
|
|
||||||
|
RPC_C.Discord_UpdatePresence(cpresence)
|
||||||
|
end
|
||||||
|
|
||||||
|
function RPC.clearPresence()
|
||||||
|
RPC_C.Discord_ClearPresence()
|
||||||
|
end
|
||||||
|
|
||||||
|
local replyMap={
|
||||||
|
no=0,
|
||||||
|
yes=1,
|
||||||
|
ignore=2,
|
||||||
|
}
|
||||||
|
|
||||||
|
-- maybe let reply take ints too (0, 1, 2) and add constants to the module
|
||||||
|
function RPC.respond(userId,reply)
|
||||||
|
checkStrArg(userId,nil,"userId","discordRPC.respond")
|
||||||
|
assert(replyMap[reply],"Argument 'reply' to discordRPC.respond must be 'yes'|'no'|'ignore'")
|
||||||
|
RPC_C.Discord_Respond(userId,replyMap[reply])
|
||||||
|
end
|
||||||
|
|
||||||
|
-- garbage collection callback
|
||||||
|
RPC.gcDummy=newproxy(true)
|
||||||
|
getmetatable(RPC.gcDummy).__gc=function()
|
||||||
|
RPC.shutdown()
|
||||||
|
ready_proxy:free()
|
||||||
|
disconnected_proxy:free()
|
||||||
|
errored_proxy:free()
|
||||||
|
joinGame_proxy:free()
|
||||||
|
spectateGame_proxy:free()
|
||||||
|
joinRequest_proxy:free()
|
||||||
|
end
|
||||||
|
|
||||||
|
function RPC.ready(userId,username,discriminator,avatar)
|
||||||
|
print(string.format("Discord: ready (%s,%s,%s,%s)",userId,username,discriminator,avatar))
|
||||||
|
end
|
||||||
|
function RPC.disconnected(errorCode,message)
|
||||||
|
print(string.format("Discord: disconnected (%d: %s)",errorCode,message))
|
||||||
|
end
|
||||||
|
function RPC.errored(errorCode,message)
|
||||||
|
print(string.format("Discord: error (%d: %s)",errorCode,message))
|
||||||
|
end
|
||||||
|
function RPC.joinGame(joinSecret)
|
||||||
|
print(string.format("Discord: join (%s)",joinSecret))
|
||||||
|
end
|
||||||
|
function RPC.spectateGame(spectateSecret)
|
||||||
|
print(string.format("Discord: spectate (%s)",spectateSecret))
|
||||||
|
end
|
||||||
|
function RPC.joinRequest(userId,username,discriminator,avatar)
|
||||||
|
print(string.format("Discord: join request (%s,%s,%s,%s)",userId,username,discriminator,avatar))
|
||||||
|
RPC.respond(userId,'yes')
|
||||||
|
end
|
||||||
|
RPC.initialize(appId,true)
|
||||||
end
|
end
|
||||||
|
|
||||||
local MyRPC={
|
local MyRPC={
|
||||||
appId='1288557386700951554',
|
C=RPC_C,
|
||||||
RPC=RPC,
|
RPC=RPC,
|
||||||
presence={
|
presence={
|
||||||
startTimestamp=os.time(),
|
startTimestamp=os.time(),
|
||||||
@@ -349,57 +289,34 @@ local MyRPC={
|
|||||||
smallImageText="",
|
smallImageText="",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if RPC then
|
|
||||||
function RPC.ready(userId,username,discriminator,avatar)
|
|
||||||
print(string.format("Discord: ready (%s,%s,%s,%s)",userId,username,discriminator,avatar))
|
|
||||||
end
|
|
||||||
|
|
||||||
function RPC.disconnected(errorCode,message)
|
|
||||||
print(string.format("Discord: disconnected (%d: %s)",errorCode,message))
|
|
||||||
end
|
|
||||||
|
|
||||||
function RPC.errored(errorCode,message)
|
|
||||||
print(string.format("Discord: error (%d: %s)",errorCode,message))
|
|
||||||
end
|
|
||||||
|
|
||||||
function RPC.joinGame(joinSecret)
|
|
||||||
print(string.format("Discord: join (%s)",joinSecret))
|
|
||||||
end
|
|
||||||
|
|
||||||
function RPC.spectateGame(spectateSecret)
|
|
||||||
print(string.format("Discord: spectate (%s)",spectateSecret))
|
|
||||||
end
|
|
||||||
|
|
||||||
function RPC.joinRequest(userId,username,discriminator,avatar)
|
|
||||||
print(string.format("Discord: join request (%s,%s,%s,%s)",userId,username,discriminator,avatar))
|
|
||||||
RPC.respond(userId,'yes')
|
|
||||||
end
|
|
||||||
|
|
||||||
RPC.initialize(MyRPC.appId,true)
|
|
||||||
else
|
|
||||||
print("DiscordRPC loading error")
|
|
||||||
print(RPC)
|
|
||||||
end
|
|
||||||
|
|
||||||
---@class DiscordRPC.presence
|
---@class DiscordRPC.presence
|
||||||
---@field startTimestamp? number
|
|
||||||
---@field details? string
|
|
||||||
---@field state? string
|
---@field state? string
|
||||||
|
---@field details? string
|
||||||
|
---@field startTimestamp? number
|
||||||
|
---@field endTimestamp? number
|
||||||
---@field largeImageKey? string
|
---@field largeImageKey? string
|
||||||
---@field largeImageText? string
|
---@field largeImageText? string
|
||||||
---@field smallImageKey? string
|
---@field smallImageKey? string
|
||||||
---@field smallImageText? string
|
---@field smallImageText? string
|
||||||
|
---@field partyId? string
|
||||||
|
---@field partySize? number
|
||||||
|
---@field partyMax? number
|
||||||
|
---@field matchSecret? string
|
||||||
|
---@field joinSecret? string
|
||||||
|
---@field spectateSecret? string
|
||||||
|
---@field instance? number
|
||||||
|
|
||||||
|
---@overload fun()
|
||||||
|
---@overload fun(state: DiscordRPC.presence)
|
||||||
---@param state string
|
---@param state string
|
||||||
---@param details string
|
---@param details string
|
||||||
---@overload fun(new: DiscordRPC.presence)
|
|
||||||
---@overload fun()
|
|
||||||
function MyRPC.update(state,details)
|
function MyRPC.update(state,details)
|
||||||
if state then
|
if state then
|
||||||
for k,v in next,
|
for k,v in next,
|
||||||
type(state)=='string'
|
type(state)=='string'
|
||||||
and {state=state,details=details}
|
and {state=state,details=details}
|
||||||
or state
|
or state
|
||||||
do
|
do
|
||||||
MyRPC.presence[k]=v
|
MyRPC.presence[k]=v
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user