Merge branch 'coopnet' into coop

This commit is contained in:
MysterD 2023-04-19 00:22:20 -07:00
commit 98c78a9470
105 changed files with 2452 additions and 1614 deletions

View file

@ -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

View file

@ -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<br />\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<br />\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'

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -1690,7 +1690,8 @@
| Identifier | Value |
| :--------- | :---- |
| NS_SOCKET | 0 |
| NS_DISCORD | 1 |
| NS_COOPNET | 1 |
| NS_MAX | 2 |
### [enum PlayerInteractions](#PlayerInteractions)
| Identifier | Value |

View file

@ -8265,26 +8265,6 @@
<br />
## [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:](#)
<br />
## [network_get_player_text_color_string](#network_get_player_text_color_string)
### Lua Example

View file

@ -7251,6 +7251,12 @@
<br />
---
# functions from smlua_deprecated.h
<br />
---
# functions from smlua_level_utils.h

View file

@ -1096,7 +1096,6 @@
<br />
- 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 @@
<br />
- smlua_deprecated.h
<br />
- smlua_level_utils.h
- [level_register](functions-4.md#level_register)
- [smlua_level_util_get_info](functions-4.md#smlua_level_util_get_info)

View file

@ -2181,6 +2181,7 @@
| enablePlayerList | `integer` | |
| enablePlayersInLevelDisplay | `integer` | |
| headlessServer | `integer` | |
| maxPlayers | `integer` | |
| playerInteractions | [enum PlayerInteractions](constants.md#enum-PlayerInteractions) | |
| playerKnockbackStrength | `integer` | |
| shareLives | `integer` | |

View file

@ -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."

View file

@ -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"

View file

@ -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"

320
lang/Italian.ini Normal file
View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -0,0 +1,66 @@
#ifndef LIBCOOPNET_H
#define LIBCOOPNET_H
#if defined(__cplusplus)
#include <cstdint>
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -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

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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) {

View file

@ -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;

View file

@ -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();

View file

@ -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;

View file

@ -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
}

View file

@ -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);

168
src/pc/discord/discord.c Normal file
View file

@ -0,0 +1,168 @@
#include "discord.h"
#include "pc/djui/djui.h"
#include "pc/debuglog.h"
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#include <winuser.h>
#else
#include <unistd.h>
#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(&params);
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, &params, &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));
}

View file

@ -1,22 +1,14 @@
#ifndef DISCORD_H
#define DISCORD_H
#include <stdint.h>
#include <stdio.h>
#include <assert.h>
#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 <Windows.h>
#else
#define DISCORD_ID_FORMAT "%ld"
#include <unistd.h>
#include <string.h>
#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
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);

View file

@ -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;
}

View file

@ -1,291 +0,0 @@
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#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 <windows.h>
# 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 <dlfcn.h>
# define DISCORDLIBEXT ".dylib"
#elif defined(__linux__) || defined(__FreeBSD__) // lets make the bold assumption for FreeBSD
# include <dlfcn.h>
# 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);
}

View file

@ -1,49 +0,0 @@
#ifndef DISCORDRPC_H
#define DISCORDRPC_H
#include <stdint.h>
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

View file

@ -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;

View file

@ -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*));

View file

@ -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;

View file

@ -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);
}

View file

@ -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);

View file

@ -0,0 +1,85 @@
#include <stdio.h>
#include <string.h>
#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;
}

View file

@ -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*));

View file

@ -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;
}

View file

@ -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);

View file

@ -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);

View file

@ -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);
}

View file

@ -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;

View file

@ -1,202 +1,31 @@
#include <stdio.h>
#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
}

View file

@ -0,0 +1,189 @@
#include <stdio.h>
#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);
}

View file

@ -0,0 +1,3 @@
#pragma once
void djui_panel_join_direct_create(struct DjuiBase* caller);

View file

@ -0,0 +1,173 @@
#include <stdio.h>
#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

View file

@ -0,0 +1,3 @@
#pragma once
void djui_panel_join_lobbies_create(struct DjuiBase* caller, const char* password);

View file

@ -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);
}

View file

@ -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

View file

@ -0,0 +1,3 @@
#pragma once
void djui_panel_join_private_create(struct DjuiBase* caller);

View file

@ -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) {

View file

@ -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 };

View file

@ -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 },

View file

@ -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"

View file

@ -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);

View file

@ -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;
}

View file

@ -0,0 +1,3 @@
#pragma once
char* network_discord_id_from_local_index(u8 localIndex);

View file

@ -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 };

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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

View file

@ -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 <windows.h>
#include <winuser.h>
#else
#include <unistd.h>
#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(&params);
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, &params, &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",
};

View file

@ -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);
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -1,7 +0,0 @@
#ifndef DISCORD_USER_H
#define DISCORD_USER_H
#include "discord.h"
struct IDiscordUserEvents* discord_user_initialize(void);
#endif

View file

@ -1,15 +1,13 @@
#include "socket/socket.h"
#include "coopnet/coopnet.h"
#include <stdio.h>
#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();
}

View file

@ -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

View file

@ -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;

View file

@ -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 {

View file

@ -1,6 +1,5 @@
#include <stdio.h>
#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;
}

View file

@ -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);

View file

@ -1,8 +1,47 @@
#include <stdio.h>
#include <zlib.h>
#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

View file

@ -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);

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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;
}

View file

@ -1,10 +1,10 @@
#include <stdio.h>
#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

View file

@ -14,11 +14,11 @@ void network_send_ping(struct NetworkPlayer* toNp) {
packet_write(&p, &timestamp, 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;

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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",
};

View file

@ -1,12 +1,13 @@
#include <stdio.h>
#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;
}

View file

@ -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);

View file

@ -0,0 +1,2 @@
#pragma once
#define VERSION_TEXT "beta 34"

Some files were not shown because too many files have changed in this diff Show more