diff --git a/Makefile b/Makefile
index 7cc3b3a3..9da5fadc 100644
--- a/Makefile
+++ b/Makefile
@@ -48,10 +48,10 @@ EXT_OPTIONS_MENU ?= 1
TEXTSAVES ?= 0
# Load resources from external files
EXTERNAL_DATA ?= 0
-# Enable Discord Rich Presence (outdated, no longer supported)
-DISCORDRPC ?= 0
-# Enable Discord Game SDK (used for Discord server hosting)
+# Enable Discord Game SDK (used for Discord invites)
DISCORD_SDK ?= 1
+# Enable CoopNet SDK (used for CoopNet server hosting)
+COOPNET ?= 1
# Enable docker build workarounds
DOCKERBUILD ?= 0
# Sets your optimization level for building.
@@ -526,14 +526,10 @@ SRC_DIRS := src src/engine src/game src/audio src/bass_audio src/menu src/buffer
BIN_DIRS := bin bin/$(VERSION)
# PC files
-SRC_DIRS += src/pc src/pc/gfx src/pc/audio src/pc/controller src/pc/fs src/pc/fs/packtypes src/pc/mods src/pc/network src/pc/network/packets src/pc/network/socket src/pc/utils src/pc/utils/miniz src/pc/djui src/pc/lua src/pc/lua/utils
-
-#ifeq ($(DISCORDRPC),1)
-# SRC_DIRS += src/pc/discord
-#endif
+SRC_DIRS += src/pc src/pc/gfx src/pc/audio src/pc/controller src/pc/fs src/pc/fs/packtypes src/pc/mods src/pc/network src/pc/network/packets src/pc/network/socket src/pc/network/coopnet src/pc/utils src/pc/utils/miniz src/pc/djui src/pc/lua src/pc/lua/utils
ifeq ($(DISCORD_SDK),1)
- SRC_DIRS += src/pc/network/discord
+ SRC_DIRS += src/pc/discord
endif
ULTRA_SRC_DIRS := lib/src lib/src/math lib/asm lib/data
@@ -605,18 +601,8 @@ ULTRA_O_FILES := $(foreach file,$(ULTRA_S_FILES),$(BUILD_DIR)/$(file:.s=.o)) \
GODDARD_O_FILES := $(foreach file,$(GODDARD_C_FILES),$(BUILD_DIR)/$(file:.c=.o))
RPC_LIBS :=
-#ifeq ($(DISCORDRPC),1)
-# ifeq ($(WINDOWS_BUILD),1)
-# RPC_LIBS := lib/discord/libdiscord-rpc.dll
-# else ifeq ($(OSX_BUILD),1)
-# # needs testing
-# RPC_LIBS := lib/discord/libdiscord-rpc.dylib
-# else
-# RPC_LIBS := lib/discord/libdiscord-rpc.so
-# endif
-#endif
-
DISCORD_SDK_LIBS :=
+
ifeq ($(DISCORD_SDK), 1)
ifeq ($(WINDOWS_BUILD),1)
ifeq ($(TARGET_BITS), 32)
@@ -756,7 +742,6 @@ else
CP := cp
endif
-#ifeq ($(DISCORDRPC),1)
ifeq ($(DISCORD_SDK),1)
LD := $(CXX)
else ifeq ($(WINDOWS_BUILD),1)
@@ -786,7 +771,7 @@ INCLUDE_DIRS := include $(BUILD_DIR) $(BUILD_DIR)/include src .
ifeq ($(TARGET_N64),1)
INCLUDE_DIRS += include/libc
else
- INCLUDE_DIRS += sound lib/lua/include $(EXTRA_INCLUDES)
+ INCLUDE_DIRS += sound lib/lua/include lib/coopnet/include $(EXTRA_INCLUDES)
endif
# Connfigure backend flags
@@ -921,9 +906,6 @@ else ifeq ($(OSX_BUILD),1)
LDFLAGS := -lm $(BACKEND_LDFLAGS) -lpthread
else
LDFLAGS := $(BITS) -march=$(TARGET_ARCH) -lm $(BACKEND_LDFLAGS) -no-pie -lpthread
-# ifeq ($(DISCORDRPC),1)
-# LDFLAGS += -ldl -Wl,-rpath .
-# endif
endif
# icon
@@ -981,6 +963,27 @@ else
LDFLAGS += -Llib/lua/linux -l:liblua53.a -ldl
endif
+# coopnet
+ifeq ($(COOPNET),1)
+ ifeq ($(WINDOWS_BUILD),1)
+ ifeq ($(TARGET_BITS), 32)
+ LDFLAGS += -Llib/coopnet/win32 -l:libcoopnet.a -l:libjuice.a -lbcrypt -lws2_32
+ else
+ LDFLAGS += -Llib/coopnet/win64 -l:libcoopnet.a -l:libjuice.a -lbcrypt -lws2_32
+ endif
+ else ifeq ($(OSX_BUILD),1)
+ LDFLAGS += -L./lib/coopnet/mac/ -l coopnet
+ else ifeq ($(TARGET_RPI),1)
+ ifneq (,$(findstring aarch64,$(machine)))
+ LDFLAGS += -Llib/coopnet/linux -l:libcoopnet-arm64.a -l:libjuice.a
+ else
+ LDFLAGS += -Llib/coopnet/linux -l:libcoopnet-arm.a -l:libjuice.a
+ endif
+ else
+ LDFLAGS += -Llib/coopnet/linux -l:libcoopnet.a -l:libjuice.a
+ endif
+endif
+
# Network/Discord/Bass (ugh, needs cleanup)
ifeq ($(WINDOWS_BUILD),1)
LDFLAGS += -L"ws2_32" -lwsock32
@@ -1057,18 +1060,18 @@ endif
CFLAGS += -DNODRAWINGDISTANCE
#endif
-# Check for Discord Rich Presence option
-#ifeq ($(DISCORDRPC),1)
-# CC_CHECK_CFLAGS += -DDISCORDRPC
-# CFLAGS += -DDISCORDRPC
-#endif
-
# Check for Discord SDK option
ifeq ($(DISCORD_SDK),1)
CC_CHECK_CFLAGS += -DDISCORD_SDK
CFLAGS += -DDISCORD_SDK
endif
+# Check for COOPNET option
+ifeq ($(COOPNET),1)
+ CC_CHECK_CFLAGS += -DCOOPNET
+ CFLAGS += -DCOOPNET
+endif
+
# Check for development option
ifeq ($(DEVELOPMENT),1)
CC_CHECK_CFLAGS += -DDEVELOPMENT
diff --git a/autogen/convert_functions.py b/autogen/convert_functions.py
index 0a932250..1f4486e1 100644
--- a/autogen/convert_functions.py
+++ b/autogen/convert_functions.py
@@ -47,6 +47,7 @@ in_files = [
"src/pc/lua/utils/smlua_text_utils.h",
"src/pc/lua/utils/smlua_audio_utils.h",
"src/pc/lua/utils/smlua_level_utils.h",
+ "src/pc/lua/utils/smlua_deprecated.h",
"src/game/object_helpers.c",
"src/game/obj_behaviors.c",
"src/game/obj_behaviors_2.c",
@@ -106,6 +107,10 @@ override_disallowed_functions = {
"src/pc/network/lag_compensation.h": [ "lag_compensation_clear", "lag_compensation_store" ],
}
+override_hide_functions = {
+ "smlua_deprecated.h" : [ ".*" ],
+}
+
lua_function_params = {
"src/pc/lua/utils/smlua_obj_utils.h::spawn_object_sync::objSetupFunction": [ "struct Object*" ]
}
@@ -763,6 +768,16 @@ def process_files():
############################################################################
+def doc_should_document(fname, identifier):
+ if fname in override_hide_functions:
+ found_match = False
+ for pattern in override_hide_functions[fname]:
+ if re.search(pattern, identifier) != None:
+ found_match = True
+ break
+ return not found_match
+ return True
+
def doc_page_link(page_num):
if page_num == 1:
return 'functions.md'
@@ -779,6 +794,9 @@ def doc_function_index(processed_files):
for function in processed_file['functions']:
if not function['implemented']:
continue
+ if not doc_should_document(processed_file['filename'], function['identifier']):
+ continue
+
s += ' - [%s](%s#%s)\n' % (function['identifier'], doc_page_link(page_num), function['identifier'])
s += '\n
\n\n'
@@ -805,10 +823,13 @@ def doc_lua_func_param(param):
s += ')'
return s
-def doc_function(function):
+def doc_function(fname, function):
if not function['implemented']:
return ''
+ if not doc_should_document(fname, function['identifier']):
+ return ''
+
fid = function['identifier']
s = '\n## [%s](#%s)\n' % (fid, fid)
@@ -860,10 +881,10 @@ def doc_function(function):
return s
-def doc_functions(functions):
+def doc_functions(fname, functions):
s = ''
for function in functions:
- s += doc_function(function)
+ s += doc_function(fname, function)
return s
def doc_files(processed_files):
@@ -879,7 +900,7 @@ def doc_files(processed_files):
for processed_file in processed_files:
s_file = '\n---'
s_file += '\n# functions from %s\n\n
\n\n' % processed_file['filename']
- s_file += doc_functions(processed_file['functions'])
+ s_file += doc_functions(processed_file['filename'], processed_file['functions'])
if len(s) + len(s_file) + extra_space > page_len_limit:
s += '---\n\n$[FUNCTION_NAV_HERE]\n\n'
diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua
index d1d6d242..d57a61c2 100644
--- a/autogen/lua_definitions/constants.lua
+++ b/autogen/lua_definitions/constants.lua
@@ -4727,7 +4727,10 @@ SYNC_DISTANCE_INFINITE = 0
NS_SOCKET = 0
--- @type NetworkSystemType
-NS_DISCORD = 1
+NS_COOPNET = 1
+
+--- @type NetworkSystemType
+NS_MAX = 2
--- @class PlayerInteractions
@@ -4744,7 +4747,7 @@ PLAYER_INTERACTIONS_PVP = 2
MAX_RX_SEQ_IDS = 64
--- @type integer
-NETWORK_PLAYER_PING_TIMEOUT = 1
+NETWORK_PLAYER_PING_TIMEOUT = 3
--- @type integer
NETWORK_PLAYER_TIMEOUT = 10
@@ -11522,13 +11525,13 @@ MAX_LOCAL_VERSION_LENGTH = 12
MAX_VERSION_LENGTH = 10
--- @type integer
-MINOR_VERSION_NUMBER = 1
+MINOR_VERSION_NUMBER = 0
--- @type integer
PATCH_VERSION_NUMBER = 0
--- @type integer
-VERSION_NUMBER = 33
+VERSION_NUMBER = 34
--- @type string
VERSION_TEXT = "beta"
diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua
index 502423d0..363df1ff 100644
--- a/autogen/lua_definitions/functions.lua
+++ b/autogen/lua_definitions/functions.lua
@@ -5639,12 +5639,6 @@ function network_player_set_description(np, description, r, g, b, a)
-- ...
end
---- @param localIndex integer
---- @return string
-function network_discord_id_from_local_index(localIndex)
- -- ...
-end
-
--- @param localIndex integer
--- @return string
function network_get_player_text_color_string(localIndex)
@@ -8015,6 +8009,12 @@ function smlua_collision_util_get(name)
-- ...
end
+--- @param localIndex integer
+--- @return string
+function network_discord_id_from_local_index(localIndex)
+ -- ...
+end
+
--- @param scriptEntryName string
--- @param courseNum integer
--- @param fullName string
diff --git a/autogen/lua_definitions/structs.lua b/autogen/lua_definitions/structs.lua
index 694dbd5f..ba54ac50 100644
--- a/autogen/lua_definitions/structs.lua
+++ b/autogen/lua_definitions/structs.lua
@@ -1721,6 +1721,7 @@
--- @field public enablePlayerList integer
--- @field public enablePlayersInLevelDisplay integer
--- @field public headlessServer integer
+--- @field public maxPlayers integer
--- @field public playerInteractions PlayerInteractions
--- @field public playerKnockbackStrength integer
--- @field public shareLives integer
diff --git a/docs/lua/constants.md b/docs/lua/constants.md
index 839127cf..a7b85f2a 100644
--- a/docs/lua/constants.md
+++ b/docs/lua/constants.md
@@ -1690,7 +1690,8 @@
| Identifier | Value |
| :--------- | :---- |
| NS_SOCKET | 0 |
-| NS_DISCORD | 1 |
+| NS_COOPNET | 1 |
+| NS_MAX | 2 |
### [enum PlayerInteractions](#PlayerInteractions)
| Identifier | Value |
diff --git a/docs/lua/functions-3.md b/docs/lua/functions-3.md
index 8d664456..1051bd4e 100644
--- a/docs/lua/functions-3.md
+++ b/docs/lua/functions-3.md
@@ -8265,26 +8265,6 @@
-## [network_discord_id_from_local_index](#network_discord_id_from_local_index)
-
-### Lua Example
-`local stringValue = network_discord_id_from_local_index(localIndex)`
-
-### Parameters
-| Field | Type |
-| ----- | ---- |
-| localIndex | `integer` |
-
-### Returns
-- `string`
-
-### C Prototype
-`char* network_discord_id_from_local_index(u8 localIndex);`
-
-[:arrow_up_small:](#)
-
-
-
## [network_get_player_text_color_string](#network_get_player_text_color_string)
### Lua Example
diff --git a/docs/lua/functions-4.md b/docs/lua/functions-4.md
index ac54f492..522f0eff 100644
--- a/docs/lua/functions-4.md
+++ b/docs/lua/functions-4.md
@@ -7251,6 +7251,12 @@
+---
+# functions from smlua_deprecated.h
+
+
+
+
---
# functions from smlua_level_utils.h
diff --git a/docs/lua/functions.md b/docs/lua/functions.md
index 2094608b..29c6b88c 100644
--- a/docs/lua/functions.md
+++ b/docs/lua/functions.md
@@ -1096,7 +1096,6 @@
- network_utils.h
- - [network_discord_id_from_local_index](functions-3.md#network_discord_id_from_local_index)
- [network_get_player_text_color_string](functions-3.md#network_get_player_text_color_string)
- [network_global_index_from_local](functions-3.md#network_global_index_from_local)
- [network_is_moderator](functions-3.md#network_is_moderator)
@@ -1490,6 +1489,10 @@
+- smlua_deprecated.h
+
+
+
- smlua_level_utils.h
- [level_register](functions-4.md#level_register)
- [smlua_level_util_get_info](functions-4.md#smlua_level_util_get_info)
diff --git a/docs/lua/structs.md b/docs/lua/structs.md
index 6f77e822..c09b5406 100644
--- a/docs/lua/structs.md
+++ b/docs/lua/structs.md
@@ -2181,6 +2181,7 @@
| enablePlayerList | `integer` | |
| enablePlayersInLevelDisplay | `integer` | |
| headlessServer | `integer` | |
+| maxPlayers | `integer` | |
| playerInteractions | [enum PlayerInteractions](constants.md#enum-PlayerInteractions) | |
| playerKnockbackStrength | `integer` | |
| shareLives | `integer` | |
diff --git a/lang/English.ini b/lang/English.ini
index 7c78aa93..0566651d 100644
--- a/lang/English.ini
+++ b/lang/English.ini
@@ -19,6 +19,16 @@ IMPORT_MOD_SUCCESS = "\\#a0ffa0\\Imported mod\n\\#c8c8c8\\'@'"
IMPORT_DYNOS_SUCCESS = "\\#a0ffa0\\Imported DynOS pack\n\\#c8c8c8\\'@'"
IMPORT_FAIL = "\\#ffa0a0\\Failed to import\n\\#c8c8c8\\'@'"
IMPORT_FAIL_INGAME = "\\#ffa0a0\\Can not import while in-game"
+COOPNET_CONNECTION_FAILED = "\\#ffa0a0\\Could not connect to CoopNet!"
+COOPNET_DISCONNECTED = "\\#ffa0a0\\Lost connection to CoopNet!"
+LOBBY_NOT_FOUND = "\\#ffa0a0\\The lobby no longer exists!"
+LOBBY_JOIN_FULL = "\\#ffa0a0\\The lobby is full!"
+LOBBY_JOIN_FAILED = "\\#ffa0a0\\Failed to join the lobby!"
+LOBBY_PASSWORD_INCORRECT = "\\#ffa0a0\\Entered the wrong lobby password!"
+COOPNET_VERSION = "\\#ffa0a0\\Your version is no longer compatible with CoopNet. Update the game!"
+PEER_FAILED = "\\#ffa0a0\\Failed to connect to player '@'"
+UNKNOWN = "unknown"
+LOBBY_HOST = "the lobby's host"
[CHAT]
KICKING = "Kicking '@'!"
@@ -177,8 +187,10 @@ SERVER_TITLE = "SERVER"
HOST_TITLE = "HOST"
DISCORD = "Discord"
DIRECT_CONNECTION = "Direct Connection"
+COOPNET = "CoopNet"
NETWORK_SYSTEM = "Network system"
PORT = "Port"
+PASSWORD = "Password"
SAVE_SLOT = "Save Slot"
SETTINGS = "Settings"
MODS = "Mods"
@@ -194,6 +206,9 @@ JOIN_TITLE = "JOIN"
JOIN_DISCORD = "To join a \\#d0d0ff\\Discord\\#c8c8c8\\ lobby:\n\nKeep the game open and click the join button on the invite.\n\nIf the invite says that the game has ended, click the name of the person that sent the invite to refresh it."
JOIN_SOCKET = "Enter \\#d0d0ff\\direct connection\\#c8c8c8\\ IP and port:"
JOIN = "Join"
+PUBLIC_LOBBIES = "Public Lobbies"
+PRIVATE_LOBBIES = "Private Lobbies"
+DIRECT = "Direct Connection"
[MAIN]
QUIT_TITLE = "QUIT"
@@ -321,3 +336,12 @@ ENV_VOLUME = "Env Volume"
[LANGUAGE]
LANGUAGE = "LANGUAGE"
+
+[LOBBIES]
+PUBLIC_LOBBIES = "PUBLIC LOBBIES"
+PRIVATE_LOBBIES = "PRIVATE LOBBIES"
+REFRESH = "Refresh"
+REFRESHING = "Refreshing..."
+ENTER_PASSWORD = "Enter the private lobby's password:"
+SEARCH = "Search"
+NONE_FOUND = "No lobbies were found."
diff --git a/lang/French.ini b/lang/French.ini
index 07f85f99..f410e5c1 100644
--- a/lang/French.ini
+++ b/lang/French.ini
@@ -177,6 +177,7 @@ SERVER_TITLE = "SERVEUR"
HOST_TITLE = "HÉBERGER"
DISCORD = "Discord"
DIRECT_CONNECTION = "Connexion Directe"
+COOPNET = "CoopNet"
NETWORK_SYSTEM = "Mode d'hébergement"
PORT = "Port"
SAVE_SLOT = "Sauvegarde"
diff --git a/lang/German.ini b/lang/German.ini
index 05277a72..998a0461 100644
--- a/lang/German.ini
+++ b/lang/German.ini
@@ -177,6 +177,7 @@ SERVER_TITLE = "SERVER"
HOST_TITLE = "VERANSTALTEN"
DISCORD = "Discord"
DIRECT_CONNECTION = "Direkte Verbindung"
+COOPNET = "CoopNet"
NETWORK_SYSTEM = "Netzwerk-System"
PORT = "Port"
SAVE_SLOT = "Save Slot"
diff --git a/lang/Italian.ini b/lang/Italian.ini
new file mode 100644
index 00000000..780ed466
--- /dev/null
+++ b/lang/Italian.ini
@@ -0,0 +1,320 @@
+[NOTIF]
+CONNECTED = "@ si è connesso"
+DISCONNECTED = "@ si è disconnesso"
+LEFT_THIS_LEVEL = "@ ha lasciato il livello"
+ENTERED_THIS_LEVEL = "@ è entrato nel livello"
+ENTERED = "@ è entrato in\n#"
+SERVER_CLOSED = "\\#ffa0a0\\Disconnesso:\\#dcdcdc\\ server chiuso"
+DISCORD_ERROR = "Errore: Discord ha avuto un errore\nPer risolverlo, prova a: \n1. Chiudere il gioco.\n2. Riavviare Discord.\n3. Avviare il gioco."
+DISCORD_DETECT = "\\#ffa0a0\\Errore:\\#c8c8c8\\ impossibile individuare Discord.\n\\#a0a0a0\\Prova a chiudere il gioco, riavviare Discord, e poi riaprire il gioco."
+DISCONNECT_FULL = "\\#ffa0a0\\Disconnesso:\\#c8c8c8\\ il server è pieno."
+DISCONNECT_KICK = "\\#ffa0a0\\Disconnesso:\\#c8c8c8\\ sei stato espulso."
+DISCONNECT_BAN = "\\#ffa0a0\\Disconnesso:\\#c8c8c8\\ sei stato bandito."
+DISCONNECT_REJOIN = "\\#ffa0a0\\Disconnesso:\\#c8c8c8\\ ricollegandoti..."
+DISCONNECT_CLOSED = "\\#ffa0a0\\Disconnesso:\\#c8c8c8\\ l'host ha interroto la connessione."
+DISCONNECT_BIG_MOD = "Il server ha una mod troppo pesante.\nDisconnessione."
+DIED = "@ è morto"
+DEBUG_FLY = "@ è entrato nello stato di debug di volo libero"
+IMPORT_MOD_SUCCESS = "\\#a0ffa0\\importata la mod\n\\#c8c8c8\\'@'"
+IMPORT_DYNOS_SUCCESS = "\\#a0ffa0\\Importato il pacchetto DynOS\n\\#c8c8c8\\'@'"
+IMPORT_FAIL = "\\#ffa0a0\\Impossibile importare\n\\#c8c8c8\\'@'"
+IMPORT_FAIL_INGAME = "\\#ffa0a0\\Impossibile importare durante la partita"
+
+[CHAT]
+KICKING = "Espulso '@'!"
+BANNING = "Bandito '@'!"
+SERVER_ONLY = "Solo il server può usare questo comando."
+PERM_BANNING = "Bandito permanentemente '@'!"
+ADD_MODERATOR = "Aggiunto '@' tra i Moderatori!"
+PLAYERS = "Giocatori"
+NO_PERMS = "Non hai il permesso di usare questo comando."
+PLAYER_NOT_FOUND = "Impossibile trovare il giocatore."
+SELF_KICK = "Non puoi espellere te stesso."
+SELF_BAN = "Non puoi bandire te stesso."
+SELF_MOD = "Non puoi renderti un moderatore."
+KICK_CONFIRM = "Sei sicuro di voler espellere '@'?\nScrivi '\\#a0ffa0\\/confirm\\#fff982\\' per confermare."
+BAN_CONFIRM = "Sei sicuro di voler bandire '@'?\nScrivi'\\#a0ffa0\\/confirm\\#fff982\\' per confermare."
+PERM_BAN_CONFIRM = "Sei sicuro di voler bandire permanentemenrte '@'?\nScrivi '\\#a0ffa0\\/confirm\\#fff982\\' per confermare."
+MOD_CONFIRM = "Sei sicuro di voler rendere '@' un moderatore?\nScrivi '\\#a0ffa0\\/confirm\\#fff982\\' per confermare."
+PLAYERS_DESC = "/players - Lista dei giocatori e dei loro ID"
+KICK_DESC = "/kick [NAME|ID] - Espelli questo giocatore dalla partita"
+BAN_DESC = "/ban [NAME|ID] - Bandisci questo giocatore dalla partita"
+PERM_BAN_DESC = "/permban [NAME|ID] - Bandisci questo giocatore da tutte le tue partite"
+MOD_DESC = "/moderator [NAME|ID] - Dai al gicatore il permesso di eseguire comandi come /kick, /ban, /permban in ogni partita che crei"
+UNRECOGNIZED = "Comando non riconosciuto."
+MOD_GRANTED = "\\#fff982\\Ora sei un moderatore."
+
+[MENU]
+BACK = "Indietro"
+CANCEL = "Annulla"
+NO = "No"
+YES = "Si"
+
+[CAMERA]
+CAMERA = "TELECAMERA"
+FREE_CAMERA = "Telecamera libera"
+ANALOG_CAMERA = "Telecamera analogica"
+MOUSE_LOOK = "Telecamera con mouse"
+INVERT_X = "Inverti X"
+INVERT_Y = "Inverti Y"
+X_SENSITIVITY = "Inverti asse X"
+Y_SENSITIVITY = "Inverti asse Y"
+AGGRESSION = "Aggressività"
+PAN_LEVEL = "Livello di panoramica"
+DECELERATION = "Decelerazione"
+
+[CHEATS]
+CHEATS = "TRUCCHI"
+MOON_JUMP = "Moon Jump"
+GOD_MODE = "God Mode"
+INFINITE_LIVES = "Vite infinite"
+SUPER_SPEED = "Super Velocità"
+RESPONSIVE_CONTROLS = "Controlli reattivi"
+RAPID_FIRE = "Fuoco Rapido (A)"
+BLJ_ANYWHERE = "BLJ Ovunque"
+ALWAYS_TRIPLE_JUMP = "Sempre Salto triplo"
+
+[CONTROLS]
+CONTROLS = "CONTROLLI"
+N64_BINDS = "Comandi N64"
+EXTRA_BINDS = "Comandi Extra"
+BACKGROUND_GAMEPAD = "Azione in Background"
+GAMEPAD = "Controller"
+DEADZONE = "Zona Morta"
+RUMBLE_STRENGTH = "Intesità Vibrazione"
+CHAT = "Chat"
+PLAYERS = "Giocatori"
+D_UP = "D Su"
+D_DOWN = "D Giù"
+D_LEFT = "D Sinistra"
+D_RIGHT = "D Destra"
+X = "X"
+Y = "Y"
+
+UP = "Su"
+DOWN = "Giù"
+LEFT = "Sinistra"
+RIGHT = "Destra"
+A = "A"
+B = "B"
+START = "Start"
+L = "L"
+R = "R"
+Z = "Z"
+C_UP = "C Su"
+C_DOWN = "C Giù"
+C_LEFT = "C Sinistra"
+C_RIGHT = "C Destra"
+
+[DISPLAY]
+DISPLAY = "GRAFICA"
+FULLSCREEN = "Schermo intero"
+PRELOAD_TEXTURES = "Precarica Textures"
+VSYNC = "VSync"
+UNCAPPED_FRAMERATE = "Fotogrammi Illimitati"
+FRAME_LIMIT = "Limite Fotogrammi"
+FAST = "Veloce"
+ACCURATE = "Accurata"
+INTERPOLATION = "Interpolazione"
+NEAREST = "Vicino"
+LINEAR = "Lineare"
+TRIPOINT = "Tripunto"
+FILTERING = "Filtraggio"
+D0P5X = "0.5x"
+D1X = "1x"
+D1P5X = "1.5x"
+D3X = "3x"
+D10X = "10x"
+D100X = "100x"
+DRAW_DISTANCE = "Distanza di simulazione"
+DYNOS_PACKS = "Pacchetti DynOS"
+
+[DYNOS]
+DYNOS = "DYNOS"
+
+[HOST_MESSAGE]
+INFO_TITLE = "INFO"
+WARN_DISCORD = "Invita gli amici facendo tasto destro sul loro nome in Discord e cliccando\n'\\#d0d0ff\\Invito a giocare\\#c8c8c8\\'.\n\npuoi invitare anche i canali dei server cliccando il pulsante \\#d0d0ff\\più\\#c8c8c8\\ vicino al posto dove scrivi.\n\nLo Stato delle Attività \\#ffa0a0\\deve essere\\#c8c8c8\\ attivo nelle\nimpostazioni utente di Discord.\n\nApparire offline \\#ffa0a0\\ti impedirà\\#c8c8c8\\ di inviare inviti."
+WARN_DISCORD2 = "\\#ffa0a0\\Errore:\\#c8c8c8\\ Impossibile individuare Discord.\n\n\\#a0a0a0\\prova a chiudre il gioco,\nriavviare Discord,\ne aprire di nuovo il gioco."
+WARN_SOCKET = "La connessione diretta \\#ffa0a0\\richiede\\#c8c8c8\\ una configurazione port forwarding nel tuo router.\n\nTrasmetti una connessione '\\#d0d0ff\\%d\\#c8c8c8\\' per l'UDP."
+HOST = "Crea"
+
+[HOST_MODS]
+ROMHACKS = "ROMHACKS"
+MODS = "MODS"
+
+[HOST_SAVE]
+SAVE_TITLE = "SALVATAGGIO"
+ERASE_TITLE = "CANCELLA"
+CONFIRM = "Sei sicuro di voler cancellare questo slot di salvataggio?"
+ERASE = "cancella"
+
+[HOST_SETTINGS]
+SETTINGS = "OPZIONI"
+NONSOLID = "Non-solida"
+SOLID = "Solida"
+FRIENDLY_FIRE = "Fuoco Amico"
+PLAYER_INTERACTION = "Interazione tra Giocatori"
+WEAK = "Debole"
+NORMAL = "Normale"
+TOO_MUCH = "Eccessiva"
+KNOCKBACK_STRENGTH = "Forza di Contraccolpo"
+LEAVE_LEVEL = "Lascia il livello"
+STAY_IN_LEVEL = "Rimani nel livello"
+NONSTOP = "Non-stop"
+ON_STAR_COLLECTION = "A stella collezzionata"
+SKIP_INTRO_CUTSCENE = "Salta la intro iniziale"
+SHARE_LIVES = "Condividi le vite"
+ENABLE_CHEATS = "Abilita i trucchi"
+BUBBLE_ON_DEATH = "Bolla alla morte"
+AMOUNT_OF_PLAYERS = "Numero di giocatori"
+
+[HOST]
+SERVER_TITLE = "SERVER"
+HOST_TITLE = "OSPITA"
+DISCORD = "Discord"
+DIRECT_CONNECTION = "Connessione diretta"
+NETWORK_SYSTEM = "Sistema di connessione"
+PORT = "Porta"
+SAVE_SLOT = "Slot di Salvataggio"
+SETTINGS = "Opzioni"
+MODS = "Mods"
+ROMHACKS = "Rom-Hacks"
+APPLY = "Applica"
+HOST = "Crea"
+
+[JOIN_MESSAGE]
+JOINING = "CONNESSIONE"
+
+[JOIN]
+JOIN_TITLE = "UNISCITI"
+JOIN_DISCORD = "Per entrare in una partita da \\#d0d0ff\\Discord\\#c8c8c8\\:\n\nMantieni il gioco aperto e clicca sul pulsante Unisciti.\n\nSe l'invito dice che la partita è finita,/nclicca sul nome della persona che l'ha mandato per ripristinarla."
+JOIN_SOCKET = "Immetti un \\#d0d0ff\\IP e una Porta\\#c8c8c8\\ per la connessione diretta:"
+JOIN = "Unisciti"
+
+[MAIN]
+QUIT_TITLE = "ABBANDONA"
+QUIT_CONFIRM = "Sei sicuro do voler abbandonare?"
+HOST = "Crea"
+JOIN = "Unisciti"
+OPTIONS = "Opzioni"
+QUIT = "Abbandona"
+
+[MENU_OPTIONS]
+MAIN_MENU = "MENÙ PRINCIPALE"
+LEVEL = "Livello"
+USE_STAGE_MUSIC = "Usa la musica del livello"
+RANDOM_STAGE = "Livello casuale"
+PLAY_VANILLA_DEMOS = "Riproduci le demo di gioco"
+
+[MISC]
+DEBUG_TITLE = "DEBUG"
+FIXED_COLLISIONS = "Collisioni Aggiustate"
+LUA_PROFILER = "Profiler Lua"
+DEBUG_PRINT = "Stampa di debug"
+DEBUG_INFO = "Info di debug"
+DEBUG_ERRORS = "Errori di debug"
+MISC_TITLE = "VARIE"
+PAUSE_IN_SINGLEPLAYER = "Metti in pausa in giocatore singolo"
+DISABLE_POPUPS = "Disabilita Popups"
+MENU_OPTIONS = "Opzioni Menù"
+DEBUG = "Debug"
+LANGUAGE = "Lingua"
+
+[MODLIST]
+MODS = "MODS"
+
+[OPTIONS]
+OPTIONS = "OPZIONI"
+PLAYER = "Giocatore"
+CAMERA = "Telecamera"
+CONTROLS = "Comandi"
+DISPLAY = "Grafica"
+SOUND = "Suono"
+MISC = "Varie"
+
+[PAUSE]
+QUIT_TITLE = "ABBANDONA"
+QUIT_HOST = "Sei sicuro di voler interrompere la connessione?"
+QUIT_CLIENT = "Sei sicuro di volerti disconnettere?"
+PAUSE_TITLE = "PAUSA"
+PLAYER = "Giocatore"
+DYNOS_PACKS = "Pacchetti DynOS"
+OPTIONS = "Opzioni"
+CHEATS = "Trucchi"
+SERVER_SETTINGS = "Impostazioni Server"
+RESUME = "Riprendi"
+STOP_HOSTING = "Interrompi la connessione"
+DISCONNECT = "Disconnettiti"
+
+[PLAYER]
+PLAYER_TITLE = "GICATORE"
+OVERALLS = "Tuta"
+SHIRT = "Maglietta"
+GLOVES = "Guanti"
+SHOES = "Scarpe"
+HAIR = "Capelli"
+SKIN = "Pelle"
+CAP = "Cappello"
+PALETTE = "PALETTE"
+PART = "Parte"
+HEX_CODE = "codice Hex"
+RED = "Rosso"
+GREEN = "Verde"
+BLUE = "Blu"
+PLAYER = "Giocatore"
+NAME = "Nome"
+MODEL = "Modello"
+PALETTE_PRESET = "Opzioni Palette"
+EDIT_PALETTE = "Modifica Palette"
+
+[PALETTE]
+MARIO = "Mario"
+LUIGI = "Luigi"
+WALUIGI = "Waluigi"
+WARIO = "Wario"
+CHUCKYA = "Chuckya"
+GOOMBA = "Goomba"
+CLOVER = "Trifoglio"
+COBALT = "Cobalto"
+FURY = "Furia"
+HOT_PINK = "Rosa Caldo"
+NICE_PINK = "Rosa Fresco"
+SEAFOAM = "Schiuma Marina"
+LILAC = "Lilla"
+COPPER = "Rame"
+AZURE = "Azurro"
+BURGUNDY = "Borgogna"
+MINT = "Menta"
+EGGPLANT = "Melanzana"
+ORANGE = "Arancia"
+ARCTIC = "Arctico"
+FIRE_MARIO = "Mario Fuoco"
+FIRE_LUIGI = "Luigi Fuoco"
+FIRE_WALUIGI = "Waluigi Fuoco"
+FIRE_WARIO = "Wario Fuoco"
+BUSY_BEE = "Ape Operaia"
+FORTRESS = "Fortezza"
+BATTLEMENTS = "Muraglia"
+BLUEBERRY_PIE = "Torta ai Mirtilli"
+RASPBERRY = "Mora"
+BUBBLEGUM = "Gomma da Masticare"
+ICE_MARIO = "Mario Ghiaccio"
+ICE_LUIGI = "Luigi Ghiaccio"
+CUSTOM = "Personalizzato"
+
+[PLAYER_LIST]
+PLAYERS = "GIOCATORI"
+NAME = "nome"
+LOCATION = "posizione"
+ACT = "atto"
+
+[SOUND]
+SOUND = "SUONO"
+MASTER_VOLUME = "Principale"
+MUSIC_VOLUME = "Musica"
+SFX_VOLUME = "Effetti sonori"
+ENV_VOLUME = "Ambiente"
+
+[LANGUAGE]
+LANGUAGE = "LINGUA"
diff --git a/lang/Portuguese.ini b/lang/Portuguese.ini
index 7a00cd56..51849db5 100644
--- a/lang/Portuguese.ini
+++ b/lang/Portuguese.ini
@@ -176,6 +176,7 @@ AMOUNT_OF_PLAYERS = "Quantidade de jogadores"
SERVER_TITLE = "SERVIDOR"
HOST_TITLE = "HOSTEAR"
DISCORD = "Discord"
+COOPNET = "CoopNet"
DIRECT_CONNECTION = "Conexão Direta"
NETWORK_SYSTEM = "Sistema de Rede"
PORT = "Porta"
diff --git a/lang/Spanish.ini b/lang/Spanish.ini
index e3c8f46a..6e332f78 100644
--- a/lang/Spanish.ini
+++ b/lang/Spanish.ini
@@ -176,6 +176,7 @@ AMOUNT_OF_PLAYERS = "Número de jugadores"
SERVER_TITLE = "PARTIDA"
HOST_TITLE = "CREAR"
DISCORD = "Discord"
+COOPNET = "CoopNet"
DIRECT_CONNECTION = "Conexión Directa"
NETWORK_SYSTEM = "Modo de conexión"
PORT = "Puerto"
diff --git a/lib/coopnet/include/libcoopnet.h b/lib/coopnet/include/libcoopnet.h
new file mode 100644
index 00000000..1e69cd9c
--- /dev/null
+++ b/lib/coopnet/include/libcoopnet.h
@@ -0,0 +1,66 @@
+#ifndef LIBCOOPNET_H
+#define LIBCOOPNET_H
+
+#if defined(__cplusplus)
+#include
+extern "C" {
+#endif
+
+#include
+#include
+
+typedef enum {
+ COOPNET_OK,
+ COOPNET_FAILED,
+ COOPNET_DISCONNECTED,
+} CoopNetRc;
+
+enum MPacketErrorNumber {
+ MERR_NONE,
+ MERR_LOBBY_NOT_FOUND,
+ MERR_LOBBY_JOIN_FULL,
+ MERR_LOBBY_JOIN_FAILED,
+ MERR_LOBBY_PASSWORD_INCORRECT,
+ MERR_COOPNET_VERSION,
+ MERR_PEER_FAILED,
+ MERR_MAX,
+};
+
+typedef struct {
+ void (*OnConnected)(uint64_t aUserId);
+ void (*OnDisconnected)(bool aIntentional);
+ void (*OnLobbyCreated)(uint64_t aLobbyId, const char* aGame, const char* aVersion, const char* aHostName, const char* aMode, uint16_t aMaxConnections);
+ void (*OnLobbyJoined)(uint64_t aLobbyId, uint64_t aUserId, uint64_t aOwnerId, uint64_t aDestId);
+ void (*OnLobbyLeft)(uint64_t aLobbyId, uint64_t aUserId);
+ void (*OnLobbyListGot)(uint64_t aLobbyId, uint64_t aOwnerId, uint16_t aConnections, uint16_t aMaxConnections, const char* aGame, const char* aVersion, const char* aHostName, const char* aMode, const char* aDescription);
+ void (*OnLobbyListFinish)(void);
+ void (*OnReceive)(uint64_t aFromUserId, const uint8_t* aData, uint64_t aSize);
+ void (*OnError)(enum MPacketErrorNumber aErrorNumber, uint64_t tag);
+ void (*OnPeerConnected)(uint64_t aPeerId);
+ void (*OnPeerDisconnected)(uint64_t aPeerId);
+} CoopNetCallbacks;
+
+typedef struct {
+ bool SkipWinsockInit;
+} CoopNetSettings;
+
+extern CoopNetCallbacks gCoopNetCallbacks;
+extern CoopNetSettings gCoopNetSettings;
+
+bool coopnet_is_connected(void);
+CoopNetRc coopnet_begin(const char* aHost, uint32_t aPort);
+CoopNetRc coopnet_shutdown(void);
+CoopNetRc coopnet_update(void);
+CoopNetRc coopnet_lobby_create(const char* aGame, const char* aVersion, const char* aHostName, const char* aMode, uint16_t aMaxConnections, const char* aPassword, const char* aDescription);
+CoopNetRc coopnet_lobby_update(uint64_t aLobbyId, const char* aGame, const char* aVersion, const char* aHostName, const char* aMode, const char* aDescription);
+CoopNetRc coopnet_lobby_join(uint64_t aLobbyId, const char* aPassword);
+CoopNetRc coopnet_lobby_leave(uint64_t aLobbyId);
+CoopNetRc coopnet_lobby_list_get(const char* aGame, const char* aPassword);
+CoopNetRc coopnet_send(const uint8_t* aData, uint64_t aDataLength);
+CoopNetRc coopnet_send_to(uint64_t aPeerId, const uint8_t* aData, uint64_t aDataLength);
+CoopNetRc coopnet_unpeer(uint64_t aPeerId);
+
+#if defined(__cplusplus)
+}
+#endif
+#endif
\ No newline at end of file
diff --git a/lib/coopnet/linux/libcoopnet.a b/lib/coopnet/linux/libcoopnet.a
new file mode 100644
index 00000000..a0412556
Binary files /dev/null and b/lib/coopnet/linux/libcoopnet.a differ
diff --git a/lib/coopnet/linux/libjuice.a b/lib/coopnet/linux/libjuice.a
new file mode 100644
index 00000000..5070060e
Binary files /dev/null and b/lib/coopnet/linux/libjuice.a differ
diff --git a/lib/coopnet/win64/libcoopnet.a b/lib/coopnet/win64/libcoopnet.a
new file mode 100644
index 00000000..cc6362b6
Binary files /dev/null and b/lib/coopnet/win64/libcoopnet.a differ
diff --git a/lib/coopnet/win64/libjuice.a b/lib/coopnet/win64/libjuice.a
new file mode 100644
index 00000000..b29076e7
Binary files /dev/null and b/lib/coopnet/win64/libjuice.a differ
diff --git a/mods/arena/arena-player.lua b/mods/arena/arena-player.lua
index 42ff344b..62857c88 100644
--- a/mods/arena/arena-player.lua
+++ b/mods/arena/arena-player.lua
@@ -28,6 +28,19 @@ for i = 0, (MAX_PLAYERS - 1) do
s.rank = 0
end
+local sKnockbackActions = {
+ ACT_SOFT_FORWARD_GROUND_KB, ACT_FORWARD_GROUND_KB, ACT_HARD_FORWARD_GROUND_KB,
+ ACT_FORWARD_AIR_KB, ACT_FORWARD_AIR_KB, ACT_HARD_FORWARD_AIR_KB,
+ ACT_FORWARD_WATER_KB, ACT_FORWARD_WATER_KB, ACT_FORWARD_WATER_KB,
+ ACT_SOFT_BACKWARD_GROUND_KB, ACT_BACKWARD_GROUND_KB, ACT_HARD_BACKWARD_GROUND_KB,
+ ACT_BACKWARD_AIR_KB, ACT_BACKWARD_AIR_KB, ACT_HARD_BACKWARD_AIR_KB,
+ ACT_BACKWARD_WATER_KB, ACT_BACKWARD_WATER_KB, ACT_BACKWARD_WATER_KB,
+
+ ACT_LEDGE_GRAB, ACT_LEDGE_CLIMB_SLOW_1, ACT_LEDGE_CLIMB_SLOW_2, ACT_LEDGE_CLIMB_DOWN, ACT_LEDGE_CLIMB_FAST,
+ ACT_GROUND_BONK, ACT_SOFT_BONK,
+
+ ACT_STOP_CROUCHING, ACT_STOMACH_SLIDE_STOP,
+}
------------
-- hammer --
------------
@@ -361,6 +374,27 @@ function mario_update(m)
local s = gPlayerSyncTable[m.playerIndex]
local np = gNetworkPlayers[m.playerIndex]
+ -- increase knockback animations
+ local animInfo = nil
+ if m.marioObj ~= nil then
+ animInfo = m.marioObj.header.gfx.animInfo
+ end
+ for i, value in ipairs(sKnockbackActions) do
+ if m.action == value then
+ local frame = animInfo.animFrame
+ local loopEnd = frame
+ if animInfo.curAnim ~= nil then
+ loopEnd = animInfo.curAnim.loopEnd
+ end
+
+ if frame < loopEnd - 2 then
+ frame = frame + 1
+ end
+
+ animInfo.animFrame = frame
+ end
+ end
+
-- clear invincibilities
m.invincTimer = 0
if m.knockbackTimer > 5 then
diff --git a/src/game/behaviors/chain_chomp.inc.c b/src/game/behaviors/chain_chomp.inc.c
index c6aada9d..c2ddd964 100644
--- a/src/game/behaviors/chain_chomp.inc.c
+++ b/src/game/behaviors/chain_chomp.inc.c
@@ -556,7 +556,7 @@ void bhv_chain_chomp_gate_init(void) {
* Update function for chain chomp gate
*/
void bhv_chain_chomp_gate_update(void) {
- if (o->parentObj->oChainChompHitGate) {
+ if (o->parentObj && o->parentObj->oChainChompHitGate) {
spawn_mist_particles_with_sound(SOUND_GENERAL_WALL_EXPLOSION);
set_camera_shake_from_point(SHAKE_POS_SMALL, o->oPosX, o->oPosY, o->oPosZ);
spawn_mist_particles_variable(0, 0x7F, 200.0f);
diff --git a/src/game/ingame_menu.c b/src/game/ingame_menu.c
index e837118d..212b4e6b 100644
--- a/src/game/ingame_menu.c
+++ b/src/game/ingame_menu.c
@@ -3246,7 +3246,10 @@ void print_hud_course_complete_coins(s16 x, s16 y) {
void play_star_fanfare_and_flash_hud(s32 arg, u8 starNum) {
if (gHudDisplay.coins == gCourseCompleteCoins && (gCurrCourseStarFlags & starNum) == 0 && gHudFlash == 0) {
gCurrCourseStarFlags |= starNum; // SM74 was spamming fanfare without this line
- play_star_fanfare();
+ if (gFanFareDebounce <= 0) {
+ gFanFareDebounce = 30 * 5;
+ play_star_fanfare();
+ }
gHudFlash = arg;
}
}
diff --git a/src/game/level_update.c b/src/game/level_update.c
index dcd4be5a..3b626048 100644
--- a/src/game/level_update.c
+++ b/src/game/level_update.c
@@ -58,6 +58,7 @@
struct SavedWarpValues gReceiveWarp = { 0 };
extern s8 sReceivedLoadedActNum;
u8 gRejectInstantWarp = 0;
+u16 gFanFareDebounce = 0;
s16 gChangeLevel = -1;
s16 gChangeLevelTransition = -1;
@@ -1612,6 +1613,8 @@ s32 update_level(void) {
sFirstCastleGroundsMenu = false;
}
+ if (gFanFareDebounce > 0) { gFanFareDebounce--; }
+
s32 changeLevel = 0;
if (gChangeLevel != -1) {
diff --git a/src/game/level_update.h b/src/game/level_update.h
index 5df96e7d..904d7de4 100644
--- a/src/game/level_update.h
+++ b/src/game/level_update.h
@@ -103,6 +103,7 @@ struct SavedWarpValues {
extern struct WarpDest sWarpDest;
extern s8 sWarpCheckpointActive;
extern u8 gRejectInstantWarp;
+extern u16 gFanFareDebounce;
extern s16 D_80339EE0;
extern s16 sDelayedWarpOp;
diff --git a/src/pc/cliopts.c b/src/pc/cliopts.c
index 77cf8409..3b8fec98 100644
--- a/src/pc/cliopts.c
+++ b/src/pc/cliopts.c
@@ -85,9 +85,6 @@ 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 ea360450..916e19b1 100644
--- a/src/pc/cliopts.h
+++ b/src/pc/cliopts.h
@@ -22,7 +22,6 @@ 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/configfile.c b/src/pc/configfile.c
index 7f485b36..dc325905 100644
--- a/src/pc/configfile.c
+++ b/src/pc/configfile.c
@@ -120,9 +120,6 @@ bool configEnableCheats = 0;
bool configBubbleDeath = true;
unsigned int configAmountofPlayers = 16;
bool configHUD = true;
-#ifdef DISCORDRPC
-bool configDiscordRPC = true;
-#endif
// coop-specific
char configJoinIp[MAX_CONFIG_STRING] = "";
unsigned int configJoinPort = DEFAULT_PORT;
@@ -157,6 +154,9 @@ bool configDebugInfo = 0;
bool configDebugError = 0;
char configLanguage[MAX_CONFIG_STRING] = "";
bool configForce4By3 = false;
+char configCoopNetIp[MAX_CONFIG_STRING] = DEFAULT_COOPNET_IP;
+unsigned int configCoopNetPort = DEFAULT_COOPNET_PORT;
+char configPassword[MAX_PLAYER_STRING] = "";
static const struct ConfigOption options[] = {
{.name = "fullscreen", .type = CONFIG_TYPE_BOOL, .boolValue = &configWindow.fullscreen},
@@ -211,9 +211,6 @@ static const struct ConfigOption options[] = {
#endif
{.name = "skip_intro", .type = CONFIG_TYPE_BOOL, .boolValue = &configSkipIntro},
{.name = "enable_cheats", .type = CONFIG_TYPE_BOOL, .boolValue = &configEnableCheats},
- #ifdef DISCORDRPC
- {.name = "discordrpc_enable", .type = CONFIG_TYPE_BOOL, .boolValue = &configDiscordRPC},
- #endif
// debug
{.name = "debug_offset", .type = CONFIG_TYPE_U64 , .u64Value = &gPcDebug.bhvOffset},
{.name = "debug_tags", .type = CONFIG_TYPE_U64 , .u64Value = gPcDebug.tags},
@@ -266,6 +263,9 @@ static const struct ConfigOption options[] = {
{.name = "debug_error", .type = CONFIG_TYPE_BOOL , .boolValue = &configDebugError},
{.name = "language", .type = CONFIG_TYPE_STRING, .stringValue = (char*)&configLanguage, .maxStringLength = MAX_CONFIG_STRING},
{.name = "force_4by3", .type = CONFIG_TYPE_BOOL, .boolValue = &configForce4By3},
+ {.name = "coopnet_ip", .type = CONFIG_TYPE_STRING, .stringValue = (char*)&configCoopNetIp, .maxStringLength = MAX_CONFIG_STRING},
+ {.name = "coopnet_port", .type = CONFIG_TYPE_UINT , .uintValue = &configCoopNetPort},
+ {.name = "coopnet_password", .type = CONFIG_TYPE_STRING, .stringValue = (char*)&configPassword, .maxStringLength = MAX_CONFIG_STRING},
};
// FunctionConfigOption functions
@@ -554,8 +554,8 @@ NEXT_OPTION:
if (configFrameLimit < 30) { configFrameLimit = 30; }
if (configFrameLimit > 3000) { configFrameLimit = 3000; }
-#ifndef DISCORD_SDK
- configNetworkSystem = 1;
+#ifndef COOPNET
+ configNetworkSystem = NS_SOCKET;
#endif
}
diff --git a/src/pc/configfile.h b/src/pc/configfile.h
index 086a9e7b..54b0bb6a 100644
--- a/src/pc/configfile.h
+++ b/src/pc/configfile.h
@@ -15,6 +15,8 @@
#define MAX_DESCRIPTION_STRING 20
#define DEFAULT_PORT 7777
+#define DEFAULT_COOPNET_IP "net.coop64.us"
+#define DEFAULT_COOPNET_PORT 34197
typedef struct {
unsigned int x, y, w, h;
@@ -78,9 +80,6 @@ extern bool configShareLives;
extern bool configEnableCheats;
extern bool configBubbleDeath;
extern unsigned int configAmountofPlayers;
-#ifdef DISCORDRPC
-extern bool configDiscordRPC;
-#endif
extern char configJoinIp[];
extern unsigned int configJoinPort;
extern unsigned int configHostPort;
@@ -112,6 +111,9 @@ extern bool configDebugInfo;
extern bool configDebugError;
extern char configLanguage[];
extern bool configForce4By3;
+extern char configCoopNetIp[];
+extern unsigned int configCoopNetPort;
+extern char configPassword[];
void configfile_load(void);
void configfile_save(const char *filename);
diff --git a/src/pc/discord/discord.c b/src/pc/discord/discord.c
new file mode 100644
index 00000000..2e56e015
--- /dev/null
+++ b/src/pc/discord/discord.c
@@ -0,0 +1,168 @@
+#include "discord.h"
+#include "pc/djui/djui.h"
+#include "pc/debuglog.h"
+
+#if defined(_WIN32) || defined(_WIN64)
+#include
+#include
+#else
+#include
+#define MAX_PATH 1024
+#endif
+
+#define MAX_LAUNCH_CMD (MAX_PATH + 12)
+
+static int64_t applicationId = 752700005210390568;
+struct DiscordApplication app = { 0 };
+static bool sFatalShown = false;
+static bool sDiscordInitialized = false;
+static bool sDiscordFailed = false;
+
+static void discord_sdk_log_callback(UNUSED void* hook_data, enum EDiscordLogLevel level, const char* message) {
+ LOG_INFO("callback (%d): %s", level, message);
+}
+
+void discord_fatal_message(int rc) { // Discord usually does this because of loss of connection to Discord
+ LOG_ERROR("Discord fatal: %d", rc);
+ /*char errorMessage[132] = { 0 };
+ snprintf(errorMessage, 132, "%s\nRC: %d", DLANG(NOTIF, DISCORD_ERROR), rc);
+ djui_popup_create(errorMessage, 6);*/
+}
+
+void discord_fatal(int rc) {
+ if (!sFatalShown) {
+ discord_fatal_message(rc);
+ sFatalShown = true;
+ }
+
+ if (rc != DiscordResult_Ok) {
+ LOG_ERROR("Discord threw an error. RC: %d", rc);
+ }
+}
+
+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] = { 0 };
+ int rc;
+#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
+ char pidpath[MAX_LAUNCH_CMD] = { 0 };
+ char fullpath[MAX_LAUNCH_CMD] = { 0 };
+ snprintf(pidpath, MAX_LAUNCH_CMD - 1, "/proc/%d/exe", getpid());
+ rc = readlink(pidpath, fullpath, MAX_LAUNCH_CMD - 1);
+ if (rc <= 0) {
+ LOG_ERROR("unable to retrieve absolute path! rc = %d", rc);
+ return;
+ }
+ snprintf(cmd, MAX_LAUNCH_CMD, "%s", fullpath);
+#endif
+ rc = app.activities->register_command(app.activities, cmd);
+ if (rc != DiscordResult_Ok) {
+ LOG_ERROR("register command failed %d", rc);
+ return;
+ }
+ LOG_INFO("cmd: %s", cmd);
+}
+
+static void on_current_user_update(UNUSED void* data) {
+ LOG_INFO("> on_current_user_update");
+ struct DiscordUser user = { 0 };
+ app.users->get_current_user(app.users, &user);
+
+ // remember user id
+ app.userId = user.id;
+
+ // copy over discord username if we haven't set one yet
+ if (configPlayerName[0] == '\0' && strlen(user.username) > 0) {
+ char* cname = configPlayerName;
+ char* dname = user.username;
+ for (int i = 0; i < MAX_PLAYER_STRING - 1; i++) {
+ if (*dname >= '!' && *dname <= '~') {
+ *cname = *dname;
+ cname++;
+ }
+ dname++;
+ }
+ }
+}
+
+struct IDiscordUserEvents* discord_user_initialize(void) {
+ LOG_INFO("> discord_user_intitialize");
+ static struct IDiscordUserEvents events = { 0 };
+ events.on_current_user_update = on_current_user_update;
+ return &events;
+}
+
+static void discord_initialize(void) {
+ if (sDiscordInitialized) { return; }
+ sDiscordInitialized = true;
+
+ if (app.core != NULL) {
+ app.core->set_log_hook(app.core, DiscordLogLevel_Debug, NULL, discord_sdk_log_callback);
+ }
+
+ // set up discord params
+ struct DiscordCreateParams params = { 0 };
+ 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();
+
+ int rc = DiscordCreate(DISCORD_VERSION, ¶ms, &app.core);
+ if (app.core != NULL) {
+ app.core->set_log_hook(app.core, DiscordLogLevel_Debug, NULL, discord_sdk_log_callback);
+ }
+
+ if (rc) {
+ LOG_ERROR("DiscordCreate failed: %d", rc);
+ //djui_popup_create(DLANG(NOTIF, DISCORD_DETECT), 3);
+ sDiscordFailed = true;
+ return;
+ }
+
+ // set up manager pointers
+ if (app.core != NULL) {
+ 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);
+ }
+
+ // get oath2 token
+ app.application->get_oauth2_token(app.application, NULL, get_oauth2_token_callback);
+
+ // set activity
+ discord_activity_update();
+ sDiscordFailed = false;
+
+ // register launch params
+ register_launch_command();
+
+ LOG_INFO("initialized");
+}
+
+u64 discord_get_user_id(void) {
+ return app.userId;
+}
+
+void discord_update(void) {
+ if (sDiscordFailed) { return; }
+ if (!sDiscordInitialized) { discord_initialize(); }
+ if (sDiscordFailed) { return; }
+
+ discord_activity_update_check();
+ DISCORD_REQUIRE(app.core->run_callbacks(app.core));
+}
diff --git a/src/pc/network/discord/discord.h b/src/pc/discord/discord.h
similarity index 70%
rename from src/pc/network/discord/discord.h
rename to src/pc/discord/discord.h
index 25a86cc5..9d8277cb 100644
--- a/src/pc/network/discord/discord.h
+++ b/src/pc/discord/discord.h
@@ -1,22 +1,14 @@
-#ifndef DISCORD_H
-#define DISCORD_H
-#include
-#include
-#include
-#pragma pack(push, 8)
+#pragma once
+
+#include "PR/ultratypes.h"
#include "discord_game_sdk.h"
-#pragma pack(pop)
+
#ifdef _WIN32
#define DISCORD_ID_FORMAT "%lld"
#include
#else
#define DISCORD_ID_FORMAT "%ld"
-#include
-#include
#endif
-#include "../network.h"
-
-void discord_fatal(int rc);
// disgusting but descriptive
#define DISCORD_REQUIRE(x) { \
@@ -26,10 +18,6 @@ void discord_fatal(int rc);
} \
}
-extern struct NetworkSystem gNetworkSystemDiscord;
-extern bool gDiscordInitialized;
-extern bool gDiscordFailed;
-
struct DiscordApplication {
struct IDiscordCore* core;
struct IDiscordUserManager* users;
@@ -41,6 +29,9 @@ struct DiscordApplication {
DiscordUserId userId;
};
-extern struct DiscordApplication app;
-
-#endif
\ No newline at end of file
+void discord_update(void);
+void discord_fatal(int rc);
+void discord_activity_update_check(void);
+void discord_activity_update(void);
+struct IDiscordActivityEvents* discord_activity_initialize(void);
+u64 discord_get_user_id(void);
diff --git a/src/pc/discord/discord_activity.c b/src/pc/discord/discord_activity.c
new file mode 100644
index 00000000..c5a5e3d3
--- /dev/null
+++ b/src/pc/discord/discord_activity.c
@@ -0,0 +1,176 @@
+#include "discord.h"
+#include "pc/djui/djui.h"
+#include "pc/mods/mods.h"
+#include "pc/debuglog.h"
+#include "pc/utils/misc.h"
+#include "pc/djui/djui_panel_join_message.h"
+#ifdef COOPNET
+#include "pc/network/coopnet/coopnet.h"
+#endif
+
+extern struct DiscordApplication app;
+struct DiscordActivity sCurActivity = { 0 };
+static int sQueuedLobby = 0;
+static uint64_t sQueuedLobbyId = 0;
+static char sQueuedLobbyPassword[64] = "";
+
+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(UNUSED void* data, const char* secret) {
+ LOG_INFO("> on_activity_join, secret: %s", secret);
+ char *token;
+
+ // extract lobby type
+ token = strtok((char*)secret, ":");
+ if (strcmp(token, "coopnet") != 0) {
+ LOG_ERROR("Tried to join unrecognized lobby type: %s", token);
+ return;
+ }
+
+#ifdef COOPNET
+ // extract lobby ID
+ token = strtok(NULL, ":");
+ char* end;
+ u64 lobbyId = strtoull(token, &end, 10);
+
+ // extract lobby password
+ token = strtok(NULL, ":");
+ if (token == NULL) { token = ""; }
+
+ // join
+ if (gNetworkType != NT_NONE) {
+ network_shutdown(true, false, false, false);
+ }
+ sQueuedLobbyId = lobbyId;
+ snprintf(sQueuedLobbyPassword, 64, "%s", token);
+ sQueuedLobby = 2;
+#endif
+}
+
+static void on_activity_join_request_callback(UNUSED void* data, enum EDiscordResult result) {
+ LOG_INFO("> on_activity_join_request_callback returned %d", (int)result);
+ DISCORD_REQUIRE(result);
+}
+
+static void on_activity_join_request(UNUSED void* data, struct DiscordUser* user) {
+ LOG_INFO("> on_activity_join_request from " DISCORD_ID_FORMAT, user->id);
+}
+
+static void strncat_len(char* destination, char* source, size_t destinationLength, size_t sourceLength) {
+ char altered[128] = { 0 };
+ snprintf(altered, (sourceLength < 127) ? sourceLength : 127, "%s", source);
+ strncat(destination, altered, destinationLength);
+}
+
+static void discord_populate_details(char* buffer, int bufferLength) {
+ // get version
+ char* version = get_version();
+ int versionLength = strlen(version);
+ snprintf(buffer, bufferLength, "%s", version);
+ buffer += versionLength;
+ bufferLength -= versionLength;
+
+ // get mod strings
+ if (gActiveMods.entryCount <= 0) { return; }
+ char* strings[gActiveMods.entryCount];
+ for (int i = 0; i < gActiveMods.entryCount; i++) {
+ strings[i] = gActiveMods.entries[i]->name;
+ }
+
+ // add seperator
+ snprintf(buffer, bufferLength, "%s", " - ");
+ buffer += 3;
+ bufferLength -= 3;
+
+ // concat mod strings
+ str_seperator_concat(buffer, bufferLength, strings, gActiveMods.entryCount, ", ");
+}
+
+void discord_activity_update(void) {
+ sCurActivity.type = DiscordActivityType_Playing;
+
+ if (gNetworkType != NT_NONE && gNetworkSystem) {
+ gNetworkSystem->get_lobby_id(sCurActivity.party.id, 128);
+ gNetworkSystem->get_lobby_secret(sCurActivity.secrets.join, 128);
+ sCurActivity.party.size.current_size = network_player_connected_count();
+ sCurActivity.party.size.max_size = gServerSettings.maxPlayers;
+ } else {
+ snprintf(sCurActivity.party.id, 128, "%s", "");
+ snprintf(sCurActivity.secrets.join, 128, "%s", "");
+ sCurActivity.party.size.current_size = 1;
+ sCurActivity.party.size.max_size = 1;
+ }
+
+ if (sCurActivity.party.size.current_size > 1) {
+ strcpy(sCurActivity.state, "Playing!");
+ } else if (gNetworkType == NT_SERVER) {
+ strcpy(sCurActivity.state, "Waiting for players...");
+ } else {
+ strcpy(sCurActivity.state, "In-game.");
+ sCurActivity.party.size.current_size = 1;
+ if (sCurActivity.party.size.max_size < 1) { sCurActivity.party.size.max_size = 1; }
+ }
+
+ char details[128] = { 0 };
+ discord_populate_details(details, 128);
+
+ if (snprintf(sCurActivity.details, 128, "%s", details) < 0) {
+ LOG_INFO("truncating details");
+ }
+
+ if (!app.activities) {
+ LOG_INFO("no activities");
+ return;
+ }
+
+ if (!app.activities->update_activity) {
+ LOG_INFO("no update_activity");
+ return;
+ }
+
+ app.activities->update_activity(app.activities, &sCurActivity, NULL, on_activity_update_callback);
+ LOG_INFO("set activity");
+}
+
+void discord_activity_update_check(void) {
+ if (sQueuedLobby > 0) {
+ if (--sQueuedLobby == 0) {
+ gCoopNetDesiredLobby = sQueuedLobbyId;
+ snprintf(gCoopNetPassword, 64, "%s", sQueuedLobbyPassword);
+ network_reset_reconnect_and_rehost();
+ network_set_system(NS_COOPNET);
+ network_init(NT_CLIENT, false);
+ djui_panel_join_message_create(NULL);
+ }
+ }
+
+ if (gNetworkType == NT_NONE) { return; }
+ bool shouldUpdate = false;
+ u8 connectedCount = network_player_connected_count();
+
+ if (connectedCount > 0) {
+ if (connectedCount != sCurActivity.party.size.current_size) {
+ shouldUpdate = true;
+ }
+ }
+
+ static int updateTimer = 30 * 60;
+ if (--updateTimer <= 0) {
+ updateTimer = 30 * 60;
+ shouldUpdate = true;
+ }
+
+ if (shouldUpdate) {
+ discord_activity_update();
+ }
+}
+
+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/discord_game_sdk.h b/src/pc/discord/discord_game_sdk.h
similarity index 100%
rename from src/pc/network/discord/discord_game_sdk.h
rename to src/pc/discord/discord_game_sdk.h
diff --git a/src/pc/discord/discordrpc.c b/src/pc/discord/discordrpc.c
deleted file mode 100644
index 25d5373e..00000000
--- a/src/pc/discord/discordrpc.c
+++ /dev/null
@@ -1,291 +0,0 @@
-#include
-#include
-#include
-#include
-
-#include "macros.h"
-#include "PR/ultratypes.h"
-#include "game/memory.h"
-#include "game/save_file.h"
-#include "pc/configfile.h"
-#include "discordrpc.h"
-
-#define DISCORDLIBFILE "libdiscord-rpc"
-
-// Thanks Microsoft for being non posix compliant
-#if defined(_WIN32)
-# include
-# define DISCORDLIBEXT ".dll"
-# define dlopen(lib, flag) LoadLibrary(TEXT(lib))
-# define dlerror() ""
-# define dlsym(handle, func) (void *)GetProcAddress(handle, func)
-# define dlclose(handle) FreeLibrary(handle)
-#elif defined(__APPLE__)
-# include
-# define DISCORDLIBEXT ".dylib"
-#elif defined(__linux__) || defined(__FreeBSD__) // lets make the bold assumption for FreeBSD
-# include
-# define DISCORDLIBEXT ".so"
-#else
-# error Unknown System
-#endif
-
-#define DISCORDLIB DISCORDLIBFILE DISCORDLIBEXT
-#define DISCORD_APP_ID "709083908708237342"
-#define DISCORD_UPDATE_RATE 5
-
-extern s16 gCurrCourseNum;
-extern s16 gCurrActNum;
-extern u8* seg2_course_name_table[];
-extern u8* seg2_act_name_table[];
-
-static time_t lastUpdatedTime;
-
-static DiscordRichPresence discordRichPresence;
-static bool initd = false;
-
-static void* handle;
-
-void (*Discord_Initialize)(const char *, DiscordEventHandlers *, int, const char *);
-void (*Discord_Shutdown)(void);
-void (*Discord_ClearPresence)(void);
-void (*Discord_UpdatePresence)(DiscordRichPresence *);
-
-static s16 lastCourseNum = -1;
-static s16 lastActNum = -1;
-
-#ifdef VERSION_EU
-#include "eu_translation.h"
-extern s32 gInGameLanguage;
-#endif
-
-static char stage[188];
-static char act[188];
-
-static char smallImageKey[5];
-static char largeImageKey[5];
-
-static const char charset[0xFF+1] = {
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 7
- ' ', ' ', 'a', 'b', 'c', 'd', 'e', 'f', // 15
- 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 23
- 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 31
- 'w', 'x', 'y', 'z', ' ', ' ', ' ', ' ', // 39
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 49
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 55
- ' ', ' ', ' ', ' ', ' ', ' ', '\'', ' ', // 63
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 71
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 79
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 87
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 95
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 103
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ',', // 111
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 119
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 127
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 135
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 143
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 151
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', '-', // 159
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 167
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 175
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 183
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 192
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 199
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 207
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 215
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 223
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 231
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 239
- ' ', ' ', '!', ' ', ' ', ' ', ' ', ' ', // 247
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' // 255
-};
-
-static void convertstring(const u8 *str, char* output) {
- s32 strPos = 0;
- bool capitalizeChar = true;
-
- while (str[strPos] != 0xFF) {
- if (str[strPos] < 0xFF) {
- output[strPos] = charset[str[strPos]];
-
- // if the char is a letter we can capatalize it
- if (capitalizeChar && 0x0A <= str[strPos] && str[strPos] <= 0x23) {
- output[strPos] -= ('a' - 'A');
- capitalizeChar = false;
- }
-
- } else {
- output[strPos] = ' ';
- }
-
- // decide if the next character should be capitalized
- switch (output[strPos]) {
- case ' ':
- if (str[strPos] != 158)
- fprintf(stdout, "Unknown Character (%i)\n", str[strPos]); // inform that an unknown char was found
- case '-':
- capitalizeChar = true;
- break;
- default:
- capitalizeChar = false;
- break;
- }
-
- strPos++;
- }
-
- output[strPos] = '\0';
-}
-
-static void on_ready(UNUSED const DiscordUser* user) {
- discord_reset();
-}
-
-static void init_discord(void) {
- DiscordEventHandlers handlers;
- memset(&handlers, 0, sizeof(handlers));
- handlers.ready = on_ready;
-
- Discord_Initialize(DISCORD_APP_ID, &handlers, false, "");
-
- initd = true;
-}
-
-static void set_details(void) {
- if (lastCourseNum != gCurrCourseNum) {
- // If we are in in Course 0 we are in the castle which doesn't have a string
- if (gCurrCourseNum) {
- void **courseNameTbl;
-
-#ifndef VERSION_EU
- courseNameTbl = segmented_to_virtual(seg2_course_name_table);
-#else
- switch (gInGameLanguage) {
- case LANGUAGE_ENGLISH:
- courseNameTbl = segmented_to_virtual(course_name_table_eu_en);
- break;
- case LANGUAGE_FRENCH:
- courseNameTbl = segmented_to_virtual(course_name_table_eu_fr);
- break;
- case LANGUAGE_GERMAN:
- courseNameTbl = segmented_to_virtual(course_name_table_eu_de);
- break;
- }
-#endif
- u8 *courseName = segmented_to_virtual(courseNameTbl[gCurrCourseNum - 1]);
-
- convertstring(&courseName[3], stage);
- } else {
- strcpy(stage, "Peach's Castle");
- }
-
- lastCourseNum = gCurrCourseNum;
- }
-}
-
-static void set_state(void) {
- if (lastActNum != gCurrActNum || lastCourseNum != gCurrCourseNum) {
- // when exiting a stage the act doesn't get reset
- if (gCurrActNum && gCurrCourseNum) {
- // any stage over 19 is a special stage without acts
- if (gCurrCourseNum < 19) {
- void **actNameTbl;
-#ifndef VERSION_EU
- actNameTbl = segmented_to_virtual(seg2_act_name_table);
-#else
- switch (gInGameLanguage) {
- case LANGUAGE_ENGLISH:
- actNameTbl = segmented_to_virtual(act_name_table_eu_en);
- break;
- case LANGUAGE_FRENCH:
- actNameTbl = segmented_to_virtual(act_name_table_eu_fr);
- break;
- case LANGUAGE_GERMAN:
- actNameTbl = segmented_to_virtual(act_name_table_eu_de);
- break;
- }
-#endif
- u8 *actName = actName = segmented_to_virtual(actNameTbl[(gCurrCourseNum - 1) * 6 + gCurrActNum - 1]);
-
- convertstring(actName, act);
- } else {
- act[0] = '\0';
- gCurrActNum = 0;
- }
- } else {
- act[0] = '\0';
- }
-
- lastActNum = gCurrActNum;
- }
-}
-
-void set_logo(void) {
- if (lastCourseNum)
- snprintf(largeImageKey, sizeof(largeImageKey), "%d", lastCourseNum);
- else
- strcpy(largeImageKey, "0");
-
- /*
- if (lastActNum)
- snprintf(smallImageKey, sizeof(largeImageKey), "%d", lastActNum);
- else
- smallImageKey[0] = '\0';
- */
-
- discordRichPresence.largeImageKey = largeImageKey;
- //discordRichPresence.largeImageText = "";
- //discordRichPresence.smallImageKey = smallImageKey;
- //discordRichPresence.smallImageText = "";
-}
-
-void discord_update_rich_presence(void) {
- if (!configDiscordRPC || !initd) return;
- if (time(NULL) < lastUpdatedTime + DISCORD_UPDATE_RATE) return;
-
- lastUpdatedTime = time(NULL);
-
- set_state();
- set_details();
- set_logo();
- Discord_UpdatePresence(&discordRichPresence);
-}
-
-void discord_shutdown(void) {
- if (handle) {
- Discord_ClearPresence();
- Discord_Shutdown();
- dlclose(handle);
- }
-}
-
-void discord_init(void) {
- if (configDiscordRPC) {
- handle = dlopen(DISCORDLIB, RTLD_LAZY);
- if (!handle) {
- fprintf(stderr, "Unable to load Discord\n%s\n", dlerror());
- return;
- }
-
- Discord_Initialize = dlsym(handle, "Discord_Initialize");
- Discord_Shutdown = dlsym(handle, "Discord_Shutdown");
- Discord_ClearPresence = dlsym(handle, "Discord_ClearPresence");
- Discord_UpdatePresence = dlsym(handle, "Discord_UpdatePresence");
-
- init_discord();
-
- discordRichPresence.details = stage;
- discordRichPresence.state = act;
-
- lastUpdatedTime = 0;
- }
-}
-
-void discord_reset(void) {
- memset( &discordRichPresence, 0, sizeof( discordRichPresence ) );
-
- set_state();
- set_details();
- set_logo();
- Discord_UpdatePresence(&discordRichPresence);
-}
diff --git a/src/pc/discord/discordrpc.h b/src/pc/discord/discordrpc.h
deleted file mode 100644
index 6d8bc6db..00000000
--- a/src/pc/discord/discordrpc.h
+++ /dev/null
@@ -1,49 +0,0 @@
-#ifndef DISCORDRPC_H
-#define DISCORDRPC_H
-
-#include
-
-typedef struct DiscordRichPresence {
- const char* state; /* max 128 bytes */
- const char* details; /* max 128 bytes */
- int64_t startTimestamp;
- int64_t endTimestamp;
- const char* largeImageKey; /* max 32 bytes */
- const char* largeImageText; /* max 128 bytes */
- const char* smallImageKey; /* max 32 bytes */
- const char* smallImageText; /* max 128 bytes */
- const char* partyId; /* max 128 bytes */
- int partySize;
- int partyMax;
- const char* matchSecret; /* max 128 bytes */
- const char* joinSecret; /* max 128 bytes */
- const char* spectateSecret; /* max 128 bytes */
- int8_t instance;
-} DiscordRichPresence;
-
-typedef struct DiscordUser {
- const char* userId;
- const char* username;
- const char* discriminator;
- const char* avatar;
-} DiscordUser;
-
-typedef struct DiscordEventHandlers {
- void (*ready)(const DiscordUser* request);
- void (*disconnected)(int errorCode, const char* message);
- void (*errored)(int errorCode, const char* message);
- void (*joinGame)(const char* joinSecret);
- void (*spectateGame)(const char* spectateSecret);
- void (*joinRequest)(const DiscordUser* request);
-} DiscordEventHandlers;
-
-#define DISCORD_REPLY_NO 0
-#define DISCORD_REPLY_YES 1
-#define DISCORD_REPLY_IGNORE 2
-
-void discord_update_rich_presence(void);
-void discord_shutdown(void);
-void discord_init(void);
-void discord_reset(void);
-
-#endif // DISCORDRPC_H
diff --git a/src/pc/djui/djui_base.c b/src/pc/djui/djui_base.c
index ed8be254..698e92cb 100644
--- a/src/pc/djui/djui_base.c
+++ b/src/pc/djui/djui_base.c
@@ -393,6 +393,19 @@ void djui_base_destroy(struct DjuiBase* base) {
base->destroy(base);
}
+void djui_base_destroy_children(struct DjuiBase* base) {
+ // destroy all children and our linked list
+ struct DjuiBaseChild* child = base->child;
+ while (child != NULL) {
+ struct DjuiBaseChild* nextChild = child->next;
+ child->base->parent = NULL;
+ djui_base_destroy(child->base);
+ free(child);
+ child = nextChild;
+ }
+ base->child = NULL;
+}
+
void djui_base_init(struct DjuiBase* parent, struct DjuiBase* base, bool (*render)(struct DjuiBase*), void (*destroy)(struct DjuiBase*)) {
memset(base, 0, sizeof(struct DjuiBase));
base->parent = parent;
diff --git a/src/pc/djui/djui_base.h b/src/pc/djui/djui_base.h
index 0c40ea0b..04a91fa2 100644
--- a/src/pc/djui/djui_base.h
+++ b/src/pc/djui/djui_base.h
@@ -41,7 +41,7 @@ struct DjuiBase {
struct DjuiInteractable* interactable;
bool addChildrenToHead;
bool abandonAfterChildRenderFail;
- s32 tag;
+ s64 tag;
bool bTag;
void (*get_cursor_hover_location)(struct DjuiBase*, f32* x, f32* y);
void (*on_child_render)(struct DjuiBase*, struct DjuiBase*);
@@ -69,4 +69,5 @@ void djui_base_compute_tree(struct DjuiBase* base);
bool djui_base_render(struct DjuiBase* base);
void djui_base_destroy(struct DjuiBase* base);
+void djui_base_destroy_children(struct DjuiBase* base);
void djui_base_init(struct DjuiBase* parent, struct DjuiBase* base, bool (*render)(struct DjuiBase*), void (*destroy)(struct DjuiBase*));
diff --git a/src/pc/djui/djui_cursor.c b/src/pc/djui/djui_cursor.c
index 7e8965b7..096d36b5 100644
--- a/src/pc/djui/djui_cursor.c
+++ b/src/pc/djui/djui_cursor.c
@@ -62,7 +62,6 @@ static f32 djui_cursor_base_distance(struct DjuiBase* base, f32 xScale, f32 ySca
static void djui_cursor_move_check(s8 xDir, s8 yDir, struct DjuiBase** pick, struct DjuiBase* base) {
if (!base->visible) { return; }
- if (!base->enabled) { return; }
if (base->interactable != NULL) {
f32 x1, y1, x2, y2;
diff --git a/src/pc/djui/djui_inputbox.c b/src/pc/djui/djui_inputbox.c
index 272de89c..662f8464 100644
--- a/src/pc/djui/djui_inputbox.c
+++ b/src/pc/djui/djui_inputbox.c
@@ -292,13 +292,13 @@ void djui_inputbox_on_key_up(UNUSED struct DjuiBase *base, int scancode) {
}
}
-static void djui_inputbox_on_focus_begin(UNUSED struct DjuiBase* base) {
+void djui_inputbox_on_focus_begin(UNUSED struct DjuiBase* base) {
sHeldControl = 0;
sHeldShift = 0;
wm_api->start_text_input();
}
-static void djui_inputbox_on_focus_end(UNUSED struct DjuiBase* base) {
+void djui_inputbox_on_focus_end(UNUSED struct DjuiBase* base) {
wm_api->stop_text_input();
}
@@ -373,15 +373,17 @@ static void djui_inputbox_render_char(struct DjuiInputbox* inputbox, char* c, f3
f32 dW = font->charWidth * font->defaultFontScale;
f32 dH = font->charHeight * font->defaultFontScale;
- f32 charWidth = font->char_width(c);
+ char* dc = inputbox->passwordChar[0] ? inputbox->passwordChar : c;
+
+ f32 charWidth = font->char_width(dc);
*drawX += charWidth * font->defaultFontScale;
- if (*c != ' ' && !djui_gfx_add_clipping_specific(&inputbox->base, dX, dY, dW, dH)) {
+ if (*dc != ' ' && !djui_gfx_add_clipping_specific(&inputbox->base, dX, dY, dW, dH)) {
if (*additionalShift > 0) {
create_dl_translation_matrix(DJUI_MTX_NOPUSH, *additionalShift, 0, 0);
*additionalShift = 0;
}
- font->render_char(c);
+ font->render_char(dc);
}
*additionalShift += charWidth;
}
@@ -398,10 +400,11 @@ static void djui_inputbox_render_selection(struct DjuiInputbox* inputbox) {
f32 x = 0;
f32 width = 0;
for (u16 i = 0; i < selection[1]; i++) {
+ char* dc = inputbox->passwordChar[0] ? inputbox->passwordChar : c;
if (i < selection[0]) {
- x += font->char_width(c);
+ x += font->char_width(dc);
} else {
- width += font->char_width(c);
+ width += font->char_width(dc);
}
c = djui_unicode_next_char(c);
}
@@ -458,7 +461,8 @@ static void djui_inputbox_keep_selection_in_view(struct DjuiInputbox* inputbox)
char* c = inputbox->buffer;
for (u16 i = 0; i < inputbox->selection[0]; i++) {
if (*c == '\0') { break; }
- cursorX += font->char_width(c) * font->defaultFontScale;
+ char* dc = inputbox->passwordChar[0] ? inputbox->passwordChar : c;
+ cursorX += font->char_width(dc) * font->defaultFontScale;
c = djui_unicode_next_char(c);
}
diff --git a/src/pc/djui/djui_inputbox.h b/src/pc/djui/djui_inputbox.h
index 6fe93f41..2b47b441 100644
--- a/src/pc/djui/djui_inputbox.h
+++ b/src/pc/djui/djui_inputbox.h
@@ -4,6 +4,7 @@
struct DjuiInputbox {
struct DjuiBase base;
char* buffer;
+ char passwordChar[2];
u16 bufferSize;
u16 selection[2];
f32 viewX;
@@ -12,6 +13,8 @@ struct DjuiInputbox {
void (*on_escape_press)(struct DjuiInputbox*);
};
+void djui_inputbox_on_focus_begin(UNUSED struct DjuiBase* base);
+void djui_inputbox_on_focus_end(UNUSED struct DjuiBase* base);
void djui_inputbox_set_text_color(struct DjuiInputbox* inputbox, u8 r, u8 g, u8 b, u8 a);
void djui_inputbox_set_text(struct DjuiInputbox* inputbox, char* text);
void djui_inputbox_select_all(struct DjuiInputbox* inputbox);
diff --git a/src/pc/djui/djui_lobby_entry.c b/src/pc/djui/djui_lobby_entry.c
new file mode 100644
index 00000000..7aea75e9
--- /dev/null
+++ b/src/pc/djui/djui_lobby_entry.c
@@ -0,0 +1,85 @@
+#include
+#include
+#include "djui.h"
+#include "djui_lobby_entry.h"
+
+#define VK_ESCAPE 1
+
+static void djui_lobby_entry_update_style(struct DjuiBase* base) {
+ struct DjuiLobbyEntry* entry = (struct DjuiLobbyEntry*)base;
+ if (!entry->base.enabled) {
+ u8 borderBrightness = 75;
+ u8 rectBrightness = 111;
+ djui_base_set_border_color(base, borderBrightness, borderBrightness, borderBrightness, 255);
+ djui_base_set_color(&entry->base, rectBrightness, rectBrightness, rectBrightness, 255);
+ //djui_base_set_location(&entry->text->base, 0.0f, 0.0f);
+ } else if (gDjuiCursorDownOn == base) {
+ djui_base_set_border_color(base, 0, 84, 153, 255);
+ djui_base_set_color(&entry->base, 204, 228, 247, 255);
+ //djui_base_set_location(&entry->text->base, 1.0f, 1.0f);
+ } else if (gDjuiHovered == base) {
+ djui_base_set_border_color(base, 0, 120, 215, 255);
+ djui_base_set_color(&entry->base, 229, 241, 251, 255);
+ //djui_base_set_location(&entry->text->base, -1.0f, -1.0f);
+ } else {
+ u8 borderBrightness = 150;
+ u8 rectBrightness = 222;
+ djui_base_set_border_color(base, borderBrightness, borderBrightness, borderBrightness, 255);
+ djui_base_set_color(&entry->base, rectBrightness, rectBrightness, rectBrightness, 255);
+ //djui_base_set_location(&entry->text->base, 0.0f, 0.0f);
+ }
+}
+
+static void djui_lobby_entry_destroy(struct DjuiBase* base) {
+ struct DjuiLobbyEntry* lobbyEntry = (struct DjuiLobbyEntry*)base;
+ if (lobbyEntry->description) { free((char*)lobbyEntry->description); }
+ free(lobbyEntry);
+}
+
+struct DjuiLobbyEntry* djui_lobby_entry_create(struct DjuiBase* parent, char* host, char* mode, char* players, char* description, void (*on_click)(struct DjuiBase*), void (*on_hover)(struct DjuiBase*), void (*on_hover_end)(struct DjuiBase*)) {
+ struct DjuiLobbyEntry* lobbyEntry = calloc(1, sizeof(struct DjuiLobbyEntry));
+ struct DjuiBase* base = &lobbyEntry->base;
+
+ lobbyEntry->description = strdup(description);
+
+ djui_base_init(parent, base, djui_rect_render, djui_lobby_entry_destroy);
+ djui_base_set_size_type(&lobbyEntry->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
+ djui_base_set_size(&lobbyEntry->base, 1.0f, 32);
+ djui_base_set_color(&lobbyEntry->base, 255, 255, 255, 128);
+ djui_base_set_border_color(&lobbyEntry->base, 128, 128, 128, 255);
+ djui_base_set_border_width(&lobbyEntry->base, 2);
+ djui_base_set_border_width_type(&lobbyEntry->base, DJUI_SVT_ABSOLUTE);
+ djui_interactable_create(base, djui_lobby_entry_update_style);
+ djui_interactable_hook_click(base, on_click);
+ djui_interactable_hook_hover(base, on_hover, on_hover_end);
+
+ u8 numColumns = 3;
+ f32 x = 0;
+ for (int i = 0; i < numColumns; i++) {
+ char* msg = NULL;
+ f32 width = 0;
+ switch (i) {
+ case 0: msg = host; width = 0.4f; break;
+ case 1: msg = mode; width = 0.45f; break;
+ case 2: msg = players; width = 0.15f; break;
+ }
+ struct DjuiText* text = djui_text_create(&lobbyEntry->base, msg);
+
+ djui_base_set_alignment(&text->base, DJUI_HALIGN_LEFT, DJUI_VALIGN_CENTER);
+
+ djui_base_set_location_type(&text->base, DJUI_SVT_RELATIVE, DJUI_SVT_RELATIVE);
+ djui_base_set_location(&text->base, x, 0.0f);
+
+ djui_base_set_size_type(&text->base, DJUI_SVT_RELATIVE, DJUI_SVT_RELATIVE);
+ djui_base_set_size(&text->base, width, 1.0f);
+
+ djui_base_set_color(&text->base, 11, 11, 11, 255);
+ djui_text_set_alignment(text, DJUI_HALIGN_CENTER, DJUI_VALIGN_CENTER);
+ djui_text_set_drop_shadow(text, 64, 64, 64, 100);
+ x += width;
+ }
+
+ djui_lobby_entry_update_style(base);
+
+ return lobbyEntry;
+}
diff --git a/src/pc/djui/djui_lobby_entry.h b/src/pc/djui/djui_lobby_entry.h
new file mode 100644
index 00000000..02c946e2
--- /dev/null
+++ b/src/pc/djui/djui_lobby_entry.h
@@ -0,0 +1,9 @@
+#pragma once
+#include "djui.h"
+
+struct DjuiLobbyEntry {
+ struct DjuiBase base;
+ const char* description;
+};
+
+struct DjuiLobbyEntry* djui_lobby_entry_create(struct DjuiBase* parent, char* host, char* mode, char* players, char* description, void (*on_click)(struct DjuiBase*), void (*on_hover)(struct DjuiBase*), void (*on_hover_end)(struct DjuiBase*));
diff --git a/src/pc/djui/djui_paginated.c b/src/pc/djui/djui_paginated.c
index 2a2f4cec..c733912e 100644
--- a/src/pc/djui/djui_paginated.c
+++ b/src/pc/djui/djui_paginated.c
@@ -8,6 +8,10 @@
// events //
////////////
+static struct DjuiButton* sPrevButton = NULL;
+static struct DjuiButton* sNextButton = NULL;
+static struct DjuiText* sPageNumText = NULL;
+
static s32 djui_paginated_get_count(struct DjuiPaginated* paginated) {
s32 count = 0;
struct DjuiBaseChild* dbc = paginated->layout->base.child;
@@ -19,35 +23,35 @@ static s32 djui_paginated_get_count(struct DjuiPaginated* paginated) {
return count;
}
-static inline void djui_paginated_set_count_text(struct DjuiPaginated* paginated, s32 count) {
+void djui_paginated_update_page_buttons(struct DjuiPaginated* paginated) {
+ s32 count = djui_paginated_get_count(paginated);
char pageNumString[32] = { 0 };
snprintf(pageNumString, 32, "%d/%d", paginated->startIndex / paginated->showCount + 1, count / paginated->showCount + 1);
- djui_text_set_text(paginated->pageNumText, pageNumString);
+ djui_text_set_text(sPageNumText, pageNumString);
+ djui_base_set_visible(&sPageNumText->base, (count > paginated->showCount));
+
+ djui_base_set_enabled(&sPrevButton->base, (paginated->startIndex > 0));
+ djui_base_set_enabled(&sNextButton->base, ((paginated->startIndex + paginated->showCount) < count));
}
static void djui_paginated_prev(struct DjuiBase* base) {
struct DjuiPaginated* paginated = (struct DjuiPaginated*)base->parent;
+
paginated->startIndex -= paginated->showCount;
-
- djui_base_set_enabled(&paginated->prevButton->base, (paginated->startIndex > 0));
- djui_base_set_enabled(&paginated->nextButton->base, true);
-
- djui_paginated_set_count_text(paginated, djui_paginated_get_count(paginated));
-
if (paginated->startIndex < 0) { paginated->startIndex = 0; }
+
+ djui_paginated_update_page_buttons(paginated);
}
static void djui_paginated_next(struct DjuiBase* base) {
struct DjuiPaginated* paginated = (struct DjuiPaginated*)base->parent;
- paginated->startIndex += paginated->showCount;
+
s32 count = djui_paginated_get_count(paginated);
-
- djui_base_set_enabled(&paginated->nextButton->base, (paginated->startIndex < count - 8));
- djui_base_set_enabled(&paginated->prevButton->base, true);
-
- djui_paginated_set_count_text(paginated, count);
-
+ paginated->startIndex += paginated->showCount;
if (paginated->startIndex >= count) { paginated->startIndex -= paginated->showCount; }
+
+ djui_paginated_update_page_buttons(paginated);
+
}
void djui_paginated_calculate_height(struct DjuiPaginated* paginated) {
@@ -71,18 +75,15 @@ void djui_paginated_calculate_height(struct DjuiPaginated* paginated) {
if (count <= paginated->showCount) {
djui_base_set_visible(&paginated->prevButton->base, false);
djui_base_set_visible(&paginated->nextButton->base, false);
- djui_base_set_visible(&paginated->pageNumText->base, false);
} else {
djui_base_set_visible(&paginated->prevButton->base, true);
djui_base_set_visible(&paginated->nextButton->base, true);
- djui_base_set_visible(&paginated->pageNumText->base, true);
height += paginated->layout->margin.value;
height += paginated->nextButton->base.height.value;
}
djui_base_set_size(&paginated->base, paginated->base.width.value, height);
-
- djui_paginated_set_count_text(paginated, count);
+ djui_paginated_update_page_buttons(paginated);
}
bool djui_paginated_render(struct DjuiBase* base) {
@@ -138,31 +139,25 @@ struct DjuiPaginated* djui_paginated_create(struct DjuiBase* parent, u32 showCou
paginated->layout = layout;
}
- {
- struct DjuiButton* button = djui_button_create(&paginated->base, "<", DJUI_BUTTON_STYLE_NORMAL, djui_paginated_prev);
- djui_base_set_alignment(&button->base, DJUI_HALIGN_LEFT, DJUI_VALIGN_BOTTOM);
- djui_base_set_size_type(&button->base, DJUI_SVT_ABSOLUTE, DJUI_SVT_ABSOLUTE);
- djui_base_set_size(&button->base, 128, 32);
- djui_base_set_enabled(&button->base, false);
- paginated->prevButton = button;
- }
+ sPrevButton = djui_button_create(&paginated->base, "<", DJUI_BUTTON_STYLE_NORMAL, djui_paginated_prev);
+ djui_base_set_alignment(&sPrevButton->base, DJUI_HALIGN_LEFT, DJUI_VALIGN_BOTTOM);
+ djui_base_set_size_type(&sPrevButton->base, DJUI_SVT_ABSOLUTE, DJUI_SVT_ABSOLUTE);
+ djui_base_set_size(&sPrevButton->base, 128, 32);
+ djui_base_set_enabled(&sPrevButton->base, false);
+ paginated->prevButton = sPrevButton;
- {
- struct DjuiText* text = djui_text_create(&paginated->base, "");
- djui_base_set_color(&text->base, 200, 200, 200, 255);
- djui_base_set_alignment(&text->base, DJUI_HALIGN_CENTER, DJUI_VALIGN_BOTTOM);
- djui_base_set_size_type(&text->base, DJUI_SVT_ABSOLUTE, DJUI_SVT_ABSOLUTE);
- text->base.y.value -= 30;
- paginated->pageNumText = text;
- }
+ sPageNumText = djui_text_create(&paginated->base, "");
+ djui_base_set_color(&sPageNumText->base, 200, 200, 200, 255);
+ djui_base_set_alignment(&sPageNumText->base, DJUI_HALIGN_CENTER, DJUI_VALIGN_BOTTOM);
+ sPageNumText->base.y.value -= 30;
- {
- struct DjuiButton* button = djui_button_create(&paginated->base, ">", DJUI_BUTTON_STYLE_NORMAL, djui_paginated_next);
- djui_base_set_alignment(&button->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_BOTTOM);
- djui_base_set_size_type(&button->base, DJUI_SVT_ABSOLUTE, DJUI_SVT_ABSOLUTE);
- djui_base_set_size(&button->base, 128, 32);
- paginated->nextButton = button;
- }
+ sNextButton = djui_button_create(&paginated->base, ">", DJUI_BUTTON_STYLE_NORMAL, djui_paginated_next);
+ djui_base_set_alignment(&sNextButton->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_BOTTOM);
+ djui_base_set_size_type(&sNextButton->base, DJUI_SVT_ABSOLUTE, DJUI_SVT_ABSOLUTE);
+ djui_base_set_size(&sNextButton->base, 128, 32);
+ paginated->nextButton = sNextButton;
+
+ djui_paginated_update_page_buttons(paginated);
return paginated;
}
diff --git a/src/pc/djui/djui_paginated.h b/src/pc/djui/djui_paginated.h
index 60c8c930..05291672 100644
--- a/src/pc/djui/djui_paginated.h
+++ b/src/pc/djui/djui_paginated.h
@@ -11,5 +11,6 @@ struct DjuiPaginated {
s32 showCount;
};
+void djui_paginated_update_page_buttons(struct DjuiPaginated* paginated);
void djui_paginated_calculate_height(struct DjuiPaginated* paginated);
struct DjuiPaginated* djui_paginated_create(struct DjuiBase* parent, u32 showCount);
diff --git a/src/pc/djui/djui_panel_host.c b/src/pc/djui/djui_panel_host.c
index 857adae5..48b8b7b6 100644
--- a/src/pc/djui/djui_panel_host.c
+++ b/src/pc/djui/djui_panel_host.c
@@ -12,16 +12,25 @@
#include "pc/configfile.h"
#include "pc/cheats.h"
-#ifdef DISCORD_SDK
-#define DJUI_HOST_NS_IS_SOCKET (configNetworkSystem == 1)
-#else
-#define DJUI_HOST_NS_IS_SOCKET (true)
-#endif
-
+struct DjuiRect* sRectPort = NULL;
+struct DjuiRect* sRectPassword = NULL;
struct DjuiInputbox* sInputboxPort = NULL;
+struct DjuiInputbox* sInputboxPassword = NULL;
static void djui_panel_host_network_system_change(UNUSED struct DjuiBase* base) {
- djui_base_set_enabled(&sInputboxPort->base, DJUI_HOST_NS_IS_SOCKET);
+#ifndef COOPNET
+ {
+ struct DjuiSelectionbox* selectionbox = (struct DjuiSelectionbox*) base;
+ if (*selectionbox->value == NS_COOPNET) {
+ selectionbox->value = NS_SOCKET;
+ }
+ }
+#endif
+
+ djui_base_set_visible(&sRectPort->base, (configNetworkSystem == NS_SOCKET));
+ djui_base_set_visible(&sRectPassword->base, (configNetworkSystem == NS_COOPNET));
+ djui_base_set_enabled(&sInputboxPort->base, (configNetworkSystem == NS_SOCKET));
+ djui_base_set_enabled(&sInputboxPassword->base, (configNetworkSystem == NS_COOPNET));
}
static bool djui_panel_host_port_valid(void) {
@@ -41,11 +50,18 @@ static bool djui_panel_host_port_valid(void) {
}
static void djui_panel_host_port_text_change(struct DjuiBase* caller) {
- struct DjuiInputbox* inputbox1 = (struct DjuiInputbox*)caller;
+ struct DjuiInputbox* sInputboxPort = (struct DjuiInputbox*)caller;
if (djui_panel_host_port_valid()) {
- djui_inputbox_set_text_color(inputbox1, 0, 0, 0, 255);
+ djui_inputbox_set_text_color(sInputboxPort, 0, 0, 0, 255);
} else {
- djui_inputbox_set_text_color(inputbox1, 255, 0, 0, 255);
+ djui_inputbox_set_text_color(sInputboxPort, 255, 0, 0, 255);
+ }
+}
+
+static void djui_panel_host_password_text_change(UNUSED struct DjuiBase* caller) {
+ snprintf(configPassword, 64, "%s", sInputboxPassword->buffer);
+ if (strlen(sInputboxPassword->buffer) >= 64) {
+ djui_inputbox_set_text(sInputboxPassword, configPassword);
}
}
@@ -65,6 +81,10 @@ static void djui_panel_host_do_host(struct DjuiBase* caller) {
if (gNetworkType == NT_SERVER) {
network_rehost_begin();
+ } else if (configNetworkSystem == NS_COOPNET) {
+ extern void djui_panel_do_host(bool reconnecting);
+ network_reset_reconnect_and_rehost();
+ djui_panel_do_host(false);
} else {
djui_panel_host_message_create(caller);
}
@@ -77,42 +97,70 @@ void djui_panel_host_create(struct DjuiBase* caller) {
: DLANG(HOST, HOST_TITLE));
struct DjuiBase* body = djui_three_panel_get_body(panel);
{
-#ifdef DISCORD_SDK
- char* nChoices[2] = { DLANG(HOST, DISCORD), DLANG(HOST, DIRECT_CONNECTION) };
+ char* nChoices[] = { DLANG(HOST, DIRECT_CONNECTION), DLANG(HOST, COOPNET) };
struct DjuiSelectionbox* selectionbox1 = djui_selectionbox_create(body, DLANG(HOST, NETWORK_SYSTEM), nChoices, 2, &configNetworkSystem, djui_panel_host_network_system_change);
if (gNetworkType == NT_SERVER) {
djui_base_set_enabled(&selectionbox1->base, false);
}
-#endif
-
struct DjuiRect* rect1 = djui_rect_container_create(body, 32);
{
- struct DjuiText* text1 = djui_text_create(&rect1->base, DLANG(HOST, PORT));
- djui_base_set_size_type(&text1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
- djui_base_set_color(&text1->base, 200, 200, 200, 255);
- djui_base_set_size(&text1->base, 0.585f, 64);
- djui_base_set_alignment(&text1->base, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP);
- if (gNetworkType == NT_SERVER) {
- djui_base_set_enabled(&text1->base, false);
+ sRectPort = djui_rect_container_create(&rect1->base, 32);
+ djui_base_set_location(&sRectPort->base, 0, 0);
+ djui_base_set_visible(&sRectPort->base, (configNetworkSystem == NS_SOCKET));
+ {
+ struct DjuiText* text1 = djui_text_create(&sRectPort->base, DLANG(HOST, PORT));
+ djui_base_set_size_type(&text1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
+ djui_base_set_color(&text1->base, 200, 200, 200, 255);
+ djui_base_set_size(&text1->base, 0.585f, 64);
+ djui_base_set_alignment(&text1->base, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP);
+ if (gNetworkType == NT_SERVER) {
+ djui_base_set_enabled(&text1->base, false);
+ }
+
+ sInputboxPort = djui_inputbox_create(&sRectPort->base, 32);
+ djui_base_set_size_type(&sInputboxPort->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
+ djui_base_set_size(&sInputboxPort->base, 0.4f, 32);
+ djui_base_set_alignment(&sInputboxPort->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP);
+ char portString[32] = { 0 };
+ snprintf(portString, 32, "%d", configHostPort);
+ djui_inputbox_set_text(sInputboxPort, portString);
+ djui_interactable_hook_value_change(&sInputboxPort->base, djui_panel_host_port_text_change);
+ if (gNetworkType == NT_SERVER) {
+ djui_base_set_enabled(&sInputboxPort->base, false);
+ } else {
+ djui_base_set_enabled(&sInputboxPort->base, (configNetworkSystem == NS_SOCKET));
+ }
}
- struct DjuiInputbox* inputbox1 = djui_inputbox_create(&rect1->base, 32);
- djui_base_set_size_type(&inputbox1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
- djui_base_set_size(&inputbox1->base, 0.4f, 32);
- djui_base_set_alignment(&inputbox1->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP);
- char portString[32] = { 0 };
- snprintf(portString, 32, "%d", configHostPort);
- djui_inputbox_set_text(inputbox1, portString);
- djui_interactable_hook_value_change(&inputbox1->base, djui_panel_host_port_text_change);
- if (gNetworkType == NT_SERVER) {
- djui_base_set_enabled(&inputbox1->base, false);
-#ifdef DISCORD_SDK
- } else {
- djui_base_set_enabled(&inputbox1->base, DJUI_HOST_NS_IS_SOCKET);
-#endif
+ sRectPassword = djui_rect_container_create(&rect1->base, 32);
+ djui_base_set_location(&sRectPassword->base, 0, 0);
+ djui_base_set_visible(&sRectPassword->base, (configNetworkSystem == NS_COOPNET));
+ {
+ struct DjuiText* text1 = djui_text_create(&sRectPassword->base, DLANG(HOST, PASSWORD));
+ djui_base_set_size_type(&text1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
+ djui_base_set_color(&text1->base, 200, 200, 200, 255);
+ djui_base_set_size(&text1->base, 0.585f, 64);
+ djui_base_set_alignment(&text1->base, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP);
+ if (gNetworkType == NT_SERVER) {
+ djui_base_set_enabled(&text1->base, false);
+ }
+
+ sInputboxPassword = djui_inputbox_create(&sRectPassword->base, 32);
+ sInputboxPassword->passwordChar[0] = '#';
+ djui_base_set_size_type(&sInputboxPassword->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
+ djui_base_set_size(&sInputboxPassword->base, 0.4f, 32);
+ djui_base_set_alignment(&sInputboxPassword->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP);
+ char portPassword[64] = { 0 };
+ snprintf(portPassword, 64, "%s", configPassword);
+ djui_inputbox_set_text(sInputboxPassword, portPassword);
+ djui_interactable_hook_value_change(&sInputboxPassword->base, djui_panel_host_password_text_change);
+ if (gNetworkType == NT_SERVER) {
+ djui_base_set_enabled(&sInputboxPassword->base, false);
+ } else {
+ djui_base_set_enabled(&sInputboxPassword->base, (configNetworkSystem == NS_COOPNET));
+ }
}
- sInputboxPort = inputbox1;
}
struct DjuiRect* rect2 = djui_rect_container_create(body, 32);
diff --git a/src/pc/djui/djui_panel_host_message.c b/src/pc/djui/djui_panel_host_message.c
index c7850494..bf515587 100644
--- a/src/pc/djui/djui_panel_host_message.c
+++ b/src/pc/djui/djui_panel_host_message.c
@@ -4,7 +4,6 @@
#include "djui_panel_menu.h"
#include "djui_panel_modlist.h"
#include "src/pc/network/network.h"
-#include "src/pc/network/discord/discord.h"
#include "src/pc/utils/misc.h"
#include "src/pc/configfile.h"
#include "pc/utils/misc.h"
@@ -14,25 +13,20 @@
#include "audio/external.h"
#include "sounds.h"
-void djui_panel_do_host(void) {
+void djui_panel_do_host(bool reconnecting) {
stop_demo(NULL);
djui_panel_shutdown();
extern s16 gCurrSaveFileNum;
gCurrSaveFileNum = configHostSaveSlot;
update_all_mario_stars();
-#ifndef DISCORD_SDK
- configNetworkSystem = 1;
- network_set_system(NS_SOCKET);
-#else
- if (configNetworkSystem == 0) {
- network_set_system(NS_DISCORD);
- } else {
- network_set_system(NS_SOCKET);
- }
+#ifndef COOPNET
+ if (configNetworkSystem == NS_COOPNET) { configNetworkSystem = NS_SOCKET; }
#endif
+ if (configNetworkSystem >= NS_MAX) { configNetworkSystem = NS_MAX; }
+ network_set_system(configNetworkSystem);
- network_init(NT_SERVER);
+ network_init(NT_SERVER, reconnecting);
djui_panel_modlist_create(NULL);
fake_lvl_init_from_save_file();
@@ -48,7 +42,7 @@ void djui_panel_do_host(void) {
void djui_panel_host_message_do_host(UNUSED struct DjuiBase* caller) {
network_reset_reconnect_and_rehost();
- djui_panel_do_host();
+ djui_panel_do_host(false);
}
void djui_panel_host_message_create(struct DjuiBase* caller) {
@@ -56,18 +50,9 @@ void djui_panel_host_message_create(struct DjuiBase* caller) {
char* warningMessage = NULL;
bool hideHostButton = false;
-#ifdef DISCORD_SDK
- if (!configNetworkSystem) {
- warningLines = gDiscordFailed ? 5 : 13;
- warningMessage = gDiscordFailed ? DLANG(HOST_MESSAGE, WARN_DISCORD2) : DLANG(HOST_MESSAGE, WARN_DISCORD);
- hideHostButton = gDiscordFailed;
- } else
-#endif
- {
- warningLines = 5;
- warningMessage = calloc(256, sizeof(char));
- sprintf(warningMessage, DLANG(HOST_MESSAGE, WARN_SOCKET), configHostPort);
- }
+ warningLines = 5;
+ warningMessage = calloc(256, sizeof(char));
+ snprintf(warningMessage, 256, DLANG(HOST_MESSAGE, WARN_SOCKET), configHostPort);
f32 textHeight = 32 * 0.8125f * warningLines + 8;
@@ -93,10 +78,5 @@ void djui_panel_host_message_create(struct DjuiBase* caller) {
}
djui_panel_add(caller, panel, NULL);
-#ifdef DISCORD_SDK
- if (configNetworkSystem)
-#endif
- {
- free(warningMessage);
- }
+ free(warningMessage);
}
diff --git a/src/pc/djui/djui_panel_host_settings.c b/src/pc/djui/djui_panel_host_settings.c
index afe0e480..a2ff3116 100644
--- a/src/pc/djui/djui_panel_host_settings.c
+++ b/src/pc/djui/djui_panel_host_settings.c
@@ -7,7 +7,6 @@
#include "pc/utils/misc.h"
#include "pc/configfile.h"
#include "pc/cheats.h"
-#include "pc/network/discord/lobby.h"
#include "djui_inputbox.h"
static unsigned int sKnockbackIndex = 0;
diff --git a/src/pc/djui/djui_panel_join.c b/src/pc/djui/djui_panel_join.c
index b9ddbf59..3c2a0732 100644
--- a/src/pc/djui/djui_panel_join.c
+++ b/src/pc/djui/djui_panel_join.c
@@ -1,202 +1,31 @@
-#include
#include "djui.h"
#include "djui_panel.h"
#include "djui_panel_menu.h"
-#include "djui_panel_join_message.h"
-#include "djui_panel_modlist.h"
-#include "src/pc/network/network.h"
-#include "src/pc/network/socket/socket.h"
-#include "src/pc/network/socket/domain_res.h"
-#include "src/pc/utils/misc.h"
-#include "src/pc/configfile.h"
-#include "src/pc/debuglog.h"
+#include "djui_panel_join_lobbies.h"
+#include "djui_panel_join_private.h"
+#include "djui_panel_join_direct.h"
+#include "pc/network/network.h"
+#include "pc/utils/misc.h"
-static struct DjuiInputbox* sInputboxIp = NULL;
-
-static bool djui_panel_join_ip_parse_numbers(char** msg) {
- int num = 0;
- for (int i = 0; i < 3; i++) {
- char c = **msg;
- if (c >= '0' && c <= '9') {
- // is number
- num *= 10;
- num += (c - '0');
- *msg = *msg + 1;
- } else if (i == 0) {
- return false;
- } else {
- break;
- }
- }
-
- return num >= 0 && num <= 255;
-}
-
-static bool djui_panel_join_ip_parse_period(char** msg) {
- char c = **msg;
- bool isPeriod = (c == '.');
- if (isPeriod) { *msg = *msg + 1; }
- return isPeriod;
-}
-
-static bool djui_panel_join_ip_parse_spacer(char** msg) {
- char c = **msg;
- bool isSpacer = (c == ':' || c == ' ');
- if (isSpacer) { *msg = *msg + 1; }
- return isSpacer;
-}
-
-static bool djui_panel_join_ip_parse_port(char** msg) {
- int port = 0;
- for (int i = 0; i < 5; i++) {
- char c = **msg;
- if (c >= '0' && c <= '9') {
- // is number
- port *= 10;
- port += (c - '0');
- *msg = *msg + 1;
- } else if (i == 0) {
- return false;
- } else {
- break;
- }
- }
-
- return port <= 65535;
-}
-
-static bool djui_panel_join_ip_valid(char* buffer) {
- char** msg = &buffer;
-
- if (!djui_panel_join_ip_parse_numbers(msg)) { return false; }
- if (!djui_panel_join_ip_parse_period(msg)) { return false; }
- if (!djui_panel_join_ip_parse_numbers(msg)) { return false; }
- if (!djui_panel_join_ip_parse_period(msg)) { return false; }
- if (!djui_panel_join_ip_parse_numbers(msg)) { return false; }
- if (!djui_panel_join_ip_parse_period(msg)) { return false; }
- if (!djui_panel_join_ip_parse_numbers(msg)) { return false; }
- if (djui_panel_join_ip_parse_spacer(msg)) {
- if (!djui_panel_join_ip_parse_port(msg)) { return false; }
- }
-
- return (**msg == '\0');
-}
-
-static void djui_panel_join_ip_text_change(struct DjuiBase* caller) {
- struct DjuiInputbox* inputbox1 = (struct DjuiInputbox*)caller;
- if (strlen(inputbox1->buffer) > 2) {
- djui_inputbox_set_text_color(inputbox1, 0, 0, 0, 255);
- } else {
- djui_inputbox_set_text_color(inputbox1, 255, 0, 0, 255);
- }
-}
-
-static void djui_panel_join_ip_text_set_new(void) {
- char buffer[256] = { 0 };
- if (snprintf(buffer, 256, "%s", sInputboxIp->buffer) < 0) {
- LOG_INFO("truncating IP");
- }
-
- bool afterSpacer = false;
- int port = 0;
- for (int i = 0; i < 256; i++) {
- if (buffer[i] == ' ' || buffer[i] == ':') {
- buffer[i] = '\0';
- afterSpacer = true;
- } else if (buffer[i] == '\0') {
- break;
- } else if (afterSpacer && buffer[i] >= '0' && buffer[i] <= '9') {
- port *= 10;
- port += buffer[i] - '0';
- }
- }
-
- snprintf(gGetHostName, MAX_CONFIG_STRING, "%s", buffer);
- if (snprintf(configJoinIp, MAX_CONFIG_STRING, "%s", buffer) < 0) {
- LOG_INFO("truncating IP");
- }
- if (port >= 1 && port <= 65535) {
- configJoinPort = port;
- } else {
- configJoinPort = DEFAULT_PORT;
- }
-}
-
-static void djui_panel_join_ip_text_set(struct DjuiInputbox* inputbox1) {
- char buffer[256] = { 0 };
- if (strlen(configJoinIp) > 0 && configJoinPort != DEFAULT_PORT) {
- if (snprintf(buffer, 256, "%s:%d", configJoinIp, configJoinPort) < 0) { LOG_INFO("truncating IP"); }
- } else if (strlen(configJoinIp) > 0) {
- if (snprintf(buffer, 256, "%s", configJoinIp) < 0) { LOG_INFO("truncating IP"); }
- } else {
- if (snprintf(buffer, 256, "localhost") < 0) { LOG_INFO("truncating IP"); }
- }
-
- djui_inputbox_set_text(inputbox1, buffer);
-}
-
-void djui_panel_join_do_join(struct DjuiBase* caller) {
- if (!(strlen(sInputboxIp->buffer) > 2)) {
- djui_interactable_set_input_focus(&sInputboxIp->base);
- djui_inputbox_select_all(sInputboxIp);
- return;
- }
- network_reset_reconnect_and_rehost();
- djui_panel_join_ip_text_set_new();
- network_set_system(NS_SOCKET);
- network_init(NT_CLIENT);
- djui_panel_join_message_create(caller);
+#ifdef COOPNET
+static void djui_panel_join_public_lobbies(struct DjuiBase* caller) {
+ djui_panel_join_lobbies_create(caller, "");
}
+#endif
void djui_panel_join_create(struct DjuiBase* caller) {
-
- struct DjuiBase* defaultBase = NULL;
+#ifndef COOPNET
+ djui_panel_join_direct_create(caller);
+#else
struct DjuiThreePanel* panel = djui_panel_menu_create(DLANG(JOIN, JOIN_TITLE));
struct DjuiBase* body = djui_three_panel_get_body(panel);
{
-#ifdef DISCORD_SDK
- struct DjuiText* text1 = djui_text_create(body, DLANG(JOIN, JOIN_DISCORD));
- djui_base_set_size_type(&text1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
- djui_base_set_size(&text1->base, 1.0f, 100);
- djui_base_compute_tree(&text1->base);
- u16 discordLines = djui_text_count_lines(text1, 12);
- f32 discordTextHeight = 32 * 0.8125f * discordLines + 8;
- djui_base_set_size(&text1->base, 1.0f, discordTextHeight);
- djui_base_set_color(&text1->base, 200, 200, 200, 255);
-#endif
- struct DjuiRect* rect1 = djui_rect_create(body);
- djui_base_set_size_type(&rect1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
- djui_base_set_size(&rect1->base, 1.0f, 2);
- djui_base_set_color(&rect1->base, 150, 150, 150, 255);
-
- struct DjuiText* text2 = djui_text_create(body, DLANG(JOIN, JOIN_SOCKET));
- djui_base_set_size_type(&text2->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
- djui_base_set_size(&text2->base, 1.0f, 100);
- djui_base_compute_tree(&text2->base);
- u16 directLines = djui_text_count_lines(text2, 12);
- f32 directTextHeight = 32 * 0.8125f * directLines + 8;
- djui_base_set_size(&text2->base, 1.0f, directTextHeight);
- djui_base_set_color(&text2->base, 200, 200, 200, 255);
-
- struct DjuiInputbox* inputbox1 = djui_inputbox_create(body, 256);
- djui_base_set_size_type(&inputbox1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
- djui_base_set_size(&inputbox1->base, 1.0f, 32.0f);
- djui_interactable_hook_value_change(&inputbox1->base, djui_panel_join_ip_text_change);
- sInputboxIp = inputbox1;
- djui_panel_join_ip_text_set(inputbox1);
-
- struct DjuiRect* rect2 = djui_rect_container_create(body, 64);
- {
- struct DjuiButton* button1 = djui_button_create(&rect2->base, DLANG(MENU, BACK), DJUI_BUTTON_STYLE_BACK, djui_panel_menu_back);
- djui_base_set_size(&button1->base, 0.485f, 64);
- djui_base_set_alignment(&button1->base, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP);
-
- struct DjuiButton* button2 = djui_button_create(&rect2->base, DLANG(JOIN, JOIN), DJUI_BUTTON_STYLE_NORMAL, djui_panel_join_do_join);
- djui_base_set_size(&button2->base, 0.485f, 64);
- djui_base_set_alignment(&button2->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP);
- defaultBase = &button2->base;
- }
+ djui_button_create(body, DLANG(JOIN, PUBLIC_LOBBIES), DJUI_BUTTON_STYLE_NORMAL, djui_panel_join_public_lobbies);
+ djui_button_create(body, DLANG(JOIN, PRIVATE_LOBBIES), DJUI_BUTTON_STYLE_NORMAL, djui_panel_join_private_create);
+ djui_button_create(body, DLANG(JOIN, DIRECT), DJUI_BUTTON_STYLE_NORMAL, djui_panel_join_direct_create);
+ djui_button_create(body, DLANG(MENU, BACK), DJUI_BUTTON_STYLE_BACK, djui_panel_menu_back);
}
- djui_panel_add(caller, panel, defaultBase);
+ djui_panel_add(caller, panel, NULL);
+#endif
}
diff --git a/src/pc/djui/djui_panel_join_direct.c b/src/pc/djui/djui_panel_join_direct.c
new file mode 100644
index 00000000..b5ef555b
--- /dev/null
+++ b/src/pc/djui/djui_panel_join_direct.c
@@ -0,0 +1,189 @@
+#include
+#include "djui.h"
+#include "djui_panel.h"
+#include "djui_panel_menu.h"
+#include "djui_panel_modlist.h"
+#include "djui_panel_join_message.h"
+#include "djui_lobby_entry.h"
+#include "src/pc/network/network.h"
+#include "src/pc/network/socket/socket.h"
+#include "src/pc/network/coopnet/coopnet.h"
+#include "src/pc/network/socket/domain_res.h"
+#include "src/pc/utils/misc.h"
+#include "src/pc/configfile.h"
+#include "src/pc/debuglog.h"
+#include "macros.h"
+
+static struct DjuiInputbox* sInputboxIp = NULL;
+
+static bool djui_panel_join_direct_ip_parse_numbers(char** msg) {
+ int num = 0;
+ for (int i = 0; i < 3; i++) {
+ char c = **msg;
+ if (c >= '0' && c <= '9') {
+ // is number
+ num *= 10;
+ num += (c - '0');
+ *msg = *msg + 1;
+ } else if (i == 0) {
+ return false;
+ } else {
+ break;
+ }
+ }
+
+ return num >= 0 && num <= 255;
+}
+
+static bool djui_panel_join_direct_ip_parse_period(char** msg) {
+ char c = **msg;
+ bool isPeriod = (c == '.');
+ if (isPeriod) { *msg = *msg + 1; }
+ return isPeriod;
+}
+
+static bool djui_panel_join_direct_ip_parse_spacer(char** msg) {
+ char c = **msg;
+ bool isSpacer = (c == ':' || c == ' ');
+ if (isSpacer) { *msg = *msg + 1; }
+ return isSpacer;
+}
+
+static bool djui_panel_join_direct_ip_parse_port(char** msg) {
+ int port = 0;
+ for (int i = 0; i < 5; i++) {
+ char c = **msg;
+ if (c >= '0' && c <= '9') {
+ // is number
+ port *= 10;
+ port += (c - '0');
+ *msg = *msg + 1;
+ } else if (i == 0) {
+ return false;
+ } else {
+ break;
+ }
+ }
+
+ return port <= 65535;
+}
+
+static bool djui_panel_join_direct_ip_valid(char* buffer) {
+ char** msg = &buffer;
+
+ if (!djui_panel_join_direct_ip_parse_numbers(msg)) { return false; }
+ if (!djui_panel_join_direct_ip_parse_period(msg)) { return false; }
+ if (!djui_panel_join_direct_ip_parse_numbers(msg)) { return false; }
+ if (!djui_panel_join_direct_ip_parse_period(msg)) { return false; }
+ if (!djui_panel_join_direct_ip_parse_numbers(msg)) { return false; }
+ if (!djui_panel_join_direct_ip_parse_period(msg)) { return false; }
+ if (!djui_panel_join_direct_ip_parse_numbers(msg)) { return false; }
+ if (djui_panel_join_direct_ip_parse_spacer(msg)) {
+ if (!djui_panel_join_direct_ip_parse_port(msg)) { return false; }
+ }
+
+ return (**msg == '\0');
+}
+
+static void djui_panel_join_direct_ip_text_change(struct DjuiBase* caller) {
+ struct DjuiInputbox* inputbox1 = (struct DjuiInputbox*)caller;
+ if (strlen(inputbox1->buffer) > 2) {
+ djui_inputbox_set_text_color(inputbox1, 0, 0, 0, 255);
+ } else {
+ djui_inputbox_set_text_color(inputbox1, 255, 0, 0, 255);
+ }
+}
+
+static void djui_panel_join_direct_ip_text_set_new(void) {
+ char buffer[256] = { 0 };
+ if (snprintf(buffer, 256, "%s", sInputboxIp->buffer) < 0) {
+ LOG_INFO("truncating IP");
+ }
+
+ bool afterSpacer = false;
+ int port = 0;
+ for (int i = 0; i < 256; i++) {
+ if (buffer[i] == ' ' || buffer[i] == ':') {
+ buffer[i] = '\0';
+ afterSpacer = true;
+ } else if (buffer[i] == '\0') {
+ break;
+ } else if (afterSpacer && buffer[i] >= '0' && buffer[i] <= '9') {
+ port *= 10;
+ port += buffer[i] - '0';
+ }
+ }
+
+ snprintf(gGetHostName, MAX_CONFIG_STRING, "%s", buffer);
+ if (snprintf(configJoinIp, MAX_CONFIG_STRING, "%s", buffer) < 0) {
+ LOG_INFO("truncating IP");
+ }
+ if (port >= 1 && port <= 65535) {
+ configJoinPort = port;
+ } else {
+ configJoinPort = DEFAULT_PORT;
+ }
+}
+
+static void djui_panel_join_direct_ip_text_set(struct DjuiInputbox* inputbox1) {
+ char buffer[256] = { 0 };
+ if (strlen(configJoinIp) > 0 && configJoinPort != DEFAULT_PORT) {
+ if (snprintf(buffer, 256, "%s:%d", configJoinIp, configJoinPort) < 0) { LOG_INFO("truncating IP"); }
+ } else if (strlen(configJoinIp) > 0) {
+ if (snprintf(buffer, 256, "%s", configJoinIp) < 0) { LOG_INFO("truncating IP"); }
+ } else {
+ if (snprintf(buffer, 256, "localhost") < 0) { LOG_INFO("truncating IP"); }
+ }
+
+ djui_inputbox_set_text(inputbox1, buffer);
+}
+
+void djui_panel_join_direct_do_join(struct DjuiBase* caller) {
+ if (!(strlen(sInputboxIp->buffer) > 2)) {
+ djui_interactable_set_input_focus(&sInputboxIp->base);
+ djui_inputbox_select_all(sInputboxIp);
+ return;
+ }
+ network_reset_reconnect_and_rehost();
+ djui_panel_join_direct_ip_text_set_new();
+ network_set_system(NS_SOCKET);
+ network_init(NT_CLIENT, false);
+ djui_panel_join_message_create(caller);
+}
+
+void djui_panel_join_direct_create(struct DjuiBase* caller) {
+ struct DjuiBase* defaultBase = NULL;
+ struct DjuiThreePanel* panel = djui_panel_menu_create(DLANG(JOIN, JOIN_TITLE));
+ struct DjuiBase* body = djui_three_panel_get_body(panel);
+ {
+ struct DjuiText* text1 = djui_text_create(body, DLANG(JOIN, JOIN_SOCKET));
+ djui_base_set_size_type(&text1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
+ djui_base_set_size(&text1->base, 1.0f, 100);
+ djui_base_compute_tree(&text1->base);
+ u16 directLines = djui_text_count_lines(text1, 12);
+ f32 directTextHeight = 32 * 0.8125f * directLines + 8;
+ djui_base_set_size(&text1->base, 1.0f, directTextHeight);
+ djui_base_set_color(&text1->base, 200, 200, 200, 255);
+
+ struct DjuiInputbox* inputbox1 = djui_inputbox_create(body, 256);
+ djui_base_set_size_type(&inputbox1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
+ djui_base_set_size(&inputbox1->base, 1.0f, 32.0f);
+ djui_interactable_hook_value_change(&inputbox1->base, djui_panel_join_direct_ip_text_change);
+ sInputboxIp = inputbox1;
+ djui_panel_join_direct_ip_text_set(inputbox1);
+
+ struct DjuiRect* rect2 = djui_rect_container_create(body, 64);
+ {
+ struct DjuiButton* button1 = djui_button_create(&rect2->base, DLANG(MENU, BACK), DJUI_BUTTON_STYLE_BACK, djui_panel_menu_back);
+ djui_base_set_size(&button1->base, 0.485f, 64);
+ djui_base_set_alignment(&button1->base, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP);
+
+ struct DjuiButton* button2 = djui_button_create(&rect2->base, DLANG(JOIN, JOIN), DJUI_BUTTON_STYLE_NORMAL, djui_panel_join_direct_do_join);
+ djui_base_set_size(&button2->base, 0.485f, 64);
+ djui_base_set_alignment(&button2->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP);
+ defaultBase = &button2->base;
+ }
+ }
+
+ djui_panel_add(caller, panel, defaultBase);
+}
diff --git a/src/pc/djui/djui_panel_join_direct.h b/src/pc/djui/djui_panel_join_direct.h
new file mode 100644
index 00000000..108748e7
--- /dev/null
+++ b/src/pc/djui/djui_panel_join_direct.h
@@ -0,0 +1,3 @@
+#pragma once
+
+void djui_panel_join_direct_create(struct DjuiBase* caller);
diff --git a/src/pc/djui/djui_panel_join_lobbies.c b/src/pc/djui/djui_panel_join_lobbies.c
new file mode 100644
index 00000000..5789b824
--- /dev/null
+++ b/src/pc/djui/djui_panel_join_lobbies.c
@@ -0,0 +1,173 @@
+#include
+#include "djui.h"
+#include "djui_panel.h"
+#include "djui_panel_menu.h"
+#include "djui_panel_join_message.h"
+#include "djui_lobby_entry.h"
+#include "src/pc/network/network.h"
+#include "src/pc/network/socket/socket.h"
+#include "src/pc/network/coopnet/coopnet.h"
+#include "src/pc/utils/misc.h"
+#include "src/pc/configfile.h"
+#include "src/pc/debuglog.h"
+#include "macros.h"
+
+#ifdef COOPNET
+
+#define DJUI_DESC_PANEL_WIDTH (410.0f + (16 * 2.0f))
+
+static struct DjuiPaginated* sLobbyPaginated = NULL;
+static struct DjuiFlowLayout* sLobbyLayout = NULL;
+static struct DjuiButton* sRefreshButton = NULL;
+static struct DjuiThreePanel* sDescriptionPanel = NULL;
+static struct DjuiText* sTooltip = NULL;
+static char* sPassword = NULL;
+
+static void djui_panel_join_lobby_description_create() {
+ f32 bodyHeight = 600;
+
+ struct DjuiThreePanel* panel = djui_three_panel_create(&gDjuiRoot->base, 64, bodyHeight, 0);
+
+ djui_base_set_alignment(&panel->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_CENTER);
+ djui_base_set_size_type(&panel->base, DJUI_SVT_ABSOLUTE, DJUI_SVT_RELATIVE);
+ djui_base_set_size(&panel->base, DJUI_DESC_PANEL_WIDTH, 1.0f);
+ djui_base_set_color(&panel->base, 0, 0, 0, 240);
+ djui_base_set_border_color(&panel->base, 0, 0, 0, 200);
+ djui_base_set_border_width(&panel->base, 8);
+ djui_base_set_padding(&panel->base, 16, 16, 16, 16);
+ {
+ struct DjuiFlowLayout* body = djui_flow_layout_create(&panel->base);
+ djui_base_set_alignment(&body->base, DJUI_HALIGN_CENTER, DJUI_VALIGN_CENTER);
+ djui_base_set_size_type(&body->base, DJUI_SVT_RELATIVE, DJUI_SVT_RELATIVE);
+ djui_base_set_size(&body->base, 1.0f, 1.0f);
+ djui_base_set_color(&body->base, 0, 0, 0, 0);
+ djui_flow_layout_set_margin(body, 16);
+ djui_flow_layout_set_flow_direction(body, DJUI_FLOW_DIR_DOWN);
+
+ struct DjuiText* description = djui_text_create(&panel->base, "");
+ djui_base_set_size_type(&description->base, DJUI_SVT_RELATIVE, DJUI_SVT_RELATIVE);
+ djui_base_set_size(&description->base, 1.0f, 1.0f);
+ djui_base_set_color(&description->base, 222, 222, 222, 255);
+ djui_text_set_alignment(description, DJUI_HALIGN_LEFT, DJUI_VALIGN_CENTER);
+ sTooltip = description;
+ }
+ sDescriptionPanel = panel;
+}
+
+static void djui_lobby_on_hover(struct DjuiBase* base) {
+ struct DjuiLobbyEntry* entry = (struct DjuiLobbyEntry*)base;
+ djui_text_set_text(sTooltip, entry->description);
+}
+
+static void djui_lobby_on_hover_end(UNUSED struct DjuiBase* base) {
+ djui_text_set_text(sTooltip, "");
+}
+
+void djui_panel_join_lobby(struct DjuiBase* caller) {
+ gCoopNetDesiredLobby = (uint64_t)caller->tag;
+ snprintf(gCoopNetPassword, 64, "%s", sPassword);
+ network_reset_reconnect_and_rehost();
+ network_set_system(NS_COOPNET);
+ network_init(NT_CLIENT, false);
+ djui_panel_join_message_create(caller);
+}
+
+void djui_panel_join_query(uint64_t aLobbyId, UNUSED uint64_t aOwnerId, uint16_t aConnections, uint16_t aMaxConnections, UNUSED const char* aGame, const char* aVersion, const char* aHostName, const char* aMode, const char* aDescription) {
+ if (!sLobbyLayout) { return; }
+
+ char playerText[64] = "";
+ snprintf(playerText, 63, "%u/%u", aConnections, aMaxConnections);
+
+
+ char mode[64] = "";
+ snprintf(mode, 64, "%s", aMode);
+
+ char version[MAX_VERSION_LENGTH] = { 0 };
+ snprintf(version, MAX_VERSION_LENGTH, "%s", get_version());
+ if (strcmp(version, aVersion) != 0) {
+ snprintf(mode, 64, "\\#ff0000\\[%s]", aVersion);
+ }
+
+ struct DjuiBase* layoutBase = &sLobbyLayout->base;
+ struct DjuiLobbyEntry* entry = djui_lobby_entry_create(layoutBase, (char*)aHostName, (char*)mode, playerText, (char*)aDescription, djui_panel_join_lobby, djui_lobby_on_hover, djui_lobby_on_hover_end);
+ entry->base.tag = (s64)aLobbyId;
+ djui_paginated_update_page_buttons(sLobbyPaginated);
+}
+
+void djui_panel_join_query_finish(void) {
+ if (!sRefreshButton) { return; }
+ djui_text_set_text(sRefreshButton->text, DLANG(LOBBIES, REFRESH));
+ djui_base_set_enabled(&sRefreshButton->base, true);
+
+ if (sLobbyLayout->base.child == NULL) {
+ struct DjuiText* text = djui_text_create(&sLobbyLayout->base, DLANG(LOBBIES, NONE_FOUND));
+ djui_base_set_size_type(&text->base, DJUI_SVT_RELATIVE, DJUI_SVT_RELATIVE);
+ djui_base_set_size(&text->base, 1, 1);
+ djui_text_set_alignment(text, DJUI_HALIGN_CENTER, DJUI_VALIGN_CENTER);
+ }
+ djui_paginated_update_page_buttons(sLobbyPaginated);
+}
+
+void djui_panel_join_lobbies_on_destroy(UNUSED struct DjuiBase* caller) {
+ if (sPassword) { free(sPassword); }
+ sPassword = NULL;
+ sRefreshButton = NULL;
+ sLobbyLayout = NULL;
+ sLobbyPaginated = NULL;
+
+ if (sDescriptionPanel != NULL) {
+ djui_base_destroy(&sDescriptionPanel->base);
+ sDescriptionPanel = NULL;
+ }
+}
+
+void djui_panel_join_lobbies_refresh(UNUSED struct DjuiBase* caller) {
+ djui_base_destroy_children(&sLobbyLayout->base);
+ djui_text_set_text(sRefreshButton->text, DLANG(LOBBIES, REFRESHING));
+ djui_base_set_enabled(&sRefreshButton->base, false);
+ djui_paginated_update_page_buttons(sLobbyPaginated);
+ ns_coopnet_query(djui_panel_join_query, djui_panel_join_query_finish, sPassword);
+}
+
+void djui_panel_join_lobbies_create(struct DjuiBase* caller, const char* password) {
+ if (sPassword) { free(sPassword); sPassword = NULL; }
+ sPassword = strdup(password);
+ bool private = (strlen(password) > 0);
+
+ djui_panel_join_lobby_description_create();
+
+ struct DjuiBase* defaultBase = NULL;
+ struct DjuiThreePanel* panel = djui_panel_menu_create(private ? DLANG(LOBBIES, PRIVATE_LOBBIES) : DLANG(LOBBIES, PUBLIC_LOBBIES));
+ struct DjuiBase* body = djui_three_panel_get_body(panel);
+ {
+ sLobbyPaginated = djui_paginated_create(body, 8);
+ sLobbyLayout = sLobbyPaginated->layout;
+ djui_flow_layout_set_margin(sLobbyLayout, 4);
+
+ bool querying = ns_coopnet_query(djui_panel_join_query, djui_panel_join_query_finish, password);
+ if (!querying) {
+ struct DjuiText* text = djui_text_create(&sLobbyLayout->base, DLANG(NOTIF, COOPNET_CONNECTION_FAILED));
+ djui_base_set_size_type(&text->base, DJUI_SVT_RELATIVE, DJUI_SVT_RELATIVE);
+ djui_base_set_size(&text->base, 1, 1);
+ djui_text_set_alignment(text, DJUI_HALIGN_CENTER, DJUI_VALIGN_CENTER);
+ }
+
+ struct DjuiRect* rect2 = djui_rect_container_create(body, 64);
+ {
+ struct DjuiButton* button1 = djui_button_create(&rect2->base, DLANG(MENU, BACK), DJUI_BUTTON_STYLE_BACK, djui_panel_menu_back);
+ djui_base_set_size(&button1->base, 0.485f, 64);
+ djui_base_set_alignment(&button1->base, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP);
+
+ sRefreshButton = djui_button_create(&rect2->base, querying ? DLANG(LOBBIES, REFRESHING) : DLANG(LOBBIES, REFRESH), DJUI_BUTTON_STYLE_NORMAL, djui_panel_join_lobbies_refresh);
+ djui_base_set_size(&sRefreshButton->base, 0.485f, 64);
+ djui_base_set_alignment(&sRefreshButton->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP);
+ djui_base_set_enabled(&sRefreshButton->base, false);
+ defaultBase = &sRefreshButton->base;
+ }
+ }
+
+ struct DjuiPanel* p = djui_panel_add(caller, panel, defaultBase);
+ p->on_panel_destroy = djui_panel_join_lobbies_on_destroy;
+}
+
+#endif
\ No newline at end of file
diff --git a/src/pc/djui/djui_panel_join_lobbies.h b/src/pc/djui/djui_panel_join_lobbies.h
new file mode 100644
index 00000000..cda56f95
--- /dev/null
+++ b/src/pc/djui/djui_panel_join_lobbies.h
@@ -0,0 +1,3 @@
+#pragma once
+
+void djui_panel_join_lobbies_create(struct DjuiBase* caller, const char* password);
diff --git a/src/pc/djui/djui_panel_join_message.c b/src/pc/djui/djui_panel_join_message.c
index 9c41e186..1144095d 100644
--- a/src/pc/djui/djui_panel_join_message.c
+++ b/src/pc/djui/djui_panel_join_message.c
@@ -22,7 +22,7 @@ void djui_panel_join_message_error(char* message) {
void djui_panel_join_message_cancel(struct DjuiBase* caller) {
if (network_is_reconnecting()) { return; }
network_reset_reconnect_and_rehost();
- network_shutdown(true, false, false);
+ network_shutdown(true, false, false, false);
djui_panel_menu_back(caller);
}
diff --git a/src/pc/djui/djui_panel_join_private.c b/src/pc/djui/djui_panel_join_private.c
new file mode 100644
index 00000000..d5d5bc59
--- /dev/null
+++ b/src/pc/djui/djui_panel_join_private.c
@@ -0,0 +1,54 @@
+#include "djui.h"
+#include "djui_panel.h"
+#include "djui_panel_menu.h"
+#include "djui_panel_join_lobbies.h"
+#include "djui_panel_join_private.h"
+#include "djui_panel_join_direct.h"
+#include "pc/network/network.h"
+#include "pc/utils/misc.h"
+
+#ifdef COOPNET
+
+static struct DjuiInputbox* sInputboxPassword = NULL;
+
+static void djui_panel_join_private_lobbies(struct DjuiBase* caller) {
+ djui_panel_join_lobbies_create(caller, sInputboxPassword->buffer);
+}
+
+void djui_panel_join_private_create(struct DjuiBase* caller) {
+ struct DjuiBase* defaultBase = NULL;
+ struct DjuiThreePanel* panel = djui_panel_menu_create(DLANG(LOBBIES, PRIVATE_LOBBIES));
+ struct DjuiBase* body = djui_three_panel_get_body(panel);
+ {
+ struct DjuiText* text1 = djui_text_create(body, DLANG(LOBBIES, ENTER_PASSWORD));
+ djui_base_set_size_type(&text1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
+ djui_base_set_size(&text1->base, 1.0f, 100);
+ djui_base_compute_tree(&text1->base);
+ u16 directLines = djui_text_count_lines(text1, 12);
+ f32 directTextHeight = 32 * 0.8125f * directLines + 8;
+ djui_base_set_size(&text1->base, 1.0f, directTextHeight);
+ djui_base_set_color(&text1->base, 200, 200, 200, 255);
+
+ struct DjuiInputbox* inputbox1 = djui_inputbox_create(body, 256);
+ inputbox1->passwordChar[0] = '#';
+ djui_base_set_size_type(&inputbox1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
+ djui_base_set_size(&inputbox1->base, 1.0f, 32.0f);
+ sInputboxPassword = inputbox1;
+
+ struct DjuiRect* rect2 = djui_rect_container_create(body, 64);
+ {
+ struct DjuiButton* button1 = djui_button_create(&rect2->base, DLANG(MENU, BACK), DJUI_BUTTON_STYLE_BACK, djui_panel_menu_back);
+ djui_base_set_size(&button1->base, 0.485f, 64);
+ djui_base_set_alignment(&button1->base, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP);
+
+ struct DjuiButton* button2 = djui_button_create(&rect2->base, DLANG(LOBBIES, SEARCH), DJUI_BUTTON_STYLE_NORMAL, djui_panel_join_private_lobbies);
+ djui_base_set_size(&button2->base, 0.485f, 64);
+ djui_base_set_alignment(&button2->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP);
+ defaultBase = &button2->base;
+ }
+ }
+
+ djui_panel_add(caller, panel, defaultBase);
+}
+
+#endif
\ No newline at end of file
diff --git a/src/pc/djui/djui_panel_join_private.h b/src/pc/djui/djui_panel_join_private.h
new file mode 100644
index 00000000..98f9abcc
--- /dev/null
+++ b/src/pc/djui/djui_panel_join_private.h
@@ -0,0 +1,3 @@
+#pragma once
+
+void djui_panel_join_private_create(struct DjuiBase* caller);
diff --git a/src/pc/djui/djui_panel_pause.c b/src/pc/djui/djui_panel_pause.c
index 2b76d709..e1bdbae0 100644
--- a/src/pc/djui/djui_panel_pause.c
+++ b/src/pc/djui/djui_panel_pause.c
@@ -22,7 +22,7 @@ static void djui_panel_pause_resume(UNUSED struct DjuiBase* caller) {
static void djui_panel_pause_quit_yes(UNUSED struct DjuiBase* caller) {
network_reset_reconnect_and_rehost();
- network_shutdown(true, false, false);
+ network_shutdown(true, false, false, false);
}
static void djui_panel_pause_quit(struct DjuiBase* caller) {
diff --git a/src/pc/djui/djui_panel_player.c b/src/pc/djui/djui_panel_player.c
index 719c8070..a05d87b0 100644
--- a/src/pc/djui/djui_panel_player.c
+++ b/src/pc/djui/djui_panel_player.c
@@ -196,6 +196,7 @@ static void djui_panel_player_name_on_focus_end(struct DjuiBase* caller) {
if (gNetworkType != NT_NONE) {
network_send_player_settings();
}
+ djui_inputbox_on_focus_end(&inputbox1->base);
}
static void djui_panel_player_value_changed(UNUSED struct DjuiBase* caller) {
@@ -255,7 +256,7 @@ void djui_panel_player_create(struct DjuiBase* caller) {
djui_inputbox_set_text(inputbox1, DLANG(PLAYER, PLAYER));
}
djui_interactable_hook_value_change(&inputbox1->base, djui_panel_player_name_text_change);
- djui_interactable_hook_focus(&inputbox1->base, NULL, NULL, djui_panel_player_name_on_focus_end);
+ djui_interactable_hook_focus(&inputbox1->base, djui_inputbox_on_focus_begin, NULL, djui_panel_player_name_on_focus_end);
}
char* modelChoices[CT_MAX] = { 0 };
diff --git a/src/pc/lua/smlua_cobject_autogen.c b/src/pc/lua/smlua_cobject_autogen.c
index bc121b77..dbff5144 100644
--- a/src/pc/lua/smlua_cobject_autogen.c
+++ b/src/pc/lua/smlua_cobject_autogen.c
@@ -1887,13 +1887,14 @@ static struct LuaObjectField sRayIntersectionInfoFields[LUA_RAY_INTERSECTION_INF
{ "surface", LVT_COBJECT_P, offsetof(struct RayIntersectionInfo, surface), false, LOT_SURFACE },
};
-#define LUA_SERVER_SETTINGS_FIELD_COUNT 10
+#define LUA_SERVER_SETTINGS_FIELD_COUNT 11
static struct LuaObjectField sServerSettingsFields[LUA_SERVER_SETTINGS_FIELD_COUNT] = {
{ "bubbleDeath", LVT_U8, offsetof(struct ServerSettings, bubbleDeath), false, LOT_NONE },
{ "enableCheats", LVT_U8, offsetof(struct ServerSettings, enableCheats), false, LOT_NONE },
{ "enablePlayerList", LVT_U8, offsetof(struct ServerSettings, enablePlayerList), false, LOT_NONE },
{ "enablePlayersInLevelDisplay", LVT_U8, offsetof(struct ServerSettings, enablePlayersInLevelDisplay), false, LOT_NONE },
{ "headlessServer", LVT_U8, offsetof(struct ServerSettings, headlessServer), false, LOT_NONE },
+ { "maxPlayers", LVT_U8, offsetof(struct ServerSettings, maxPlayers), false, LOT_NONE },
{ "playerInteractions", LVT_S32, offsetof(struct ServerSettings, playerInteractions), false, LOT_NONE },
{ "playerKnockbackStrength", LVT_U8, offsetof(struct ServerSettings, playerKnockbackStrength), false, LOT_NONE },
{ "shareLives", LVT_U8, offsetof(struct ServerSettings, shareLives), false, LOT_NONE },
diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c
index 422513fe..0f1b414b 100644
--- a/src/pc/lua/smlua_constants_autogen.c
+++ b/src/pc/lua/smlua_constants_autogen.c
@@ -1759,7 +1759,8 @@ char gSmluaConstants[] = ""
"SYNC_DISTANCE_INFINITE = 0\n"
"PACKET_LENGTH = 3000\n"
"NS_SOCKET = 0\n"
-"NS_DISCORD = 1\n"
+"NS_COOPNET = 1\n"
+"NS_MAX = 2\n"
"PLAYER_INTERACTIONS_NONE = 0\n"
"PLAYER_INTERACTIONS_SOLID = 1\n"
"PLAYER_INTERACTIONS_PVP = 2\n"
@@ -1767,7 +1768,7 @@ char gSmluaConstants[] = ""
"UNKNOWN_GLOBAL_INDEX = (-1)\n"
"UNKNOWN_NETWORK_INDEX = (-1)\n"
"NETWORK_PLAYER_TIMEOUT = 10\n"
-"NETWORK_PLAYER_PING_TIMEOUT = 1\n"
+"NETWORK_PLAYER_PING_TIMEOUT = 3\n"
"MAX_RX_SEQ_IDS = 64\n"
"USE_REAL_PALETTE_VAR = 0xFF\n"
"NPT_UNKNOWN = 0\n"
@@ -4015,8 +4016,8 @@ char gSmluaConstants[] = ""
"COOP_OBJ_FLAG_NON_SYNC = (1 << 2)\n"
"COOP_OBJ_FLAG_INITIALIZED = (1 << 3)\n"
"VERSION_TEXT = 'beta'\n"
-"VERSION_NUMBER = 33\n"
-"MINOR_VERSION_NUMBER = 1\n"
+"VERSION_NUMBER = 34\n"
+"MINOR_VERSION_NUMBER = 0\n"
"PATCH_VERSION_NUMBER = 0\n"
"MAX_VERSION_LENGTH = 10\n"
"MAX_LOCAL_VERSION_LENGTH = 12\n"
diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c
index 8b63200c..926a7fa2 100644
--- a/src/pc/lua/smlua_functions_autogen.c
+++ b/src/pc/lua/smlua_functions_autogen.c
@@ -30,6 +30,7 @@
#include "src/pc/lua/utils/smlua_text_utils.h"
#include "src/pc/lua/utils/smlua_audio_utils.h"
#include "src/pc/lua/utils/smlua_level_utils.h"
+#include "src/pc/lua/utils/smlua_deprecated.h"
#include "src/game/object_list_processor.h"
#include "src/game/behavior_actions.h"
#include "src/game/mario_misc.h"
@@ -18741,23 +18742,6 @@ int smlua_func_network_player_set_description(lua_State* L) {
// network_utils.h //
/////////////////////
-int smlua_func_network_discord_id_from_local_index(lua_State* L) {
- if (L == NULL) { return 0; }
-
- int top = lua_gettop(L);
- if (top != 1) {
- LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "network_discord_id_from_local_index", 1, top);
- return 0;
- }
-
- u8 localIndex = smlua_to_integer(L, 1);
- if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "network_discord_id_from_local_index"); return 0; }
-
- lua_pushstring(L, network_discord_id_from_local_index(localIndex));
-
- return 1;
-}
-
int smlua_func_network_get_player_text_color_string(lua_State* L) {
if (L == NULL) { return 0; }
@@ -26273,6 +26257,27 @@ int smlua_func_smlua_collision_util_get(lua_State* L) {
return 1;
}
+ ////////////////////////
+ // smlua_deprecated.h //
+////////////////////////
+
+int smlua_func_network_discord_id_from_local_index(lua_State* L) {
+ if (L == NULL) { return 0; }
+
+ int top = lua_gettop(L);
+ if (top != 1) {
+ LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "network_discord_id_from_local_index", 1, top);
+ return 0;
+ }
+
+ u8 localIndex = smlua_to_integer(L, 1);
+ if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "network_discord_id_from_local_index"); return 0; }
+
+ lua_pushstring(L, network_discord_id_from_local_index(localIndex));
+
+ return 1;
+}
+
/////////////////////////
// smlua_level_utils.h //
/////////////////////////
@@ -30186,7 +30191,6 @@ void smlua_bind_functions_autogen(void) {
smlua_bind_function(L, "network_player_set_description", smlua_func_network_player_set_description);
// network_utils.h
- smlua_bind_function(L, "network_discord_id_from_local_index", smlua_func_network_discord_id_from_local_index);
smlua_bind_function(L, "network_get_player_text_color_string", smlua_func_network_get_player_text_color_string);
smlua_bind_function(L, "network_global_index_from_local", smlua_func_network_global_index_from_local);
smlua_bind_function(L, "network_is_moderator", smlua_func_network_is_moderator);
@@ -30571,6 +30575,9 @@ void smlua_bind_functions_autogen(void) {
smlua_bind_function(L, "get_water_surface_pseudo_floor", smlua_func_get_water_surface_pseudo_floor);
smlua_bind_function(L, "smlua_collision_util_get", smlua_func_smlua_collision_util_get);
+ // smlua_deprecated.h
+ smlua_bind_function(L, "network_discord_id_from_local_index", smlua_func_network_discord_id_from_local_index);
+
// smlua_level_utils.h
smlua_bind_function(L, "level_register", smlua_func_level_register);
smlua_bind_function(L, "smlua_level_util_get_info", smlua_func_smlua_level_util_get_info);
diff --git a/src/pc/lua/utils/smlua_deprecated.c b/src/pc/lua/utils/smlua_deprecated.c
new file mode 100644
index 00000000..d5ab47cb
--- /dev/null
+++ b/src/pc/lua/utils/smlua_deprecated.c
@@ -0,0 +1,15 @@
+#include "types.h"
+#ifdef DISCORD_SDK
+#include "pc/discord/discord.h"
+#endif
+
+char* network_discord_id_from_local_index(u8 localIndex) {
+#ifdef DISCORD_SDK
+ static char sDiscordId[64] = "";
+ if (localIndex == 0) {
+ snprintf(sDiscordId, 64, "%" PRIu64 "", (uint64_t)discord_get_user_id());
+ return sDiscordId;
+ }
+#endif
+ return NULL;
+}
diff --git a/src/pc/lua/utils/smlua_deprecated.h b/src/pc/lua/utils/smlua_deprecated.h
new file mode 100644
index 00000000..09622d33
--- /dev/null
+++ b/src/pc/lua/utils/smlua_deprecated.h
@@ -0,0 +1,3 @@
+#pragma once
+
+char* network_discord_id_from_local_index(u8 localIndex);
diff --git a/src/pc/mods/mod.c b/src/pc/mods/mod.c
index 138f2f2c..fe018170 100644
--- a/src/pc/mods/mod.c
+++ b/src/pc/mods/mod.c
@@ -7,6 +7,19 @@
#include "pc/utils/md5.h"
#include "pc/debuglog.h"
+size_t mod_get_lua_size(struct Mod* mod) {
+ if (!mod) { return 0; }
+ size_t size = 0;
+
+ for (int i = 0; i < mod->fileCount; i++) {
+ struct ModFile* file = &mod->files[i];
+ if (!str_ends_with(file->relativePath, ".lua")) { continue; }
+ size += file->size;
+ }
+
+ return size;
+}
+
static void mod_activate_bin(struct ModFile* file) {
// copy geo name
char geoName[64] = { 0 };
diff --git a/src/pc/mods/mod.h b/src/pc/mods/mod.h
index e5b56cfb..9496f508 100644
--- a/src/pc/mods/mod.h
+++ b/src/pc/mods/mod.h
@@ -37,6 +37,7 @@ struct Mod {
u8 customBehaviorIndex;
};
+size_t mod_get_lua_size(struct Mod* mod);
void mod_activate(struct Mod* mod);
void mod_clear(struct Mod* mod);
bool mod_load(struct Mods* mods, char* basePath, char* modName);
diff --git a/src/pc/mods/mods.c b/src/pc/mods/mods.c
index 5345a5c8..c3611c43 100644
--- a/src/pc/mods/mods.c
+++ b/src/pc/mods/mods.c
@@ -20,6 +20,23 @@ struct LocalEnabledPath {
struct LocalEnabledPath* sLocalEnabledPaths = NULL;
+void mods_get_main_mod_name(char* destination, u32 maxSize) {
+ struct Mod* picked = NULL;
+ size_t pickedSize = 0;
+
+ for (unsigned int i = 0; i < gLocalMods.entryCount; i++) {
+ struct Mod* mod = gLocalMods.entries[i];
+ if (!mod->enabled) { continue; }
+ size_t size = mod_get_lua_size(mod);
+ if (size > pickedSize) {
+ picked = mod;
+ pickedSize = size;
+ }
+ }
+
+ snprintf(destination, maxSize, "%s", picked ? picked->name : "Super Mario 64");
+}
+
static void mods_local_store_enabled(void) {
assert(sLocalEnabledPaths == NULL);
struct LocalEnabledPath* prev = NULL;
diff --git a/src/pc/mods/mods.h b/src/pc/mods/mods.h
index 59a5f1f5..3560e125 100644
--- a/src/pc/mods/mods.h
+++ b/src/pc/mods/mods.h
@@ -22,6 +22,7 @@ extern struct Mods gActiveMods;
extern char gRemoteModsBasePath[];
+void mods_get_main_mod_name(char* destination, u32 maxSize);
bool mods_generate_remote_base_path(void);
void mods_activate(struct Mods* mods);
void mods_clear(struct Mods* mods);
diff --git a/src/pc/network/coopnet/coopnet.c b/src/pc/network/coopnet/coopnet.c
new file mode 100644
index 00000000..02570bf1
--- /dev/null
+++ b/src/pc/network/coopnet/coopnet.c
@@ -0,0 +1,293 @@
+#include "libcoopnet.h"
+#include "coopnet.h"
+#include "coopnet_id.h"
+#include "pc/network/network.h"
+#include "pc/network/version.h"
+#include "pc/djui/djui_language.h"
+#include "pc/djui/djui_popup.h"
+#include "pc/mods/mods.h"
+#include "pc/utils/misc.h"
+#include "pc/debuglog.h"
+#ifdef DISCORD_SDK
+#include "pc/discord/discord.h"
+#endif
+
+#ifdef COOPNET
+
+#define CN_GAME_STR "sm64ex-coop"
+
+uint64_t gCoopNetDesiredLobby = 0;
+char gCoopNetPassword[64] = "";
+char sCoopNetDescription[256] = "";
+
+static uint64_t sLocalLobbyId = 0;
+static uint64_t sLocalLobbyOwnerId = 0;
+static enum NetworkType sNetworkType;
+static bool sReconnecting = false;
+
+static CoopNetRc coopnet_initialize(void);
+
+bool ns_coopnet_query(QueryCallbackPtr callback, QueryFinishCallbackPtr finishCallback, const char* password) {
+ gCoopNetCallbacks.OnLobbyListGot = callback;
+ gCoopNetCallbacks.OnLobbyListFinish = finishCallback;
+ if (coopnet_initialize() != COOPNET_OK) { return false; }
+ if (coopnet_lobby_list_get(CN_GAME_STR, password) != COOPNET_OK) { return false; }
+ return true;
+}
+
+static void coopnet_on_connected(uint64_t userId) {
+ coopnet_set_local_user_id(userId);
+}
+
+static void coopnet_on_disconnected(bool intentional) {
+ LOG_INFO("Coopnet shutdown!");
+ if (!intentional) {
+ djui_popup_create(DLANG(NOTIF, COOPNET_DISCONNECTED), 2);
+ }
+ coopnet_shutdown();
+ gCoopNetCallbacks.OnLobbyListGot = NULL;
+ gCoopNetCallbacks.OnLobbyListFinish = NULL;
+}
+
+static void coopnet_on_peer_disconnected(uint64_t peerId) {
+ u8 localIndex = coopnet_user_id_to_local_index(peerId);
+ if (localIndex != UNKNOWN_LOCAL_INDEX && gNetworkPlayers[localIndex].connected) {
+ network_player_disconnected(gNetworkPlayers[localIndex].globalIndex);
+ }
+}
+
+static void coopnet_on_receive(uint64_t userId, const uint8_t* data, uint64_t dataLength) {
+ coopnet_set_user_id(0, userId);
+ u8 localIndex = coopnet_user_id_to_local_index(userId);
+ network_receive(localIndex, &userId, (u8*)data, dataLength);
+}
+
+static void coopnet_on_lobby_joined(uint64_t lobbyId, uint64_t userId, uint64_t ownerId, uint64_t destId) {
+ LOG_INFO("coopnet_on_lobby_joined!");
+ coopnet_set_user_id(0, ownerId);
+ sLocalLobbyId = lobbyId;
+ sLocalLobbyOwnerId = ownerId;
+
+ if (userId == coopnet_get_local_user_id()) {
+ coopnet_clear_dest_ids();
+ }
+
+ coopnet_save_dest_id(userId, destId);
+
+ if (userId == coopnet_get_local_user_id() && gNetworkType == NT_CLIENT) {
+ network_send_mod_list_request();
+ }
+#ifdef DISCORD_SDK
+ discord_activity_update();
+#endif
+}
+
+static void coopnet_on_lobby_left(uint64_t lobbyId, uint64_t userId) {
+ LOG_INFO("coopnet_on_lobby_left!");
+ coopnet_clear_dest_id(userId);
+ if (lobbyId == sLocalLobbyId && userId == coopnet_get_local_user_id()) {
+ network_shutdown(false, false, true, false);
+ }
+}
+
+static void coopnet_on_error(enum MPacketErrorNumber error, uint64_t tag) {
+ switch (error) {
+ case MERR_COOPNET_VERSION:
+ djui_popup_create(DLANG(NOTIF, COOPNET_VERSION), 2);
+ network_shutdown(false, false, false, false);
+ break;
+ case MERR_PEER_FAILED:
+ {
+ char built[256] = { 0 };
+ u8 localIndex = coopnet_user_id_to_local_index(tag);
+ char* name = DLANG(NOTIF, UNKNOWN);
+ if (localIndex == 0) {
+ name = DLANG(NOTIF, LOBBY_HOST);
+ } else if (localIndex != UNKNOWN_LOCAL_INDEX && gNetworkPlayers[localIndex].connected) {
+ name = gNetworkPlayers[localIndex].name;
+ }
+ djui_language_replace(DLANG(NOTIF, PEER_FAILED), built, 256, '@', name);
+ djui_popup_create(built, 2);
+ }
+ break;
+ case MERR_LOBBY_NOT_FOUND:
+ djui_popup_create(DLANG(NOTIF, LOBBY_NOT_FOUND), 2);
+ network_shutdown(false, false, false, false);
+ break;
+ case MERR_LOBBY_JOIN_FULL:
+ djui_popup_create(DLANG(NOTIF, LOBBY_JOIN_FULL), 2);
+ network_shutdown(false, false, false, false);
+ break;
+ case MERR_LOBBY_JOIN_FAILED:
+ djui_popup_create(DLANG(NOTIF, LOBBY_JOIN_FAILED), 2);
+ network_shutdown(false, false, false, false);
+ break;
+ case MERR_LOBBY_PASSWORD_INCORRECT:
+ djui_popup_create(DLANG(NOTIF, LOBBY_PASSWORD_INCORRECT), 2);
+ network_shutdown(false, false, false, false);
+ break;
+ case MERR_NONE:
+ case MERR_MAX:
+ break;
+ }
+}
+static bool ns_coopnet_initialize(enum NetworkType networkType, bool reconnecting) {
+ sNetworkType = networkType;
+ sReconnecting = reconnecting;
+ if (reconnecting) { return true; }
+ return coopnet_is_connected()
+ ? true
+ : (coopnet_initialize() == COOPNET_OK);
+}
+
+static char* ns_coopnet_get_id_str(u8 localIndex) {
+ static char id_str[32] = { 0 };
+ if (localIndex == UNKNOWN_LOCAL_INDEX) {
+ snprintf(id_str, 32, "???");
+ } else {
+ uint64_t userId = ns_coopnet_get_id(localIndex);
+ uint64_t destId = coopnet_get_dest_id(userId);
+ snprintf(id_str, 32, "%" PRIu64 "", destId);
+ }
+ return id_str;
+}
+
+static bool ns_coopnet_match_addr(void* addr1, void* addr2) {
+ return !memcmp(addr1, addr2, sizeof(u64));
+}
+
+bool ns_coopnet_is_connected(void) {
+ return coopnet_is_connected();
+}
+
+static void coopnet_populate_description(void) {
+ char* buffer = sCoopNetDescription;
+ int bufferLength = 256;
+ // get version
+ char* version = get_version();
+ int versionLength = strlen(version);
+ snprintf(buffer, bufferLength, "%s", version);
+ buffer += versionLength;
+ bufferLength -= versionLength;
+
+ // get mod strings
+ if (gActiveMods.entryCount <= 0) { return; }
+ char* strings[gActiveMods.entryCount];
+ for (int i = 0; i < gActiveMods.entryCount; i++) {
+ strings[i] = gActiveMods.entries[i]->name;
+ }
+
+ // add seperator
+ char* sep = "\n\nMods:\n";
+ snprintf(buffer, bufferLength, "%s", sep);
+ buffer += strlen(sep);
+ bufferLength -= strlen(sep);
+
+ // concat mod strings
+ str_seperator_concat(buffer, bufferLength, strings, gActiveMods.entryCount, "\n");
+}
+
+void ns_coopnet_update(void) {
+ if (!coopnet_is_connected()) { return; }
+
+ coopnet_update();
+ if (gNetworkType != NT_NONE && sNetworkType != NT_NONE) {
+ if (sNetworkType == NT_SERVER) {
+ char mode[64] = "";
+ mods_get_main_mod_name(mode, 64);
+ if (sReconnecting) {
+ LOG_INFO("Update lobby");
+ coopnet_populate_description();
+ coopnet_lobby_update(sLocalLobbyId, CN_GAME_STR, get_version(), configPlayerName, mode, sCoopNetDescription);
+ } else {
+ LOG_INFO("Create lobby");
+ snprintf(gCoopNetPassword, 64, "%s", configPassword);
+ coopnet_populate_description();
+ coopnet_lobby_create(CN_GAME_STR, get_version(), configPlayerName, mode, (uint16_t)configAmountofPlayers, gCoopNetPassword, sCoopNetDescription);
+ }
+ } else if (sNetworkType == NT_CLIENT) {
+ LOG_INFO("Join lobby");
+ coopnet_lobby_join(gCoopNetDesiredLobby, gCoopNetPassword);
+ }
+ sNetworkType = NT_NONE;
+ }
+}
+
+static int ns_coopnet_network_send(u8 localIndex, void* address, u8* data, u16 dataLength) {
+ if (!coopnet_is_connected()) { return 1; }
+ //if (gCurLobbyId == 0) { return 2; }
+ u64 userId = coopnet_raw_get_id(localIndex);
+ if (localIndex == 0 && address != NULL) { userId = *(u64*)address; }
+ coopnet_send_to(userId, data, dataLength);
+
+ return 0;
+}
+
+static bool coopnet_allow_invite(void) {
+ if (sLocalLobbyId == 0) { return false; }
+ return (sLocalLobbyOwnerId == coopnet_get_local_user_id()) || (strlen(gCoopNetPassword) == 0);
+}
+
+static void ns_coopnet_get_lobby_id(UNUSED char* destination, UNUSED u32 destLength) {
+ if (sLocalLobbyId == 0) {
+ snprintf(destination, destLength, "%s", "");
+ } else {
+ snprintf(destination, destLength, "coopnet:%" PRIu64 "", sLocalLobbyId);
+ }
+}
+
+static void ns_coopnet_get_lobby_secret(UNUSED char* destination, UNUSED u32 destLength) {
+ if (sLocalLobbyId == 0 || !coopnet_allow_invite()) {
+ snprintf(destination, destLength, "%s", "");
+ } else {
+ snprintf(destination, destLength, "coopnet:%" PRIu64":%s", sLocalLobbyId, gCoopNetPassword);
+ }
+}
+
+static void ns_coopnet_shutdown(bool reconnecting) {
+ if (reconnecting) { return; }
+ LOG_INFO("Coopnet shutdown!");
+ coopnet_shutdown();
+ gCoopNetCallbacks.OnLobbyListGot = NULL;
+ gCoopNetCallbacks.OnLobbyListFinish = NULL;
+
+ sLocalLobbyId = 0;
+ sLocalLobbyOwnerId = 0;
+}
+
+static CoopNetRc coopnet_initialize(void) {
+ gCoopNetCallbacks.OnConnected = coopnet_on_connected;
+ gCoopNetCallbacks.OnDisconnected = coopnet_on_disconnected;
+ gCoopNetCallbacks.OnReceive = coopnet_on_receive;
+ gCoopNetCallbacks.OnLobbyJoined = coopnet_on_lobby_joined;
+ gCoopNetCallbacks.OnLobbyLeft = coopnet_on_lobby_left;
+ gCoopNetCallbacks.OnError = coopnet_on_error;
+ gCoopNetCallbacks.OnPeerDisconnected = coopnet_on_peer_disconnected;
+
+ if (coopnet_is_connected()) { return COOPNET_OK; }
+
+ CoopNetRc rc = coopnet_begin(configCoopNetIp, configCoopNetPort);
+ if (rc == COOPNET_FAILED) {
+ djui_popup_create(DLANG(NOTIF, COOPNET_CONNECTION_FAILED), 2);
+ }
+ return rc;
+}
+
+struct NetworkSystem gNetworkSystemCoopNet = {
+ .initialize = ns_coopnet_initialize,
+ .get_id = ns_coopnet_get_id,
+ .get_id_str = ns_coopnet_get_id_str,
+ .save_id = ns_coopnet_save_id,
+ .clear_id = ns_coopnet_clear_id,
+ .dup_addr = ns_coopnet_dup_addr,
+ .match_addr = ns_coopnet_match_addr,
+ .update = ns_coopnet_update,
+ .send = ns_coopnet_network_send,
+ .get_lobby_id = ns_coopnet_get_lobby_id,
+ .get_lobby_secret = ns_coopnet_get_lobby_secret,
+ .shutdown = ns_coopnet_shutdown,
+ .requireServerBroadcast = false,
+ .name = "CoopNet",
+};
+
+#endif
diff --git a/src/pc/network/coopnet/coopnet.h b/src/pc/network/coopnet/coopnet.h
new file mode 100644
index 00000000..ec094c7d
--- /dev/null
+++ b/src/pc/network/coopnet/coopnet.h
@@ -0,0 +1,17 @@
+#ifndef COOPNET_H
+#define COOPNET_H
+#ifdef COOPNET
+
+typedef void (*QueryCallbackPtr)(uint64_t aLobbyId, uint64_t aOwnerId, uint16_t aConnections, uint16_t aMaxConnections, const char* aGame, const char* aVersion, const char* aHostName, const char* aMode, const char* aDescription);
+typedef void (*QueryFinishCallbackPtr)(void);
+
+extern struct NetworkSystem gNetworkSystemCoopNet;
+extern uint64_t gCoopNetDesiredLobby;
+extern char gCoopNetPassword[];
+
+bool ns_coopnet_query(QueryCallbackPtr callback, QueryFinishCallbackPtr finishCallback, const char* password);
+bool ns_coopnet_is_connected(void);
+void ns_coopnet_update(void);
+
+#endif
+#endif
\ No newline at end of file
diff --git a/src/pc/network/coopnet/coopnet_id.c b/src/pc/network/coopnet/coopnet_id.c
new file mode 100644
index 00000000..ea254ae2
--- /dev/null
+++ b/src/pc/network/coopnet/coopnet_id.c
@@ -0,0 +1,103 @@
+#include "libcoopnet.h"
+#include "coopnet.h"
+#include "pc/network/network.h"
+#include "pc/debuglog.h"
+
+static uint64_t sLocalUserId = 0;
+static uint64_t sNetworkUserIds[MAX_PLAYERS] = { 0 };
+
+#define MAX_DEST_IDS (MAX_PLAYERS * 2)
+struct DestinationId {
+ uint64_t userId;
+ uint64_t destId;
+};
+struct DestinationId sDestinationIds[MAX_DEST_IDS] = { 0 };
+
+void coopnet_save_dest_id(uint64_t userId, uint64_t destId) {
+ struct DestinationId* dest = NULL;
+ for (int i = 0; i < MAX_DEST_IDS; i++) {
+ if (sDestinationIds[i].userId == userId) {
+ sDestinationIds[i].destId = destId;
+ return;
+ } else if (dest == NULL && sDestinationIds[i].userId == 0) {
+ dest = &sDestinationIds[i];
+ }
+ }
+ if (dest) {
+ dest->userId = userId;
+ dest->destId = destId;
+ }
+}
+
+void coopnet_clear_dest_id(uint64_t userId) {
+ for (int i = 0; i < MAX_DEST_IDS; i++) {
+ if (sDestinationIds[i].userId == userId) {
+ sDestinationIds[i].userId = 0;
+ sDestinationIds[i].destId = 0;
+ }
+ }
+}
+
+void coopnet_clear_dest_ids(void) {
+ for (int i = 0; i < MAX_DEST_IDS; i++) {
+ sDestinationIds[i].userId = 0;
+ sDestinationIds[i].destId = 0;
+ }
+}
+
+uint64_t coopnet_get_dest_id(uint64_t userId) {
+ for (int i = 0; i < MAX_DEST_IDS; i++) {
+ if (sDestinationIds[i].userId == userId) {
+ return sDestinationIds[i].destId;
+ }
+ }
+ return 0;
+}
+
+u8 coopnet_user_id_to_local_index(uint64_t userId) {
+ for (int i = 1; i < MAX_PLAYERS; i++) {
+ if (gNetworkPlayers[i].connected && sNetworkUserIds[i] == userId) {
+ return i;
+ }
+ }
+ return UNKNOWN_LOCAL_INDEX;
+}
+
+void coopnet_set_user_id(uint8_t localIndex, uint64_t userId) {
+ sNetworkUserIds[localIndex] = userId;
+}
+
+uint64_t coopnet_get_local_user_id(void) {
+ return sLocalUserId;
+}
+
+void coopnet_set_local_user_id(uint64_t userId) {
+ sLocalUserId = userId;
+}
+
+s64 coopnet_raw_get_id(u8 localIndex) {
+ return (s64)sNetworkUserIds[localIndex];
+}
+
+s64 ns_coopnet_get_id(u8 localIndex) {
+ if (localIndex == 0) { return (s64)sLocalUserId; }
+ return (s64)sNetworkUserIds[localIndex];
+}
+
+void ns_coopnet_save_id(u8 localIndex, s64 networkId) {
+ SOFT_ASSERT(localIndex > 0);
+ SOFT_ASSERT(localIndex < MAX_PLAYERS);
+ sNetworkUserIds[localIndex] = (networkId == 0) ? sNetworkUserIds[0] : (u64)networkId;
+}
+
+void ns_coopnet_clear_id(u8 localIndex) {
+ if (localIndex == 0) { return; }
+ SOFT_ASSERT(localIndex < MAX_PLAYERS);
+ sNetworkUserIds[localIndex] = 0;
+}
+
+void* ns_coopnet_dup_addr(u8 localIndex) {
+ void* address = malloc(sizeof(u64));
+ memcpy(address, &sNetworkUserIds[localIndex], sizeof(u64));
+ return address;
+}
diff --git a/src/pc/network/coopnet/coopnet_id.h b/src/pc/network/coopnet/coopnet_id.h
new file mode 100644
index 00000000..df5c7c4e
--- /dev/null
+++ b/src/pc/network/coopnet/coopnet_id.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "PR/ultratypes.h"
+
+void coopnet_save_dest_id(uint64_t userId, uint64_t destId);
+void coopnet_clear_dest_id(uint64_t userId);
+void coopnet_clear_dest_ids(void);
+uint64_t coopnet_get_dest_id(uint64_t userId);
+
+u8 coopnet_user_id_to_local_index(uint64_t userId);
+void coopnet_set_user_id(uint8_t localIndex, uint64_t userId);
+uint64_t coopnet_get_local_user_id(void);
+void coopnet_set_local_user_id(uint64_t userId);
+
+s64 coopnet_raw_get_id(u8 localIndex);
+s64 ns_coopnet_get_id(u8 localIndex);
+void ns_coopnet_save_id(u8 localIndex, s64 networkId);
+void ns_coopnet_clear_id(u8 localIndex);
+void* ns_coopnet_dup_addr(u8 localIndex);
\ No newline at end of file
diff --git a/src/pc/network/discord/activity.c b/src/pc/network/discord/activity.c
deleted file mode 100644
index e787927c..00000000
--- a/src/pc/network/discord/activity.c
+++ /dev/null
@@ -1,171 +0,0 @@
-#include "activity.h"
-#include "lobby.h"
-#include "discord_network.h"
-#include "pc/network/network.h"
-#include "pc/network/version.h"
-#include "pc/djui/djui.h"
-#include "pc/mods/mods.h"
-#include "pc/logfile.h"
-
-#define HASH_LENGTH 8
-struct DiscordActivity gCurActivity = { 0 };
-bool gActivityLock = false;
-
-static void on_activity_update_callback(UNUSED void* data, enum EDiscordResult result) {
- LOGFILE_INFO(LFT_DISCORD, "> 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) {
- gActivityLock = false;
- LOGFILE_INFO(LFT_DISCORD, "> on_activity_join_callback returned %d, lobby " DISCORD_ID_FORMAT ", owner " DISCORD_ID_FORMAT, result, lobby->id, lobby->owner_id);
- DISCORD_REQUIRE(result);
- if (gNetworkType != NT_NONE) {
- LOGFILE_ERROR(LFT_DISCORD, "Joined lobby when already connected somewhere!");
- return;
- }
- network_reset_reconnect_and_rehost();
- network_init(NT_CLIENT);
-
- gCurActivity.type = DiscordActivityType_Playing;
- if (snprintf(gCurActivity.party.id, 128, DISCORD_ID_FORMAT, lobby->id) < 0) {
- LOGFILE_ERROR(LFT_DISCORD, "Truncating party 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);
-
- if (gNetworkPlayerServer == NULL) {
- network_player_connected(NPT_SERVER, 0, 0, &DEFAULT_MARIO_PALETTE, "Player");
- }
- ns_discord_save_id(gNetworkPlayerServer->localIndex, lobby->owner_id);
- network_send_mod_list_request();
-
- gNetworkUserIds[0] = lobby->owner_id;
-}
-
-static void on_activity_join(UNUSED void* data, const char* secret) {
- LOGFILE_INFO(LFT_DISCORD, "> on_activity_join, secret: %s", secret);
- if (gActivityLock) { return; }
- gActivityLock = true;
- djui_connect_menu_open();
- 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) {
- LOGFILE_INFO(LFT_DISCORD, "> on_activity_join_request_callback returned %d", (int)result);
- DISCORD_REQUIRE(result);
-}
-
-static void on_activity_join_request(UNUSED void* data, struct DiscordUser* user) {
- LOGFILE_INFO(LFT_DISCORD, "> on_activity_join_request from " DISCORD_ID_FORMAT, user->id);
- //app.activities->send_request_reply(app.activities, user->id, DiscordActivityJoinRequestReply_Yes, NULL, on_activity_join_request_callback);
-}
-
-static void strncat_len(char* destination, char* source, size_t destinationLength, size_t sourceLength) {
- char altered[128] = { 0 };
- snprintf(altered, (sourceLength < 127) ? sourceLength : 127, "%s", source);
- strncat(destination, altered, destinationLength);
-}
-
-static bool discord_populate_details(char* details, bool shorten) {
- snprintf(details, 127, "%s", get_version());
-
- bool displayDash = true;
- bool displayComma = false;
- size_t catLength = shorten ? 14 : 64;
-
- if (gRegisteredMods.string != NULL) {
- strncat_len(details, " - ", 127, catLength);
- displayDash = false;
-
- // add patches to activity
- struct StringLinkedList* node = &gRegisteredMods;
- while (node != NULL && node->string != NULL) {
- if (displayComma) { strncat_len(details, ", ", 127, catLength); }
- strncat_len(details, node->string, 127, catLength);
- displayComma = true;
- node = node->next;
- }
- }
-
- if (gActiveMods.entryCount > 0) {
- // add mods to activity
- for (int i = 0; i < gActiveMods.entryCount; i++) {
- struct Mod* mod = gActiveMods.entries[i];
- if (displayDash) { strncat_len(details, " - ", 127, catLength); }
- if (displayComma) { strncat_len(details, ", ", 127, catLength); }
-
- strncat_len(details, mod->name, 127, catLength);
-
- displayDash = false;
- displayComma = true;
- }
- }
-
- return (strlen(details) >= 125);
-}
-
-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 players...");
- } else {
- strcpy(gCurActivity.state, "In-game.");
- gCurActivity.party.size.current_size = 1;
- if (gCurActivity.party.size.max_size < 1) { gCurActivity.party.size.max_size = 1; }
- }
-
- char details[256] = { 0 };
- bool overrun = discord_populate_details(details, false);
- if (overrun) {
- discord_populate_details(details, true);
- }
-
- if (snprintf(gCurActivity.details, 125, "%s", details) < 0) {
- LOGFILE_INFO(LFT_DISCORD, "truncating details");
- }
-
- if (!app.activities) {
- LOGFILE_INFO(LFT_DISCORD, "no activities");
- return;
- }
-
- if (!app.activities->update_activity) {
- LOGFILE_INFO(LFT_DISCORD, "no update_activity");
- return;
- }
-
- app.activities->update_activity(app.activities, &gCurActivity, NULL, on_activity_update_callback);
- LOGFILE_INFO(LFT_DISCORD, "set activity");
-}
-
-void discord_activity_update_check(void) {
- if (gNetworkType == NT_NONE) { return; }
- bool shouldUpdate = false;
- u8 connectedCount = network_player_connected_count();
-
- if (connectedCount > 0) {
- if (connectedCount != gCurActivity.party.size.current_size) {
- gCurActivity.party.size.current_size = connectedCount;
- shouldUpdate = true;
- }
- }
-
- if (shouldUpdate) {
- discord_activity_update(gNetworkType == NT_SERVER);
- }
-}
-
-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
deleted file mode 100644
index 43eb25a1..00000000
--- a/src/pc/network/discord/activity.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef DISCORD_ACTIVITY_H
-#define DISCORD_ACTIVITY_H
-#include "discord.h"
-
-extern struct DiscordActivity gCurActivity;
-
-void discord_activity_update(bool hosting);
-void discord_activity_update_check(void);
-struct IDiscordActivityEvents* discord_activity_initialize(void);
-extern bool gActivityLock;
-
-#endif
\ No newline at end of file
diff --git a/src/pc/network/discord/discord.c b/src/pc/network/discord/discord.c
deleted file mode 100644
index 0311d760..00000000
--- a/src/pc/network/discord/discord.c
+++ /dev/null
@@ -1,203 +0,0 @@
-#include "discord.h"
-#include "user.h"
-#include "activity.h"
-#include "lobby.h"
-#include "discord_network.h"
-#include "pc/network/version.h"
-#include "pc/djui/djui.h"
-#include "pc/logfile.h"
-
-#if defined(_WIN32) || defined(_WIN64)
-#include
-#include
-#else
-#include
-#define MAX_PATH 1024
-#endif
-
-#define MAX_LAUNCH_CMD (MAX_PATH + 12)
-
-static int64_t applicationId = 752700005210390568;
-struct DiscordApplication app = { 0 };
-bool gDiscordInitialized = false;
-bool gDiscordFailed = false;
-bool alreadyRun = false;
-
-static void discord_sdk_log_callback(UNUSED void* hook_data, enum EDiscordLogLevel level, const char* message) {
- LOGFILE_INFO(LFT_DISCORD, "callback (%d): %s", level, message);
-}
-
-void discord_fatal_message(int rc) { // Discord usually does this because of loss of connection to Discord
- char errorMessage[132] = { 0 };
- snprintf(errorMessage, 132, "%s\nRC: %d", DLANG(NOTIF, DISCORD_ERROR), rc);
- djui_popup_create(errorMessage, 6);
-}
-
-void discord_fatal(int rc) {
- if (!alreadyRun) {
- discord_fatal_message(rc);
- alreadyRun = true;
- }
-
- if (rc != DiscordResult_Ok) {
- LOG_ERROR("Discord threw an error. RC: %d", rc);
- }
-}
-
-static void set_instance_env_variable(void) {
- // set local instance id
- char environmentVariables[128] = { 0 };
- int instance = (gCLIOpts.Discord == 0) ? 0 : (gCLIOpts.Discord - 1);
- snprintf(environmentVariables, 128, "DISCORD_INSTANCE_ID=%d", instance);
- putenv(environmentVariables);
- LOGFILE_INFO(LFT_DISCORD, "set environment variables: %s", environmentVariables);
-}
-
-static void get_oauth2_token_callback(UNUSED void* data, enum EDiscordResult result, struct DiscordOAuth2Token* token) {
- LOGFILE_INFO(LFT_DISCORD, "> get_oauth2_token_callback returned %d", result);
- if (result != DiscordResult_Ok) { return; }
- LOGFILE_INFO(LFT_DISCORD, "OAuth2 token: %s", token->access_token);
-}
-
-static void register_launch_command(void) {
- char cmd[MAX_LAUNCH_CMD] = { 0 };
- int rc;
-#if defined(_WIN32) || defined(_WIN64)
- HMODULE hModule = GetModuleHandle(NULL);
- if (hModule == NULL) {
- LOGFILE_ERROR(LFT_DISCORD, "unable to retrieve absolute path!");
- return;
- }
- GetModuleFileName(hModule, cmd, sizeof(cmd));
-#else
- char path[MAX_LAUNCH_CMD] = { 0 };
- snprintf(path, MAX_LAUNCH_CMD - 1, "/proc/%d/exe", getpid());
- rc = readlink(path, cmd, MAX_LAUNCH_CMD - 1);
- if (rc <= 0) {
- LOGFILE_ERROR(LFT_DISCORD, "unable to retrieve absolute path! rc = %d", rc);
- return;
- }
-#endif
- strncat(cmd, " --discord 1", MAX_LAUNCH_CMD - 1);
- rc = app.activities->register_command(app.activities, cmd);
- if (rc != DiscordResult_Ok) {
- LOGFILE_ERROR(LFT_DISCORD, "register command failed %d", rc);
- return;
- }
- LOGFILE_INFO(LFT_DISCORD, "cmd: %s", cmd);
-}
-
-static void* ns_discord_dup_addr(u8 localIndex) {
- void* address = malloc(sizeof(DiscordUserId));
- memcpy(address, &gNetworkUserIds[localIndex], sizeof(DiscordUserId));
- return address;
-}
-
-static bool ns_discord_match_addr(void* addr1, void* addr2) {
- return !memcmp(addr1, addr2, sizeof(u64));
-}
-
-static void ns_discord_update(void) {
- if (!gDiscordInitialized) { return; }
-
- discord_activity_update_check();
- discord_lobby_update();
- DISCORD_REQUIRE(app.core->run_callbacks(app.core));
- discord_network_flush();
-}
-
-static bool ns_discord_initialize(enum NetworkType networkType) {
- if (gDiscordReconnecting) { return true; }
-#ifdef DEBUG
- set_instance_env_variable();
-#endif
-
- if (app.core != NULL) {
- app.core->set_log_hook(app.core, DiscordLogLevel_Debug, NULL, discord_sdk_log_callback);
- }
-
- if (!gDiscordInitialized) {
- // set up discord params
- struct DiscordCreateParams params = { 0 };
- 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();
-
- gCurLobbyId = 0;
- gLobbyCreateRetry = false;
- gLobbyCreateAttempts = 0;
- gLobbyCreateAttemptElapsed = 0;
-
- int rc = DiscordCreate(DISCORD_VERSION, ¶ms, &app.core);
- if (app.core != NULL) {
- app.core->set_log_hook(app.core, DiscordLogLevel_Debug, NULL, discord_sdk_log_callback);
- }
- gDiscordFailed = false;
- if (networkType != NT_NONE) {
- DISCORD_REQUIRE(rc);
- } else if (rc) {
- LOGFILE_ERROR(LFT_DISCORD, "DiscordCreate failed: %d", rc);
- djui_popup_create(DLANG(NOTIF, DISCORD_DETECT), 3);
- gDiscordFailed = true;
- return false;
- }
-
- // set up manager pointers
- if (app.core != NULL) {
- 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();
- gActivityLock = true;
- } else {
- gActivityLock = false;
- }
-
- gDiscordInitialized = true;
- LOGFILE_INFO(LFT_DISCORD, "initialized");
-
- return true;
-}
-
-static void ns_discord_shutdown(void) {
- if (gDiscordReconnecting) { return; }
- if (!gDiscordInitialized) { return; }
- discord_lobby_leave();
- gActivityLock = false;
- LOGFILE_INFO(LFT_DISCORD, "shutdown");
-}
-
-struct NetworkSystem gNetworkSystemDiscord = {
- .initialize = ns_discord_initialize,
- .get_id = ns_discord_get_id,
- .get_id_str = ns_discord_get_id_str,
- .save_id = ns_discord_save_id,
- .clear_id = ns_discord_clear_id,
- .dup_addr = ns_discord_dup_addr,
- .match_addr = ns_discord_match_addr,
- .update = ns_discord_update,
- .send = ns_discord_network_send,
- .shutdown = ns_discord_shutdown,
- .requireServerBroadcast = false,
- .name = "Discord",
-};
diff --git a/src/pc/network/discord/discord_network.c b/src/pc/network/discord/discord_network.c
deleted file mode 100644
index 381b3950..00000000
--- a/src/pc/network/discord/discord_network.c
+++ /dev/null
@@ -1,84 +0,0 @@
-#include "discord_network.h"
-#include "lobby.h"
-#include "pc/logfile.h"
-
-int64_t gNetworkUserIds[MAX_PLAYERS] = { 0 };
-
-u8 discord_user_id_to_local_index(int64_t userId) {
- for (int i = 1; i < MAX_PLAYERS; i++) {
- if (gNetworkPlayers[i].connected && gNetworkUserIds[i] == userId) {
- return i;
- }
- }
- return UNKNOWN_LOCAL_INDEX;
-}
-
-int ns_discord_network_send(u8 localIndex, void* address, u8* data, u16 dataLength) {
- if (!gDiscordInitialized) { return 1; }
- if (gCurLobbyId == 0) { return 2; }
- DiscordUserId userId = gNetworkUserIds[localIndex];
- if (localIndex == 0 && address != NULL) { userId = *(DiscordUserId*)address; }
- DISCORD_REQUIRE(app.lobbies->send_network_message(app.lobbies, gCurLobbyId, userId, 0, data, dataLength));
- return 0;
-}
-
-void discord_network_on_message(UNUSED void* eventData, UNUSED int64_t lobbyId, int64_t userId, UNUSED uint8_t channelId, uint8_t* data, uint32_t dataLength) {
- gNetworkUserIds[0] = userId;
-
- u8 localIndex = UNKNOWN_LOCAL_INDEX;
- for (int i = 1; i < MAX_PLAYERS; i++) {
- if (gNetworkUserIds[i] == userId) {
- localIndex = i;
- break;
- }
- }
-
- network_receive(localIndex, &userId, (u8*)data, (u16)dataLength);
-}
-
-void discord_network_flush(void) {
- app.lobbies->flush_network(app.lobbies);
-}
-
-s64 ns_discord_get_id(u8 localId) {
- if (localId == 0) { return app.userId; }
- return gNetworkUserIds[localId];
-}
-
-char* ns_discord_get_id_str(u8 localId) {
- static char id_str[22] = { 0 };
- if (localId == UNKNOWN_LOCAL_INDEX) {
- snprintf(id_str, 22, "???");
- } else {
- snprintf(id_str, 22, "%lld", (long long int)ns_discord_get_id(localId));
- }
- return id_str;
-}
-
-void ns_discord_save_id(u8 localId, s64 networkId) {
- SOFT_ASSERT(localId > 0);
- SOFT_ASSERT(localId < MAX_PLAYERS);
- gNetworkUserIds[localId] = (networkId == 0) ? gNetworkUserIds[0] : networkId;
- LOGFILE_INFO(LFT_DISCORD, "saved user id %d == " DISCORD_ID_FORMAT, localId, gNetworkUserIds[localId]);
-}
-
-void ns_discord_clear_id(u8 localId) {
- if (localId == 0) { return; }
- SOFT_ASSERT(localId < MAX_PLAYERS);
- gNetworkUserIds[localId] = 0;
- LOGFILE_INFO(LFT_DISCORD, "cleared user id %d == " DISCORD_ID_FORMAT, localId, gNetworkUserIds[localId]);
-}
-
-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));
- LOGFILE_INFO(LFT_DISCORD, "network initialized");
-}
-
-void discord_network_shutdown(void) {
- if (gCurLobbyId == 0) { return; }
- app.lobbies->flush_network(app.lobbies);
- app.lobbies->disconnect_network(app.lobbies, gCurLobbyId);
- app.lobbies->flush_network(app.lobbies);
- LOGFILE_INFO(LFT_DISCORD, "shutdown network, lobby = " DISCORD_ID_FORMAT, 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
deleted file mode 100644
index a4acb13b..00000000
--- a/src/pc/network/discord/discord_network.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef DISCORD_NETWORK_H
-#define DISCORD_NETWORK_H
-#include "discord.h"
-
-extern int64_t gNetworkUserIds[MAX_PLAYERS];
-
-u8 discord_user_id_to_local_index(int64_t userId);
-int ns_discord_network_send(u8 localIndex, void* addr, 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);
-s64 ns_discord_get_id(u8 localId);
-char* ns_discord_get_id_str(u8 localId);
-void ns_discord_save_id(u8 localId, s64 networkId);
-void ns_discord_clear_id(u8 localId);
-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
deleted file mode 100644
index f99cf533..00000000
--- a/src/pc/network/discord/lobby.c
+++ /dev/null
@@ -1,137 +0,0 @@
-#include "lobby.h"
-#include "activity.h"
-#include "discord_network.h"
-#include "pc/logfile.h"
-#include "pc/utils/misc.h"
-#include "pc/configfile.h"
-
-#define MAX_LOBBY_RETRY 5
-#define MAX_LOBBY_RETRY_WAIT_TIME 6
-
-static bool isHosting = false;
-DiscordLobbyId gCurLobbyId = 0;
-
-bool gLobbyCreateRetry = false;
-u8 gLobbyCreateAttempts = 0;
-f32 gLobbyCreateAttemptElapsed = 0;
-
-void discord_lobby_update(void) {
- if (gCurLobbyId != 0) { return; }
- if (!gLobbyCreateRetry) { return; }
- f32 timeUntilRetry = (clock_elapsed() - gLobbyCreateAttemptElapsed);
- if (timeUntilRetry < MAX_LOBBY_RETRY_WAIT_TIME) { return; }
- gLobbyCreateRetry = false;
- discord_lobby_create();
-}
-
-static void on_lobby_create_callback(UNUSED void* data, enum EDiscordResult result, struct DiscordLobby* lobby) {
- LOGFILE_INFO(LFT_DISCORD, "> on_lobby_create returned %d", (int)result);
-
- if (result != DiscordResult_Ok && gLobbyCreateAttempts < MAX_LOBBY_RETRY) {
- LOGFILE_INFO(LFT_DISCORD, "rescheduling lobby creation");
- gLobbyCreateRetry = true;
- gLobbyCreateAttempts++;
- gLobbyCreateAttemptElapsed = clock_elapsed();
- return;
- }
-
- DISCORD_REQUIRE(result);
- LOGFILE_INFO(LFT_DISCORD, "Lobby id: " DISCORD_ID_FORMAT, lobby->id);
- LOGFILE_INFO(LFT_DISCORD, "Lobby type: %u", lobby->type);
- LOGFILE_INFO(LFT_DISCORD, "Lobby owner id: " DISCORD_ID_FORMAT, lobby->owner_id);
- LOGFILE_INFO(LFT_DISCORD, "Lobby secret: %s", lobby->secret);
- LOGFILE_INFO(LFT_DISCORD, "Lobby capacity: %u", lobby->capacity);
- LOGFILE_INFO(LFT_DISCORD, "Lobby locked: %d", lobby->locked);
-
- gCurActivity.type = DiscordActivityType_Playing;
- if (snprintf(gCurActivity.party.id, 128, DISCORD_ID_FORMAT, lobby->id) < 0) {
- LOGFILE_ERROR(LFT_DISCORD, "truncating party id");
- }
- gCurActivity.party.size.current_size = 1;
- gCurActivity.party.size.max_size = configAmountofPlayers;
-
- char secretJoin[128] = "";
- if (snprintf(secretJoin, 128, DISCORD_ID_FORMAT ":%s", lobby->id, lobby->secret) < 0) {
- LOGFILE_ERROR(LFT_DISCORD, "truncating 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) {
- LOGFILE_INFO(LFT_DISCORD, "> on_lobby_update id: " DISCORD_ID_FORMAT, lobbyId);
-}
-
-static void on_member_connect(UNUSED void* data, int64_t lobbyId, int64_t userId) {
- LOGFILE_INFO(LFT_DISCORD, "> on_member_connect lobby: " DISCORD_ID_FORMAT ", user: " DISCORD_ID_FORMAT, 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) {
- LOGFILE_INFO(LFT_DISCORD, "> on_member_update lobby: " DISCORD_ID_FORMAT ", user: " DISCORD_ID_FORMAT, lobbyId, userId);
-}
-
-static void on_member_disconnect(UNUSED void* data, int64_t lobbyId, int64_t userId) {
- LOGFILE_INFO(LFT_DISCORD, "> on_member_disconnect lobby: " DISCORD_ID_FORMAT ", user: " DISCORD_ID_FORMAT, lobbyId, userId);
- u8 localIndex = discord_user_id_to_local_index(userId);
- if (localIndex != UNKNOWN_LOCAL_INDEX && gNetworkPlayers[localIndex].connected) {
- network_player_disconnected(gNetworkPlayers[localIndex].globalIndex);
- }
- 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, MAX_PLAYERS);
- 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) {
- LOGFILE_INFO(LFT_DISCORD, "> on_lobby_leave returned %d", result);
- DISCORD_REQUIRE(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);
- }
-
- LOGFILE_INFO(LFT_DISCORD, "left lobby " DISCORD_ID_FORMAT, gCurLobbyId);
-
- if (snprintf(gCurActivity.party.id, 128, "%s", "none") < 0) {
- LOGFILE_ERROR(LFT_DISCORD, "Truncating party id");
- }
- gCurActivity.party.size.current_size = 1;
- gCurActivity.party.size.max_size = 1;
- discord_activity_update(gNetworkType == NT_SERVER);
-
- 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;
-}
diff --git a/src/pc/network/discord/lobby.h b/src/pc/network/discord/lobby.h
deleted file mode 100644
index 4e0d32d2..00000000
--- a/src/pc/network/discord/lobby.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#ifndef DISCORD_LOBBY_H
-#define DISCORD_LOBBY_H
-#include "discord.h"
-
-extern DiscordLobbyId gCurLobbyId;
-
-extern bool gLobbyCreateRetry;
-extern u8 gLobbyCreateAttempts;
-extern f32 gLobbyCreateAttemptElapsed;
-
-void discord_lobby_update(void);
-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
deleted file mode 100644
index bd49c427..00000000
--- a/src/pc/network/discord/user.c
+++ /dev/null
@@ -1,34 +0,0 @@
-#include "user.h"
-#include "pc/configfile.h"
-#include "pc/logfile.h"
-#include "pc/crash_handler.h"
-
-static void on_current_user_update(UNUSED void* data) {
- LOGFILE_INFO(LFT_DISCORD, "> on_current_user_update");
- struct DiscordUser user = { 0 };
- app.users->get_current_user(app.users, &user);
-
- // remember user id
- app.userId = user.id;
- gPcDebug.id = user.id;
-
- // copy over discord username if we haven't set one yet
- if (configPlayerName[0] == '\0' && strlen(user.username) > 0) {
- char* cname = configPlayerName;
- char* dname = user.username;
- for (int i = 0; i < MAX_PLAYER_STRING - 1; i++) {
- if (*dname >= '!' && *dname <= '~') {
- *cname = *dname;
- cname++;
- }
- dname++;
- }
- }
-}
-
-struct IDiscordUserEvents* discord_user_initialize(void) {
- LOGFILE_INFO(LFT_DISCORD, "> discord_user_intitialize");
- 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
deleted file mode 100644
index a3089192..00000000
--- a/src/pc/network/discord/user.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#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 ea5eedf5..1d268c98 100644
--- a/src/pc/network/network.c
+++ b/src/pc/network/network.c
@@ -1,15 +1,13 @@
#include "socket/socket.h"
+#include "coopnet/coopnet.h"
#include
#include "network.h"
#include "object_fields.h"
+#include "game/level_update.h"
#include "object_constants.h"
#include "behavior_table.h"
#include "src/game/hardcoded.h"
#include "src/game/scroll_targets.h"
-#ifdef DISCORD_SDK
-#include "discord/discord.h"
-#include "discord/activity.h"
-#endif
#include "pc/configfile.h"
#include "pc/cheats.h"
#include "pc/djui/djui.h"
@@ -30,6 +28,10 @@
#include "game/level_geo.h"
#include "menu/intro_geo.h"
+#ifdef DISCORD_SDK
+#include "pc/discord/discord.h"
+#endif
+
// fix warnings when including rendering_graph_node
#undef near
#undef far
@@ -40,11 +42,7 @@ extern s16 sCurrPlayMode;
extern s16 gCurrCourseNum, gCurrActStarNum, gCurrLevelNum, gCurrAreaIndex;
enum NetworkType gNetworkType = NT_NONE;
-#ifdef DISCORD_SDK
-struct NetworkSystem* gNetworkSystem = &gNetworkSystemDiscord;
-#else
struct NetworkSystem* gNetworkSystem = &gNetworkSystemSocket;
-#endif
#define LOADING_LEVEL_THRESHOLD 10
#define MAX_PACKETS_PER_SECOND_PER_PLAYER ((u16)100)
@@ -57,7 +55,6 @@ u32 gNetworkAreaTimer = 0;
void* gNetworkServerAddr = NULL;
bool gNetworkSentJoin = false;
u16 gNetworkRequestLocationTimer = 0;
-bool gDiscordReconnecting = false;
u8 gDebugPacketIdBuffer[256] = { 0xFF };
u8 gDebugPacketSentBuffer[256] = { 0 };
@@ -80,20 +77,22 @@ struct ServerSettings gServerSettings = {
.enablePlayersInLevelDisplay = 1,
.enablePlayerList = 1,
.headlessServer = 0,
+ .maxPlayers = MAX_PLAYERS,
};
void network_set_system(enum NetworkSystemType nsType) {
network_forget_all_reliable();
+
switch (nsType) {
case NS_SOCKET: gNetworkSystem = &gNetworkSystemSocket; break;
-#ifdef DISCORD_SDK
- case NS_DISCORD: gNetworkSystem = &gNetworkSystemDiscord; break;
+#ifdef COOPNET
+ case NS_COOPNET: gNetworkSystem = &gNetworkSystemCoopNet; break;
#endif
- default: LOG_ERROR("Unknown network system: %d", nsType);
+ default: gNetworkSystem = &gNetworkSystemSocket; LOG_ERROR("Unknown network system: %d", nsType); break;
}
}
-bool network_init(enum NetworkType inNetworkType) {
+bool network_init(enum NetworkType inNetworkType, bool reconnecting) {
// reset override hide hud
extern u8 gOverrideHideHud;
gOverrideHideHud = 0;
@@ -116,6 +115,7 @@ bool network_init(enum NetworkType inNetworkType) {
gServerSettings.shareLives = configShareLives;
gServerSettings.enableCheats = configEnableCheats;
gServerSettings.bubbleDeath = configBubbleDeath;
+ gServerSettings.maxPlayers = configAmountofPlayers;
#if defined(RAPI_DUMMY) || defined(WAPI_DUMMY)
gServerSettings.headlessServer = (inNetworkType == NT_SERVER);
#else
@@ -124,7 +124,7 @@ bool network_init(enum NetworkType inNetworkType) {
// initialize the network system
gNetworkSentJoin = false;
- int rc = gNetworkSystem->initialize(inNetworkType);
+ int rc = gNetworkSystem->initialize(inNetworkType, reconnecting);
if (!rc) {
LOG_ERROR("failed to initialize network system");
return false;
@@ -155,17 +155,15 @@ bool network_init(enum NetworkType inNetworkType) {
gChangeLevelTransition = gLevelValues.entryLevel;
}
-#ifdef DISCORD_SDK
- if (gNetworkSystem == &gNetworkSystemDiscord) {
- discord_activity_update(true);
- }
-#endif
-
djui_chat_box_create();
}
configfile_save(configfile_name());
+#ifdef DISCORD_SDK
+ discord_activity_update();
+#endif
+
LOG_INFO("initialized");
return true;
@@ -312,8 +310,15 @@ void network_send_to(u8 localIndex, struct Packet* p) {
if (p->keepSendingAfterDisconnect) {
localIndex = 0; // Force this type of packet to use the saved addr
}
- int rc = gNetworkSystem->send(localIndex, p->addr, p->buffer, p->cursor + sizeof(u32));
- if (rc == SOCKET_ERROR) { LOG_ERROR("send error %d", rc); return; }
+ u8* buffer = NULL;
+ u32 len = 0;
+ packet_compress(p, &buffer, &len);
+ if (!buffer || len == 0) {
+ LOG_ERROR("Failed to compress!");
+ } else {
+ int rc = gNetworkSystem->send(localIndex, p->addr, buffer, len);
+ if (rc == SOCKET_ERROR) { LOG_ERROR("send error %d", rc); return; }
+ }
}
p->sent = true;
@@ -378,14 +383,16 @@ void network_receive(u8 localIndex, void* addr, u8* data, u16 dataLength) {
.buffer = { 0 },
.dataLength = dataLength,
};
- memcpy(p.buffer, data, dataLength);
+ if (!packet_decompress(&p, data, dataLength)) {
+ LOG_ERROR("Failed to decompress!");
+ return;
+ }
if (localIndex != UNKNOWN_LOCAL_INDEX && localIndex != 0) {
gNetworkPlayers[localIndex].lastReceived = clock_elapsed();
}
// subtract and check hash
- p.dataLength -= sizeof(u32);
if (!packet_check_hash(&p)) {
LOG_ERROR("invalid packet hash!");
return;
@@ -407,7 +414,6 @@ void network_reset_reconnect_and_rehost(void) {
sNetworkReconnectTimer = 0;
sNetworkRehostTimer = 0;
sNetworkReconnectType = NS_SOCKET;
- gDiscordReconnecting = false;
}
void network_reconnect_begin(void) {
@@ -417,17 +423,15 @@ void network_reconnect_begin(void) {
sNetworkReconnectTimer = 2 * 30;
-#ifdef DISCORD_SDK
- sNetworkReconnectType = (gNetworkSystem == &gNetworkSystemDiscord)
- ? NS_DISCORD
+#ifdef COOPNET
+ sNetworkReconnectType = (gNetworkSystem == &gNetworkSystemCoopNet)
+ ? NS_COOPNET
: NS_SOCKET;
#else
sNetworkReconnectType = NS_SOCKET;
#endif
- gDiscordReconnecting = true;
- network_shutdown(false, false, false);
- gDiscordReconnecting = false;
+ network_shutdown(false, false, false, true);
djui_connect_menu_open();
}
@@ -438,11 +442,11 @@ static void network_reconnect_update(void) {
if (sNetworkReconnectType == NS_SOCKET) {
network_set_system(NS_SOCKET);
+ } else if (sNetworkReconnectType == NS_COOPNET) {
+ network_set_system(NS_COOPNET);
}
- gDiscordReconnecting = true;
- network_init(NT_CLIENT);
- gDiscordReconnecting = false;
+ network_init(NT_CLIENT, true);
network_send_mod_list_request();
}
@@ -460,21 +464,17 @@ void network_rehost_begin(void) {
network_player_disconnected(i);
}
- gDiscordReconnecting = true;
- network_shutdown(false, false, false);
- gDiscordReconnecting = false;
+ network_shutdown(false, false, false, true);
sNetworkRehostTimer = 2;
}
static void network_rehost_update(void) {
- extern void djui_panel_do_host(void);
+ extern void djui_panel_do_host(bool reconnecting);
if (sNetworkRehostTimer <= 0) { return; }
if (--sNetworkRehostTimer != 0) { return; }
- gDiscordReconnecting = true;
- djui_panel_do_host();
- gDiscordReconnecting = false;
+ djui_panel_do_host(true);
}
static void network_update_area_timer(void) {
@@ -509,8 +509,15 @@ static void network_update_area_timer(void) {
}
}
-void network_update(void) {
+#ifdef COOPNET
+void network_update_coopnet(void) {
+ if (gNetworkType != NT_NONE) { return; }
+ if (!ns_coopnet_is_connected()) { return; }
+ ns_coopnet_update();
+}
+#endif
+void network_update(void) {
if (gNetworkStartupTimer > 0) {
gNetworkStartupTimer--;
}
@@ -518,6 +525,10 @@ void network_update(void) {
network_rehost_update();
network_reconnect_update();
+#ifdef COOPNET
+ network_update_coopnet();
+#endif
+
// check for level loaded event
if (networkLoadingLevel < LOADING_LEVEL_THRESHOLD) {
networkLoadingLevel++;
@@ -585,7 +596,7 @@ void network_register_mod(char* modName) {
string_linked_list_append(&gRegisteredMods, modName);
}
-void network_shutdown(bool sendLeaving, bool exiting, bool popup) {
+void network_shutdown(bool sendLeaving, bool exiting, bool popup, bool reconnecting) {
if (gDjuiChatBox != NULL) {
djui_base_destroy(&gDjuiChatBox->base);
gDjuiChatBox = NULL;
@@ -599,7 +610,7 @@ void network_shutdown(bool sendLeaving, bool exiting, bool popup) {
if (gNetworkPlayerLocal != NULL && sendLeaving) { network_send_leaving(gNetworkPlayerLocal->globalIndex); }
network_player_shutdown(popup);
- gNetworkSystem->shutdown();
+ gNetworkSystem->shutdown(reconnecting);
if (gNetworkServerAddr != NULL) {
free(gNetworkServerAddr);
@@ -607,15 +618,10 @@ void network_shutdown(bool sendLeaving, bool exiting, bool popup) {
}
gNetworkPlayerServer = NULL;
- if (sNetworkReconnectTimer <= 0 || sNetworkReconnectType != NS_DISCORD) {
+ if (sNetworkReconnectTimer <= 0 || sNetworkReconnectType != NS_COOPNET) {
gNetworkType = NT_NONE;
}
-
-#ifdef DISCORD_SDK
- network_set_system(NS_DISCORD);
-#endif
-
if (exiting) { return; }
// reset other stuff
@@ -627,6 +633,9 @@ void network_shutdown(bool sendLeaving, bool exiting, bool popup) {
gOverrideNear = 0;
gOverrideFar = 0;
gOverrideFOV = 0;
+ gCurrActStarNum = 0;
+ gCurrActNum = 0;
+ gCurrCreditsEntry = NULL;
gLightingDir[0] = 0;
gLightingDir[1] = 0;
gLightingDir[2] = 0;
@@ -677,4 +686,9 @@ void network_shutdown(bool sendLeaving, bool exiting, bool popup) {
gDjuiInMainMenu = true;
djui_panel_main_create(NULL);
}
+
+#ifdef DISCORD_SDK
+ discord_activity_update();
+#endif
+ packet_ordered_clear_all();
}
diff --git a/src/pc/network/network.h b/src/pc/network/network.h
index b2f15471..7f12e95e 100644
--- a/src/pc/network/network.h
+++ b/src/pc/network/network.h
@@ -36,11 +36,12 @@ extern struct MarioState gMarioStates[];
enum NetworkSystemType {
NS_SOCKET,
- NS_DISCORD,
+ NS_COOPNET,
+ NS_MAX,
};
struct NetworkSystem {
- bool (*initialize)(enum NetworkType);
+ bool (*initialize)(enum NetworkType, bool reconnecting);
s64 (*get_id)(u8 localIndex);
char* (*get_id_str)(u8 localIndex);
void (*save_id)(u8 localIndex, s64 networkId);
@@ -49,7 +50,9 @@ struct NetworkSystem {
bool (*match_addr)(void* addr1, void* addr2);
void (*update)(void);
int (*send)(u8 localIndex, void* addr, u8* data, u16 dataLength);
- void (*shutdown)(void);
+ void (*get_lobby_id)(char* destination, u32 destLength);
+ void (*get_lobby_secret)(char* destination, u32 destLength);
+ void (*shutdown)(bool reconnecting);
bool requireServerBroadcast;
char* name;
};
@@ -71,6 +74,7 @@ struct ServerSettings {
u8 enablePlayersInLevelDisplay;
u8 enablePlayerList;
u8 headlessServer;
+ u8 maxPlayers;
};
// Networking-specific externs
@@ -89,11 +93,10 @@ extern u8 gDebugPacketIdBuffer[];
extern u8 gDebugPacketSentBuffer[];
extern u8 gDebugPacketOnBuffer;
extern u32 gNetworkStartupTimer;
-extern bool gDiscordReconnecting;
// network.c
void network_set_system(enum NetworkSystemType nsType);
-bool network_init(enum NetworkType inNetworkType);
+bool network_init(enum NetworkType inNetworkType, bool reconnecting);
void network_on_init_area(void);
void network_on_loaded_area(void);
bool network_allow_unknown_local_index(enum PacketType packetType);
@@ -107,6 +110,6 @@ bool network_is_reconnecting(void);
void network_rehost_begin(void);
void network_update(void);
void network_register_mod(char* modName);
-void network_shutdown(bool sendLeaving, bool exiting, bool popup);
+void network_shutdown(bool sendLeaving, bool exiting, bool popup, bool reconnecting);
#endif
diff --git a/src/pc/network/network_player.c b/src/pc/network/network_player.c
index 8bbcdd9a..db74aa9a 100644
--- a/src/pc/network/network_player.c
+++ b/src/pc/network/network_player.c
@@ -10,7 +10,11 @@
#include "game/hardcoded.h"
#include "game/object_helpers.h"
#include "pc/lua/smlua_hooks.h"
+#include "pc/network/socket/socket.h"
#include "lag_compensation.h"
+#ifdef DISCORD_SDK
+#include "pc/discord/discord.h"
+#endif
struct NetworkPlayer gNetworkPlayers[MAX_PLAYERS] = { 0 };
struct NetworkPlayer *gNetworkPlayerLocal = NULL;
@@ -176,13 +180,15 @@ void network_player_update(void) {
if (!np->connected && i > 0) { continue; }
float elapsed = (clock_elapsed() - np->lastReceived);
-#ifndef DEVELOPMENT
+#ifdef DEVELOPMENT
+ if (elapsed > NETWORK_PLAYER_TIMEOUT && (gNetworkSystem != &gNetworkSystemSocket)) {
+#else
if (elapsed > NETWORK_PLAYER_TIMEOUT) {
+#endif
LOG_INFO("dropping player %d", i);
network_player_disconnected(i);
continue;
}
-#endif
elapsed = (clock_elapsed() - np->lastSent);
if (elapsed > NETWORK_PLAYER_TIMEOUT / 3.0f) {
network_send_keep_alive(np->localIndex);
@@ -193,12 +199,14 @@ void network_player_update(void) {
if (!np->connected) { return; }
float elapsed = (clock_elapsed() - np->lastReceived);
-#ifndef DEVELOPMENT
+#ifdef DEVELOPMENT
+ if (elapsed > NETWORK_PLAYER_TIMEOUT * 1.5f && (gNetworkSystem != &gNetworkSystemSocket)) {
+#else
if (elapsed > NETWORK_PLAYER_TIMEOUT * 1.5f) {
- LOG_INFO("dropping due to no server connectivity");
- network_shutdown(false, false, true);
- }
#endif
+ LOG_INFO("dropping due to no server connectivity");
+ network_shutdown(false, false, true, false);
+ }
elapsed = (clock_elapsed() - np->lastSent);
if (elapsed > NETWORK_PLAYER_TIMEOUT / 3.0f) {
@@ -254,7 +262,7 @@ u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex, u8 mode
np->type = type;
np->localIndex = localIndex;
np->globalIndex = globalIndex;
- np->ping = 50;
+ np->ping = 600;
if ((type != NPT_LOCAL) && (gNetworkType == NT_SERVER || type == NPT_SERVER)) { gNetworkSystem->save_id(localIndex, 0); }
network_player_set_description(np, NULL, 0, 0, 0, 0);
@@ -291,7 +299,6 @@ u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex, u8 mode
}
for (s32 j = 0; j < MAX_RX_SEQ_IDS; j++) { np->rxSeqIds[j] = 0; np->rxPacketHash[j] = 0; }
- packet_ordered_clear(globalIndex);
// set up network player pointers
if (type == NPT_LOCAL) {
@@ -311,6 +318,9 @@ u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex, u8 mode
smlua_call_event_hooks_mario_param(HOOK_ON_PLAYER_CONNECTED, &gMarioStates[localIndex]);
+#ifdef DISCORD_SDK
+ discord_activity_update();
+#endif
return localIndex;
}
@@ -321,7 +331,7 @@ u8 network_player_disconnected(u8 globalIndex) {
LOG_ERROR("player disconnected, but it's local.. this shouldn't happen!");
return UNKNOWN_GLOBAL_INDEX;
} else {
- network_shutdown(true, false, true);
+ network_shutdown(true, false, true, false);
}
}
@@ -360,6 +370,10 @@ u8 network_player_disconnected(u8 globalIndex) {
memset(np, 0, sizeof(struct NetworkPlayer));
+#ifdef DISCORD_SDK
+ discord_activity_update();
+#endif
+
return i;
}
return UNKNOWN_GLOBAL_INDEX;
@@ -428,7 +442,7 @@ void network_player_update_course_level(struct NetworkPlayer* np, s16 courseNum,
}
// If this machine's player changed to a different location, then all of the other np locations are no longer valid
- for (u32 i = 1; i < configAmountofPlayers; i++) {
+ for (u32 i = 1; i < MAX_PLAYERS; i++) {
struct NetworkPlayer* npi = &gNetworkPlayers[i];
if ((!npi->connected) || npi == gNetworkPlayerLocal) { continue; }
npi->currPositionValid = false;
diff --git a/src/pc/network/network_player.h b/src/pc/network/network_player.h
index 3a365054..2013ec12 100644
--- a/src/pc/network/network_player.h
+++ b/src/pc/network/network_player.h
@@ -8,9 +8,9 @@
#define UNKNOWN_LOCAL_INDEX ((u8)-1)
#define UNKNOWN_GLOBAL_INDEX ((u8)-1)
#define UNKNOWN_NETWORK_INDEX ((u64)-1)
-#define NETWORK_PLAYER_TIMEOUT 10
-#define NETWORK_PLAYER_PING_TIMEOUT 1
-#define MAX_RX_SEQ_IDS 64
+#define NETWORK_PLAYER_TIMEOUT 15
+#define NETWORK_PLAYER_PING_TIMEOUT 3
+#define MAX_RX_SEQ_IDS 256
#define USE_REAL_PALETTE_VAR 0xFF
enum NetworkPlayerType {
diff --git a/src/pc/network/network_utils.c b/src/pc/network/network_utils.c
index 7be8fbc1..18b6b1ff 100644
--- a/src/pc/network/network_utils.c
+++ b/src/pc/network/network_utils.c
@@ -1,6 +1,5 @@
#include
#include "network_utils.h"
-#include "discord/discord.h"
#include "game/mario_misc.h"
u8 network_global_index_from_local(u8 localIndex) {
@@ -23,15 +22,6 @@ u8 network_local_index_from_global(u8 globalIndex) {
return globalIndex + ((globalIndex < gNetworkPlayerLocal->globalIndex) ? 1 : 0);
}
-#ifdef DISCORD_SDK
-char* network_discord_id_from_local_index(u8 localIndex) {
- if (gNetworkSystem == &gNetworkSystemDiscord) { return gNetworkSystem->get_id_str(localIndex); }
-#else
-char* network_discord_id_from_local_index(UNUSED u8 localIndex) {
-#endif
- return NULL;
-}
-
bool network_is_server(void) {
return gNetworkType == NT_SERVER;
}
diff --git a/src/pc/network/network_utils.h b/src/pc/network/network_utils.h
index f1eabed9..de7de04d 100644
--- a/src/pc/network/network_utils.h
+++ b/src/pc/network/network_utils.h
@@ -7,8 +7,6 @@
u8 network_global_index_from_local(u8 localIndex);
u8 network_local_index_from_global(u8 globalIndex);
-char* network_discord_id_from_local_index(u8 localIndex);
-
bool network_is_server(void);
bool network_is_moderator(void);
diff --git a/src/pc/network/packets/packet.c b/src/pc/network/packets/packet.c
index 0596f4f4..dcf8e403 100644
--- a/src/pc/network/packets/packet.c
+++ b/src/pc/network/packets/packet.c
@@ -1,8 +1,47 @@
#include
+#include
#include "../network.h"
#include "pc/network/ban_list.h"
#include "pc/debuglog.h"
+static u32 sCompBufferLen = 0;
+static Bytef* sCompBuffer = NULL;
+
+static void increase_comp_buffer(u32 compressedLen) {
+ if (compressedLen <= sCompBufferLen && sCompBuffer) { return; }
+
+ if (sCompBuffer != NULL) { free(sCompBuffer); }
+
+ sCompBufferLen = compressedLen;
+ sCompBuffer = (Bytef*)malloc(sCompBufferLen);
+}
+
+void packet_compress(struct Packet* p, u8** compBuffer, u32* compSize) {
+ uLong sourceSize = p->dataLength + sizeof(u32);
+ uLongf compressedLen = compressBound(sourceSize);
+ increase_comp_buffer(PACKET_LENGTH);
+
+ if (sCompBuffer && compress2((Bytef*)sCompBuffer, &compressedLen, (Bytef*)p->buffer, sourceSize, Z_BEST_COMPRESSION) == Z_OK) {
+ *compBuffer = sCompBuffer;
+ *compSize = compressedLen;
+ } else {
+ *compBuffer = NULL;
+ *compSize = 0;
+ }
+}
+
+bool packet_decompress(struct Packet* p, u8* compBuffer, u32 compSize) {
+ increase_comp_buffer(PACKET_LENGTH);
+ if (!sCompBuffer) { return false; }
+ uLong decompSize = PACKET_LENGTH;
+ if (uncompress((Bytef*)p->buffer, &decompSize, (Bytef*)compBuffer, compSize) == Z_OK) {
+ p->dataLength = decompSize - sizeof(u32);
+ return true;
+ } else {
+ return false;
+ }
+}
+
void packet_process(struct Packet* p) {
if (gNetworkType == NT_NONE) { return; }
if (p->levelAreaMustMatch) {
@@ -156,7 +195,7 @@ void packet_receive(struct Packet* p) {
np->rxSeqIds[np->onRxSeqId] = p->seqId;
np->rxPacketHash[np->onRxSeqId] = packetHash;
np->onRxSeqId++;
- if (np->onRxSeqId >= MAX_RX_SEQ_IDS) { np->onRxSeqId = 0; }
+ //if (np->onRxSeqId >= MAX_RX_SEQ_IDS) { np->onRxSeqId = 0; }
}
// parse the packet without processing the rest
diff --git a/src/pc/network/packets/packet.h b/src/pc/network/packets/packet.h
index c74d13e5..859ac620 100644
--- a/src/pc/network/packets/packet.h
+++ b/src/pc/network/packets/packet.h
@@ -102,8 +102,8 @@ struct Packet {
u16 seqId;
bool sent;
u8 orderedFromGlobalId;
- u8 orderedGroupId;
- u8 orderedSeqId;
+ u16 orderedGroupId;
+ u16 orderedSeqId;
u8 courseNum;
u8 actNum;
u8 levelNum;
@@ -147,7 +147,11 @@ struct LSTNetworkType {
size_t size;
};
+extern u8 gAllowOrderedPacketClear;
+
// packet.c
+void packet_compress(struct Packet* p, u8** compBuffer, u32* compSize);
+bool packet_decompress(struct Packet* p, u8* compBuffer, u32 compSize);
void packet_process(struct Packet* p);
void packet_receive(struct Packet* packet);
bool packet_spoofed(struct Packet* p, u8 globalIndex);
@@ -164,6 +168,7 @@ u32 packet_hash(struct Packet* packet);
bool packet_check_hash(struct Packet* packet);
void packet_ordered_begin(void);
void packet_ordered_end(void);
+void packet_ordered_clear_all(void);
void packet_set_ordered_data(struct Packet* packet);
// packet_reliable.c
@@ -176,7 +181,7 @@ void network_update_reliable(void);
// packet_ordered.c
void packet_ordered_add(struct Packet* p);
-void packet_ordered_clear_table(u8 globalIndex, u8 groupdId);
+void packet_ordered_clear_table(u8 globalIndex, u16 groupdId);
void packet_ordered_clear(u8 globalIndex);
void packet_ordered_update(void);
diff --git a/src/pc/network/packets/packet_join.c b/src/pc/network/packets/packet_join.c
index a171d232..4cc4f321 100644
--- a/src/pc/network/packets/packet_join.c
+++ b/src/pc/network/packets/packet_join.c
@@ -80,7 +80,7 @@ void network_send_join(struct Packet* joinRequestPacket) {
// figure out id
u8 globalIndex = joinRequestPacket->localIndex;
if (globalIndex == UNKNOWN_LOCAL_INDEX) {
- for (u32 i = 1; i < configAmountofPlayers; i++) {
+ for (u32 i = 1; i < MAX_PLAYERS; i++) {
if (!gNetworkPlayers[i].connected) {
globalIndex = i;
break;
@@ -119,6 +119,7 @@ void network_send_join(struct Packet* joinRequestPacket) {
packet_write(&p, &gServerSettings.enableCheats, sizeof(u8));
packet_write(&p, &gServerSettings.bubbleDeath, sizeof(u8));
packet_write(&p, &gServerSettings.headlessServer, sizeof(u8));
+ packet_write(&p, &gServerSettings.maxPlayers, sizeof(u8));
packet_write(&p, eeprom, sizeof(u8) * 512);
u8 modCount = string_linked_list_count(&gRegisteredMods);
@@ -165,7 +166,7 @@ void network_receive_join(struct Packet* p) {
packet_read(p, &remoteVersion, sizeof(u8) * MAX_VERSION_LENGTH);
LOG_INFO("server has version: %s", version);
if (memcmp(version, remoteVersion, MAX_VERSION_LENGTH) != 0) {
- network_shutdown(true, false, false);
+ network_shutdown(true, false, false, false);
LOG_ERROR("version mismatch");
char mismatchMessage[256] = { 0 };
snprintf(mismatchMessage, 256, "\\#ffa0a0\\Error:\\#c8c8c8\\ Version mismatch.\n\nYour version: \\#a0a0ff\\%s\\#c8c8c8\\\nTheir version: \\#a0a0ff\\%s\\#c8c8c8\\\n\nSomeone is out of date!\n", version, remoteVersion);
@@ -183,6 +184,7 @@ void network_receive_join(struct Packet* p) {
packet_read(p, &gServerSettings.enableCheats, sizeof(u8));
packet_read(p, &gServerSettings.bubbleDeath, sizeof(u8));
packet_read(p, &gServerSettings.headlessServer, sizeof(u8));
+ packet_read(p, &gServerSettings.maxPlayers, sizeof(u8));
packet_read(p, eeprom, sizeof(u8) * 512);
packet_read(p, &modCount, sizeof(u8));
@@ -196,7 +198,7 @@ void network_receive_join(struct Packet* p) {
}
if (string_linked_list_mismatch(&gRegisteredMods, &head)) {
- network_shutdown(true, false, false);
+ network_shutdown(true, false, false, false);
struct StringBuilder* builder = string_builder_create(512);
string_builder_append(builder, "\\#ffa0a0\\Error:\\#c8c8c8\\ mods don't match.\n\n");
@@ -253,4 +255,6 @@ void network_receive_join(struct Packet* p) {
smlua_call_event_hooks(HOOK_JOINED_GAME);
extern s16 gChangeLevel;
gChangeLevel = gLevelValues.entryLevel;
+
+ gAllowOrderedPacketClear = 1;
}
diff --git a/src/pc/network/packets/packet_kick.c b/src/pc/network/packets/packet_kick.c
index 5ead7675..9f2d006d 100644
--- a/src/pc/network/packets/packet_kick.c
+++ b/src/pc/network/packets/packet_kick.c
@@ -4,7 +4,8 @@
#include "pc/djui/djui.h"
#include "pc/utils/misc.h"
-f32 lastReconnectTime = -9999999;
+f32 sLastReconnectTime = -9999999;
+f32 sLastNotifyTime = -9999999;
void network_send_kick(u8 localIndex, enum KickReasonType kickReason) {
u8 kickReasonType = kickReason;
@@ -30,21 +31,24 @@ void network_receive_kick(struct Packet* p) {
packet_read(p, &kickReasonType, sizeof(u8));
enum KickReasonType kickReason = kickReasonType;
- switch (kickReason) {
- case EKT_FULL_PARTY: djui_popup_create(DLANG(NOTIF, DISCONNECT_FULL), 1); break;
- case EKT_KICKED: djui_popup_create(DLANG(NOTIF, DISCONNECT_KICK), 1); break;
- case EKT_BANNED: djui_popup_create(DLANG(NOTIF, DISCONNECT_BAN), 1); break;
- default: djui_popup_create(DLANG(NOTIF, DISCONNECT_CLOSED), 1); break;
+ f32 now = clock_elapsed();
+ if ((now - sLastNotifyTime) > 3) {
+ sLastNotifyTime = now;
+ switch (kickReason) {
+ case EKT_FULL_PARTY: djui_popup_create(DLANG(NOTIF, DISCONNECT_FULL), 1); break;
+ case EKT_KICKED: djui_popup_create(DLANG(NOTIF, DISCONNECT_KICK), 1); break;
+ case EKT_BANNED: djui_popup_create(DLANG(NOTIF, DISCONNECT_BAN), 1); break;
+ default: djui_popup_create(DLANG(NOTIF, DISCONNECT_CLOSED), 1); break;
+ }
}
if (kickReason == EKT_REJOIN) {
- f32 now = clock_elapsed();
- if ((now - lastReconnectTime) > 3) {
- lastReconnectTime = now;
+ if ((now - sLastReconnectTime) > 3) {
+ sLastReconnectTime = now;
network_reconnect_begin();
djui_popup_create(DLANG(NOTIF, DISCONNECT_REJOIN), 1);
}
} else {
- network_shutdown(false, false, false);
+ network_shutdown(false, false, false, false);
}
}
diff --git a/src/pc/network/packets/packet_mod_list.c b/src/pc/network/packets/packet_mod_list.c
index c5e0790d..9f71aecb 100644
--- a/src/pc/network/packets/packet_mod_list.c
+++ b/src/pc/network/packets/packet_mod_list.c
@@ -25,6 +25,7 @@ void network_send_mod_list_request(void) {
network_send_to(PACKET_DESTINATION_SERVER, &p);
LOG_INFO("sending mod list request");
+ gAllowOrderedPacketClear = 0;
}
void network_receive_mod_list_request(UNUSED struct Packet* p) {
@@ -140,7 +141,7 @@ void network_receive_mod_list(struct Packet* p) {
packet_read(p, &remoteVersion, sizeof(u8) * MAX_VERSION_LENGTH);
LOG_INFO("server has version: %s", version);
if (memcmp(version, remoteVersion, MAX_VERSION_LENGTH) != 0) {
- network_shutdown(true, false, false);
+ network_shutdown(true, false, false, false);
LOG_ERROR("version mismatch");
char mismatchMessage[256] = { 0 };
snprintf(mismatchMessage, 256, "\\#ffa0a0\\Error:\\#c8c8c8\\ Version mismatch.\n\nYour version: \\#a0a0ff\\%s\\#c8c8c8\\\nTheir version: \\#a0a0ff\\%s\\#c8c8c8\\\n\nSomeone is out of date!\n", version, remoteVersion);
@@ -241,7 +242,7 @@ void network_receive_mod_list_entry(struct Packet* p) {
// sanity check mod size
if (mod->size >= MAX_MOD_SIZE) {
djui_popup_create(DLANG(NOTIF, DISCONNECT_BIG_MOD), 4);
- network_shutdown(false, false, false);
+ network_shutdown(false, false, false, false);
return;
}
diff --git a/src/pc/network/packets/packet_ordered.c b/src/pc/network/packets/packet_ordered.c
index 92192bc6..13f75a02 100644
--- a/src/pc/network/packets/packet_ordered.c
+++ b/src/pc/network/packets/packet_ordered.c
@@ -1,10 +1,10 @@
#include
#include "../network.h"
#include "pc/utils/misc.h"
-#define DISABLE_MODULE_LOG 1
+//#define DISABLE_MODULE_LOG 1
#include "pc/debuglog.h"
-#define PACKET_ORDERED_TIMEOUT 15
+#define PACKET_ORDERED_TIMEOUT 30
struct OrderedPacketList {
struct Packet p;
@@ -13,14 +13,15 @@ struct OrderedPacketList {
struct OrderedPacketTable {
u8 fromGlobalId;
- u8 groupId;
- u8 processSeqId;
+ u16 groupId;
+ u16 processSeqId;
f32 lastReceived;
struct OrderedPacketList* packets;
struct OrderedPacketTable* next;
};
static struct OrderedPacketTable* orderedPacketTable[MAX_PLAYERS] = { 0 };
+u8 gAllowOrderedPacketClear = 1;
static void packet_ordered_check_for_processing(struct OrderedPacketTable* opt) {
// sanity check
@@ -159,7 +160,7 @@ void packet_ordered_add(struct Packet* p) {
packet_ordered_add_to_table(opt, p);
}
-void packet_ordered_clear_table(u8 globalIndex, u8 groupId) {
+void packet_ordered_clear_table(u8 globalIndex, u16 groupId) {
LOG_INFO("clearing out ordered packet table for %d (%d)", globalIndex, groupId);
struct OrderedPacketTable* opt = orderedPacketTable[globalIndex];
@@ -196,6 +197,11 @@ void packet_ordered_clear_table(u8 globalIndex, u8 groupId) {
}
void packet_ordered_clear(u8 globalIndex) {
+ if (!gAllowOrderedPacketClear) {
+ LOG_INFO("disallowed ordered packets to be cleared");
+ return;
+ }
+
LOG_INFO("clearing out all ordered packet tables for %d", globalIndex);
struct OrderedPacketTable* opt = orderedPacketTable[globalIndex];
@@ -219,6 +225,14 @@ void packet_ordered_clear(u8 globalIndex) {
orderedPacketTable[globalIndex] = NULL;
}
+
+void packet_ordered_clear_all(void) {
+ gAllowOrderedPacketClear = 1;
+ for (int i = 0; i < MAX_PLAYERS; i++) {
+ packet_ordered_clear(i);
+ }
+}
+
void packet_ordered_update(void) {
f32 currentClock = clock_elapsed();
// check all ordered tables for a time out
diff --git a/src/pc/network/packets/packet_ping.c b/src/pc/network/packets/packet_ping.c
index 2670648e..467538bb 100644
--- a/src/pc/network/packets/packet_ping.c
+++ b/src/pc/network/packets/packet_ping.c
@@ -14,11 +14,11 @@ void network_send_ping(struct NetworkPlayer* toNp) {
packet_write(&p, ×tamp, sizeof(f64));
network_send_to(toNp->localIndex, &p);
- //LOG_INFO("tx ping");
+ LOG_INFO("tx ping");
}
void network_receive_ping(struct Packet* p) {
- //LOG_INFO("rx ping");
+ LOG_INFO("rx ping");
u8 globalIndex;
f64 timestamp;
diff --git a/src/pc/network/packets/packet_player.c b/src/pc/network/packets/packet_player.c
index 66b88281..ade47861 100644
--- a/src/pc/network/packets/packet_player.c
+++ b/src/pc/network/packets/packet_player.c
@@ -403,9 +403,33 @@ void network_receive_player(struct Packet* p) {
void network_update_player(void) {
if (!network_player_any_connected()) { return; }
+ struct MarioState* m = &gMarioStates[0];
u8 localIsHeadless = (&gNetworkPlayers[0] == gNetworkPlayerServer && gServerSettings.headlessServer);
- if (!localIsHeadless) {
- network_send_player(0);
- }
+ if (localIsHeadless) { return; }
+
+ // figure out if we should send it or not
+ static u8 sTicksSinceSend = 0;
+ static u32 sLastPlayerAction = 0;
+ static f32 sLastStickX = 0;
+ static f32 sLastStickY = 0;
+ static u32 sLastButtonDown = 0;
+ static u32 sLastButtonPressed = 0;
+
+ f32 stickDist = sqrtf(powf(sLastStickX - m->controller->stickX, 2) + powf(sLastStickY - m->controller->stickY, 2));
+ bool shouldSend = (sTicksSinceSend > 2)
+ || (sLastPlayerAction != m->action)
+ || (sLastButtonDown != m->controller->buttonDown)
+ || (sLastButtonPressed != m->controller->buttonPressed)
+ || (stickDist > 5.0f);
+
+ if (!shouldSend) { sTicksSinceSend++; return; }
+ network_send_player(0);
+ sTicksSinceSend = 0;
+
+ sLastPlayerAction = m->action;
+ sLastStickX = m->controller->stickX;
+ sLastStickY = m->controller->stickY;
+ sLastButtonDown = m->controller->buttonDown;
+ sLastButtonPressed = m->controller->buttonPressed;
}
diff --git a/src/pc/network/packets/packet_read_write.c b/src/pc/network/packets/packet_read_write.c
index f6169f0f..613acd98 100644
--- a/src/pc/network/packets/packet_read_write.c
+++ b/src/pc/network/packets/packet_read_write.c
@@ -4,13 +4,13 @@
#define PACKET_FLAG_BUFFER_OFFSET 3
#define PACKET_DESTINATION_BUFFER_OFFSET 4
-#define PACKET_ORDERED_SEQ_ID_OFFSET 7
+#define PACKET_ORDERED_SEQ_ID_OFFSET 8
static u16 sNextSeqNum = 1;
static bool sOrderedPackets = false;
-static u8 sCurrentOrderedGroupId = 0;
-static u8 sCurrentOrderedSeqId = 0;
+static u16 sCurrentOrderedGroupId = 0;
+static u16 sCurrentOrderedSeqId = 0;
void packet_init(struct Packet* packet, enum PacketType packetType, bool reliable, enum PacketLevelMatchType levelAreaMustMatch) {
memset(packet->buffer, 0, PACKET_LENGTH);
@@ -54,8 +54,8 @@ void packet_init(struct Packet* packet, enum PacketType packetType, bool reliabl
// write ordered packet information
if (sOrderedPackets) {
packet_write(packet, &packet->orderedFromGlobalId, sizeof(u8));
- packet_write(packet, &packet->orderedGroupId, sizeof(u8));
- packet_write(packet, &packet->orderedSeqId, sizeof(u8));
+ packet_write(packet, &packet->orderedGroupId, sizeof(u16));
+ packet_write(packet, &packet->orderedSeqId, sizeof(u16));
}
// write location
@@ -162,8 +162,8 @@ u8 packet_initial_read(struct Packet* packet) {
// read ordered packet information
if (packetIsOrdered) {
packet_read(packet, &packet->orderedFromGlobalId, sizeof(u8));
- packet_read(packet, &packet->orderedGroupId, sizeof(u8));
- packet_read(packet, &packet->orderedSeqId, sizeof(u8));
+ packet_read(packet, &packet->orderedGroupId, sizeof(u16));
+ packet_read(packet, &packet->orderedSeqId, sizeof(u16));
}
// read location
@@ -231,5 +231,6 @@ void packet_set_ordered_data(struct Packet* packet) {
if (packet->orderedGroupId == 0) { return; }
if (packet->orderedSeqId != 0) { return; }
packet->orderedSeqId = sCurrentOrderedSeqId++;
- packet->buffer[PACKET_ORDERED_SEQ_ID_OFFSET] = packet->orderedSeqId;
+ u16* seqId = (u16*)&packet->buffer[PACKET_ORDERED_SEQ_ID_OFFSET];
+ *seqId = packet->orderedSeqId;
}
\ No newline at end of file
diff --git a/src/pc/network/packets/packet_reliable.c b/src/pc/network/packets/packet_reliable.c
index 86f48a05..05b52534 100644
--- a/src/pc/network/packets/packet_reliable.c
+++ b/src/pc/network/packets/packet_reliable.c
@@ -35,6 +35,7 @@ static void remove_node_from_list(struct PacketLinkedList* node) {
}
void network_forget_all_reliable(void) {
+ LOG_INFO("Clearing all reliable!");
struct PacketLinkedList* node = head;
while (node != NULL) {
struct PacketLinkedList* next = node->next;
@@ -47,6 +48,7 @@ void network_forget_all_reliable(void) {
void network_forget_all_reliable_from(u8 localIndex) {
if (localIndex == 0) { return; }
+ LOG_INFO("Clearing all reliable from %u", localIndex);
struct PacketLinkedList* node = head;
while (node != NULL) {
struct PacketLinkedList* next = node->next;
diff --git a/src/pc/network/socket/socket.c b/src/pc/network/socket/socket.c
index fb428469..3fc16460 100644
--- a/src/pc/network/socket/socket.c
+++ b/src/pc/network/socket/socket.c
@@ -59,7 +59,7 @@ static int socket_receive(SOCKET socket, struct sockaddr_in* rxAddr, u8* buffer,
return NO_ERROR;
}
-static bool ns_socket_initialize(enum NetworkType networkType) {
+static bool ns_socket_initialize(enum NetworkType networkType, UNUSED bool reconnecting) {
// sanity check port
unsigned int port = (networkType == NT_CLIENT) ? configJoinPort : configHostPort;
@@ -180,7 +180,15 @@ static int ns_socket_send(u8 localIndex, void* address, u8* data, u16 dataLength
return rc;
}
-static void ns_socket_shutdown(void) {
+static void ns_socket_get_lobby_id(char* destination, u32 destLength) {
+ snprintf(destination, destLength, "%s", ""); // TODO: we can probably hook this up
+}
+
+static void ns_socket_get_lobby_secret(char* destination, u32 destLength) {
+ snprintf(destination, destLength, "%s", ""); // TODO: we can probably hook this up
+}
+
+static void ns_socket_shutdown(UNUSED bool reconnecting) {
socket_shutdown(sCurSocket);
sCurSocket = INVALID_SOCKET;
for (u16 i = 0; i < MAX_PLAYERS; i++) {
@@ -190,16 +198,18 @@ static void ns_socket_shutdown(void) {
}
struct NetworkSystem gNetworkSystemSocket = {
- .initialize = ns_socket_initialize,
- .get_id = ns_socket_get_id,
- .get_id_str = ns_socket_get_id_str,
- .save_id = ns_socket_save_id,
- .clear_id = ns_socket_clear_id,
- .dup_addr = ns_socket_dup_addr,
- .match_addr = ns_socket_match_addr,
- .update = ns_socket_update,
- .send = ns_socket_send,
- .shutdown = ns_socket_shutdown,
+ .initialize = ns_socket_initialize,
+ .get_id = ns_socket_get_id,
+ .get_id_str = ns_socket_get_id_str,
+ .save_id = ns_socket_save_id,
+ .clear_id = ns_socket_clear_id,
+ .dup_addr = ns_socket_dup_addr,
+ .match_addr = ns_socket_match_addr,
+ .update = ns_socket_update,
+ .send = ns_socket_send,
+ .get_lobby_id = ns_socket_get_lobby_id,
+ .get_lobby_secret = ns_socket_get_lobby_secret,
+ .shutdown = ns_socket_shutdown,
.requireServerBroadcast = true,
- .name = "Socket",
+ .name = "Socket",
};
diff --git a/src/pc/network/version.c b/src/pc/network/version.c
index 6657e13f..8457eb6c 100644
--- a/src/pc/network/version.c
+++ b/src/pc/network/version.c
@@ -1,12 +1,13 @@
#include
#include "version.h"
+#include "version_text.h"
#include "types.h"
static char sVersionString[MAX_VERSION_LENGTH] = { 0 };
static char sLocalVersionString[MAX_LOCAL_VERSION_LENGTH] = { 0 };
char* get_version(void) {
- snprintf(sVersionString, MAX_VERSION_LENGTH, "%s %d.%d", VERSION_TEXT, VERSION_NUMBER, MINOR_VERSION_NUMBER);
+ snprintf(sVersionString, MAX_VERSION_LENGTH, "%s", VERSION_TEXT);
return sVersionString;
}
@@ -14,6 +15,6 @@ char* get_version_local(void) {
if (PATCH_VERSION_NUMBER <= 0) {
return get_version();
}
- snprintf(sLocalVersionString, MAX_LOCAL_VERSION_LENGTH, "%s %d.%d.%d", VERSION_TEXT, VERSION_NUMBER, MINOR_VERSION_NUMBER, PATCH_VERSION_NUMBER);
+ snprintf(sLocalVersionString, MAX_LOCAL_VERSION_LENGTH, "%s.%d", VERSION_TEXT, PATCH_VERSION_NUMBER);
return sLocalVersionString;
}
diff --git a/src/pc/network/version.h b/src/pc/network/version.h
index 72842593..4903621f 100644
--- a/src/pc/network/version.h
+++ b/src/pc/network/version.h
@@ -1,13 +1,11 @@
#ifndef VERSION_H
#define VERSION_H
-#define VERSION_TEXT "beta"
-#define VERSION_NUMBER 33
-#define MINOR_VERSION_NUMBER 1
#define PATCH_VERSION_NUMBER 0
-#define MAX_VERSION_LENGTH 10
-#define MAX_LOCAL_VERSION_LENGTH 12
+#define MAX_VERSION_LENGTH 28
+#define MAX_LOCAL_VERSION_LENGTH 32
+
char* get_version(void);
char* get_version_local(void);
diff --git a/src/pc/network/version_text.h b/src/pc/network/version_text.h
new file mode 100644
index 00000000..be6263c2
--- /dev/null
+++ b/src/pc/network/version_text.h
@@ -0,0 +1,2 @@
+#pragma once
+#define VERSION_TEXT "beta 34"
\ No newline at end of file
diff --git a/src/pc/pc_main.c b/src/pc/pc_main.c
index 010c37a1..0f2bb4de 100644
--- a/src/pc/pc_main.c
+++ b/src/pc/pc_main.c
@@ -43,9 +43,6 @@
#include "src/bass_audio/bass_audio_helpers.h"
#include "pc/lua/utils/smlua_audio_utils.h"
-#ifdef DISCORDRPC
-#include "pc/discord/discordrpc.h"
-#endif
#include "pc/network/version.h"
#include "pc/network/socket/domain_res.h"
#include "pc/network/network_player.h"
@@ -60,6 +57,10 @@
#include "menu/intro_geo.h"
+#ifdef DISCORD_SDK
+#include "pc/discord/discord.h"
+#endif
+
OSMesg D_80339BEC;
OSMesgQueue gSIEventMesgQueue;
@@ -246,15 +247,12 @@ void audio_shutdown(void) {
}
void game_deinit(void) {
-#ifdef DISCORDRPC
- discord_shutdown();
-#endif
configfile_save(configfile_name());
controller_shutdown();
audio_custom_shutdown();
audio_shutdown();
gfx_shutdown();
- network_shutdown(true, true, false);
+ network_shutdown(true, true, false, false);
smlua_shutdown();
mods_shutdown();
inited = false;
@@ -367,15 +365,15 @@ void main_func(void) {
snprintf(gGetHostName, MAX_CONFIG_STRING, "%s", gCLIOpts.JoinIp);
snprintf(configJoinIp, MAX_CONFIG_STRING, "%s", gCLIOpts.JoinIp);
configJoinPort = gCLIOpts.NetworkPort;
- network_init(NT_CLIENT);
+ network_init(NT_CLIENT, false);
} else if (gCLIOpts.Network == NT_SERVER) {
network_set_system(NS_SOCKET);
configHostPort = gCLIOpts.NetworkPort;
- network_init(NT_SERVER);
+ network_init(NT_SERVER, false);
djui_panel_shutdown();
djui_panel_modlist_create(NULL);
} else {
- network_init(NT_NONE);
+ network_init(NT_NONE, false);
}
audio_init();
@@ -396,14 +394,10 @@ void main_func(void) {
}
#endif
-#ifdef DISCORDRPC
- discord_init();
-#endif
-
while (true) {
wm_api->main_loop(produce_one_frame);
-#ifdef DISCORDRPC
- discord_update_rich_presence();
+#ifdef DISCORD_SDK
+ discord_update();
#endif
#ifdef DEBUG
fflush(stdout);
diff --git a/src/pc/utils/misc.c b/src/pc/utils/misc.c
index 246e5bc2..6567dfd6 100644
--- a/src/pc/utils/misc.c
+++ b/src/pc/utils/misc.c
@@ -531,3 +531,53 @@ void detect_and_skip_mtx_interpolation(Mtx** mtxPrev, Mtx** mtx) {
*mtx = *mtxPrev;
}
}
+
+void str_seperator_concat(char *output_buffer, int buffer_size, char** strings, int num_strings, char* seperator) {
+ // empty buffer
+ memset(output_buffer, 0, buffer_size);
+ if (num_strings <= 0) { return; }
+
+ // Calculate the total length of all strings
+ int string_length[num_strings];
+ int total_length = 0;
+ for (int i = 0; i < num_strings; i++) {
+ string_length[i] = strlen(strings[i]);
+ total_length += string_length[i];
+ }
+
+ // get the seperator length
+ int seperator_length = strlen(seperator);
+ int seperators_length = (num_strings - 1) * seperator_length;
+ if (seperators_length + 8 < buffer_size) {
+ // Shorten the largest string over and over until we fit
+ while (total_length + seperators_length >= buffer_size) {
+ int* largest = NULL;
+ for (int i = 0; i < num_strings; i++) {
+ if (largest == NULL || string_length[i] >= *largest) {
+ largest = &string_length[i];
+ }
+ }
+ if (largest == NULL || *largest == 0) { break; }
+ *largest = *largest - 1;
+ total_length--;
+ }
+ }
+
+ // Fill the buffer
+ int buffer_index = 0;
+ for (int i = 0; i < num_strings; i++) {
+ // Concat string
+ int amount = MIN(buffer_size - buffer_index, string_length[i] + 1);
+ if (amount <= 0) { break; }
+ snprintf(&output_buffer[buffer_index], amount, "%s", strings[i]);
+ buffer_index += string_length[i];
+
+ // Concat seperator
+ if (i != (num_strings - 1)) {
+ int amount = MIN(buffer_size - buffer_index, seperator_length + 1);
+ if (amount <= 0) { break; }
+ snprintf(&output_buffer[buffer_index], amount, "%s", seperator);
+ buffer_index += seperator_length;
+ }
+ }
+}
diff --git a/src/pc/utils/misc.h b/src/pc/utils/misc.h
index 1c65bba3..26f7e706 100644
--- a/src/pc/utils/misc.h
+++ b/src/pc/utils/misc.h
@@ -23,4 +23,6 @@ void delta_interpolate_rgba(u8* res, u8* a, u8* b, f32 delta);
void delta_interpolate_mtx(Mtx* out, Mtx* a, Mtx* b, f32 delta);
void detect_and_skip_mtx_interpolation(Mtx** mtxPrev, Mtx** mtx);
+void str_seperator_concat(char *output_buffer, int buffer_size, char** strings, int num_strings, char* seperator);
+
#endif
\ No newline at end of file
diff --git a/textures/custom_font/custom_font_normal.rgba32.png b/textures/custom_font/custom_font_normal.rgba32.png
index c4552460..14dee894 100644
Binary files a/textures/custom_font/custom_font_normal.rgba32.png and b/textures/custom_font/custom_font_normal.rgba32.png differ
diff --git a/textures/custom_font/custom_font_tiny.rgba32.png b/textures/custom_font/custom_font_tiny.rgba32.png
index 43e8a70f..843af03e 100644
Binary files a/textures/custom_font/custom_font_tiny.rgba32.png and b/textures/custom_font/custom_font_tiny.rgba32.png differ