1049 lines
47 KiB
Lua
1049 lines
47 KiB
Lua
--[[
|
|
MIT License
|
|
|
|
Copyright for portions of mpvcord are held by Wachid Adi Nugroho, 2018 as part of mpv-discordRPC (https://github.com/cniw/mpv-discordRPC).
|
|
All other copyright for mpvcord are held by Yuto Takano, 2021.
|
|
|
|
Copyright (c) 2018 Wachid Adi Nugroho
|
|
Copyright (c) 2021 Yuto Takano
|
|
|
|
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.
|
|
--]]
|
|
|
|
local ffi = require "ffi"
|
|
|
|
-- Get the host os to load correct lib
|
|
local osname = love.system.getOS()
|
|
local libGameSDK = nil
|
|
|
|
-- FFI requires the libraries really be files just sitting in the filesystem. It
|
|
-- can't load libraries from a .love archive, nor a fused executable on Windows.
|
|
-- Merely using love.filesystem.getSource() only works when running LOVE with
|
|
-- the game unarchived from command line, like "love .".
|
|
--
|
|
-- 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
|
|
local libName32 = "/libs/discordGameSDK/lib/x86/discord_game_sdk"
|
|
local libName64 = "/libs/discordGameSDK/lib/x86_64/discord_game_sdk"
|
|
|
|
if osname == "Linux" and jit.arch == "x64" then
|
|
libGameSDK = ffi.load(source..libName64..".so")
|
|
elseif osname == "OS X" and jit.arch == "x64" then
|
|
libGameSDK = ffi.load(source..libName64..".dylib")
|
|
elseif osname == "Windows" and (jit.arch == "x86" or jit.arch == "x64") then
|
|
-- I would strongly advise never touching this. It was not trivial to get correct. -nightmareci
|
|
|
|
ffi.cdef[[
|
|
typedef uint32_t DWORD;
|
|
typedef char CHAR;
|
|
typedef CHAR *LPSTR;
|
|
typedef const CHAR *LPCSTR;
|
|
typedef wchar_t WCHAR;
|
|
typedef WCHAR *LPWSTR;
|
|
typedef LPWSTR PWSTR;
|
|
typedef const WCHAR *LPCWSTR;
|
|
|
|
static const DWORD CP_UTF8 = 65001;
|
|
int32_t MultiByteToWideChar(
|
|
DWORD CodePage,
|
|
DWORD dwFlags,
|
|
LPCSTR lpMultiByteStr,
|
|
int32_t cbMultiByte,
|
|
LPWSTR lpWideCharStr,
|
|
int32_t cchWideChar
|
|
);
|
|
|
|
int32_t WideCharToMultiByte(
|
|
DWORD CodePage,
|
|
DWORD dwFlags,
|
|
LPCWSTR lpWideCharStr,
|
|
int32_t cchWideChar,
|
|
LPSTR lpMultiByteStr,
|
|
int32_t cbMultiByte,
|
|
void* lpDefaultChar,
|
|
void* lpUsedDefaultChar
|
|
);
|
|
|
|
DWORD GetShortPathNameW(
|
|
LPCWSTR lpszLongPath,
|
|
LPWSTR lpszShortPath,
|
|
DWORD cchBuffer
|
|
);
|
|
]]
|
|
|
|
local originalWideSize = ffi.C.MultiByteToWideChar(ffi.C.CP_UTF8, 0, source, -1, nil, 0)
|
|
local originalWide = ffi.new('WCHAR[?]', originalWideSize)
|
|
ffi.C.MultiByteToWideChar(ffi.C.CP_UTF8, 0, source, -1, originalWide, originalWideSize)
|
|
|
|
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)
|
|
|
|
source = ffi.string(sourceChar)
|
|
|
|
|
|
if jit.arch == "x86" then
|
|
libGameSDK = ffi.load(source..libName32..".dll")
|
|
elseif jit.arch == "x64" then
|
|
libGameSDK = ffi.load(source..libName64..".dll")
|
|
end
|
|
else
|
|
error(string.format("Discord GameSDK not supported on OS \"%s\", CPU architecture \"%s\"", osname, jit.arch))
|
|
end
|
|
|
|
ffi.cdef[[
|
|
void free(void *ptr);
|
|
void *malloc(size_t size);
|
|
void *memset(void *s, int c, size_t n);
|
|
|
|
enum{DISCORD_VERSION = 2};
|
|
enum{DISCORD_APPLICATION_MANAGER_VERSION = 1};
|
|
enum{DISCORD_USER_MANAGER_VERSION = 1};
|
|
enum{DISCORD_IMAGE_MANAGER_VERSION = 1};
|
|
enum{DISCORD_ACTIVITY_MANAGER_VERSION = 1};
|
|
enum{DISCORD_RELATIONSHIP_MANAGER_VERSION = 1};
|
|
enum{DISCORD_LOBBY_MANAGER_VERSION = 1};
|
|
enum{DISCORD_NETWORK_MANAGER_VERSION = 1};
|
|
enum{DISCORD_OVERLAY_MANAGER_VERSION = 1};
|
|
enum{DISCORD_STORAGE_MANAGER_VERSION = 1};
|
|
enum{DISCORD_STORE_MANAGER_VERSION = 1};
|
|
enum{DISCORD_VOICE_MANAGER_VERSION = 1};
|
|
enum{DISCORD_ACHIEVEMENT_MANAGER_VERSION = 1};
|
|
|
|
enum EDiscordResult {
|
|
DiscordResult_Ok = 0,
|
|
DiscordResult_ServiceUnavailable = 1,
|
|
DiscordResult_InvalidVersion = 2,
|
|
DiscordResult_LockFailed = 3,
|
|
DiscordResult_InternalError = 4,
|
|
DiscordResult_InvalidPayload = 5,
|
|
DiscordResult_InvalidCommand = 6,
|
|
DiscordResult_InvalidPermissions = 7,
|
|
DiscordResult_NotFetched = 8,
|
|
DiscordResult_NotFound = 9,
|
|
DiscordResult_Conflict = 10,
|
|
DiscordResult_InvalidSecret = 11,
|
|
DiscordResult_InvalidJoinSecret = 12,
|
|
DiscordResult_NoEligibleActivity = 13,
|
|
DiscordResult_InvalidInvite = 14,
|
|
DiscordResult_NotAuthenticated = 15,
|
|
DiscordResult_InvalidAccessToken = 16,
|
|
DiscordResult_ApplicationMismatch = 17,
|
|
DiscordResult_InvalidDataUrl = 18,
|
|
DiscordResult_InvalidBase64 = 19,
|
|
DiscordResult_NotFiltered = 20,
|
|
DiscordResult_LobbyFull = 21,
|
|
DiscordResult_InvalidLobbySecret = 22,
|
|
DiscordResult_InvalidFilename = 23,
|
|
DiscordResult_InvalidFileSize = 24,
|
|
DiscordResult_InvalidEntitlement = 25,
|
|
DiscordResult_NotInstalled = 26,
|
|
DiscordResult_NotRunning = 27,
|
|
DiscordResult_InsufficientBuffer = 28,
|
|
DiscordResult_PurchaseCanceled = 29,
|
|
DiscordResult_InvalidGuild = 30,
|
|
DiscordResult_InvalidEvent = 31,
|
|
DiscordResult_InvalidChannel = 32,
|
|
DiscordResult_InvalidOrigin = 33,
|
|
DiscordResult_RateLimited = 34,
|
|
DiscordResult_OAuth2Error = 35,
|
|
DiscordResult_SelectChannelTimeout = 36,
|
|
DiscordResult_GetGuildTimeout = 37,
|
|
DiscordResult_SelectVoiceForceRequired = 38,
|
|
DiscordResult_CaptureShortcutAlreadyListening = 39,
|
|
DiscordResult_UnauthorizedForAchievement = 40,
|
|
DiscordResult_InvalidGiftCode = 41,
|
|
DiscordResult_PurchaseError = 42,
|
|
DiscordResult_TransactionAborted = 43,
|
|
};
|
|
|
|
enum EDiscordCreateFlags {
|
|
DiscordCreateFlags_Default = 0,
|
|
DiscordCreateFlags_NoRequireDiscord = 1,
|
|
};
|
|
|
|
enum EDiscordLogLevel {
|
|
DiscordLogLevel_Error = 1,
|
|
DiscordLogLevel_Warn,
|
|
DiscordLogLevel_Info,
|
|
DiscordLogLevel_Debug,
|
|
};
|
|
|
|
enum EDiscordUserFlag {
|
|
DiscordUserFlag_Partner = 2,
|
|
DiscordUserFlag_HypeSquadEvents = 4,
|
|
DiscordUserFlag_HypeSquadHouse1 = 64,
|
|
DiscordUserFlag_HypeSquadHouse2 = 128,
|
|
DiscordUserFlag_HypeSquadHouse3 = 256,
|
|
};
|
|
|
|
enum EDiscordPremiumType {
|
|
DiscordPremiumType_None = 0,
|
|
DiscordPremiumType_Tier1 = 1,
|
|
DiscordPremiumType_Tier2 = 2,
|
|
};
|
|
|
|
enum EDiscordImageType {
|
|
DiscordImageType_User,
|
|
};
|
|
|
|
enum EDiscordActivityType {
|
|
DiscordActivityType_Playing,
|
|
DiscordActivityType_Streaming,
|
|
DiscordActivityType_Listening,
|
|
DiscordActivityType_Watching,
|
|
};
|
|
|
|
enum EDiscordActivityActionType {
|
|
DiscordActivityActionType_Join = 1,
|
|
DiscordActivityActionType_Spectate,
|
|
};
|
|
|
|
enum EDiscordActivityJoinRequestReply {
|
|
DiscordActivityJoinRequestReply_No,
|
|
DiscordActivityJoinRequestReply_Yes,
|
|
DiscordActivityJoinRequestReply_Ignore,
|
|
};
|
|
|
|
enum EDiscordStatus {
|
|
DiscordStatus_Offline = 0,
|
|
DiscordStatus_Online = 1,
|
|
DiscordStatus_Idle = 2,
|
|
DiscordStatus_DoNotDisturb = 3,
|
|
};
|
|
|
|
enum EDiscordRelationshipType {
|
|
DiscordRelationshipType_None,
|
|
DiscordRelationshipType_Friend,
|
|
DiscordRelationshipType_Blocked,
|
|
DiscordRelationshipType_PendingIncoming,
|
|
DiscordRelationshipType_PendingOutgoing,
|
|
DiscordRelationshipType_Implicit,
|
|
};
|
|
|
|
enum EDiscordLobbyType {
|
|
DiscordLobbyType_Private = 1,
|
|
DiscordLobbyType_Public,
|
|
};
|
|
|
|
enum EDiscordLobbySearchComparison {
|
|
DiscordLobbySearchComparison_LessThanOrEqual = -2,
|
|
DiscordLobbySearchComparison_LessThan,
|
|
DiscordLobbySearchComparison_Equal,
|
|
DiscordLobbySearchComparison_GreaterThan,
|
|
DiscordLobbySearchComparison_GreaterThanOrEqual,
|
|
DiscordLobbySearchComparison_NotEqual,
|
|
};
|
|
|
|
enum EDiscordLobbySearchCast {
|
|
DiscordLobbySearchCast_String = 1,
|
|
DiscordLobbySearchCast_Number,
|
|
};
|
|
|
|
enum EDiscordLobbySearchDistance {
|
|
DiscordLobbySearchDistance_Local,
|
|
DiscordLobbySearchDistance_Default,
|
|
DiscordLobbySearchDistance_Extended,
|
|
DiscordLobbySearchDistance_Global,
|
|
};
|
|
|
|
enum EDiscordEntitlementType {
|
|
DiscordEntitlementType_Purchase = 1,
|
|
DiscordEntitlementType_PremiumSubscription,
|
|
DiscordEntitlementType_DeveloperGift,
|
|
DiscordEntitlementType_TestModePurchase,
|
|
DiscordEntitlementType_FreePurchase,
|
|
DiscordEntitlementType_UserGift,
|
|
DiscordEntitlementType_PremiumPurchase,
|
|
};
|
|
|
|
enum EDiscordSkuType {
|
|
DiscordSkuType_Application = 1,
|
|
DiscordSkuType_DLC,
|
|
DiscordSkuType_Consumable,
|
|
DiscordSkuType_Bundle,
|
|
};
|
|
|
|
enum EDiscordInputModeType {
|
|
DiscordInputModeType_VoiceActivity = 0,
|
|
DiscordInputModeType_PushToTalk,
|
|
};
|
|
|
|
typedef int64_t DiscordClientId;
|
|
typedef int32_t DiscordVersion;
|
|
typedef int64_t DiscordSnowflake;
|
|
typedef int64_t DiscordTimestamp;
|
|
typedef DiscordSnowflake DiscordUserId;
|
|
typedef char DiscordLocale[128];
|
|
typedef char DiscordBranch[4096];
|
|
typedef DiscordSnowflake DiscordLobbyId;
|
|
typedef char DiscordLobbySecret[128];
|
|
typedef char DiscordMetadataKey[256];
|
|
typedef char DiscordMetadataValue[4096];
|
|
typedef uint64_t DiscordNetworkPeerId;
|
|
typedef uint8_t DiscordNetworkChannelId;
|
|
typedef char DiscordPath[4096];
|
|
typedef char DiscordDateTime[64];
|
|
|
|
struct DiscordUser {
|
|
DiscordUserId id;
|
|
char username[256];
|
|
char discriminator[8];
|
|
char avatar[128];
|
|
bool bot;
|
|
};
|
|
|
|
struct DiscordOAuth2Token {
|
|
char access_token[128];
|
|
char scopes[1024];
|
|
DiscordTimestamp expires;
|
|
};
|
|
|
|
struct DiscordImageHandle {
|
|
enum EDiscordImageType type;
|
|
int64_t id;
|
|
uint32_t size;
|
|
};
|
|
|
|
struct DiscordImageDimensions {
|
|
uint32_t width;
|
|
uint32_t height;
|
|
};
|
|
|
|
|
|
struct DiscordActivityTimestamps {
|
|
DiscordTimestamp start;
|
|
DiscordTimestamp end;
|
|
};
|
|
|
|
struct DiscordActivityAssets {
|
|
char large_image[128];
|
|
char large_text[128];
|
|
char small_image[128];
|
|
char small_text[128];
|
|
};
|
|
|
|
struct DiscordPartySize {
|
|
int32_t current_size;
|
|
int32_t max_size;
|
|
};
|
|
|
|
struct DiscordActivityParty {
|
|
char id[128];
|
|
struct DiscordPartySize size;
|
|
};
|
|
|
|
struct DiscordActivitySecrets {
|
|
char match[128];
|
|
char join[128];
|
|
char spectate[128];
|
|
};
|
|
|
|
struct DiscordActivity {
|
|
enum EDiscordActivityType type;
|
|
int64_t application_id;
|
|
char name[128];
|
|
char state[128];
|
|
char details[128];
|
|
struct DiscordActivityTimestamps timestamps;
|
|
struct DiscordActivityAssets assets;
|
|
struct DiscordActivityParty party;
|
|
struct DiscordActivitySecrets secrets;
|
|
bool instance;
|
|
};
|
|
|
|
struct DiscordPresence {
|
|
enum EDiscordStatus status;
|
|
struct DiscordActivity activity;
|
|
};
|
|
|
|
struct DiscordRelationship {
|
|
enum EDiscordRelationshipType type;
|
|
struct DiscordUser user;
|
|
struct DiscordPresence presence;
|
|
};
|
|
|
|
struct DiscordLobby {
|
|
DiscordLobbyId id;
|
|
enum EDiscordLobbyType type;
|
|
DiscordUserId owner_id;
|
|
DiscordLobbySecret secret;
|
|
uint32_t capacity;
|
|
bool locked;
|
|
};
|
|
|
|
struct DiscordFileStat {
|
|
const char filename[260];
|
|
uint64_t size;
|
|
uint64_t last_modified;
|
|
};
|
|
|
|
struct DiscordEntitlement {
|
|
DiscordSnowflake id;
|
|
enum EDiscordEntitlementType type;
|
|
DiscordSnowflake sku_id;
|
|
};
|
|
|
|
struct DiscordSkuPrice {
|
|
uint32_t amount;
|
|
const char currency[16];
|
|
};
|
|
|
|
struct DiscordSku {
|
|
DiscordSnowflake id;
|
|
enum EDiscordSkuType type;
|
|
const char name[256];
|
|
struct DiscordSkuPrice price;
|
|
};
|
|
|
|
struct DiscordInputMode {
|
|
enum EDiscordInputModeType type;
|
|
const char shortcut[256];
|
|
};
|
|
|
|
struct DiscordUserAchievement {
|
|
DiscordSnowflake user_id;
|
|
DiscordSnowflake achievement_id;
|
|
uint8_t percent_complete;
|
|
DiscordDateTime unlocked_at;
|
|
};
|
|
|
|
struct IDiscordLobbyTransaction {
|
|
enum EDiscordResult (*set_type)(struct IDiscordLobbyTransaction* lobby_transaction, enum EDiscordLobbyType type);
|
|
enum EDiscordResult (*set_owner)(struct IDiscordLobbyTransaction* lobby_transaction, DiscordUserId owner_id);
|
|
enum EDiscordResult (*set_capacity)(struct IDiscordLobbyTransaction* lobby_transaction, uint32_t capacity);
|
|
enum EDiscordResult (*set_metadata)(struct IDiscordLobbyTransaction* lobby_transaction, DiscordMetadataKey key, DiscordMetadataValue value);
|
|
enum EDiscordResult (*delete_metadata)(struct IDiscordLobbyTransaction* lobby_transaction, DiscordMetadataKey key);
|
|
enum EDiscordResult (*set_locked)(struct IDiscordLobbyTransaction* lobby_transaction, bool locked);
|
|
};
|
|
|
|
struct IDiscordLobbyMemberTransaction {
|
|
enum EDiscordResult (*set_metadata)(struct IDiscordLobbyMemberTransaction* lobby_member_transaction, DiscordMetadataKey key, DiscordMetadataValue value);
|
|
enum EDiscordResult (*delete_metadata)(struct IDiscordLobbyMemberTransaction* lobby_member_transaction, DiscordMetadataKey key);
|
|
};
|
|
|
|
struct IDiscordLobbySearchQuery {
|
|
enum EDiscordResult (*filter)(struct IDiscordLobbySearchQuery* lobby_search_query, DiscordMetadataKey key, enum EDiscordLobbySearchComparison comparison, enum EDiscordLobbySearchCast cast, DiscordMetadataValue value);
|
|
enum EDiscordResult (*sort)(struct IDiscordLobbySearchQuery* lobby_search_query, DiscordMetadataKey key, enum EDiscordLobbySearchCast cast, DiscordMetadataValue value);
|
|
enum EDiscordResult (*limit)(struct IDiscordLobbySearchQuery* lobby_search_query, uint32_t limit);
|
|
enum EDiscordResult (*distance)(struct IDiscordLobbySearchQuery* lobby_search_query, enum EDiscordLobbySearchDistance distance);
|
|
};
|
|
|
|
typedef void* IDiscordApplicationEvents;
|
|
|
|
struct IDiscordApplicationManager {
|
|
void (*validate_or_exit)(struct IDiscordApplicationManager* manager, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result));
|
|
void (*get_current_locale)(struct IDiscordApplicationManager* manager, DiscordLocale* locale);
|
|
void (*get_current_branch)(struct IDiscordApplicationManager* manager, DiscordBranch* branch);
|
|
void (*get_oauth2_token)(struct IDiscordApplicationManager* manager, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result, struct DiscordOAuth2Token* oauth2_token));
|
|
void (*get_ticket)(struct IDiscordApplicationManager* manager, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result, const char* data));
|
|
};
|
|
|
|
struct IDiscordUserEvents {
|
|
void (*on_current_user_update)(void* event_data);
|
|
};
|
|
|
|
struct IDiscordUserManager {
|
|
enum EDiscordResult (*get_current_user)(struct IDiscordUserManager* manager, struct DiscordUser* current_user);
|
|
void (*get_user)(struct IDiscordUserManager* manager, DiscordUserId user_id, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result, struct DiscordUser* user));
|
|
enum EDiscordResult (*get_current_user_premium_type)(struct IDiscordUserManager* manager, enum EDiscordPremiumType* premium_type);
|
|
enum EDiscordResult (*current_user_has_flag)(struct IDiscordUserManager* manager, enum EDiscordUserFlag flag, bool* has_flag);
|
|
};
|
|
|
|
typedef void* IDiscordImageEvents;
|
|
|
|
struct IDiscordImageManager {
|
|
void (*fetch)(struct IDiscordImageManager* manager, struct DiscordImageHandle handle, bool refresh, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result, struct DiscordImageHandle handle_result));
|
|
enum EDiscordResult (*get_dimensions)(struct IDiscordImageManager* manager, struct DiscordImageHandle handle, struct DiscordImageDimensions* dimensions);
|
|
enum EDiscordResult (*get_data)(struct IDiscordImageManager* manager, struct DiscordImageHandle handle, uint8_t* data, uint32_t data_length);
|
|
};
|
|
|
|
struct IDiscordActivityEvents {
|
|
void (*on_activity_join)(void* event_data, const char* secret);
|
|
void (*on_activity_spectate)(void* event_data, const char* secret);
|
|
void (*on_activity_join_request)(void* event_data, struct DiscordUser* user);
|
|
void (*on_activity_invite)(void* event_data, enum EDiscordActivityActionType type, struct DiscordUser* user, struct DiscordActivity* activity);
|
|
};
|
|
|
|
struct IDiscordActivityManager {
|
|
enum EDiscordResult (*register_command)(struct IDiscordActivityManager* manager, const char* command);
|
|
enum EDiscordResult (*register_steam)(struct IDiscordActivityManager* manager, uint32_t steam_id);
|
|
void (*update_activity)(struct IDiscordActivityManager* manager, struct DiscordActivity* activity, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result));
|
|
void (*clear_activity)(struct IDiscordActivityManager* manager, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result));
|
|
void (*send_request_reply)(struct IDiscordActivityManager* manager, DiscordUserId user_id, enum EDiscordActivityJoinRequestReply reply, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result));
|
|
void (*send_invite)(struct IDiscordActivityManager* manager, DiscordUserId user_id, enum EDiscordActivityActionType type, const char* content, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result));
|
|
void (*accept_invite)(struct IDiscordActivityManager* manager, DiscordUserId user_id, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result));
|
|
};
|
|
|
|
struct IDiscordRelationshipEvents {
|
|
void (*on_refresh)(void* event_data);
|
|
void (*on_relationship_update)(void* event_data, struct DiscordRelationship* relationship);
|
|
};
|
|
|
|
struct IDiscordRelationshipManager {
|
|
void (*filter)(struct IDiscordRelationshipManager* manager, void* filter_data, bool (*filter)(void* filter_data, struct DiscordRelationship* relationship));
|
|
enum EDiscordResult (*count)(struct IDiscordRelationshipManager* manager, int32_t* count);
|
|
enum EDiscordResult (*get)(struct IDiscordRelationshipManager* manager, DiscordUserId user_id, struct DiscordRelationship* relationship);
|
|
enum EDiscordResult (*get_at)(struct IDiscordRelationshipManager* manager, uint32_t index, struct DiscordRelationship* relationship);
|
|
};
|
|
|
|
struct IDiscordLobbyEvents {
|
|
void (*on_lobby_update)(void* event_data, int64_t lobby_id);
|
|
void (*on_lobby_delete)(void* event_data, int64_t lobby_id, uint32_t reason);
|
|
void (*on_member_connect)(void* event_data, int64_t lobby_id, int64_t user_id);
|
|
void (*on_member_update)(void* event_data, int64_t lobby_id, int64_t user_id);
|
|
void (*on_member_disconnect)(void* event_data, int64_t lobby_id, int64_t user_id);
|
|
void (*on_lobby_message)(void* event_data, int64_t lobby_id, int64_t user_id, uint8_t* data, uint32_t data_length);
|
|
void (*on_speaking)(void* event_data, int64_t lobby_id, int64_t user_id, bool speaking);
|
|
void (*on_network_message)(void* event_data, int64_t lobby_id, int64_t user_id, uint8_t channel_id, uint8_t* data, uint32_t data_length);
|
|
};
|
|
|
|
struct IDiscordLobbyManager {
|
|
enum EDiscordResult (*get_lobby_create_transaction)(struct IDiscordLobbyManager* manager, struct IDiscordLobbyTransaction** transaction);
|
|
enum EDiscordResult (*get_lobby_update_transaction)(struct IDiscordLobbyManager* manager, DiscordLobbyId lobby_id, struct IDiscordLobbyTransaction** transaction);
|
|
enum EDiscordResult (*get_member_update_transaction)(struct IDiscordLobbyManager* manager, DiscordLobbyId lobby_id, DiscordUserId user_id, struct IDiscordLobbyMemberTransaction** transaction);
|
|
void (*create_lobby)(struct IDiscordLobbyManager* manager, struct IDiscordLobbyTransaction* transaction, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result, struct DiscordLobby* lobby));
|
|
void (*update_lobby)(struct IDiscordLobbyManager* manager, DiscordLobbyId lobby_id, struct IDiscordLobbyTransaction* transaction, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result));
|
|
void (*delete_lobby)(struct IDiscordLobbyManager* manager, DiscordLobbyId lobby_id, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result));
|
|
void (*connect_lobby)(struct IDiscordLobbyManager* manager, DiscordLobbyId lobby_id, DiscordLobbySecret secret, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result, struct DiscordLobby* lobby));
|
|
void (*connect_lobby_with_activity_secret)(struct IDiscordLobbyManager* manager, DiscordLobbySecret activity_secret, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result, struct DiscordLobby* lobby));
|
|
void (*disconnect_lobby)(struct IDiscordLobbyManager* manager, DiscordLobbyId lobby_id, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result));
|
|
enum EDiscordResult (*get_lobby)(struct IDiscordLobbyManager* manager, DiscordLobbyId lobby_id, struct DiscordLobby* lobby);
|
|
enum EDiscordResult (*get_lobby_activity_secret)(struct IDiscordLobbyManager* manager, DiscordLobbyId lobby_id, DiscordLobbySecret* secret);
|
|
enum EDiscordResult (*get_lobby_metadata_value)(struct IDiscordLobbyManager* manager, DiscordLobbyId lobby_id, DiscordMetadataKey key, DiscordMetadataValue* value);
|
|
enum EDiscordResult (*get_lobby_metadata_key)(struct IDiscordLobbyManager* manager, DiscordLobbyId lobby_id, int32_t index, DiscordMetadataKey* key);
|
|
enum EDiscordResult (*lobby_metadata_count)(struct IDiscordLobbyManager* manager, DiscordLobbyId lobby_id, int32_t* count);
|
|
enum EDiscordResult (*member_count)(struct IDiscordLobbyManager* manager, DiscordLobbyId lobby_id, int32_t* count);
|
|
enum EDiscordResult (*get_member_user_id)(struct IDiscordLobbyManager* manager, DiscordLobbyId lobby_id, int32_t index, DiscordUserId* user_id);
|
|
enum EDiscordResult (*get_member_user)(struct IDiscordLobbyManager* manager, DiscordLobbyId lobby_id, DiscordUserId user_id, struct DiscordUser* user);
|
|
enum EDiscordResult (*get_member_metadata_value)(struct IDiscordLobbyManager* manager, DiscordLobbyId lobby_id, DiscordUserId user_id, DiscordMetadataKey key, DiscordMetadataValue* value);
|
|
enum EDiscordResult (*get_member_metadata_key)(struct IDiscordLobbyManager* manager, DiscordLobbyId lobby_id, DiscordUserId user_id, int32_t index, DiscordMetadataKey* key);
|
|
enum EDiscordResult (*member_metadata_count)(struct IDiscordLobbyManager* manager, DiscordLobbyId lobby_id, DiscordUserId user_id, int32_t* count);
|
|
void (*update_member)(struct IDiscordLobbyManager* manager, DiscordLobbyId lobby_id, DiscordUserId user_id, struct IDiscordLobbyMemberTransaction* transaction, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result));
|
|
void (*send_lobby_message)(struct IDiscordLobbyManager* manager, DiscordLobbyId lobby_id, uint8_t* data, uint32_t data_length, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result));
|
|
enum EDiscordResult (*get_search_query)(struct IDiscordLobbyManager* manager, struct IDiscordLobbySearchQuery** query);
|
|
void (*search)(struct IDiscordLobbyManager* manager, struct IDiscordLobbySearchQuery* query, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result));
|
|
void (*lobby_count)(struct IDiscordLobbyManager* manager, int32_t* count);
|
|
enum EDiscordResult (*get_lobby_id)(struct IDiscordLobbyManager* manager, int32_t index, DiscordLobbyId* lobby_id);
|
|
void (*connect_voice)(struct IDiscordLobbyManager* manager, DiscordLobbyId lobby_id, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result));
|
|
void (*disconnect_voice)(struct IDiscordLobbyManager* manager, DiscordLobbyId lobby_id, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result));
|
|
enum EDiscordResult (*connect_network)(struct IDiscordLobbyManager* manager, DiscordLobbyId lobby_id);
|
|
enum EDiscordResult (*disconnect_network)(struct IDiscordLobbyManager* manager, DiscordLobbyId lobby_id);
|
|
enum EDiscordResult (*flush_network)(struct IDiscordLobbyManager* manager);
|
|
enum EDiscordResult (*open_network_channel)(struct IDiscordLobbyManager* manager, DiscordLobbyId lobby_id, uint8_t channel_id, bool reliable);
|
|
enum EDiscordResult (*send_network_message)(struct IDiscordLobbyManager* manager, DiscordLobbyId lobby_id, DiscordUserId user_id, uint8_t channel_id, uint8_t* data, uint32_t data_length);
|
|
};
|
|
|
|
struct IDiscordNetworkEvents {
|
|
void (*on_message)(void* event_data, DiscordNetworkPeerId peer_id, DiscordNetworkChannelId channel_id, uint8_t* data, uint32_t data_length);
|
|
void (*on_route_update)(void* event_data, const char* route_data);
|
|
};
|
|
|
|
struct IDiscordNetworkManager {
|
|
/**
|
|
* Get the local peer ID for this process.
|
|
*/
|
|
void (*get_peer_id)(struct IDiscordNetworkManager* manager, DiscordNetworkPeerId* peer_id);
|
|
/**
|
|
* Send pending network messages.
|
|
*/
|
|
enum EDiscordResult (*flush)(struct IDiscordNetworkManager* manager);
|
|
/**
|
|
* Open a connection to a remote peer.
|
|
*/
|
|
enum EDiscordResult (*open_peer)(struct IDiscordNetworkManager* manager, DiscordNetworkPeerId peer_id, const char* route_data);
|
|
/**
|
|
* Update the route data for a connected peer.
|
|
*/
|
|
enum EDiscordResult (*update_peer)(struct IDiscordNetworkManager* manager, DiscordNetworkPeerId peer_id, const char* route_data);
|
|
/**
|
|
* Close the connection to a remote peer.
|
|
*/
|
|
enum EDiscordResult (*close_peer)(struct IDiscordNetworkManager* manager, DiscordNetworkPeerId peer_id);
|
|
/**
|
|
* Open a message channel to a connected peer.
|
|
*/
|
|
enum EDiscordResult (*open_channel)(struct IDiscordNetworkManager* manager, DiscordNetworkPeerId peer_id, DiscordNetworkChannelId channel_id, bool reliable);
|
|
/**
|
|
* Close a message channel to a connected peer.
|
|
*/
|
|
enum EDiscordResult (*close_channel)(struct IDiscordNetworkManager* manager, DiscordNetworkPeerId peer_id, DiscordNetworkChannelId channel_id);
|
|
/**
|
|
* Send a message to a connected peer over an opened message channel.
|
|
*/
|
|
enum EDiscordResult (*send_message)(struct IDiscordNetworkManager* manager, DiscordNetworkPeerId peer_id, DiscordNetworkChannelId channel_id, uint8_t* data, uint32_t data_length);
|
|
};
|
|
|
|
struct IDiscordOverlayEvents {
|
|
void (*on_toggle)(void* event_data, bool locked);
|
|
};
|
|
|
|
struct IDiscordOverlayManager {
|
|
void (*is_enabled)(struct IDiscordOverlayManager* manager, bool* enabled);
|
|
void (*is_locked)(struct IDiscordOverlayManager* manager, bool* locked);
|
|
void (*set_locked)(struct IDiscordOverlayManager* manager, bool locked, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result));
|
|
void (*open_activity_invite)(struct IDiscordOverlayManager* manager, enum EDiscordActivityActionType type, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result));
|
|
void (*open_guild_invite)(struct IDiscordOverlayManager* manager, const char* code, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result));
|
|
void (*open_voice_settings)(struct IDiscordOverlayManager* manager, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result));
|
|
};
|
|
|
|
typedef void* IDiscordStorageEvents;
|
|
|
|
struct IDiscordStorageManager {
|
|
enum EDiscordResult (*read)(struct IDiscordStorageManager* manager, const char* name, uint8_t* data, uint32_t data_length, uint32_t* read);
|
|
void (*read_async)(struct IDiscordStorageManager* manager, const char* name, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result, uint8_t* data, uint32_t data_length));
|
|
void (*read_async_partial)(struct IDiscordStorageManager* manager, const char* name, uint64_t offset, uint64_t length, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result, uint8_t* data, uint32_t data_length));
|
|
enum EDiscordResult (*write)(struct IDiscordStorageManager* manager, const char* name, uint8_t* data, uint32_t data_length);
|
|
void (*write_async)(struct IDiscordStorageManager* manager, const char* name, uint8_t* data, uint32_t data_length, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result));
|
|
enum EDiscordResult (*delete_)(struct IDiscordStorageManager* manager, const char* name);
|
|
enum EDiscordResult (*exists)(struct IDiscordStorageManager* manager, const char* name, bool* exists);
|
|
void (*count)(struct IDiscordStorageManager* manager, int32_t* count);
|
|
enum EDiscordResult (*stat)(struct IDiscordStorageManager* manager, const char* name, struct DiscordFileStat* stat);
|
|
enum EDiscordResult (*stat_at)(struct IDiscordStorageManager* manager, int32_t index, struct DiscordFileStat* stat);
|
|
enum EDiscordResult (*get_path)(struct IDiscordStorageManager* manager, DiscordPath* path);
|
|
};
|
|
|
|
struct IDiscordStoreEvents {
|
|
void (*on_entitlement_create)(void* event_data, struct DiscordEntitlement* entitlement);
|
|
void (*on_entitlement_delete)(void* event_data, struct DiscordEntitlement* entitlement);
|
|
};
|
|
|
|
struct IDiscordStoreManager {
|
|
void (*fetch_skus)(struct IDiscordStoreManager* manager, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result));
|
|
void (*count_skus)(struct IDiscordStoreManager* manager, int32_t* count);
|
|
enum EDiscordResult (*get_sku)(struct IDiscordStoreManager* manager, DiscordSnowflake sku_id, struct DiscordSku* sku);
|
|
enum EDiscordResult (*get_sku_at)(struct IDiscordStoreManager* manager, int32_t index, struct DiscordSku* sku);
|
|
void (*fetch_entitlements)(struct IDiscordStoreManager* manager, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result));
|
|
void (*count_entitlements)(struct IDiscordStoreManager* manager, int32_t* count);
|
|
enum EDiscordResult (*get_entitlement)(struct IDiscordStoreManager* manager, DiscordSnowflake entitlement_id, struct DiscordEntitlement* entitlement);
|
|
enum EDiscordResult (*get_entitlement_at)(struct IDiscordStoreManager* manager, int32_t index, struct DiscordEntitlement* entitlement);
|
|
enum EDiscordResult (*has_sku_entitlement)(struct IDiscordStoreManager* manager, DiscordSnowflake sku_id, bool* has_entitlement);
|
|
void (*start_purchase)(struct IDiscordStoreManager* manager, DiscordSnowflake sku_id, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result));
|
|
};
|
|
|
|
struct IDiscordVoiceEvents {
|
|
void (*on_settings_update)(void* event_data);
|
|
};
|
|
|
|
struct IDiscordVoiceManager {
|
|
enum EDiscordResult (*get_input_mode)(struct IDiscordVoiceManager* manager, struct DiscordInputMode* input_mode);
|
|
void (*set_input_mode)(struct IDiscordVoiceManager* manager, struct DiscordInputMode input_mode, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result));
|
|
enum EDiscordResult (*is_self_mute)(struct IDiscordVoiceManager* manager, bool* mute);
|
|
enum EDiscordResult (*set_self_mute)(struct IDiscordVoiceManager* manager, bool mute);
|
|
enum EDiscordResult (*is_self_deaf)(struct IDiscordVoiceManager* manager, bool* deaf);
|
|
enum EDiscordResult (*set_self_deaf)(struct IDiscordVoiceManager* manager, bool deaf);
|
|
enum EDiscordResult (*is_local_mute)(struct IDiscordVoiceManager* manager, DiscordSnowflake user_id, bool* mute);
|
|
enum EDiscordResult (*set_local_mute)(struct IDiscordVoiceManager* manager, DiscordSnowflake user_id, bool mute);
|
|
enum EDiscordResult (*get_local_volume)(struct IDiscordVoiceManager* manager, DiscordSnowflake user_id, uint8_t* volume);
|
|
enum EDiscordResult (*set_local_volume)(struct IDiscordVoiceManager* manager, DiscordSnowflake user_id, uint8_t volume);
|
|
};
|
|
|
|
struct IDiscordAchievementEvents {
|
|
void (*on_user_achievement_update)(void* event_data, struct DiscordUserAchievement* user_achievement);
|
|
};
|
|
|
|
struct IDiscordAchievementManager {
|
|
void (*set_user_achievement)(struct IDiscordAchievementManager* manager, DiscordSnowflake achievement_id, uint8_t percent_complete, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result));
|
|
void (*fetch_user_achievements)(struct IDiscordAchievementManager* manager, void* callback_data, void (*callback)(void* callback_data, enum EDiscordResult result));
|
|
void (*count_user_achievements)(struct IDiscordAchievementManager* manager, int32_t* count);
|
|
enum EDiscordResult (*get_user_achievement)(struct IDiscordAchievementManager* manager, DiscordSnowflake user_achievement_id, struct DiscordUserAchievement* user_achievement);
|
|
enum EDiscordResult (*get_user_achievement_at)(struct IDiscordAchievementManager* manager, int32_t index, struct DiscordUserAchievement* user_achievement);
|
|
};
|
|
|
|
typedef void* IDiscordCoreEvents;
|
|
|
|
struct IDiscordCore {
|
|
void (*destroy)(struct IDiscordCore* core);
|
|
enum EDiscordResult (*run_callbacks)(struct IDiscordCore* core);
|
|
void (*set_log_hook)(struct IDiscordCore* core, enum EDiscordLogLevel min_level, void* hook_data, void (*hook)(void* hook_data, enum EDiscordLogLevel level, const char* message));
|
|
struct IDiscordApplicationManager* (*get_application_manager)(struct IDiscordCore* core);
|
|
struct IDiscordUserManager* (*get_user_manager)(struct IDiscordCore* core);
|
|
struct IDiscordImageManager* (*get_image_manager)(struct IDiscordCore* core);
|
|
struct IDiscordActivityManager* (*get_activity_manager)(struct IDiscordCore* core);
|
|
struct IDiscordRelationshipManager* (*get_relationship_manager)(struct IDiscordCore* core);
|
|
struct IDiscordLobbyManager* (*get_lobby_manager)(struct IDiscordCore* core);
|
|
struct IDiscordNetworkManager* (*get_network_manager)(struct IDiscordCore* core);
|
|
struct IDiscordOverlayManager* (*get_overlay_manager)(struct IDiscordCore* core);
|
|
struct IDiscordStorageManager* (*get_storage_manager)(struct IDiscordCore* core);
|
|
struct IDiscordStoreManager* (*get_store_manager)(struct IDiscordCore* core);
|
|
struct IDiscordVoiceManager* (*get_voice_manager)(struct IDiscordCore* core);
|
|
struct IDiscordAchievementManager* (*get_achievement_manager)(struct IDiscordCore* core);
|
|
};
|
|
|
|
struct DiscordCreateParams {
|
|
DiscordClientId client_id;
|
|
uint64_t flags;
|
|
IDiscordCoreEvents* events;
|
|
void* event_data;
|
|
IDiscordApplicationEvents* application_events;
|
|
DiscordVersion application_version;
|
|
struct IDiscordUserEvents* user_events;
|
|
DiscordVersion user_version;
|
|
IDiscordImageEvents* image_events;
|
|
DiscordVersion image_version;
|
|
struct IDiscordActivityEvents* activity_events;
|
|
DiscordVersion activity_version;
|
|
struct IDiscordRelationshipEvents* relationship_events;
|
|
DiscordVersion relationship_version;
|
|
struct IDiscordLobbyEvents* lobby_events;
|
|
DiscordVersion lobby_version;
|
|
struct IDiscordNetworkEvents* network_events;
|
|
DiscordVersion network_version;
|
|
struct IDiscordOverlayEvents* overlay_events;
|
|
DiscordVersion overlay_version;
|
|
IDiscordStorageEvents* storage_events;
|
|
DiscordVersion storage_version;
|
|
struct IDiscordStoreEvents* store_events;
|
|
DiscordVersion store_version;
|
|
struct IDiscordVoiceEvents* voice_events;
|
|
DiscordVersion voice_version;
|
|
struct IDiscordAchievementEvents* achievement_events;
|
|
DiscordVersion achievement_version;
|
|
};
|
|
|
|
static void* DiscordCreateParamsSetDefault(struct DiscordCreateParams* params);
|
|
|
|
enum EDiscordResult DiscordCreate(DiscordVersion version, struct DiscordCreateParams* params, struct IDiscordCore** result);
|
|
|
|
typedef void (*callbackPtr)(void* callback_data, enum EDiscordResult result);
|
|
|
|
typedef void (*loggerPtr)(void* hook_data, enum EDiscordLogLevel level, const char* message);
|
|
|
|
typedef void (*onUserUpdatedPtr)(void* data);
|
|
|
|
typedef void (*onOAuth2Ptr)(void* data, enum EDiscordResult result, struct DiscordOAuth2Token* token);
|
|
|
|
struct Application {
|
|
struct IDiscordCore* core;
|
|
struct IDiscordUserManager* users;
|
|
struct IDiscordAchievementManager* achievements;
|
|
struct IDiscordActivityManager* activities;
|
|
struct IDiscordRelationshipManager* relationships;
|
|
struct IDiscordApplicationManager* application;
|
|
struct IDiscordLobbyManager* lobbies;
|
|
DiscordUserId user_id;
|
|
};
|
|
]]
|
|
|
|
-- Implement set default function in Lua because
|
|
-- somehow I couldn't get FFI to call the static method
|
|
-- DiscordCreateParamsSetDefault
|
|
local function create_params_set_default(paramsPtr)
|
|
paramsPtr[0].application_version = libGameSDK.DISCORD_APPLICATION_MANAGER_VERSION
|
|
paramsPtr[0].user_version = libGameSDK.DISCORD_USER_MANAGER_VERSION
|
|
paramsPtr[0].image_version = libGameSDK.DISCORD_IMAGE_MANAGER_VERSION
|
|
paramsPtr[0].activity_version = libGameSDK.DISCORD_ACTIVITY_MANAGER_VERSION
|
|
paramsPtr[0].relationship_version = libGameSDK.DISCORD_RELATIONSHIP_MANAGER_VERSION
|
|
paramsPtr[0].lobby_version = libGameSDK.DISCORD_LOBBY_MANAGER_VERSION
|
|
paramsPtr[0].network_version = libGameSDK.DISCORD_NETWORK_MANAGER_VERSION
|
|
paramsPtr[0].overlay_version = libGameSDK.DISCORD_OVERLAY_MANAGER_VERSION
|
|
paramsPtr[0].storage_version = libGameSDK.DISCORD_STORAGE_MANAGER_VERSION
|
|
paramsPtr[0].store_version = libGameSDK.DISCORD_STORE_MANAGER_VERSION
|
|
paramsPtr[0].voice_version = libGameSDK.DISCORD_VOICE_MANAGER_VERSION
|
|
paramsPtr[0].achievement_version = libGameSDK.DISCORD_ACHIEVEMENT_MANAGER_VERSION
|
|
return params
|
|
end
|
|
|
|
local discordGameSDK = {} -- module table
|
|
|
|
-- proxy to detect garbage collection of the module
|
|
discordGameSDK.gcDummy = newproxy(true)
|
|
|
|
local on_user_updated = ffi.cast("onUserUpdatedPtr", function(data)
|
|
local appPtr = ffi.cast("struct Application*", data)
|
|
local app = appPtr[0]
|
|
local user = ffi.new("struct DiscordUser")
|
|
local userPtr = ffi.new("struct DiscordUser[1]", user)
|
|
app.users.get_current_user(app.users, userPtr)
|
|
user = userPtr[0]
|
|
print(string.format("Displaying Discord Status on user: %s#%s",
|
|
ffi.string(user.username),
|
|
ffi.string(user.discriminator)))
|
|
end)
|
|
|
|
local loggerCallback = ffi.cast("loggerPtr", function (data, level, message)
|
|
print(string.format("Discord reported an error of severity %s: %s",
|
|
tostring(level),
|
|
ffi.string(message)))
|
|
end)
|
|
|
|
-- Helper function to make sure the input is a given type
|
|
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
|
|
|
|
-- Helper function to make sure the input is a string within length
|
|
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
|
|
|
|
-- Helper function to make sure the input is within a max int value
|
|
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
|
|
|
|
-- This function is basically a complete line by line port
|
|
-- from examples/c/main.c in the Game SDK. with Lua quirks
|
|
-- here and there.
|
|
|
|
-- Pass by reference kinda works but kinda doesn't, and
|
|
-- so user_events != userEventsPtr[0], and app != appPtr[0].
|
|
-- The desired one is always [0], so we simply do not use
|
|
-- the original objects after a pointer to them have been made.
|
|
function discordGameSDK.initialize(clientId)
|
|
local app = ffi.new("struct Application")
|
|
local appPtr = ffi.new("struct Application[1]", app)
|
|
ffi.C.memset(appPtr, 0, ffi.sizeof(app))
|
|
|
|
local user_events = ffi.new("struct IDiscordUserEvents")
|
|
local userEventsPtr = ffi.new("struct IDiscordUserEvents[1]", user_events)
|
|
ffi.C.memset(userEventsPtr, 0, ffi.sizeof(user_events))
|
|
|
|
userEventsPtr[0].on_current_user_update = on_user_updated
|
|
|
|
local params = ffi.new("struct DiscordCreateParams")
|
|
local paramsPtr = ffi.new("struct DiscordCreateParams[1]", params)
|
|
ffi.C.memset(paramsPtr, 0, ffi.sizeof(params))
|
|
create_params_set_default(paramsPtr)
|
|
paramsPtr[0].client_id = clientId
|
|
paramsPtr[0].flags = libGameSDK.DiscordCreateFlags_NoRequireDiscord
|
|
paramsPtr[0].event_data = appPtr
|
|
paramsPtr[0].user_events = userEventsPtr
|
|
|
|
local corePtrPtr = ffi.new("struct IDiscordCore*[1]", app.core)
|
|
ffi.C.memset(corePtrPtr, 0, ffi.sizeof(app.core))
|
|
|
|
-- Attempt to create a Discord instance
|
|
x = libGameSDK.DiscordCreate(libGameSDK.DISCORD_VERSION, paramsPtr, corePtrPtr)
|
|
running = x == libGameSDK.DiscordResult_Ok
|
|
|
|
if running then
|
|
appPtr[0].core = corePtrPtr[0]
|
|
|
|
appPtr[0].core:set_log_hook(libGameSDK.DiscordLogLevel_Debug, appPtr, loggerCallback)
|
|
|
|
appPtr[0].activities = appPtr[0].core[0]:get_activity_manager()
|
|
appPtr[0].application = appPtr[0].core[0]:get_application_manager()
|
|
appPtr[0].users = appPtr[0].core[0]:get_user_manager()
|
|
|
|
discordGameSDK.runCallbacks(appPtr[0].core)
|
|
end
|
|
|
|
-- By http://lua-users.org/lists/lua-l/2011-04/msg00516.html,
|
|
-- The LuaJIT FFI Garbage Collector doesn't follow pointers when
|
|
-- determining which references to keep. Therefore, "you must
|
|
-- not store the only reference to an object allocated with
|
|
-- ffi.new() as a pointer in a struct field."
|
|
-- Hence, a referencesTable is passed around instead of just
|
|
-- the app, although none of the keys are used apart from app.
|
|
local referencesTable = {
|
|
running = running,
|
|
clientId = clientId,
|
|
app = appPtr[0],
|
|
appPtr = appPtr,
|
|
userEvents = userEvents,
|
|
userEventsPtr = userEventsPtr,
|
|
corePtr = appPtr[0].core,
|
|
corePtrPtr = corePtrPtr,
|
|
activities = appPtr[0].activities,
|
|
application = appPtr[0].application,
|
|
users = appPtr[0].users
|
|
}
|
|
|
|
-- Prevent memory leak when called multiple times by explicitly
|
|
-- calling the garbage collector (twice, because some SO user said so)
|
|
collectgarbage()
|
|
collectgarbage()
|
|
|
|
return referencesTable
|
|
|
|
end
|
|
|
|
function discordGameSDK.shutdown(app)
|
|
app.core:destroy()
|
|
end
|
|
|
|
function discordGameSDK.runCallbacks(core)
|
|
return core.run_callbacks(core)
|
|
end
|
|
|
|
local updateActivityCallback = ffi.cast("callbackPtr", function(callback_data, discord_result)
|
|
if discord_result == libGameSDK.DiscordResult_Ok then
|
|
print("Successfully updated Discord activity")
|
|
else
|
|
print("Failed updating Discord activity: " .. tostring(discord_result))
|
|
end
|
|
end)
|
|
|
|
local clearActivityCallback = ffi.cast("callbackPtr", function(callback_data, discord_result)
|
|
if discord_result == libGameSDK.DiscordResult_Ok then
|
|
print("Successfully cleared Discord activity")
|
|
else
|
|
print("Failed clearing Discord activity: " .. tostring(discord_result))
|
|
end
|
|
end)
|
|
|
|
function discordGameSDK.updatePresence(referencesTable, presence)
|
|
-- If Discord isn't running, try initialising.
|
|
-- If it still fails, then return early.
|
|
if not referencesTable.running then
|
|
referencesTable = discordGameSDK.initialize(referencesTable.clientId)
|
|
if not referencesTable.running then
|
|
return referencesTable
|
|
end
|
|
end
|
|
|
|
local func = "discordGameSDK.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.start_time, 64, "presence.start_time", func, true)
|
|
checkIntArg(presence.end_time, 64, "presence.end_time", func, true)
|
|
|
|
checkStrArg(presence.large_image, 127, "presence.large_image", func, true)
|
|
checkStrArg(presence.large_text, 127, "presence.large_text", func, true)
|
|
checkStrArg(presence.small_image, 127, "presence.small_image", func, true)
|
|
checkStrArg(presence.small_text, 127, "presence.small_text", func, true)
|
|
|
|
checkStrArg(presence.party_id, 127, "presence.party_id", func, true)
|
|
checkIntArg(presence.party_size, 32, "presence.party_size", func, true)
|
|
checkIntArg(presence.party_max, 32, "presence.party_max", func, true)
|
|
|
|
checkStrArg(presence.match_secret, 127, "presence.match_secret", func, true)
|
|
checkStrArg(presence.join_secret, 127, "presence.join_secret", func, true)
|
|
checkStrArg(presence.spectate_secret, 127, "presence.spectate_secret", func, true)
|
|
|
|
local app = referencesTable.app
|
|
app.activities = app.core[0]:get_activity_manager()
|
|
app.application = app.core[0]:get_application_manager()
|
|
app.users = app.core[0]:get_user_manager()
|
|
|
|
local activity = ffi.new("struct DiscordActivity")
|
|
local activityPtr = ffi.new("struct DiscordActivity[1]", activity)
|
|
ffi.C.memset(activityPtr, 0, ffi.sizeof(activity))
|
|
|
|
activityPtr[0].type = libGameSDK.DiscordActivityType_Playing
|
|
activityPtr[0].state = presence.state or ""
|
|
activityPtr[0].details = presence.details or ""
|
|
activityPtr[0].timestamps.start = presence.start_time or 0
|
|
activityPtr[0].timestamps["end"] = presence.end_time or 0
|
|
activityPtr[0].assets.large_image = presence.large_image or ""
|
|
activityPtr[0].assets.large_text = presence.large_text or ""
|
|
activityPtr[0].assets.small_image = presence.small_image or ""
|
|
activityPtr[0].assets.small_text = presence.small_text or ""
|
|
activityPtr[0].party.id = presence.party_id or ""
|
|
activityPtr[0].party.size.current_size = presence.party_size or 0
|
|
activityPtr[0].party.size.max_size = presence.party_max or 0
|
|
activityPtr[0].secrets.match = presence.match_secret or ""
|
|
activityPtr[0].secrets.join = presence.join_secret or ""
|
|
activityPtr[0].secrets.spectate = presence.spectate_secret or ""
|
|
|
|
app.activities:update_activity(activityPtr, app.core, updateActivityCallback)
|
|
x = discordGameSDK.runCallbacks(app.core)
|
|
running = x == libGameSDK.DiscordResult_Ok
|
|
|
|
-- Make sure garbage memory is collected, or otherwise
|
|
-- this script will keep on increasming RAM usage by about 8KB
|
|
-- everytime updateActivity or clearActivity is run.
|
|
collectgarbage()
|
|
collectgarbage()
|
|
referencesTable.running = running
|
|
return referencesTable
|
|
end
|
|
|
|
function discordGameSDK.clearPresence(referencesTable)
|
|
-- If Discord isn't running, don't even try.
|
|
if not referencesTable.running then
|
|
return referencesTable
|
|
end
|
|
|
|
local app = referencesTable.app
|
|
app.activities = app.core[0]:get_activity_manager()
|
|
app.application = app.core[0]:get_application_manager()
|
|
app.users = app.core[0]:get_user_manager()
|
|
|
|
app.activities:clear_activity(app.core, clearActivityCallback)
|
|
x = discordGameSDK.runCallbacks(app.core)
|
|
running = x == libGameSDK.DiscordResult_Ok
|
|
|
|
collectgarbage()
|
|
collectgarbage()
|
|
referencesTable.running = running
|
|
return referencesTable
|
|
end
|
|
|
|
-- Attach a finaliser to run when discordGameSDK is garbage
|
|
-- collected. This happens when mpv quits.
|
|
getmetatable(discordGameSDK.gcDummy).__gc = function ()
|
|
discordGameSDK.shutdown()
|
|
updateActivityCallback:free()
|
|
clearActivityCallback:free()
|
|
loggerCallback:free()
|
|
end
|
|
|
|
-- http://luajit.org/ext_ffi_semantics.html#callback :
|
|
-- It is by default not allowed for C to callback into Lua, when
|
|
-- Lua had originally called into C. jit.off() allows it, so any
|
|
-- function that calls a callback needs to be wrapped in it.
|
|
jit.off(discordGameSDK.updatePresence)
|
|
jit.off(discordGameSDK.clearPresence)
|
|
jit.off(discordGameSDK.runCallbacks)
|
|
jit.off(discordGameSDK.initialize)
|
|
|
|
return discordGameSDK
|