diff --git a/.gitignore b/.gitignore
index 64c56cce..3a0c8b0c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -78,6 +78,7 @@ sm64config.txt
!/sound/**/*custom*/**/*.aiff
!/assets/**/*custom*.bin
!/assets/**/*custom*/**/*.bin
+!/lib/discordsdk/*.*
# visual studio
build-windows-visual-studio/.vs
diff --git a/Makefile b/Makefile
index ffaf4cc8..e1edb219 100644
--- a/Makefile
+++ b/Makefile
@@ -54,8 +54,9 @@ DISCORDRPC ?= 0
DOCKERBUILD ?= 0
# Force various options due since coop assumes they are set this way
-NODRAWINGDISTANCE = 1
-TEXTSAVES = 0
+NODRAWINGDISTANCE := 1
+TEXTSAVES := 0
+DISCORDRPC := 0
# Various workarounds for weird toolchains
@@ -292,7 +293,8 @@ LEVEL_DIRS := $(patsubst levels/%,%,$(dir $(wildcard levels/*/header.h)))
# Directories containing source files
# Hi, I'm a PC
-SRC_DIRS := src src/engine src/game src/audio src/menu src/buffers actors levels bin data assets src/pc src/pc/gfx src/pc/audio src/pc/controller src/pc/fs src/pc/fs/packtypes src/pc/network src/pc/network/packets src/pc/network/socket
+SRC_DIRS := src src/engine src/game src/audio src/menu src/buffers actors levels bin data assets src/pc src/pc/gfx src/pc/audio src/pc/controller src/pc/fs src/pc/fs/packtypes
+SRC_DIRS += src/pc/network src/pc/network/packets src/pc/network/socket src/pc/network/discord
ASM_DIRS :=
ifeq ($(DISCORDRPC),1)
@@ -427,6 +429,16 @@ ifeq ($(DISCORDRPC),1)
endif
endif
+DISCORD_SDK_LIBS :=
+ifeq ($(WINDOWS_BUILD),1)
+ DISCORD_SDK_LIBS := lib/discordsdk/discord_game_sdk.dll
+else ifeq ($(OSX_BUILD),1)
+ # needs testing
+ DISCORD_SDK_LIBS := lib/discordsdk/discord_game_sdk.dylib
+else
+ DISCORD_SDK_LIBS := lib/discordsdk/discord_game_sdk.so
+endif
+
# Automatic dependency files
DEP_FILES := $(O_FILES:.o=.d) $(ULTRA_O_FILES:.o=.d) $(GODDARD_O_FILES:.o=.d) $(BUILD_DIR)/$(LD_SCRIPT).d
@@ -659,7 +671,7 @@ ifeq ($(TARGET_WEB),1)
LDFLAGS := -lm -lGL -lSDL2 -no-pie -s TOTAL_MEMORY=20MB -g4 --source-map-base http://localhost:8080/ -s "EXTRA_EXPORTED_RUNTIME_METHODS=['callMain']"
else ifeq ($(WINDOWS_BUILD),1)
- LDFLAGS := $(BITS) -march=$(TARGET_ARCH) -Llib -L"ws2_32" -lwsock32 -lpthread $(BACKEND_LDFLAGS) -static
+ LDFLAGS := $(BITS) -march=$(TARGET_ARCH) -Llib -lpthread $(BACKEND_LDFLAGS) -static
ifeq ($(CROSS),)
LDFLAGS += -no-pie
endif
@@ -679,7 +691,17 @@ else
LDFLAGS += -ldl -Wl,-rpath .
endif
-endif # End of LDFLAGS
+endif
+
+# coop specific libraries
+
+ifeq ($(WINDOWS_BUILD),1)
+ LDFLAGS += -L"ws2_32" -lwsock32
+endif
+
+LDFLAGS += -Wl,-Bdynamic -ldiscord_game_sdk
+
+# End of LDFLAGS
# Prevent a crash with -sopt
export LANG := C
@@ -768,6 +790,9 @@ load: $(ROM)
$(BUILD_DIR)/$(RPC_LIBS):
@$(CP) -f $(RPC_LIBS) $(BUILD_DIR)
+$(BUILD_DIR)/$(DISCORD_SDK_LIBS):
+ @$(CP) -f $(DISCORD_SDK_LIBS) $(BUILD_DIR)
+
libultra: $(BUILD_DIR)/libultra.a
$(BUILD_DIR)/asm/boot.o: $(IPL3_RAW_FILES)
@@ -1022,7 +1047,7 @@ $(BUILD_DIR)/%.o: %.s
-$(EXE): $(O_FILES) $(MIO0_FILES:.mio0=.o) $(SOUND_OBJ_FILES) $(ULTRA_O_FILES) $(GODDARD_O_FILES) $(BUILD_DIR)/$(RPC_LIBS)
+$(EXE): $(O_FILES) $(MIO0_FILES:.mio0=.o) $(SOUND_OBJ_FILES) $(ULTRA_O_FILES) $(GODDARD_O_FILES) $(BUILD_DIR)/$(RPC_LIBS) $(BUILD_DIR)/$(DISCORD_SDK_LIBS)
$(LD) -L $(BUILD_DIR) -o $@ $(O_FILES) $(SOUND_OBJ_FILES) $(ULTRA_O_FILES) $(GODDARD_O_FILES) $(LDFLAGS)
.PHONY: all clean distclean default diff test load libultra res
diff --git a/build-windows-visual-studio/sm64ex.vcxproj b/build-windows-visual-studio/sm64ex.vcxproj
index 56024294..eac557d5 100644
--- a/build-windows-visual-studio/sm64ex.vcxproj
+++ b/build-windows-visual-studio/sm64ex.vcxproj
@@ -3949,6 +3949,11 @@
+
+
+
+
+
@@ -4308,6 +4313,12 @@
+
+
+
+
+
+
diff --git a/build-windows-visual-studio/sm64ex.vcxproj.filters b/build-windows-visual-studio/sm64ex.vcxproj.filters
index 2579529d..5e56b6d8 100644
--- a/build-windows-visual-studio/sm64ex.vcxproj.filters
+++ b/build-windows-visual-studio/sm64ex.vcxproj.filters
@@ -3427,6 +3427,12 @@
{b1b4937e-775c-4a0c-92f5-48b010783e4d}
+
+ {6d48a98e-e5ac-4409-a834-f2756dabfa4a}
+
+
+ {7fd7fed2-3f22-4bbf-a118-8ff54107c341}
+
@@ -15015,6 +15021,21 @@
Source Files\src\pc\controller
+
+ Source Files\src\pc\network\discord
+
+
+ Source Files\src\pc\network\discord
+
+
+ Source Files\src\pc\network\discord
+
+
+ Source Files\src\pc\network\discord
+
+
+ Source Files\src\pc\network\discord
+
@@ -15937,5 +15958,23 @@
Source Files\src\pc\controller
+
+ Header Files\src\pc\network\discord
+
+
+ Header Files\src\pc\network\discord
+
+
+ Header Files\src\pc\network\discord
+
+
+ Header Files\src\pc\network\discord
+
+
+ Header Files\src\pc\network\discord
+
+
+ Header Files\src\pc\network\discord
+
\ No newline at end of file
diff --git a/lib/discordsdk/discord_game_sdk.bundle b/lib/discordsdk/discord_game_sdk.bundle
new file mode 100644
index 00000000..24045f79
Binary files /dev/null and b/lib/discordsdk/discord_game_sdk.bundle differ
diff --git a/lib/discordsdk/discord_game_sdk.dll b/lib/discordsdk/discord_game_sdk.dll
new file mode 100644
index 00000000..10a8928f
Binary files /dev/null and b/lib/discordsdk/discord_game_sdk.dll differ
diff --git a/lib/discordsdk/discord_game_sdk.dll.lib b/lib/discordsdk/discord_game_sdk.dll.lib
new file mode 100644
index 00000000..8ab3d4cb
Binary files /dev/null and b/lib/discordsdk/discord_game_sdk.dll.lib differ
diff --git a/lib/discordsdk/discord_game_sdk.dylib b/lib/discordsdk/discord_game_sdk.dylib
new file mode 100644
index 00000000..24045f79
Binary files /dev/null and b/lib/discordsdk/discord_game_sdk.dylib differ
diff --git a/lib/discordsdk/discord_game_sdk.so b/lib/discordsdk/discord_game_sdk.so
new file mode 100644
index 00000000..e4657606
Binary files /dev/null and b/lib/discordsdk/discord_game_sdk.so differ
diff --git a/network.sh b/network.sh
index ec2677d1..7a5c0434 100755
--- a/network.sh
+++ b/network.sh
@@ -11,10 +11,16 @@ if [ ! -f "$FILE" ]; then
FILE=./build/us_pc/sm64.us.f3dex2e
fi
-$FILE --server 27015 --configfile sm64config_server.txt &
+#$FILE --discord 2 --configfile sm64config_server.txt &
+#$FILE --discord 1 --configfile sm64config_client.txt &
+#exit
+
+#$FILE --server 27015 --configfile sm64config_server.txt &
#$FILE --client 127.0.0.1 27015 --configfile sm64config_client.txt &
#exit
+$FILE --server 27015 --configfile sm64config_server.txt &
+
# debug if cgdb exists
if ! [ -x "$(command -v cgdb)" ]; then
$FILE --client 127.0.0.1 27015 --configfile sm64config_client.txt &
diff --git a/src/menu/file_select.c b/src/menu/file_select.c
index b3bddb90..719962b3 100644
--- a/src/menu/file_select.c
+++ b/src/menu/file_select.c
@@ -42,6 +42,8 @@
*/
static u8 joinVersionMismatch = FALSE;
+static char joinMenuCustomText[64] = { 0 };
+static u8 forceOpenJoinMenu = 0;
#ifdef VERSION_US
// The current sound mode is automatically centered on US due to
@@ -385,6 +387,9 @@ void exit_join_to_network_menu(void) {
// Begin exit
if (sMainMenuButtons[MENU_BUTTON_JOIN]->oMenuButtonState == MENU_BUTTON_STATE_FULLSCREEN
&& sCursorClickingTimer == 2) {
+ // clear custom text
+ joinMenuCustomText[0] = '\0';
+
play_sound(SOUND_MENU_CAMERA_ZOOM_OUT, gDefaultSoundArgs);
sMainMenuButtons[MENU_BUTTON_JOIN]->oMenuButtonState = MENU_BUTTON_STATE_SHRINKING;
network_shutdown();
@@ -468,8 +473,37 @@ void render_network_mode_menu_buttons(struct Object* soundModeButton) {
sMainMenuButtons[MENU_BUTTON_JOIN]->oFaceAngleRoll = 0;
}
+void open_join_menu(char* customText) {
+ if (sMainMenuButtons[MENU_BUTTON_JOIN] == NULL) {
+ forceOpenJoinMenu = (forceOpenJoinMenu == 0) ? 1 : forceOpenJoinMenu;
+ } else if (sMainMenuButtons[MENU_BUTTON_JOIN]->oMenuButtonState != MENU_BUTTON_STATE_FULLSCREEN) {
+ forceOpenJoinMenu = 0;
+ play_sound(SOUND_MENU_CAMERA_ZOOM_IN, gDefaultSoundArgs);
+ sMainMenuButtons[MENU_BUTTON_JOIN]->oMenuButtonState = MENU_BUTTON_STATE_GROWING;
+ sSelectedButtonID = MENU_BUTTON_JOIN;
+ sCurrentMenuLevel = MENU_LAYER_SUBMENU;
+ } else {
+ forceOpenJoinMenu = 0;
+ }
+
+ if (customText == joinMenuCustomText) { return; }
+
+ if (customText != NULL) {
+ strncpy(joinMenuCustomText, customText, 63);
+ } else if (*gTextInput == '\0') {
+ joinMenuCustomText[0] = '\0';
+ }
+}
+
void check_network_mode_menu_clicked_buttons(struct Object* networkModeButton) {
if (networkModeButton->oMenuButtonState == MENU_BUTTON_STATE_FULLSCREEN) {
+
+ if (forceOpenJoinMenu && forceOpenJoinMenu++ > 3) {
+ forceOpenJoinMenu = 0;
+ open_join_menu(joinMenuCustomText);
+ return;
+ }
+
s32 buttonID;
// Configure sound mode menu button group
for (buttonID = MENU_BUTTON_NETWORK_MIN; buttonID < MENU_BUTTON_NETWORK_MAX; buttonID++) {
@@ -486,9 +520,7 @@ void check_network_mode_menu_clicked_buttons(struct Object* networkModeButton) {
}
}
else if (buttonID == MENU_BUTTON_JOIN) {
- play_sound(SOUND_MENU_CAMERA_ZOOM_IN, gDefaultSoundArgs);
- sMainMenuButtons[buttonID]->oMenuButtonState = MENU_BUTTON_STATE_GROWING;
- sSelectedButtonID = buttonID;
+ open_join_menu(NULL);
// start input
keyboard_start_text_input(TIM_IP, keyboard_exit_join_to_network_menu, join_server_as_client);
@@ -565,16 +597,21 @@ void print_join_mode_menu_strings(void) {
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, sTextBaseAlpha);
// Print level name
- print_generic_ascii_string(JOIN_LEVEL_NAME_X, 191 - (12 * 0), "Type or paste the host's IP.");
- print_generic_ascii_string(JOIN_LEVEL_NAME_X, 191 - (12 * 2), gTextInput);
+ if (joinMenuCustomText[0] != '\0') {
+ print_generic_ascii_string(JOIN_LEVEL_NAME_X, 191 - (14 * 3), joinMenuCustomText);
+ } else {
+ print_generic_ascii_string(JOIN_LEVEL_NAME_X, 191 - (14 * 0), "Accept a Discord invite.");
+ print_generic_ascii_string(JOIN_LEVEL_NAME_X, 191 - (14 * 1), "Alternatively, type or paste the host's IP.");
+ print_generic_ascii_string(JOIN_LEVEL_NAME_X, 191 - (14 * 3), gTextInput);
+ }
// Print status
if (joinVersionMismatch) {
- print_generic_ascii_string(JOIN_LEVEL_NAME_X, 191 - (12 * 14), "Error - versions don't match. Both should rebuild!");
+ print_generic_ascii_string(JOIN_LEVEL_NAME_X, 191 - (13 * 14), "Error - versions don't match. Both should rebuild!");
} else if (gNetworkType == NT_CLIENT) {
- print_generic_ascii_string(JOIN_LEVEL_NAME_X, 191 - (12 * 14), "Connecting...");
+ print_generic_ascii_string(JOIN_LEVEL_NAME_X, 191 - (13 * 14), "Connecting...");
} else if (strlen(gTextInput) > 0) {
- print_generic_ascii_string(JOIN_LEVEL_NAME_X, 191 - (12 * 14), "Press (ENTER) to join.");
+ print_generic_ascii_string(JOIN_LEVEL_NAME_X, 191 - (13 * 14), "Press (ENTER) to directly connect.");
}
gSPDisplayList(gDisplayListHead++, dl_menu_ia8_text_end);
@@ -1619,6 +1656,7 @@ void check_main_menu_clicked_buttons(void) {
button->oMenuButtonTimer = 0;
sSelectedButtonID = MENU_BUTTON_NETWORK_MODE;
+
networkInit = TRUE;
}
diff --git a/src/menu/file_select.h b/src/menu/file_select.h
index 1bf4b21e..63e74045 100644
--- a/src/menu/file_select.h
+++ b/src/menu/file_select.h
@@ -145,5 +145,6 @@ s32 lvl_init_menu_values_and_cursor_pos(UNUSED s32 arg, UNUSED s32 unused);
s32 lvl_update_obj_and_load_file_selected(UNUSED s32 arg, UNUSED s32 unused);
void joined_server_as_client(s16 fileIndex);
void joined_server_version_mismatch(void);
+void open_join_menu(char* customText);
#endif // FILE_SELECT_H
diff --git a/src/pc/cliopts.c b/src/pc/cliopts.c
index 3f20f911..4643975c 100644
--- a/src/pc/cliopts.c
+++ b/src/pc/cliopts.c
@@ -79,6 +79,9 @@ void parse_cli_opts(int argc, char* argv[]) {
else if (strcmp(argv[i], "--savepath") == 0 && (i + 1) < argc)
arg_string("--savepath", argv[++i], gCLIOpts.SavePath, SYS_MAX_PATH);
+ else if (strcmp(argv[i], "--discord") == 0 && (i + 1) < argc)
+ arg_uint("--discord", argv[++i], &gCLIOpts.Discord);
+
// Print help
else if (strcmp(argv[i], "--help") == 0) {
print_help();
diff --git a/src/pc/cliopts.h b/src/pc/cliopts.h
index 916e19b1..ea360450 100644
--- a/src/pc/cliopts.h
+++ b/src/pc/cliopts.h
@@ -22,6 +22,7 @@ struct PCCLIOptions {
char ConfigFile[SYS_MAX_PATH];
char SavePath[SYS_MAX_PATH];
char GameDir[SYS_MAX_PATH];
+ unsigned int Discord;
};
extern struct PCCLIOptions gCLIOpts;
diff --git a/src/pc/network/discord/activity.c b/src/pc/network/discord/activity.c
new file mode 100644
index 00000000..7d3f1802
--- /dev/null
+++ b/src/pc/network/discord/activity.c
@@ -0,0 +1,73 @@
+#include "activity.h"
+#include "lobby.h"
+#include "discord_network.h"
+#include "pc/debuglog.h"
+#include "menu/file_select.h"
+
+#define HASH_LENGTH 8
+struct DiscordActivity gCurActivity = { 0 };
+
+static void on_activity_update_callback(UNUSED void* data, enum EDiscordResult result) {
+ LOG_INFO("> on_activity_update_callback returned %d", result);
+ DISCORD_REQUIRE(result);
+}
+
+static void on_activity_join_callback(UNUSED void* data, enum EDiscordResult result, struct DiscordLobby* lobby) {
+ LOG_INFO("> on_activity_join_callback returned %d, lobby %lld", result, lobby->id);
+ DISCORD_REQUIRE(result);
+ network_init(NT_CLIENT);
+
+ gCurActivity.type = DiscordActivityType_Playing;
+ snprintf(gCurActivity.party.id, 128, "%lld", lobby->id);
+ gCurActivity.party.size.current_size = 2;
+ gCurActivity.party.size.max_size = lobby->capacity;
+
+ gCurLobbyId = lobby->id;
+
+ discord_network_init(lobby->id);
+ discord_activity_update(false);
+
+ network_on_joined();
+}
+
+static void on_activity_join(UNUSED void* data, const char* secret) {
+ LOG_INFO("> on_activity_join, secret: %s", secret);
+ open_join_menu("Joining Discord invite...");
+ app.lobbies->connect_lobby_with_activity_secret(app.lobbies, (char*)secret, NULL, on_activity_join_callback);
+}
+
+static void on_activity_join_request_callback(UNUSED void* data, enum EDiscordResult result) {
+ LOG_INFO("> on_activity_join_request_callback returned %d", (int)result);
+}
+
+static void on_activity_join_request(UNUSED void* data, struct DiscordUser* user) {
+ LOG_INFO("> on_activity_join_request from %lld", user->id);
+ //app.activities->send_request_reply(app.activities, user->id, DiscordActivityJoinRequestReply_Yes, NULL, on_activity_join_request_callback);
+}
+
+void discord_activity_update(bool hosting) {
+ gCurActivity.type = DiscordActivityType_Playing;
+ if (gCurActivity.party.size.current_size > 1) {
+ strcpy(gCurActivity.state, "Playing!");
+ } else if (hosting) {
+ strcpy(gCurActivity.state, "Waiting for player...");
+ } else {
+ strcpy(gCurActivity.state, "In-game.");
+ gCurActivity.party.size.current_size = 1;
+ gCurActivity.party.size.max_size = 1;
+ }
+
+ char hash[HASH_LENGTH] = GIT_HASH;
+ strcpy(gCurActivity.details, "version ");
+ strncat(gCurActivity.details, GIT_HASH, 127);
+
+ app.activities->update_activity(app.activities, &gCurActivity, NULL, on_activity_update_callback);
+ LOG_INFO("set activity");
+}
+
+struct IDiscordActivityEvents* discord_activity_initialize(void) {
+ static struct IDiscordActivityEvents events = { 0 };
+ events.on_activity_join = on_activity_join;
+ events.on_activity_join_request = on_activity_join_request;
+ return &events;
+}
\ No newline at end of file
diff --git a/src/pc/network/discord/activity.h b/src/pc/network/discord/activity.h
new file mode 100644
index 00000000..2fced611
--- /dev/null
+++ b/src/pc/network/discord/activity.h
@@ -0,0 +1,10 @@
+#ifndef DISCORD_ACTIVITY_H
+#define DISCORD_ACTIVITY_H
+#include "discord.h"
+
+extern struct DiscordActivity gCurActivity;
+
+void discord_activity_update(bool hosting);
+struct IDiscordActivityEvents* discord_activity_initialize(void);
+
+#endif
\ No newline at end of file
diff --git a/src/pc/network/discord/discord.c b/src/pc/network/discord/discord.c
new file mode 100644
index 00000000..a82c8a54
--- /dev/null
+++ b/src/pc/network/discord/discord.c
@@ -0,0 +1,120 @@
+#include "discord.h"
+#include "user.h"
+#include "activity.h"
+#include "lobby.h"
+#include "discord_network.h"
+#include "pc/debuglog.h"
+
+#if defined(_WIN32) || defined(_WIN64)
+#include
+#else
+#include
+#endif
+
+#define MAX_LAUNCH_CMD (MAX_PATH + 12)
+
+static int64_t applicationId = 752700005210390568;
+struct DiscordApplication app = { 0 };
+bool gDiscordInitialized = false;
+
+static void set_instance_env_variable(void) {
+ // set local instance id
+ char environmentVariables[64] = { 0 };
+ int instance = (gCLIOpts.Discord == 0) ? 0 : (gCLIOpts.Discord - 1);
+ sprintf(environmentVariables, "DISCORD_INSTANCE_ID=%d", instance);
+ putenv(environmentVariables);
+ LOG_INFO("set environment variables: %s", environmentVariables);
+}
+
+static void get_oauth2_token_callback(UNUSED void* data, enum EDiscordResult result, struct DiscordOAuth2Token* token) {
+ LOG_INFO("> get_oauth2_token_callback returned %d", result);
+ if (result != DiscordResult_Ok) { return; }
+ LOG_INFO("OAuth2 token: %s", token->access_token);
+}
+
+static void register_launch_command(void) {
+ char cmd[MAX_LAUNCH_CMD];
+#if defined(_WIN32) || defined(_WIN64)
+ HMODULE hModule = GetModuleHandle(NULL);
+ if (hModule == NULL) {
+ LOG_ERROR("unable to retrieve absolute path!");
+ return;
+ }
+ GetModuleFileName(hModule, cmd, sizeof(cmd));
+#else
+ int rc = readlink("/proc/self/exe", cmd, sizeof(MAX_LAUNCH_CMD) - 1);
+ if (rc) {
+ LOG_ERROR("unable to retrieve absolute path! rc = %d", rc);
+ return;
+ }
+#endif
+ strncat(cmd, " --discord 1", MAX_LAUNCH_CMD - 1);
+ DISCORD_REQUIRE(app.activities->register_command(app.activities, cmd));
+ LOG_INFO("cmd: %s", cmd);
+}
+
+static void ns_discord_update(void) {
+ if (!gDiscordInitialized) { return; }
+ DISCORD_REQUIRE(app.core->run_callbacks(app.core));
+ discord_network_flush();
+}
+
+static bool ns_discord_initialize(enum NetworkType networkType) {
+#ifdef DEBUG
+ set_instance_env_variable();
+#endif
+ if (!gDiscordInitialized) {
+ // set up discord params
+ struct DiscordCreateParams params;
+ DiscordCreateParamsSetDefault(¶ms);
+ params.client_id = applicationId;
+ params.flags = DiscordCreateFlags_NoRequireDiscord;
+ params.event_data = &app;
+ params.user_events = discord_user_initialize();
+ params.activity_events = discord_activity_initialize();
+ params.lobby_events = discord_lobby_initialize();
+
+ int rc = DiscordCreate(DISCORD_VERSION, ¶ms, &app.core);
+ if (rc) {
+ LOG_ERROR("DiscordCreate failed: %d", rc);
+ return false;
+ }
+
+ // set up manager pointers
+ app.users = app.core->get_user_manager(app.core);
+ app.achievements = app.core->get_achievement_manager(app.core);
+ app.activities = app.core->get_activity_manager(app.core);
+ app.application = app.core->get_application_manager(app.core);
+ app.lobbies = app.core->get_lobby_manager(app.core);
+
+ // register launch params
+ register_launch_command();
+
+ // get oath2 token
+ app.application->get_oauth2_token(app.application, NULL, get_oauth2_token_callback);
+
+ // set activity
+ discord_activity_update(false);
+ }
+
+ // create lobby
+ if (networkType == NT_SERVER) { discord_lobby_create(); }
+
+ gDiscordInitialized = true;
+ LOG_INFO("initialized");
+
+ return true;
+}
+
+static void ns_discord_shutdown(void) {
+ if (!gDiscordInitialized) { return; }
+ discord_lobby_leave();
+ LOG_INFO("shutdown");
+}
+
+struct NetworkSystem gNetworkSystemDiscord = {
+ .initialize = ns_discord_initialize,
+ .update = ns_discord_update,
+ .send = ns_discord_network_send,
+ .shutdown = ns_discord_shutdown,
+};
diff --git a/src/pc/network/discord/discord.h b/src/pc/network/discord/discord.h
new file mode 100644
index 00000000..76b0a708
--- /dev/null
+++ b/src/pc/network/discord/discord.h
@@ -0,0 +1,35 @@
+#ifndef DISCORD_H
+#define DISCORD_H
+#include
+#include
+#include
+#pragma pack(push, 8)
+#include "discord_game_sdk.h"
+#pragma pack(pop)
+#ifdef _WIN32
+#include
+#else
+#include
+#include
+#endif
+#include "../network.h"
+
+#define DISCORD_REQUIRE(x) assert(x == DiscordResult_Ok)
+
+extern struct NetworkSystem gNetworkSystemDiscord;
+extern bool gDiscordInitialized;
+
+struct DiscordApplication {
+ struct IDiscordCore* core;
+ struct IDiscordUserManager* users;
+ struct IDiscordAchievementManager* achievements;
+ struct IDiscordActivityManager* activities;
+ struct IDiscordRelationshipManager* relationships;
+ struct IDiscordApplicationManager* application;
+ struct IDiscordLobbyManager* lobbies;
+ DiscordUserId userId;
+};
+
+extern struct DiscordApplication app;
+
+#endif
\ No newline at end of file
diff --git a/src/pc/network/discord/discord_game_sdk.h b/src/pc/network/discord/discord_game_sdk.h
new file mode 100644
index 00000000..81cc2bdb
--- /dev/null
+++ b/src/pc/network/discord/discord_game_sdk.h
@@ -0,0 +1,646 @@
+#ifndef _DISCORD_GAME_SDK_H_
+#define _DISCORD_GAME_SDK_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+#ifndef __cplusplus
+#include
+#endif
+
+#define DISCORD_VERSION 2
+#define DISCORD_APPLICATION_MANAGER_VERSION 1
+#define DISCORD_USER_MANAGER_VERSION 1
+#define DISCORD_IMAGE_MANAGER_VERSION 1
+#define DISCORD_ACTIVITY_MANAGER_VERSION 1
+#define DISCORD_RELATIONSHIP_MANAGER_VERSION 1
+#define DISCORD_LOBBY_MANAGER_VERSION 1
+#define DISCORD_NETWORK_MANAGER_VERSION 1
+#define DISCORD_OVERLAY_MANAGER_VERSION 1
+#define DISCORD_STORAGE_MANAGER_VERSION 1
+#define DISCORD_STORE_MANAGER_VERSION 1
+#define DISCORD_VOICE_MANAGER_VERSION 1
+#define 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 {
+ 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;
+ char currency[16];
+};
+
+struct DiscordSku {
+ DiscordSnowflake id;
+ enum EDiscordSkuType type;
+ char name[256];
+ struct DiscordSkuPrice price;
+};
+
+struct DiscordInputMode {
+ enum EDiscordInputModeType type;
+ 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;
+};
+
+#ifdef __cplusplus
+inline
+#else
+static
+#endif
+void DiscordCreateParamsSetDefault(struct DiscordCreateParams* params)
+{
+ memset(params, 0, sizeof(struct DiscordCreateParams));
+ params->application_version = DISCORD_APPLICATION_MANAGER_VERSION;
+ params->user_version = DISCORD_USER_MANAGER_VERSION;
+ params->image_version = DISCORD_IMAGE_MANAGER_VERSION;
+ params->activity_version = DISCORD_ACTIVITY_MANAGER_VERSION;
+ params->relationship_version = DISCORD_RELATIONSHIP_MANAGER_VERSION;
+ params->lobby_version = DISCORD_LOBBY_MANAGER_VERSION;
+ params->network_version = DISCORD_NETWORK_MANAGER_VERSION;
+ params->overlay_version = DISCORD_OVERLAY_MANAGER_VERSION;
+ params->storage_version = DISCORD_STORAGE_MANAGER_VERSION;
+ params->store_version = DISCORD_STORE_MANAGER_VERSION;
+ params->voice_version = DISCORD_VOICE_MANAGER_VERSION;
+ params->achievement_version = DISCORD_ACHIEVEMENT_MANAGER_VERSION;
+}
+
+enum EDiscordResult DiscordCreate(DiscordVersion version, struct DiscordCreateParams* params, struct IDiscordCore** result);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
\ No newline at end of file
diff --git a/src/pc/network/discord/discord_network.c b/src/pc/network/discord/discord_network.c
new file mode 100644
index 00000000..a2eead7f
--- /dev/null
+++ b/src/pc/network/discord/discord_network.c
@@ -0,0 +1,39 @@
+#include "discord_network.h"
+#include "lobby.h"
+#include "pc/debuglog.h"
+
+int ns_discord_network_send(u8* data, u16 dataLength) {
+ if (!gDiscordInitialized) { return 1; }
+ if (gCurLobbyId == 0) { return 2; }
+ int32_t memberCount = 0;
+ DISCORD_REQUIRE(app.lobbies->member_count(app.lobbies, gCurLobbyId, &memberCount));
+ if (memberCount <= 1) { return 3; }
+
+ for (int i = 0; i < memberCount; i++) {
+ DiscordUserId userId;
+ DISCORD_REQUIRE(app.lobbies->get_member_user_id(app.lobbies, gCurLobbyId, i, &userId));
+ if (userId == app.userId) { continue; }
+ DISCORD_REQUIRE(app.lobbies->send_network_message(app.lobbies, gCurLobbyId, userId, 0, data, dataLength));
+ }
+ return 0;
+}
+
+void discord_network_on_message(UNUSED void* eventData, int64_t lobbyId, int64_t userId, uint8_t channelId, uint8_t* data, uint32_t dataLength) {
+ network_receive((u8*)data, (u16)dataLength);
+}
+
+void discord_network_flush(void) {
+ app.lobbies->flush_network(app.lobbies);
+}
+
+void discord_network_init(int64_t lobbyId) {
+ DISCORD_REQUIRE(app.lobbies->connect_network(app.lobbies, lobbyId));
+ DISCORD_REQUIRE(app.lobbies->open_network_channel(app.lobbies, lobbyId, 0, false));
+}
+
+void discord_network_shutdown(void) {
+ app.lobbies->flush_network(app.lobbies);
+ if (gCurLobbyId == 0) { return; }
+ app.lobbies->disconnect_network(app.lobbies, gCurLobbyId);
+ LOG_INFO("shutdown network, lobby = %lld", gCurLobbyId);
+}
\ No newline at end of file
diff --git a/src/pc/network/discord/discord_network.h b/src/pc/network/discord/discord_network.h
new file mode 100644
index 00000000..582e1195
--- /dev/null
+++ b/src/pc/network/discord/discord_network.h
@@ -0,0 +1,11 @@
+#ifndef DISCORD_NETWORK_H
+#define DISCORD_NETWORK_H
+#include "discord.h"
+
+int ns_discord_network_send(u8* data, u16 dataLength);
+void discord_network_on_message(UNUSED void* eventData, int64_t lobbyId, int64_t userId, uint8_t channelId, uint8_t* data, uint32_t dataLength);
+void discord_network_flush(void);
+void discord_network_init(int64_t lobbyId);
+void discord_network_shutdown(void);
+
+#endif
\ No newline at end of file
diff --git a/src/pc/network/discord/lobby.c b/src/pc/network/discord/lobby.c
new file mode 100644
index 00000000..e2949a11
--- /dev/null
+++ b/src/pc/network/discord/lobby.c
@@ -0,0 +1,93 @@
+#include "lobby.h"
+#include "activity.h"
+#include "discord_network.h"
+#include "pc/debuglog.h"
+
+static bool isHosting = false;
+DiscordLobbyId gCurLobbyId = 0;
+
+static void on_lobby_create_callback(UNUSED void* data, enum EDiscordResult result, struct DiscordLobby* lobby) {
+ LOG_INFO("> on_lobby_update returned %d\n", (int)result);
+ LOG_INFO("Lobby id: %lld\n", lobby->id);
+ LOG_INFO("Lobby type: %u\n", lobby->type);
+ LOG_INFO("Lobby owner id: %lld\n", lobby->owner_id);
+ LOG_INFO("Lobby secret: %s\n", lobby->secret);
+ LOG_INFO("Lobby capacity: %u\n", lobby->capacity);
+ LOG_INFO("Lobby locked: %d\n", lobby->locked);
+
+ gCurActivity.type = DiscordActivityType_Playing;
+ snprintf(gCurActivity.party.id, 128, "%lld", lobby->id);
+ gCurActivity.party.size.current_size = 1;
+ gCurActivity.party.size.max_size = 2;
+
+ char secretJoin[128] = "";
+ snprintf(secretJoin, 128, "%lld:%s", lobby->id, lobby->secret);
+ strcpy(gCurActivity.secrets.join, secretJoin);
+
+ isHosting = true;
+ gCurLobbyId = lobby->id;
+
+ discord_network_init(lobby->id);
+ discord_activity_update(true);
+}
+
+static void on_lobby_update(UNUSED void* data, int64_t lobbyId) {
+ LOG_INFO("> on_lobby_update id: %lld", lobbyId);
+}
+
+static void on_member_connect(UNUSED void* data, int64_t lobbyId, int64_t userId) {
+ LOG_INFO("> on_member_connect lobby: %lld, user: %lld", lobbyId, userId);
+ gCurActivity.party.size.current_size++;
+ discord_activity_update(true);
+}
+
+static void on_member_update(UNUSED void* data, int64_t lobbyId, int64_t userId) {
+ LOG_INFO("> on_member_update lobby: %lld, user: %lld", lobbyId, userId);
+}
+
+static void on_member_disconnect(UNUSED void* data, int64_t lobbyId, int64_t userId) {
+ LOG_INFO("> on_member_disconnect lobby: %lld, user: %lld", lobbyId, userId);
+ gCurActivity.party.size.current_size--;
+ discord_activity_update(isHosting);
+}
+
+void discord_lobby_create(void) {
+ struct IDiscordLobbyTransaction* txn = { 0 };
+
+ DISCORD_REQUIRE(app.lobbies->get_lobby_create_transaction(app.lobbies, &txn));
+ txn->set_capacity(txn, 2);
+ txn->set_type(txn, DiscordLobbyType_Public);
+ //txn->set_metadata(txn, "a", "123");
+
+ app.lobbies->create_lobby(app.lobbies, txn, NULL, on_lobby_create_callback);
+}
+
+static void on_lobby_leave_callback(UNUSED void* data, enum EDiscordResult result) {
+ LOG_INFO("> on_lobby_leave returned %d", result);
+}
+
+void discord_lobby_leave(void) {
+ if (gCurLobbyId == 0) { return; }
+
+ discord_network_shutdown();
+ if (isHosting) {
+ app.lobbies->delete_lobby(app.lobbies, gCurLobbyId, NULL, on_lobby_leave_callback);
+ } else {
+ app.lobbies->disconnect_lobby(app.lobbies, gCurLobbyId, NULL, on_lobby_leave_callback);
+ }
+
+ LOG_INFO("left lobby %lld", gCurLobbyId);
+
+ isHosting = false;
+ gCurLobbyId = 0;
+}
+
+struct IDiscordLobbyEvents* discord_lobby_initialize(void) {
+ static struct IDiscordLobbyEvents events = { 0 };
+ events.on_lobby_update = on_lobby_update;
+ events.on_member_connect = on_member_connect;
+ events.on_member_update = on_member_update;
+ events.on_member_disconnect = on_member_disconnect;
+ events.on_network_message = discord_network_on_message;
+ return &events;
+}
\ No newline at end of file
diff --git a/src/pc/network/discord/lobby.h b/src/pc/network/discord/lobby.h
new file mode 100644
index 00000000..3dec9a3d
--- /dev/null
+++ b/src/pc/network/discord/lobby.h
@@ -0,0 +1,11 @@
+#ifndef DISCORD_LOBBY_H
+#define DISCORD_LOBBY_H
+#include "discord.h"
+
+extern DiscordLobbyId gCurLobbyId;
+
+void discord_lobby_create(void);
+void discord_lobby_leave(void);
+struct IDiscordLobbyEvents* discord_lobby_initialize(void);
+
+#endif
\ No newline at end of file
diff --git a/src/pc/network/discord/user.c b/src/pc/network/discord/user.c
new file mode 100644
index 00000000..f06cf3b2
--- /dev/null
+++ b/src/pc/network/discord/user.c
@@ -0,0 +1,15 @@
+#include "user.h"
+#include "pc/debuglog.h"
+
+static void on_current_user_update(UNUSED void* data) {
+ LOG_INFO("> on_current_user_update");
+ struct DiscordUser user;
+ app.users->get_current_user(app.users, &user);
+ app.userId = user.id;
+}
+
+struct IDiscordUserEvents* discord_user_initialize(void) {
+ static struct IDiscordUserEvents events = { 0 };
+ events.on_current_user_update = on_current_user_update;
+ return &events;
+}
\ No newline at end of file
diff --git a/src/pc/network/discord/user.h b/src/pc/network/discord/user.h
new file mode 100644
index 00000000..a3089192
--- /dev/null
+++ b/src/pc/network/discord/user.h
@@ -0,0 +1,7 @@
+#ifndef DISCORD_USER_H
+#define DISCORD_USER_H
+#include "discord.h"
+
+struct IDiscordUserEvents* discord_user_initialize(void);
+
+#endif
\ No newline at end of file
diff --git a/src/pc/network/network.c b/src/pc/network/network.c
index 5a0da0d0..6f5fbad8 100644
--- a/src/pc/network/network.c
+++ b/src/pc/network/network.c
@@ -3,6 +3,7 @@
#include "object_fields.h"
#include "object_constants.h"
#include "socket/socket.h"
+#include "discord/discord.h"
#include "pc/configfile.h"
#include "pc/debuglog.h"
@@ -10,7 +11,7 @@
extern s16 sCurrPlayMode;
enum NetworkType gNetworkType = NT_NONE;
-struct NetworkSystem* gNetworkSystem = &gNetworkSystemSocket;
+struct NetworkSystem* gNetworkSystem = &gNetworkSystemDiscord;
#define LOADING_LEVEL_THRESHOLD 10
u8 networkLoadingLevel = 0;
@@ -21,6 +22,15 @@ struct ServerSettings gServerSettings = {
.playerKnockbackStrength = 25,
};
+
+void network_set_system(enum NetworkSystemType nsType) {
+ switch (nsType) {
+ case NS_SOCKET: gNetworkSystem = &gNetworkSystemSocket; break;
+ case NS_DISCORD: gNetworkSystem = &gNetworkSystemDiscord; break;
+ default: LOG_ERROR("Unknown network system: %d", nsType);
+ }
+}
+
bool network_init(enum NetworkType inNetworkType) {
// sanity check network system
if (gNetworkSystem == NULL) {
@@ -28,6 +38,11 @@ bool network_init(enum NetworkType inNetworkType) {
return false;
}
+ // set server settings
+ gServerSettings.playerInteractions = configPlayerInteraction;
+ gServerSettings.playerKnockbackStrength = configPlayerKnockbackStrength;
+ gServerSettings.stayInLevelAfterStar = configStayInLevelAfterStar;
+
// initialize the network system
int rc = gNetworkSystem->initialize(inNetworkType);
if (!rc) {
@@ -38,26 +53,15 @@ bool network_init(enum NetworkType inNetworkType) {
// set network type
gNetworkType = inNetworkType;
- // set server settings
- if (gNetworkType == NT_SERVER) {
- gServerSettings.playerInteractions = configPlayerInteraction;
- gServerSettings.playerKnockbackStrength = configPlayerKnockbackStrength;
- gServerSettings.stayInLevelAfterStar = configStayInLevelAfterStar;
- }
-
- // exit early if we're not really initializing the network
- if (gNetworkType == NT_NONE) {
- return true;
- }
-
- // send connection request
- if (gNetworkType == NT_CLIENT) {
- network_send_save_file_request();
- }
+ LOG_INFO("initialized");
return true;
}
+void network_on_joined(void) {
+ network_send_save_file_request();
+}
+
void network_on_init_level(void) {
// reset loading timer
networkLoadingLevel = 0;
@@ -131,7 +135,6 @@ void network_receive(u8* data, u16 dataLength) {
}
void network_update(void) {
- if (gNetworkType == NT_NONE) { return; }
// check for level loaded event
if (!gNetworkLevelLoaded) {
@@ -141,10 +144,12 @@ void network_update(void) {
}
}
- // figure out which update loop to run
- if (sCurrPlayMode == PLAY_MODE_NORMAL || sCurrPlayMode == PLAY_MODE_PAUSED) {
- network_update_player();
- network_update_objects();
+ // send out update packets
+ if (gNetworkType != NT_NONE) {
+ if (sCurrPlayMode == PLAY_MODE_NORMAL || sCurrPlayMode == PLAY_MODE_PAUSED) {
+ network_update_player();
+ network_update_objects();
+ }
}
// receive packets
@@ -153,13 +158,16 @@ void network_update(void) {
}
// update reliable packets
- network_update_reliable();
+ if (gNetworkType != NT_NONE) {
+ network_update_reliable();
+ }
}
void network_shutdown(void) {
if (gNetworkType == NT_NONE) { return; }
- gNetworkType = NT_NONE;
if (gNetworkSystem == NULL) { LOG_ERROR("no network system attached"); return; }
gNetworkSystem->shutdown();
+
+ gNetworkType = NT_NONE;
}
diff --git a/src/pc/network/network.h b/src/pc/network/network.h
index 16972fee..2fc84107 100644
--- a/src/pc/network/network.h
+++ b/src/pc/network/network.h
@@ -16,7 +16,14 @@ extern struct MarioState gMarioStates[];
#define MAX_SYNC_OBJECTS 256 // note: increasing this requires code to be rewritten
#define MAX_SYNC_OBJECT_FIELDS 64
#define PACKET_LENGTH 1024
-#define NETWORKTYPESTR (gNetworkType == NT_CLIENT ? "Client" : "Server")
+#define NETWORKTYPESTR (gNetworkType == NT_CLIENT \
+ ? "Client" \
+ : (gNetworkType == NT_SERVER ? "Server" : " None ")) \
+
+enum NetworkSystemType {
+ NS_SOCKET,
+ NS_DISCORD,
+};
struct NetworkSystem {
bool (*initialize)(enum NetworkType);
@@ -92,11 +99,13 @@ extern struct SyncObject gSyncObjects[];
extern struct ServerSettings gServerSettings;
// network.c
+void network_set_system(enum NetworkSystemType nsType);
bool network_init(enum NetworkType inNetworkType);
void network_on_init_level(void);
void network_on_loaded_level(void);
void network_send(struct Packet* p);
void network_receive(u8* data, u16 dataLength);
+void network_on_joined(void);
void network_update(void);
void network_shutdown(void);
diff --git a/src/pc/network/socket/socket.c b/src/pc/network/socket/socket.c
index d3382772..5421096e 100644
--- a/src/pc/network/socket/socket.c
+++ b/src/pc/network/socket/socket.c
@@ -2,6 +2,7 @@
#include "socket.h"
#include "pc/configfile.h"
#include "pc/debuglog.h"
+#include "menu/file_select.h"
static SOCKET curSocket = INVALID_SOCKET;
struct sockaddr_in txAddr = { 0 };
@@ -70,6 +71,16 @@ static bool ns_socket_initialize(enum NetworkType networkType) {
LOG_INFO("connecting to %s %u", configJoinIp, port);
}
+ // kick off first packet
+ if (networkType == NT_CLIENT) {
+ char joinText[128] = { 0 };
+ snprintf(joinText, 63, "%s %d", configJoinIp, configJoinPort);
+ open_join_menu(joinText);
+
+ gNetworkType = NT_CLIENT;
+ network_on_joined();
+ }
+
LOG_INFO("initialized");
// success
diff --git a/src/pc/network/socket/socket_windows.h b/src/pc/network/socket/socket_windows.h
index 4fd8f4a1..50dad0cf 100644
--- a/src/pc/network/socket/socket_windows.h
+++ b/src/pc/network/socket/socket_windows.h
@@ -3,7 +3,6 @@
#include
#include
-#include "socket.h"
#define SOCKET_LAST_ERROR WSAGetLastError()
#define SOCKET_EWOULDBLOCK WSAEWOULDBLOCK
diff --git a/src/pc/pc_main.c b/src/pc/pc_main.c
index fc99f4f5..c5a252ba 100644
--- a/src/pc/pc_main.c
+++ b/src/pc/pc_main.c
@@ -262,12 +262,16 @@ void main_func(void) {
}
if (gCLIOpts.Network == NT_CLIENT) {
+ network_set_system(NS_SOCKET);
strncpy(configJoinIp, gCLIOpts.JoinIp, IP_MAX_LEN);
configJoinPort = gCLIOpts.NetworkPort;
network_init(NT_CLIENT);
} else if (gCLIOpts.Network == NT_SERVER) {
+ network_set_system(NS_SOCKET);
configHostPort = gCLIOpts.NetworkPort;
network_init(NT_SERVER);
+ } else {
+ network_init(NT_NONE);
}
audio_init();