diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua index 18c43586..d57a61c2 100644 --- a/autogen/lua_definitions/constants.lua +++ b/autogen/lua_definitions/constants.lua @@ -4747,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 diff --git a/lang/Italian.ini b/lang/Italian.ini new file mode 100644 index 00000000..780ed466 --- /dev/null +++ b/lang/Italian.ini @@ -0,0 +1,320 @@ +[NOTIF] +CONNECTED = "@ si è connesso" +DISCONNECTED = "@ si è disconnesso" +LEFT_THIS_LEVEL = "@ ha lasciato il livello" +ENTERED_THIS_LEVEL = "@ è entrato nel livello" +ENTERED = "@ è entrato in\n#" +SERVER_CLOSED = "\\#ffa0a0\\Disconnesso:\\#dcdcdc\\ server chiuso" +DISCORD_ERROR = "Errore: Discord ha avuto un errore\nPer risolverlo, prova a: \n1. Chiudere il gioco.\n2. Riavviare Discord.\n3. Avviare il gioco." +DISCORD_DETECT = "\\#ffa0a0\\Errore:\\#c8c8c8\\ impossibile individuare Discord.\n\\#a0a0a0\\Prova a chiudere il gioco, riavviare Discord, e poi riaprire il gioco." +DISCONNECT_FULL = "\\#ffa0a0\\Disconnesso:\\#c8c8c8\\ il server è pieno." +DISCONNECT_KICK = "\\#ffa0a0\\Disconnesso:\\#c8c8c8\\ sei stato espulso." +DISCONNECT_BAN = "\\#ffa0a0\\Disconnesso:\\#c8c8c8\\ sei stato bandito." +DISCONNECT_REJOIN = "\\#ffa0a0\\Disconnesso:\\#c8c8c8\\ ricollegandoti..." +DISCONNECT_CLOSED = "\\#ffa0a0\\Disconnesso:\\#c8c8c8\\ l'host ha interroto la connessione." +DISCONNECT_BIG_MOD = "Il server ha una mod troppo pesante.\nDisconnessione." +DIED = "@ è morto" +DEBUG_FLY = "@ è entrato nello stato di debug di volo libero" +IMPORT_MOD_SUCCESS = "\\#a0ffa0\\importata la mod\n\\#c8c8c8\\'@'" +IMPORT_DYNOS_SUCCESS = "\\#a0ffa0\\Importato il pacchetto DynOS\n\\#c8c8c8\\'@'" +IMPORT_FAIL = "\\#ffa0a0\\Impossibile importare\n\\#c8c8c8\\'@'" +IMPORT_FAIL_INGAME = "\\#ffa0a0\\Impossibile importare durante la partita" + +[CHAT] +KICKING = "Espulso '@'!" +BANNING = "Bandito '@'!" +SERVER_ONLY = "Solo il server può usare questo comando." +PERM_BANNING = "Bandito permanentemente '@'!" +ADD_MODERATOR = "Aggiunto '@' tra i Moderatori!" +PLAYERS = "Giocatori" +NO_PERMS = "Non hai il permesso di usare questo comando." +PLAYER_NOT_FOUND = "Impossibile trovare il giocatore." +SELF_KICK = "Non puoi espellere te stesso." +SELF_BAN = "Non puoi bandire te stesso." +SELF_MOD = "Non puoi renderti un moderatore." +KICK_CONFIRM = "Sei sicuro di voler espellere '@'?\nScrivi '\\#a0ffa0\\/confirm\\#fff982\\' per confermare." +BAN_CONFIRM = "Sei sicuro di voler bandire '@'?\nScrivi'\\#a0ffa0\\/confirm\\#fff982\\' per confermare." +PERM_BAN_CONFIRM = "Sei sicuro di voler bandire permanentemenrte '@'?\nScrivi '\\#a0ffa0\\/confirm\\#fff982\\' per confermare." +MOD_CONFIRM = "Sei sicuro di voler rendere '@' un moderatore?\nScrivi '\\#a0ffa0\\/confirm\\#fff982\\' per confermare." +PLAYERS_DESC = "/players - Lista dei giocatori e dei loro ID" +KICK_DESC = "/kick [NAME|ID] - Espelli questo giocatore dalla partita" +BAN_DESC = "/ban [NAME|ID] - Bandisci questo giocatore dalla partita" +PERM_BAN_DESC = "/permban [NAME|ID] - Bandisci questo giocatore da tutte le tue partite" +MOD_DESC = "/moderator [NAME|ID] - Dai al gicatore il permesso di eseguire comandi come /kick, /ban, /permban in ogni partita che crei" +UNRECOGNIZED = "Comando non riconosciuto." +MOD_GRANTED = "\\#fff982\\Ora sei un moderatore." + +[MENU] +BACK = "Indietro" +CANCEL = "Annulla" +NO = "No" +YES = "Si" + +[CAMERA] +CAMERA = "TELECAMERA" +FREE_CAMERA = "Telecamera libera" +ANALOG_CAMERA = "Telecamera analogica" +MOUSE_LOOK = "Telecamera con mouse" +INVERT_X = "Inverti X" +INVERT_Y = "Inverti Y" +X_SENSITIVITY = "Inverti asse X" +Y_SENSITIVITY = "Inverti asse Y" +AGGRESSION = "Aggressività" +PAN_LEVEL = "Livello di panoramica" +DECELERATION = "Decelerazione" + +[CHEATS] +CHEATS = "TRUCCHI" +MOON_JUMP = "Moon Jump" +GOD_MODE = "God Mode" +INFINITE_LIVES = "Vite infinite" +SUPER_SPEED = "Super Velocità" +RESPONSIVE_CONTROLS = "Controlli reattivi" +RAPID_FIRE = "Fuoco Rapido (A)" +BLJ_ANYWHERE = "BLJ Ovunque" +ALWAYS_TRIPLE_JUMP = "Sempre Salto triplo" + +[CONTROLS] +CONTROLS = "CONTROLLI" +N64_BINDS = "Comandi N64" +EXTRA_BINDS = "Comandi Extra" +BACKGROUND_GAMEPAD = "Azione in Background" +GAMEPAD = "Controller" +DEADZONE = "Zona Morta" +RUMBLE_STRENGTH = "Intesità Vibrazione" +CHAT = "Chat" +PLAYERS = "Giocatori" +D_UP = "D Su" +D_DOWN = "D Giù" +D_LEFT = "D Sinistra" +D_RIGHT = "D Destra" +X = "X" +Y = "Y" + +UP = "Su" +DOWN = "Giù" +LEFT = "Sinistra" +RIGHT = "Destra" +A = "A" +B = "B" +START = "Start" +L = "L" +R = "R" +Z = "Z" +C_UP = "C Su" +C_DOWN = "C Giù" +C_LEFT = "C Sinistra" +C_RIGHT = "C Destra" + +[DISPLAY] +DISPLAY = "GRAFICA" +FULLSCREEN = "Schermo intero" +PRELOAD_TEXTURES = "Precarica Textures" +VSYNC = "VSync" +UNCAPPED_FRAMERATE = "Fotogrammi Illimitati" +FRAME_LIMIT = "Limite Fotogrammi" +FAST = "Veloce" +ACCURATE = "Accurata" +INTERPOLATION = "Interpolazione" +NEAREST = "Vicino" +LINEAR = "Lineare" +TRIPOINT = "Tripunto" +FILTERING = "Filtraggio" +D0P5X = "0.5x" +D1X = "1x" +D1P5X = "1.5x" +D3X = "3x" +D10X = "10x" +D100X = "100x" +DRAW_DISTANCE = "Distanza di simulazione" +DYNOS_PACKS = "Pacchetti DynOS" + +[DYNOS] +DYNOS = "DYNOS" + +[HOST_MESSAGE] +INFO_TITLE = "INFO" +WARN_DISCORD = "Invita gli amici facendo tasto destro sul loro nome in Discord e cliccando\n'\\#d0d0ff\\Invito a giocare\\#c8c8c8\\'.\n\npuoi invitare anche i canali dei server cliccando il pulsante \\#d0d0ff\\più\\#c8c8c8\\ vicino al posto dove scrivi.\n\nLo Stato delle Attività \\#ffa0a0\\deve essere\\#c8c8c8\\ attivo nelle\nimpostazioni utente di Discord.\n\nApparire offline \\#ffa0a0\\ti impedirà\\#c8c8c8\\ di inviare inviti." +WARN_DISCORD2 = "\\#ffa0a0\\Errore:\\#c8c8c8\\ Impossibile individuare Discord.\n\n\\#a0a0a0\\prova a chiudre il gioco,\nriavviare Discord,\ne aprire di nuovo il gioco." +WARN_SOCKET = "La connessione diretta \\#ffa0a0\\richiede\\#c8c8c8\\ una configurazione port forwarding nel tuo router.\n\nTrasmetti una connessione '\\#d0d0ff\\%d\\#c8c8c8\\' per l'UDP." +HOST = "Crea" + +[HOST_MODS] +ROMHACKS = "ROMHACKS" +MODS = "MODS" + +[HOST_SAVE] +SAVE_TITLE = "SALVATAGGIO" +ERASE_TITLE = "CANCELLA" +CONFIRM = "Sei sicuro di voler cancellare questo slot di salvataggio?" +ERASE = "cancella" + +[HOST_SETTINGS] +SETTINGS = "OPZIONI" +NONSOLID = "Non-solida" +SOLID = "Solida" +FRIENDLY_FIRE = "Fuoco Amico" +PLAYER_INTERACTION = "Interazione tra Giocatori" +WEAK = "Debole" +NORMAL = "Normale" +TOO_MUCH = "Eccessiva" +KNOCKBACK_STRENGTH = "Forza di Contraccolpo" +LEAVE_LEVEL = "Lascia il livello" +STAY_IN_LEVEL = "Rimani nel livello" +NONSTOP = "Non-stop" +ON_STAR_COLLECTION = "A stella collezzionata" +SKIP_INTRO_CUTSCENE = "Salta la intro iniziale" +SHARE_LIVES = "Condividi le vite" +ENABLE_CHEATS = "Abilita i trucchi" +BUBBLE_ON_DEATH = "Bolla alla morte" +AMOUNT_OF_PLAYERS = "Numero di giocatori" + +[HOST] +SERVER_TITLE = "SERVER" +HOST_TITLE = "OSPITA" +DISCORD = "Discord" +DIRECT_CONNECTION = "Connessione diretta" +NETWORK_SYSTEM = "Sistema di connessione" +PORT = "Porta" +SAVE_SLOT = "Slot di Salvataggio" +SETTINGS = "Opzioni" +MODS = "Mods" +ROMHACKS = "Rom-Hacks" +APPLY = "Applica" +HOST = "Crea" + +[JOIN_MESSAGE] +JOINING = "CONNESSIONE" + +[JOIN] +JOIN_TITLE = "UNISCITI" +JOIN_DISCORD = "Per entrare in una partita da \\#d0d0ff\\Discord\\#c8c8c8\\:\n\nMantieni il gioco aperto e clicca sul pulsante Unisciti.\n\nSe l'invito dice che la partita è finita,/nclicca sul nome della persona che l'ha mandato per ripristinarla." +JOIN_SOCKET = "Immetti un \\#d0d0ff\\IP e una Porta\\#c8c8c8\\ per la connessione diretta:" +JOIN = "Unisciti" + +[MAIN] +QUIT_TITLE = "ABBANDONA" +QUIT_CONFIRM = "Sei sicuro do voler abbandonare?" +HOST = "Crea" +JOIN = "Unisciti" +OPTIONS = "Opzioni" +QUIT = "Abbandona" + +[MENU_OPTIONS] +MAIN_MENU = "MENÙ PRINCIPALE" +LEVEL = "Livello" +USE_STAGE_MUSIC = "Usa la musica del livello" +RANDOM_STAGE = "Livello casuale" +PLAY_VANILLA_DEMOS = "Riproduci le demo di gioco" + +[MISC] +DEBUG_TITLE = "DEBUG" +FIXED_COLLISIONS = "Collisioni Aggiustate" +LUA_PROFILER = "Profiler Lua" +DEBUG_PRINT = "Stampa di debug" +DEBUG_INFO = "Info di debug" +DEBUG_ERRORS = "Errori di debug" +MISC_TITLE = "VARIE" +PAUSE_IN_SINGLEPLAYER = "Metti in pausa in giocatore singolo" +DISABLE_POPUPS = "Disabilita Popups" +MENU_OPTIONS = "Opzioni Menù" +DEBUG = "Debug" +LANGUAGE = "Lingua" + +[MODLIST] +MODS = "MODS" + +[OPTIONS] +OPTIONS = "OPZIONI" +PLAYER = "Giocatore" +CAMERA = "Telecamera" +CONTROLS = "Comandi" +DISPLAY = "Grafica" +SOUND = "Suono" +MISC = "Varie" + +[PAUSE] +QUIT_TITLE = "ABBANDONA" +QUIT_HOST = "Sei sicuro di voler interrompere la connessione?" +QUIT_CLIENT = "Sei sicuro di volerti disconnettere?" +PAUSE_TITLE = "PAUSA" +PLAYER = "Giocatore" +DYNOS_PACKS = "Pacchetti DynOS" +OPTIONS = "Opzioni" +CHEATS = "Trucchi" +SERVER_SETTINGS = "Impostazioni Server" +RESUME = "Riprendi" +STOP_HOSTING = "Interrompi la connessione" +DISCONNECT = "Disconnettiti" + +[PLAYER] +PLAYER_TITLE = "GICATORE" +OVERALLS = "Tuta" +SHIRT = "Maglietta" +GLOVES = "Guanti" +SHOES = "Scarpe" +HAIR = "Capelli" +SKIN = "Pelle" +CAP = "Cappello" +PALETTE = "PALETTE" +PART = "Parte" +HEX_CODE = "codice Hex" +RED = "Rosso" +GREEN = "Verde" +BLUE = "Blu" +PLAYER = "Giocatore" +NAME = "Nome" +MODEL = "Modello" +PALETTE_PRESET = "Opzioni Palette" +EDIT_PALETTE = "Modifica Palette" + +[PALETTE] +MARIO = "Mario" +LUIGI = "Luigi" +WALUIGI = "Waluigi" +WARIO = "Wario" +CHUCKYA = "Chuckya" +GOOMBA = "Goomba" +CLOVER = "Trifoglio" +COBALT = "Cobalto" +FURY = "Furia" +HOT_PINK = "Rosa Caldo" +NICE_PINK = "Rosa Fresco" +SEAFOAM = "Schiuma Marina" +LILAC = "Lilla" +COPPER = "Rame" +AZURE = "Azurro" +BURGUNDY = "Borgogna" +MINT = "Menta" +EGGPLANT = "Melanzana" +ORANGE = "Arancia" +ARCTIC = "Arctico" +FIRE_MARIO = "Mario Fuoco" +FIRE_LUIGI = "Luigi Fuoco" +FIRE_WALUIGI = "Waluigi Fuoco" +FIRE_WARIO = "Wario Fuoco" +BUSY_BEE = "Ape Operaia" +FORTRESS = "Fortezza" +BATTLEMENTS = "Muraglia" +BLUEBERRY_PIE = "Torta ai Mirtilli" +RASPBERRY = "Mora" +BUBBLEGUM = "Gomma da Masticare" +ICE_MARIO = "Mario Ghiaccio" +ICE_LUIGI = "Luigi Ghiaccio" +CUSTOM = "Personalizzato" + +[PLAYER_LIST] +PLAYERS = "GIOCATORI" +NAME = "nome" +LOCATION = "posizione" +ACT = "atto" + +[SOUND] +SOUND = "SUONO" +MASTER_VOLUME = "Principale" +MUSIC_VOLUME = "Musica" +SFX_VOLUME = "Effetti sonori" +ENV_VOLUME = "Ambiente" + +[LANGUAGE] +LANGUAGE = "LINGUA" diff --git a/lib/coopnet/include/libcoopnet.h b/lib/coopnet/include/libcoopnet.h index 09e0e9b3..47ceddb8 100644 --- a/lib/coopnet/include/libcoopnet.h +++ b/lib/coopnet/include/libcoopnet.h @@ -30,7 +30,7 @@ typedef struct { 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); + 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); @@ -49,8 +49,8 @@ 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); -CoopNetRc coopnet_lobby_update(uint64_t aLobbyId, const char* aGame, const char* aVersion, const char* aHostName, const char* aMode); +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); diff --git a/lib/coopnet/linux/libcoopnet.a b/lib/coopnet/linux/libcoopnet.a index b60075fb..38041dcd 100644 Binary files a/lib/coopnet/linux/libcoopnet.a and b/lib/coopnet/linux/libcoopnet.a differ diff --git a/src/pc/discord/discord_activity.c b/src/pc/discord/discord_activity.c index 53b9ebd1..c5a5e3d3 100644 --- a/src/pc/discord/discord_activity.c +++ b/src/pc/discord/discord_activity.c @@ -2,6 +2,7 @@ #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" @@ -9,6 +10,9 @@ 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); @@ -30,7 +34,7 @@ static void on_activity_join(UNUSED void* data, const char* secret) { // extract lobby ID token = strtok(NULL, ":"); char* end; - u64 lobbyId = strtoll(token, &end, 10); + u64 lobbyId = strtoull(token, &end, 10); // extract lobby password token = strtok(NULL, ":"); @@ -40,13 +44,9 @@ static void on_activity_join(UNUSED void* data, const char* secret) { if (gNetworkType != NT_NONE) { network_shutdown(true, false, false, false); } - gCoopNetDesiredLobby = lobbyId; - snprintf(gCoopNetPassword, 64, "%s", token); - - network_reset_reconnect_and_rehost(); - network_set_system(NS_COOPNET); - network_init(NT_CLIENT, false); - djui_panel_join_message_create(NULL); + sQueuedLobbyId = lobbyId; + snprintf(sQueuedLobbyPassword, 64, "%s", token); + sQueuedLobby = 2; #endif } @@ -65,28 +65,28 @@ static void strncat_len(char* destination, char* source, size_t destinationLengt strncat(destination, altered, destinationLength); } -static bool discord_populate_details(char* details, bool shorten) { - snprintf(details, 127, "%s", get_version()); +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; - bool displayDash = true; - bool displayComma = false; - size_t catLength = shorten ? 14 : 64; - - // add mods to activity - if (gActiveMods.entryCount > 0) { - 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; - } + // 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; } - return (strlen(details) >= 125); + // 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) { @@ -114,13 +114,10 @@ void discord_activity_update(void) { if (sCurActivity.party.size.max_size < 1) { sCurActivity.party.size.max_size = 1; } } - char details[256] = { 0 }; - bool overrun = discord_populate_details(details, false); - if (overrun) { - discord_populate_details(details, true); - } + char details[128] = { 0 }; + discord_populate_details(details, 128); - if (snprintf(sCurActivity.details, 125, "%s", details) < 0) { + if (snprintf(sCurActivity.details, 128, "%s", details) < 0) { LOG_INFO("truncating details"); } @@ -139,6 +136,17 @@ void discord_activity_update(void) { } 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(); diff --git a/src/pc/djui/djui_language.c b/src/pc/djui/djui_language.c index 707457f6..8830944e 100644 --- a/src/pc/djui/djui_language.c +++ b/src/pc/djui/djui_language.c @@ -2,6 +2,8 @@ #include "djui_unicode.h" #include "pc/ini.h" #include "pc/platform.h" +#include "pc/mods/mods.h" +#include "pc/mods/mods_utils.h" ini_t* sLang = NULL; @@ -13,9 +15,12 @@ bool djui_language_init(char* lang) { } // construct path + char exePath[SYS_MAX_PATH] = ""; + path_get_folder((char*)path_to_executable(), exePath); + char path[SYS_MAX_PATH] = ""; if (!lang || lang[0] == '\0') { lang = "English"; } - snprintf(path, SYS_MAX_PATH, "%s/lang/%s.ini", sys_exe_path(), lang); + snprintf(path, SYS_MAX_PATH, "%s/lang/%s.ini", exePath, lang); // load sLang = ini_load(path); diff --git a/src/pc/djui/djui_lobby_entry.c b/src/pc/djui/djui_lobby_entry.c index b2d480a5..7aea75e9 100644 --- a/src/pc/djui/djui_lobby_entry.c +++ b/src/pc/djui/djui_lobby_entry.c @@ -32,13 +32,16 @@ static void djui_lobby_entry_update_style(struct DjuiBase* base) { 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, void (*on_click)(struct DjuiBase*)) { +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); @@ -48,6 +51,7 @@ struct DjuiLobbyEntry* djui_lobby_entry_create(struct DjuiBase* parent, char* ho 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; diff --git a/src/pc/djui/djui_lobby_entry.h b/src/pc/djui/djui_lobby_entry.h index e96fca23..02c946e2 100644 --- a/src/pc/djui/djui_lobby_entry.h +++ b/src/pc/djui/djui_lobby_entry.h @@ -3,6 +3,7 @@ struct DjuiLobbyEntry { struct DjuiBase base; + const char* description; }; -struct DjuiLobbyEntry* djui_lobby_entry_create(struct DjuiBase* parent, char* host, char* mode, char* players, void (*on_click)(struct DjuiBase*)); +struct DjuiLobbyEntry* djui_lobby_entry_create(struct DjuiBase* parent, char* host, char* mode, char* players, char* description, void (*on_click)(struct DjuiBase*), void (*on_hover)(struct DjuiBase*), void (*on_hover_end)(struct DjuiBase*)); diff --git a/src/pc/djui/djui_panel_join_lobbies.c b/src/pc/djui/djui_panel_join_lobbies.c index 09cd43ed..43c458f3 100644 --- a/src/pc/djui/djui_panel_join_lobbies.c +++ b/src/pc/djui/djui_panel_join_lobbies.c @@ -14,10 +14,54 @@ #ifdef COOPNET +#define DJUI_DESC_PANEL_WIDTH (410.0f + (16 * 2.0f)) + 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); @@ -27,7 +71,7 @@ void djui_panel_join_lobby(struct DjuiBase* caller) { 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, UNUSED const char* aVersion, const char* aHostName, const char* aMode) { +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] = ""; @@ -44,7 +88,7 @@ void djui_panel_join_query(uint64_t aLobbyId, UNUSED uint64_t aOwnerId, uint16_t } struct DjuiBase* layoutBase = &sLobbyLayout->base; - struct DjuiLobbyEntry* entry = djui_lobby_entry_create(layoutBase, (char*)aHostName, (char*)mode, playerText, djui_panel_join_lobby); + 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; } @@ -66,6 +110,11 @@ void djui_panel_join_lobbies_on_destroy(UNUSED struct DjuiBase* caller) { sPassword = NULL; sRefreshButton = NULL; sLobbyLayout = NULL; + + if (sDescriptionPanel != NULL) { + djui_base_destroy(&sDescriptionPanel->base); + sDescriptionPanel = NULL; + } } void djui_panel_join_lobbies_refresh(UNUSED struct DjuiBase* caller) { @@ -79,7 +128,8 @@ void djui_panel_join_lobbies_create(struct DjuiBase* caller, const char* passwor if (sPassword) { free(sPassword); sPassword = NULL; } sPassword = strdup(password); bool private = (strlen(password) > 0); - bool querying = ns_coopnet_query(djui_panel_join_query, djui_panel_join_query_finish, password); + + 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)); @@ -89,17 +139,7 @@ void djui_panel_join_lobbies_create(struct DjuiBase* caller, const char* passwor sLobbyLayout = paginated->layout; djui_flow_layout_set_margin(sLobbyLayout, 4); - #if 0 - struct DjuiBase* layoutBase = &sLobbyLayout->base; - for (int i = 0; i < 1; i++) { - djui_lobby_entry_create(layoutBase, "MysterD", "Super Mario 64", "15/16", NULL); - djui_lobby_entry_create(layoutBase, "djoslin0", "Star Road", "1/16", NULL); - djui_lobby_entry_create(layoutBase, "abcdefghijklmnopqrs", "Snowstorm Avalanche", "16/16", NULL); - djui_lobby_entry_create(layoutBase, "Prince Frizzy", "Hide and Seek", "4/16", NULL); - djui_lobby_entry_create(layoutBase, "Sunk", "Super Mario 74 (+EE)", "5/8", NULL); - } - djui_paginated_calculate_height(paginated); - #endif + 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); diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c index 7343f989..0f1b414b 100644 --- a/src/pc/lua/smlua_constants_autogen.c +++ b/src/pc/lua/smlua_constants_autogen.c @@ -1768,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" diff --git a/src/pc/network/coopnet/coopnet.c b/src/pc/network/coopnet/coopnet.c index 8c2928ed..cd4aafea 100644 --- a/src/pc/network/coopnet/coopnet.c +++ b/src/pc/network/coopnet/coopnet.c @@ -6,6 +6,7 @@ #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" @@ -17,6 +18,7 @@ uint64_t gCoopNetDesiredLobby = 0; char gCoopNetPassword[64] = ""; +char sCoopNetDescription[256] = ""; static uint64_t sLocalLobbyId = 0; static uint64_t sLocalLobbyOwnerId = 0; @@ -139,6 +141,33 @@ 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; } @@ -149,11 +178,13 @@ void ns_coopnet_update(void) { mods_get_main_mod_name(mode, 64); if (sReconnecting) { LOG_INFO("Update lobby"); - coopnet_lobby_update(sLocalLobbyId, CN_GAME_STR, get_version(), configPlayerName, mode); + 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_lobby_create(CN_GAME_STR, get_version(), configPlayerName, mode, (uint16_t)configAmountofPlayers, gCoopNetPassword); + 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"); @@ -173,6 +204,11 @@ static int ns_coopnet_network_send(u8 localIndex, void* address, u8* data, u16 d 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", ""); @@ -182,7 +218,7 @@ static void ns_coopnet_get_lobby_id(UNUSED char* destination, UNUSED u32 destLen } static void ns_coopnet_get_lobby_secret(UNUSED char* destination, UNUSED u32 destLength) { - if (sLocalLobbyId == 0) { + if (sLocalLobbyId == 0 || !coopnet_allow_invite()) { snprintf(destination, destLength, "%s", ""); } else { snprintf(destination, destLength, "coopnet:%" PRIu64":%s", sLocalLobbyId, gCoopNetPassword); diff --git a/src/pc/network/coopnet/coopnet.h b/src/pc/network/coopnet/coopnet.h index 49987e34..ec094c7d 100644 --- a/src/pc/network/coopnet/coopnet.h +++ b/src/pc/network/coopnet/coopnet.h @@ -2,7 +2,7 @@ #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); +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; diff --git a/src/pc/network/network_player.c b/src/pc/network/network_player.c index ac1b4ed3..70376c5a 100644 --- a/src/pc/network/network_player.c +++ b/src/pc/network/network_player.c @@ -10,6 +10,7 @@ #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" @@ -179,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); @@ -196,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) { +#endif LOG_INFO("dropping due to no server connectivity"); network_shutdown(false, false, true, false); } -#endif elapsed = (clock_elapsed() - np->lastSent); if (elapsed > NETWORK_PLAYER_TIMEOUT / 3.0f) { diff --git a/src/pc/utils/misc.c b/src/pc/utils/misc.c index 246e5bc2..6567dfd6 100644 --- a/src/pc/utils/misc.c +++ b/src/pc/utils/misc.c @@ -531,3 +531,53 @@ void detect_and_skip_mtx_interpolation(Mtx** mtxPrev, Mtx** mtx) { *mtx = *mtxPrev; } } + +void str_seperator_concat(char *output_buffer, int buffer_size, char** strings, int num_strings, char* seperator) { + // empty buffer + memset(output_buffer, 0, buffer_size); + if (num_strings <= 0) { return; } + + // Calculate the total length of all strings + int string_length[num_strings]; + int total_length = 0; + for (int i = 0; i < num_strings; i++) { + string_length[i] = strlen(strings[i]); + total_length += string_length[i]; + } + + // get the seperator length + int seperator_length = strlen(seperator); + int seperators_length = (num_strings - 1) * seperator_length; + if (seperators_length + 8 < buffer_size) { + // Shorten the largest string over and over until we fit + while (total_length + seperators_length >= buffer_size) { + int* largest = NULL; + for (int i = 0; i < num_strings; i++) { + if (largest == NULL || string_length[i] >= *largest) { + largest = &string_length[i]; + } + } + if (largest == NULL || *largest == 0) { break; } + *largest = *largest - 1; + total_length--; + } + } + + // Fill the buffer + int buffer_index = 0; + for (int i = 0; i < num_strings; i++) { + // Concat string + int amount = MIN(buffer_size - buffer_index, string_length[i] + 1); + if (amount <= 0) { break; } + snprintf(&output_buffer[buffer_index], amount, "%s", strings[i]); + buffer_index += string_length[i]; + + // Concat seperator + if (i != (num_strings - 1)) { + int amount = MIN(buffer_size - buffer_index, seperator_length + 1); + if (amount <= 0) { break; } + snprintf(&output_buffer[buffer_index], amount, "%s", seperator); + buffer_index += seperator_length; + } + } +} diff --git a/src/pc/utils/misc.h b/src/pc/utils/misc.h index 1c65bba3..26f7e706 100644 --- a/src/pc/utils/misc.h +++ b/src/pc/utils/misc.h @@ -23,4 +23,6 @@ void delta_interpolate_rgba(u8* res, u8* a, u8* b, f32 delta); void delta_interpolate_mtx(Mtx* out, Mtx* a, Mtx* b, f32 delta); void detect_and_skip_mtx_interpolation(Mtx** mtxPrev, Mtx** mtx); +void str_seperator_concat(char *output_buffer, int buffer_size, char** strings, int num_strings, char* seperator); + #endif \ No newline at end of file