mirror of
https://github.com/SashLilac/cambridge.git
synced 2024-11-16 15:49:03 -06:00
05230ac046
- Uses BG previously from the input config screen, which has gotten a new BG - Minor tweak on the input config screen to display all inputs names regardless of if they are bound or not - Added Mod1 function to `funcs.lua`, may be useful again sometime - Added game settings * Manual locking (per gamemode, per ruleset, on harddrop or on softdrop) * Piece colours (per ruleset, TTC or Arika) * World Reverse toggle - Moved the discordRPC `libs/` directory, as it's a third party library - Edited the `discordRPC.lua` file to look for the dll at the right place regardless of how you run the game (until we fuse it that is) This should have probably been done in several commits, sorry about that
253 lines
9.1 KiB
Lua
253 lines
9.1 KiB
Lua
local ffi = require "ffi"
|
|
local discordRPClib = ffi.load(love.filesystem.getSource().."/libs/discord-rpc")
|
|
|
|
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 discordRPC = {} -- module table
|
|
|
|
-- proxy to detect garbage collection of the module
|
|
discordRPC.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 discordRPC.ready then
|
|
discordRPC.ready(unpackDiscordUser(request))
|
|
end
|
|
end)
|
|
|
|
local disconnected_proxy = ffi.cast("disconnectedPtr", function(errorCode, message)
|
|
if discordRPC.disconnected then
|
|
discordRPC.disconnected(errorCode, ffi.string(message))
|
|
end
|
|
end)
|
|
|
|
local errored_proxy = ffi.cast("erroredPtr", function(errorCode, message)
|
|
if discordRPC.errored then
|
|
discordRPC.errored(errorCode, ffi.string(message))
|
|
end
|
|
end)
|
|
|
|
local joinGame_proxy = ffi.cast("joinGamePtr", function(joinSecret)
|
|
if discordRPC.joinGame then
|
|
discordRPC.joinGame(ffi.string(joinSecret))
|
|
end
|
|
end)
|
|
|
|
local spectateGame_proxy = ffi.cast("spectateGamePtr", function(spectateSecret)
|
|
if discordRPC.spectateGame then
|
|
discordRPC.spectateGame(ffi.string(spectateSecret))
|
|
end
|
|
end)
|
|
|
|
local joinRequest_proxy = ffi.cast("joinRequestPtr", function(request)
|
|
if discordRPC.joinRequest then
|
|
discordRPC.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 discordRPC.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
|
|
|
|
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
|
|
|
|
discordRPClib.Discord_Initialize(applicationId, eventHandlers,
|
|
autoRegister and 1 or 0, optionalSteamId)
|
|
end
|
|
|
|
function discordRPC.shutdown()
|
|
discordRPClib.Discord_Shutdown()
|
|
end
|
|
|
|
function discordRPC.runCallbacks()
|
|
discordRPClib.Discord_RunCallbacks()
|
|
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(discordRPC.runCallbacks)
|
|
|
|
function discordRPC.updatePresence(presence)
|
|
local func = "discordRPC.updatePresence"
|
|
checkArg(presence, "table", "presence", func)
|
|
|
|
-- -1 for string length because of 0-termination
|
|
checkStrArg(presence.state, 127, "presence.state", func, true)
|
|
checkStrArg(presence.details, 127, "presence.details", func, true)
|
|
|
|
checkIntArg(presence.startTimestamp, 64, "presence.startTimestamp", func, true)
|
|
checkIntArg(presence.endTimestamp, 64, "presence.endTimestamp", func, true)
|
|
|
|
checkStrArg(presence.largeImageKey, 31, "presence.largeImageKey", func, true)
|
|
checkStrArg(presence.largeImageText, 127, "presence.largeImageText", func, true)
|
|
checkStrArg(presence.smallImageKey, 31, "presence.smallImageKey", func, true)
|
|
checkStrArg(presence.smallImageText, 127, "presence.smallImageText", func, true)
|
|
checkStrArg(presence.partyId, 127, "presence.partyId", func, true)
|
|
|
|
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
|
|
|
|
discordRPClib.Discord_UpdatePresence(cpresence)
|
|
end
|
|
|
|
function discordRPC.clearPresence()
|
|
discordRPClib.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 discordRPC.respond(userId, reply)
|
|
checkStrArg(userId, nil, "userId", "discordRPC.respond")
|
|
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
|
|
getmetatable(discordRPC.gcDummy).__gc = function()
|
|
discordRPC.shutdown()
|
|
ready_proxy:free()
|
|
disconnected_proxy:free()
|
|
errored_proxy:free()
|
|
joinGame_proxy:free()
|
|
spectateGame_proxy:free()
|
|
joinRequest_proxy:free()
|
|
end
|
|
|
|
return discordRPC
|