From 88a552f601a490696c167bdf6f856e38d3f93cfc Mon Sep 17 00:00:00 2001 From: EmeraldLockdown <86802223+EmeraldLoc@users.noreply.github.com> Date: Mon, 9 Mar 2026 22:21:40 -0500 Subject: [PATCH] Push up changes, language stuff, add mod moderation commands, yipee --- autogen/lua_definitions/constants.lua | 9 +++ autogen/lua_definitions/functions.lua | 14 +++++ docs/lua/constants.md | 8 +++ docs/lua/functions-3.md | 24 ++++++++ docs/lua/functions-7.md | 24 ++++++++ docs/lua/functions.md | 2 + lang/English.ini | 2 + src/pc/chat_commands.c | 18 +++--- src/pc/djui/djui_language.c | 2 +- .../djui_panel_moderation_confirm_action.c | 5 +- .../djui_panel_moderation_list_inspector.c | 7 ++- .../djui/djui_panel_moderator_menu_inspect.c | 13 ++++- src/pc/djui/djui_popup.c | 29 ++++++++-- src/pc/djui/djui_popup.h | 3 + src/pc/lua/smlua_constants_autogen.c | 3 + src/pc/lua/smlua_functions_autogen.c | 43 +++++++++++++++ src/pc/lua/utils/smlua_misc_utils.c | 25 +++++++++ src/pc/lua/utils/smlua_misc_utils.h | 9 +++ src/pc/network/moderation.c | 6 +- src/pc/network/moderation.h | 4 ++ src/pc/network/moderation_list.c | 5 +- src/pc/network/moderation_list.h | 3 +- src/pc/network/network.c | 10 +++- src/pc/network/packets/packet.c | 4 +- src/pc/network/packets/packet.h | 3 +- src/pc/network/packets/packet_command_mod.c | 4 +- src/pc/network/packets/packet_join.c | 2 +- src/pc/network/packets/packet_kick.c | 55 ++++++++++++++++--- src/pc/network/packets/packet_moderation.c | 16 +++--- src/pc/network/packets/packet_player.c | 2 +- 30 files changed, 306 insertions(+), 48 deletions(-) diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua index fef72a27c..9616d057e 100644 --- a/autogen/lua_definitions/constants.lua +++ b/autogen/lua_definitions/constants.lua @@ -8237,6 +8237,15 @@ HOOK_MAX = 60 --- @type LuaHookedEventType --- @type integer MAX_HOOKED_BEHAVIORS = 1024 +DC_LEAVE = 0 --- @type DisconnectType +DC_KICK = 1 --- @type DisconnectType +DC_BAN = 2 --- @type DisconnectType + +--- @alias DisconnectType +--- | `DC_LEAVE` +--- | `DC_KICK` +--- | `DC_BAN` + HUD_DISPLAY_LIVES = 0 --- @type HudDisplayValue HUD_DISPLAY_COINS = 1 --- @type HudDisplayValue HUD_DISPLAY_STARS = 2 --- @type HudDisplayValue diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua index 468a38f0a..ed8a77f12 100644 --- a/autogen/lua_definitions/functions.lua +++ b/autogen/lua_definitions/functions.lua @@ -4149,6 +4149,13 @@ function djui_menu_get_rainbow_string_color(color) -- ... end +--- @param message string +--- @param paddingLines integer +--- Creates an auto-scaling popup that says `message` which will always show the entire message with padding lines of 'paddingLines' +function djui_popup_create_auto_scaling(message, paddingLines) + -- ... +end + --- @param message string --- @param lines integer --- Creates a popup that says `message` and has `lines` @@ -11554,6 +11561,13 @@ function get_coopnet_id(localIndex) -- ... end +--- @param dcType DisconnectType +--- @param reason? string +--- Disconnects the local player with DisconnectType `dcType` (default is DC_LEAVE) because of `reason` (optional). +function network_disconnect(dcType, reason) + -- ... +end + --- @return number --- Gets the master volume level function get_volume_master() diff --git a/docs/lua/constants.md b/docs/lua/constants.md index 8f49768aa..32bdeb4f7 100644 --- a/docs/lua/constants.md +++ b/docs/lua/constants.md @@ -84,6 +84,7 @@ - [enum LuaActionHookType](#enum-LuaActionHookType) - [enum LuaModMenuElementType](#enum-LuaModMenuElementType) - [smlua_misc_utils.h](#smlua_misc_utilsh) + - [enum DisconnectType](#enum-DisconnectType) - [enum HudDisplayValue](#enum-HudDisplayValue) - [enum HudDisplayFlags](#enum-HudDisplayFlags) - [enum ActSelectHudPart](#enum-ActSelectHudPart) @@ -3533,6 +3534,13 @@ ## [smlua_misc_utils.h](#smlua_misc_utils.h) +### [enum DisconnectType](#DisconnectType) +| Identifier | Value | +| :--------- | :---- | +| DC_LEAVE | 0 | +| DC_KICK | 1 | +| DC_BAN | 2 | + ### [enum HudDisplayValue](#HudDisplayValue) | Identifier | Value | | :--------- | :---- | diff --git a/docs/lua/functions-3.md b/docs/lua/functions-3.md index 7b1704e71..251563542 100644 --- a/docs/lua/functions-3.md +++ b/docs/lua/functions-3.md @@ -3823,6 +3823,30 @@ Gets the header hex color code from a `DJUI_RAINBOW_COLOR_*` constant
+## [djui_popup_create_auto_scaling](#djui_popup_create_auto_scaling) + +### Description +Creates an auto-scaling popup that says `message` which will always show the entire message with padding lines of 'paddingLines' + +### Lua Example +`djui_popup_create_auto_scaling(message, paddingLines)` + +### Parameters +| Field | Type | +| ----- | ---- | +| message | `string` | +| paddingLines | `integer` | + +### Returns +- None + +### C Prototype +`void djui_popup_create_auto_scaling(const char* message, int paddingLines);` + +[:arrow_up_small:](#) + +
+ ## [djui_popup_create](#djui_popup_create) ### Description diff --git a/docs/lua/functions-7.md b/docs/lua/functions-7.md index 207edbb6f..8514b8376 100644 --- a/docs/lua/functions-7.md +++ b/docs/lua/functions-7.md @@ -1840,6 +1840,30 @@ Gets the CoopNet ID of a player with `localIndex` if CoopNet is being used and t
+## [network_disconnect](#network_disconnect) + +### Description +Disconnects the local player with DisconnectType `dcType` (default is DC_LEAVE) because of `reason` (optional). + +### Lua Example +`network_disconnect(dcType, reason)` + +### Parameters +| Field | Type | +| ----- | ---- | +| dcType | [enum DisconnectType](constants.md#enum-DisconnectType) | +| reason | `string` | + +### Returns +- None + +### C Prototype +`void network_disconnect(enum DisconnectType dcType, OPTIONAL const char* reason);` + +[:arrow_up_small:](#) + +
+ ## [get_volume_master](#get_volume_master) ### Description diff --git a/docs/lua/functions.md b/docs/lua/functions.md index da30dd6f9..35876b82a 100644 --- a/docs/lua/functions.md +++ b/docs/lua/functions.md @@ -809,6 +809,7 @@
- djui_popup.h + - [djui_popup_create_auto_scaling](functions-3.md#djui_popup_create_auto_scaling) - [djui_popup_create](functions-3.md#djui_popup_create)
@@ -2057,6 +2058,7 @@ - [get_time_stop_flags](functions-7.md#get_time_stop_flags) - [get_local_discord_id](functions-7.md#get_local_discord_id) - [get_coopnet_id](functions-7.md#get_coopnet_id) + - [network_disconnect](functions-7.md#network_disconnect) - [get_volume_master](functions-7.md#get_volume_master) - [get_volume_level](functions-7.md#get_volume_level) - [get_volume_sfx](functions-7.md#get_volume_sfx) diff --git a/lang/English.ini b/lang/English.ini index c22da1361..bd415a766 100644 --- a/lang/English.ini +++ b/lang/English.ini @@ -9,7 +9,9 @@ DISCORD_ERROR = "Discord threw an error.\nTo fix, try: \n1. Close the game.\n2. DISCORD_DETECT = "\\#ffa0a0\\Error:\\#dcdcdc\\ Could not detect Discord.\n\\#a0a0a0\\Try closing the game, restarting Discord, and opening the game again." DISCONNECT_FULL = "\\#ffa0a0\\Disconnected:\\#dcdcdc\\ The server is full." DISCONNECT_KICK = "\\#ffa0a0\\Disconnected:\\#dcdcdc\\ You have been kicked." +DISCONNECT_KICK_REASON = "\\#ffa0a0\\Disconnected:\\#dcdcdc\\ You have been kicked. Reason: @" DISCONNECT_BAN = "\\#ffa0a0\\Disconnected:\\#dcdcdc\\ You have been banned." +DISCONNECT_BAN_REASON = "\\#ffa0a0\\Disconnected:\\#dcdcdc\\ You have been banned. Reason: @" DISCONNECT_REJOIN = "\\#ffa0a0\\Disconnected:\\#dcdcdc\\ Rejoining..." DISCONNECT_CLOSED = "\\#ffa0a0\\Disconnected:\\#dcdcdc\\ Host closed the connection." DISCONNECT_BIG_MOD = "Server had too large of a mod.\nQuitting." diff --git a/src/pc/chat_commands.c b/src/pc/chat_commands.c index 099061cd2..9646182bb 100644 --- a/src/pc/chat_commands.c +++ b/src/pc/chat_commands.c @@ -54,14 +54,18 @@ bool exec_chat_command(char* command) { enum ChatConfirmCommand ccc = sConfirming; sConfirming = CCC_NONE; - if (ccc != CCC_NONE && strcmp("/confirm", command) == 0) { + if (ccc != CCC_NONE && (strcmp("/confirm", command) == 0 || str_starts_with(command, "/confirm "))) { struct NetworkPlayer* np = &gNetworkPlayers[sConfirmPlayerIndex]; if (!np->connected) return true; + char* reason = NULL; + if (str_starts_with(command, "/confirm ")) { + reason = &command[9]; + } if (gNetworkType == NT_SERVER || npl->moderator) { if (ccc == CCC_KICK) { chat_construct_player_message(np, DLANG(CHAT, KICKING)); if (gNetworkType == NT_SERVER) { - network_send_kick(np->localIndex, EKT_KICKED); + network_send_kick(np->localIndex, EKT_KICKED, reason); network_player_disconnected(np->localIndex); } else { network_send_chat_command(np->globalIndex, CCC_KICK); @@ -73,10 +77,10 @@ bool exec_chat_command(char* command) { if (ccc == CCC_BAN) { chat_construct_player_message(np, DLANG(CHAT, BANNING)); if (gNetworkType == NT_SERVER) { - network_send_kick(np->localIndex, EKT_BANNED); + network_send_kick(np->localIndex, EKT_BANNED, reason); // TODO: Moderation: Allow you to insert a reason - moderation_list_add(MODERATION_LIST_TYPE_BAN, np->localIndex, "", false); + moderation_list_add(MODERATION_LIST_TYPE_BAN, np->localIndex, reason, false); network_player_disconnected(np->localIndex); } else { network_send_chat_command(np->globalIndex, CCC_BAN); @@ -86,9 +90,9 @@ bool exec_chat_command(char* command) { } if (gNetworkType == NT_SERVER && ccc == CCC_PERMBAN) { chat_construct_player_message(np, DLANG(CHAT, PERM_BANNING)); - network_send_kick(np->localIndex, EKT_BANNED); + network_send_kick(np->localIndex, EKT_BANNED, reason); // TODO: Moderation: Allow you to insert a reason - moderation_list_add(MODERATION_LIST_TYPE_BAN, np->localIndex, "", true); + moderation_list_add(MODERATION_LIST_TYPE_BAN, np->localIndex, reason, true); network_player_disconnected(np->localIndex); return true; } @@ -97,7 +101,7 @@ bool exec_chat_command(char* command) { np->moderator = true; network_send_moderator(np->localIndex); // TODO: Moderation: Allow you to insert a reason - moderation_list_add(MODERATION_LIST_TYPE_MODERATOR, np->localIndex, "", true); + moderation_list_add(MODERATION_LIST_TYPE_MODERATOR, np->localIndex, reason, true); return true; } } diff --git a/src/pc/djui/djui_language.c b/src/pc/djui/djui_language.c index 1de53e651..370ad6d48 100644 --- a/src/pc/djui/djui_language.c +++ b/src/pc/djui/djui_language.c @@ -45,7 +45,7 @@ void djui_language_replace(char* src, char* dst, int size, char key, char* value char* o = dst; while (*c != '\0') { if (*c == key) { - snprintf(o, size - (o - dst), "%s", value); + snprintf(o, size - (o - dst), "%s", value == NULL ? "" : value); } else { djui_unicode_get_char(c, tmpChar); snprintf(o, size - (o - dst), "%s", tmpChar); diff --git a/src/pc/djui/djui_panel_moderation_confirm_action.c b/src/pc/djui/djui_panel_moderation_confirm_action.c index b2507abe2..a6c65ecae 100644 --- a/src/pc/djui/djui_panel_moderation_confirm_action.c +++ b/src/pc/djui/djui_panel_moderation_confirm_action.c @@ -84,6 +84,7 @@ static void djui_panel_moderation_confirm_destroy(struct DjuiBase* base) { free(sReason); sReason = NULL; sPermanent = false; + sOnYesClick = NULL; } void djui_panel_moderation_confirm_create_body(struct DjuiBase* caller, char* title, char* message, u8 localIndex, u8 action, bool permanent, char* address, void (*on_yes_click)(struct DjuiBase*)) { @@ -95,7 +96,7 @@ void djui_panel_moderation_confirm_create_body(struct DjuiBase* caller, char* ti struct DjuiText* text = djui_text_create(body, message); djui_base_set_size_type(&text->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); - if (action == MODERATION_ACTION_BAN || action == MODERATION_ACTION_MOD) { + if (action == MODERATION_ACTION_KICK || action == MODERATION_ACTION_BAN || action == MODERATION_ACTION_MOD) { struct DjuiRect* rect1 = djui_rect_container_create(body, 32); { struct DjuiText* text1 = djui_text_create(&rect1->base, DLANG(MODERATION, REASON)); @@ -112,7 +113,7 @@ void djui_panel_moderation_confirm_create_body(struct DjuiBase* caller, char* ti djui_interactable_hook_value_change(&inputbox1->base, djui_panel_moderation_confirm_reason_text_change); } - djui_checkbox_create(body, DLANG(MODERATION, PERMANENT), &sPermanent, NULL); + if (action != MODERATION_ACTION_KICK) djui_checkbox_create(body, DLANG(MODERATION, PERMANENT), &sPermanent, NULL); } djui_base_set_size(&text->base, 1.0f, 64); diff --git a/src/pc/djui/djui_panel_moderation_list_inspector.c b/src/pc/djui/djui_panel_moderation_list_inspector.c index d6ffd2003..2fec5ca0a 100644 --- a/src/pc/djui/djui_panel_moderation_list_inspector.c +++ b/src/pc/djui/djui_panel_moderation_list_inspector.c @@ -71,9 +71,10 @@ void djui_panel_moderation_list_inspect_create(struct DjuiBase* caller) { djui_text_set_drop_shadow(discordIdText, 64, 64, 64, 100); } - if (entry->reason && strcmp(entry->reason, "") != 0) { - char reasonStr[512]; - djui_language_replace(DLANG(MODERATION, REASON_INFO), reasonStr, 512, '@', entry->reason); + if (entry->reason[0] != '\0') { + int reasonStrLen = MAX_REASON_LENGTH + strlen(DLANG(MODERATION, REASON_INFO)) + 1; + char reasonStr[reasonStrLen]; + djui_language_replace(DLANG(MODERATION, REASON_INFO), reasonStr, reasonStrLen, '@', entry->reason); struct DjuiText* reasonText = djui_text_create(body, reasonStr); djui_base_set_color(&reasonText->base, 220, 220, 220, 255); djui_base_set_size_type(&reasonText->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); diff --git a/src/pc/djui/djui_panel_moderator_menu_inspect.c b/src/pc/djui/djui_panel_moderator_menu_inspect.c index 50a4e0830..10f76c523 100644 --- a/src/pc/djui/djui_panel_moderator_menu_inspect.c +++ b/src/pc/djui/djui_panel_moderator_menu_inspect.c @@ -4,6 +4,7 @@ #include "djui_panel.h" #include "djui_panel_menu.h" #include "djui_panel_moderator_menu.h" +#include "djui_panel_moderator_menu_inspect.h" #include "djui_panel_moderation_list.h" #include "djui_panel_moderation_confirm_action.h" #include "pc/network/network.h" @@ -12,8 +13,10 @@ struct DjuiButton* sModButton = NULL; static u8 sSelectedIndex = 0; +static void djui_panel_moderator_inspector_validate_and_reload(); + static void djui_panel_moderator_menu_action_button_click(struct DjuiBase* caller) { - djui_panel_moderation_confirm_create(caller, caller->uTag, caller->tag, caller->bTag, djui_panel_moderator_menu_reload); + djui_panel_moderation_confirm_create(caller, caller->uTag, caller->tag, caller->bTag, djui_panel_moderator_inspector_validate_and_reload); } static void djui_panel_moderator_menu_inspector_destroy(struct DjuiBase* base) { @@ -22,6 +25,14 @@ static void djui_panel_moderator_menu_inspector_destroy(struct DjuiBase* base) { sModButton = NULL; } +static void djui_panel_moderator_inspector_validate_and_reload() { + djui_panel_moderator_menu_reload(); + if (sSelectedIndex >= MAX_PLAYERS) djui_panel_back_by(2); + struct NetworkPlayer* np = &gNetworkPlayers[sSelectedIndex]; + if (!np->connected) djui_panel_back_by(2); + djui_panel_moderator_inspector_reload(); +} + void djui_panel_moderator_inspector_reload() { if (!sModButton) return; if (sSelectedIndex >= MAX_PLAYERS) return; diff --git a/src/pc/djui/djui_popup.c b/src/pc/djui/djui_popup.c index 4d0225a45..01673ae64 100644 --- a/src/pc/djui/djui_popup.c +++ b/src/pc/djui/djui_popup.c @@ -5,7 +5,9 @@ #include "pc/utils/misc.h" #include "pc/configfile.h" #include "pc/lua/utils/smlua_misc_utils.h" +#include "djui_hud_utils.h" +#define DJUI_POPUP_WIDTH 400.0f #define DJUI_POPUP_LIFETIME 6.0f struct DjuiPopupList { @@ -35,25 +37,27 @@ static void djui_popup_destroy(struct DjuiBase* base) { free(popup); } -void djui_popup_create(const char* message, int lines) { +static void djui_popup_create_interal(const char* message, int lines, int paddingLines) { if (djui_is_popup_disabled()) { return; } + if (paddingLines < 0) paddingLines = 0; struct DjuiPopup* popup = calloc(1, sizeof(struct DjuiPopup)); struct DjuiBase* base = &popup->base; - f32 height = lines * 32 + 32; + f32 height = (lines + paddingLines) * 32 + 32; djui_base_init(&gDjuiRoot->base, base, djui_popup_render, djui_popup_destroy); djui_base_set_alignment(base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP); djui_base_set_location(base, 8, -height); - djui_base_set_size(base, 400, height); + djui_base_set_size(base, DJUI_POPUP_WIDTH, height); djui_base_set_border_width(base, 4); djui_base_set_color(base, 0, 0, 0, 220); djui_base_set_border_color(base, 0, 0, 0, 180); struct DjuiText* text = djui_text_create(base, message); - djui_base_set_size_type(&text->base, DJUI_SVT_RELATIVE, DJUI_SVT_RELATIVE); - djui_base_set_size(&text->base, 1.0f, 1.0f); + djui_base_set_alignment(&text->base, DJUI_HALIGN_CENTER, DJUI_VALIGN_CENTER); + djui_base_set_size_type(&text->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&text->base, 1.0f, lines * 32.0f); djui_base_set_color(&text->base, 220, 220, 220, 255); - djui_text_set_alignment(text, DJUI_HALIGN_CENTER, DJUI_VALIGN_CENTER); + djui_text_set_alignment(text, DJUI_HALIGN_CENTER, DJUI_VALIGN_TOP); djui_text_set_drop_shadow(text, 0, 0, 0, 64); popup->text = text; @@ -62,6 +66,19 @@ void djui_popup_create(const char* message, int lines) { play_sound(SOUND_MENU_PINCH_MARIO_FACE, gGlobalSoundSource); } +void djui_popup_create(const char* message, int lines) { + int linesReq = (int)ceilf(djui_hud_measure_text(message) / DJUI_POPUP_WIDTH); + if (linesReq < 1) linesReq = 1; + if (linesReq > lines) linesReq = lines; + djui_popup_create_interal(message, linesReq, lines - linesReq); +} + +void djui_popup_create_auto_scaling(const char* message, int paddingLines) { + int linesReq = (int)ceilf(djui_hud_measure_text(message) / DJUI_POPUP_WIDTH); + if (linesReq < 1) linesReq = 1; + djui_popup_create_interal(message, linesReq, paddingLines); +} + void djui_popup_update(void) { struct DjuiPopupList* node = sPopupListHead; struct DjuiPopupList* last = NULL; diff --git a/src/pc/djui/djui_popup.h b/src/pc/djui/djui_popup.h index c823baa87..eb62c5884 100644 --- a/src/pc/djui/djui_popup.h +++ b/src/pc/djui/djui_popup.h @@ -6,6 +6,9 @@ struct DjuiPopup { struct DjuiText* text; }; +/* |description|Creates an auto-scaling popup that says `message` which will always show the entire message with padding lines of 'paddingLines'|descriptionEnd| */ +void djui_popup_create_auto_scaling(const char* message, int paddingLines); /* |description|Creates a popup that says `message` and has `lines`|descriptionEnd| */ void djui_popup_create(const char* message, int lines); + void djui_popup_update(void); diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c index bc67f2ae0..4d3120e08 100644 --- a/src/pc/lua/smlua_constants_autogen.c +++ b/src/pc/lua/smlua_constants_autogen.c @@ -3536,6 +3536,9 @@ char gSmluaConstants[] = "" "HOOK_ON_PACKET_BYTESTRING_RECEIVE=59\n" "HOOK_MAX=60\n" "MAX_HOOKED_BEHAVIORS=1024\n" +"DC_LEAVE=0\n" +"DC_KICK=1\n" +"DC_BAN=2\n" "HUD_DISPLAY_LIVES=0\n" "HUD_DISPLAY_COINS=1\n" "HUD_DISPLAY_STARS=2\n" diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c index b0424d43f..9f7e9c400 100644 --- a/src/pc/lua/smlua_functions_autogen.c +++ b/src/pc/lua/smlua_functions_autogen.c @@ -13067,6 +13067,25 @@ int smlua_func_djui_menu_get_rainbow_string_color(lua_State* L) { // djui_popup.h // ////////////////// +int smlua_func_djui_popup_create_auto_scaling(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 2) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "djui_popup_create_auto_scaling", 2, top); + return 0; + } + + const char* message = smlua_to_string(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "djui_popup_create_auto_scaling"); return 0; } + int paddingLines = smlua_to_integer(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "djui_popup_create_auto_scaling"); return 0; } + + djui_popup_create_auto_scaling(message, paddingLines); + + return 1; +} + int smlua_func_djui_popup_create(lua_State* L) { if (L == NULL) { return 0; } @@ -34051,6 +34070,28 @@ int smlua_func_get_coopnet_id(lua_State* L) { return 1; } +int smlua_func_network_disconnect(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top < 1 || top > 2) { + LOG_LUA_LINE("Improper param count for '%s': Expected between %u and %u, Received %u", "network_disconnect", 1, 2, top); + return 0; + } + + int dcType = smlua_to_integer(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "network_disconnect"); return 0; } + const char* reason = (const char*) NULL; + if (top >= 2) { + reason = smlua_to_string(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "network_disconnect"); return 0; } + } + + network_disconnect(dcType, reason); + + return 1; +} + int smlua_func_get_volume_master(UNUSED lua_State* L) { if (L == NULL) { return 0; } @@ -37446,6 +37487,7 @@ void smlua_bind_functions_autogen(void) { smlua_bind_function(L, "djui_menu_get_rainbow_string_color", smlua_func_djui_menu_get_rainbow_string_color); // djui_popup.h + smlua_bind_function(L, "djui_popup_create_auto_scaling", smlua_func_djui_popup_create_auto_scaling); smlua_bind_function(L, "djui_popup_create", smlua_func_djui_popup_create); // external.h @@ -38617,6 +38659,7 @@ void smlua_bind_functions_autogen(void) { smlua_bind_function(L, "get_time_stop_flags", smlua_func_get_time_stop_flags); smlua_bind_function(L, "get_local_discord_id", smlua_func_get_local_discord_id); smlua_bind_function(L, "get_coopnet_id", smlua_func_get_coopnet_id); + smlua_bind_function(L, "network_disconnect", smlua_func_network_disconnect); smlua_bind_function(L, "get_volume_master", smlua_func_get_volume_master); smlua_bind_function(L, "get_volume_level", smlua_func_get_volume_level); smlua_bind_function(L, "get_volume_sfx", smlua_func_get_volume_sfx); diff --git a/src/pc/lua/utils/smlua_misc_utils.c b/src/pc/lua/utils/smlua_misc_utils.c index de447b3d1..3f7e53081 100644 --- a/src/pc/lua/utils/smlua_misc_utils.c +++ b/src/pc/lua/utils/smlua_misc_utils.c @@ -31,6 +31,7 @@ #include "game/rumble_init.h" #include "game/sound_init.h" #include "pc/lua/utils/smlua_audio_utils.h" +#include "pc/network/moderation.h" #ifdef DISCORD_SDK #include "pc/discord/discord.h" @@ -524,6 +525,30 @@ const char* get_coopnet_id(UNUSED s8 localIndex) { /// +void network_disconnect(enum DisconnectType dcType, OPTIONAL const char* reason) { + switch (dcType) { + case DC_KICK: + if (gNetworkType == NT_SERVER) { + LOG_LUA("network_disconnect: Cannot kick the server!"); + return; + } + network_send_moderation_action(MODERATION_ACTION_KICK, 0, (char*)reason, false); + break; + case DC_BAN: + if (gNetworkType == NT_SERVER) { + LOG_LUA("network_disconnect: Cannot ban the server!"); + return; + } + network_send_moderation_action(MODERATION_ACTION_BAN, 0, (char*)reason, false); + break; + default: + gQueuedDisconnect = dcType; + break; + } +} + +/// + f32 get_volume_master(void) { return gLuaVolumeMaster; } diff --git a/src/pc/lua/utils/smlua_misc_utils.h b/src/pc/lua/utils/smlua_misc_utils.h index c9f07b98b..5fce12641 100644 --- a/src/pc/lua/utils/smlua_misc_utils.h +++ b/src/pc/lua/utils/smlua_misc_utils.h @@ -5,6 +5,12 @@ #include "game/camera.h" #include "pc/lua/smlua_utils.h" +enum DisconnectType { + DC_LEAVE, + DC_KICK, + DC_BAN +}; + enum HudDisplayValue { HUD_DISPLAY_LIVES, HUD_DISPLAY_COINS, @@ -219,6 +225,9 @@ const char* get_local_discord_id(void); /* |description|Gets the CoopNet ID of a player with `localIndex` if CoopNet is being used and the player is connected, otherwise "-1" is returned|descriptionEnd| */ const char* get_coopnet_id(s8 localIndex); +/* |description|Disconnects the local player with DisconnectType `dcType` (default is DC_LEAVE) because of `reason` (optional).|descriptionEnd| */ +void network_disconnect(enum DisconnectType dcType, OPTIONAL const char* reason); + /* |description|Gets the master volume level|descriptionEnd| */ f32 get_volume_master(void); /* |description|Gets the volume level of music|descriptionEnd| */ diff --git a/src/pc/network/moderation.c b/src/pc/network/moderation.c index 32dad08ae..e63b603a9 100644 --- a/src/pc/network/moderation.c +++ b/src/pc/network/moderation.c @@ -8,6 +8,8 @@ #include "pc/debuglog.h" #include "pc/ini.h" +u8 gQueuedDisconnect = QUEUED_DISCONNECT_NONE; + void djui_reload_moderation_panels() { djui_panel_moderator_menu_reload(); djui_panel_moderation_list_reload(); @@ -28,7 +30,7 @@ void network_kick_player(u8 localIndex, char* reason) { LOG_ERROR("Tried to perform moderation on disconnected player!"); return; } - network_send_kick(np->localIndex, EKT_KICKED); + network_send_kick(np->localIndex, EKT_KICKED, reason); network_player_disconnected(np->globalIndex); } @@ -47,7 +49,7 @@ void network_ban_player(u8 localIndex, char* reason, bool permanent) { return; } moderation_list_add(MODERATION_LIST_TYPE_BAN, localIndex, reason, permanent); - network_send_kick(np->localIndex, EKT_BANNED); + network_send_kick(np->localIndex, EKT_BANNED, reason); network_player_disconnected(np->globalIndex); } diff --git a/src/pc/network/moderation.h b/src/pc/network/moderation.h index 64bec5986..e4f9e8d5b 100644 --- a/src/pc/network/moderation.h +++ b/src/pc/network/moderation.h @@ -1,6 +1,8 @@ #pragma once #include +#define QUEUED_DISCONNECT_NONE 255 + enum ModerationActions { MODERATION_ACTION_KICK, MODERATION_ACTION_BAN, @@ -10,6 +12,8 @@ enum ModerationActions { MODERATION_ACTION_COUNT, }; +extern u8 gQueuedDisconnect; + void djui_reload_moderation_panels(); void network_kick_player(u8 localIndex, char* reason); void network_ban_player(u8 localIndex, char* reason, bool permanent); diff --git a/src/pc/network/moderation_list.c b/src/pc/network/moderation_list.c index fa5d36e69..3471f40a6 100644 --- a/src/pc/network/moderation_list.c +++ b/src/pc/network/moderation_list.c @@ -92,7 +92,7 @@ void moderation_list_load() { entry->playerColor[2] = strtol(safe_ini_get(iniFile, entrySection, "playerColorB"), NULL, 0); entry->address = strdup(safe_ini_get(iniFile, entrySection, "address")); entry->discordId = strdup(safe_ini_get(iniFile, entrySection, "discordId")); - entry->reason = strdup(safe_ini_get(iniFile, entrySection, "reason")); + snprintf(entry->reason, MAX_REASON_LENGTH, "%s", safe_ini_get(iniFile, entrySection, "reason")); list->list[list->count++] = entry; } @@ -114,7 +114,7 @@ void moderation_list_add(enum ModerationListType type, u8 localIndex, char* reas memcpy(entry->playerColor, network_get_player_text_color(np->localIndex), 3); entry->address = strdup(gNetworkSystem->get_id_str(localIndex)); entry->discordId = strdup(network_discord_id_from_local_index(localIndex)); - entry->reason = strdup(reason ? reason : ""); + snprintf(entry->reason, MAX_REASON_LENGTH, "%s", reason ? reason : ""); entry->permanent = permanent; time(&entry->time); @@ -131,7 +131,6 @@ void moderation_list_remove(enum ModerationListType type, char* address) { free(list->list[i]->playerName); free(list->list[i]->address); free(list->list[i]->discordId); - free(list->list[i]->reason); free(list->list[i]); for (u16 j = i; j < list->count - 1; j++) { diff --git a/src/pc/network/moderation_list.h b/src/pc/network/moderation_list.h index cbbdd9be7..1ca5a31ae 100644 --- a/src/pc/network/moderation_list.h +++ b/src/pc/network/moderation_list.h @@ -5,6 +5,7 @@ #define MODERATION_LIST_FILEPATH "moderation_list.ini" #define MAX_MODERATION_LIST_ENTRIES 1024 +#define MAX_REASON_LENGTH 256 enum ModerationListType { MODERATION_LIST_TYPE_BAN, @@ -18,7 +19,7 @@ struct ModerationEntry { u8 playerColor[3]; char* address; char* discordId; - char* reason; + char reason[MAX_REASON_LENGTH]; bool permanent; }; diff --git a/src/pc/network/network.c b/src/pc/network/network.c index 6bd1d4882..16b00593f 100644 --- a/src/pc/network/network.c +++ b/src/pc/network/network.c @@ -6,6 +6,7 @@ #include "game/level_update.h" #include "object_constants.h" #include "behavior_table.h" +#include "moderation.h" #include "pc/configfile.h" #include "pc/djui/djui.h" #include "pc/djui/djui_panel.h" @@ -498,7 +499,7 @@ void network_rehost_begin(void) { struct NetworkPlayer* np = &gNetworkPlayers[i]; if (!np->connected) { continue; } - network_send_kick(i, EKT_REJOIN); + network_send_kick(i, EKT_REJOIN, NULL); network_player_disconnected(i); } @@ -635,6 +636,13 @@ void network_update(void) { network_reset_reconnect_and_rehost(); network_shutdown(true, false, false, false); } + + if (gNetworkType != NT_NONE && !gDjuiInMainMenu && gQueuedDisconnect != QUEUED_DISCONNECT_NONE) { + network_reset_reconnect_and_rehost(); + network_shutdown(true, false, false, false); + } else { + gQueuedDisconnect = QUEUED_DISCONNECT_NONE; + } } static inline void color_set(Color color, u8 r, u8 g, u8 b) { diff --git a/src/pc/network/packets/packet.c b/src/pc/network/packets/packet.c index b2ccc9596..38e837158 100644 --- a/src/pc/network/packets/packet.c +++ b/src/pc/network/packets/packet.c @@ -156,7 +156,7 @@ void packet_receive(struct Packet* p) { if (gNetworkType == NT_SERVER) { if (moderation_list_contains(MODERATION_LIST_TYPE_BAN, gNetworkSystem->get_id_str(p->localIndex))) { LOG_INFO("kicking banned player"); - network_send_kick(0, EKT_BANNED); + network_send_kick(0, EKT_BANNED, NULL); return; } } @@ -179,7 +179,7 @@ void packet_receive(struct Packet* p) { } if (packetType != PACKET_PLAYER) { LOG_INFO("closing connection for packetType: %d", packetType); - network_send_kick(0, EKT_CLOSE_CONNECTION); + network_send_kick(0, EKT_CLOSE_CONNECTION, NULL); } LOG_INFO("refusing packet from unknown player, packetType: %d", packetType); return; diff --git a/src/pc/network/packets/packet.h b/src/pc/network/packets/packet.h index 9b2427fd7..56b48e07d 100644 --- a/src/pc/network/packets/packet.h +++ b/src/pc/network/packets/packet.h @@ -238,7 +238,8 @@ void network_send_chat(char* message, u8 globalIndex); void network_receive_chat(struct Packet* p); // packet_kick.c -void network_send_kick(u8 localIndex, enum KickReasonType kickReason); +void network_create_kick_popup(enum KickReasonType kickReason, char* reason); +void network_send_kick(u8 localIndex, enum KickReasonType kickReason, char* reason); void network_receive_kick(struct Packet* p); // packet_command_mod.c diff --git a/src/pc/network/packets/packet_command_mod.c b/src/pc/network/packets/packet_command_mod.c index 58cdba588..55acf9e14 100644 --- a/src/pc/network/packets/packet_command_mod.c +++ b/src/pc/network/packets/packet_command_mod.c @@ -42,11 +42,11 @@ void network_receive_chat_command(struct Packet *p) { } char message[256] = { 0 }; if (CCC == CCC_KICK) { - network_send_kick(np->localIndex, EKT_KICKED); + network_send_kick(np->localIndex, EKT_KICKED, NULL); snprintf(message, 256, "\\#fff982\\Kicked '%s%s\\#fff982\\'!", network_get_player_text_color_string(np->localIndex), np->name); } if (CCC == CCC_BAN) { - network_send_kick(np->localIndex, EKT_BANNED); + network_send_kick(np->localIndex, EKT_BANNED, NULL); // TODO: Moderation: Allow you to insert a reason moderation_list_add(MODERATION_LIST_TYPE_BAN, np->localIndex, "", false); snprintf(message, 256, "\\#fff982\\Banned '%s%s\\#fff982\\'!", network_get_player_text_color_string(np->localIndex), np->name); diff --git a/src/pc/network/packets/packet_join.c b/src/pc/network/packets/packet_join.c index 4d643452d..abfd46b37 100644 --- a/src/pc/network/packets/packet_join.c +++ b/src/pc/network/packets/packet_join.c @@ -96,7 +96,7 @@ void network_send_join(struct Packet* joinRequestPacket) { } } if (globalIndex == UNKNOWN_LOCAL_INDEX || connectedCount >= gServerSettings.maxPlayers) { - network_send_kick(0, EKT_FULL_PARTY); + network_send_kick(0, EKT_FULL_PARTY, NULL); return; } } diff --git a/src/pc/network/packets/packet_kick.c b/src/pc/network/packets/packet_kick.c index b39bd591c..5fb933546 100644 --- a/src/pc/network/packets/packet_kick.c +++ b/src/pc/network/packets/packet_kick.c @@ -1,5 +1,6 @@ #include #include "../network.h" +#include "../moderation.h" #include "pc/debuglog.h" #include "pc/djui/djui.h" #include "pc/utils/misc.h" @@ -7,7 +8,38 @@ f32 sLastReconnectTime = -9999999; f32 sLastNotifyTime = -9999999; -void network_send_kick(u8 localIndex, enum KickReasonType kickReason) { +void network_create_kick_popup(enum KickReasonType kickReason, char* reason) { + char* text = NULL; + + switch (kickReason) { + case EKT_FULL_PARTY: + text = DLANG(NOTIF, DISCONNECT_FULL); + break; + case EKT_KICKED: + if (reason && reason[0] != '\0') { + text = DLANG(NOTIF, DISCONNECT_KICK_REASON); + } else { + text = DLANG(NOTIF, DISCONNECT_KICK); + } + break; + case EKT_BANNED: + if (reason && reason[0] != '\0') { + text = DLANG(NOTIF, DISCONNECT_BAN_REASON); + } else { + text = DLANG(NOTIF, DISCONNECT_BAN); + } + break; + default: + text = DLANG(NOTIF, DISCONNECT_CLOSED); + break; + } + + char popupText[512] = { 0 }; + djui_language_replace(text, popupText, 512, '@', reason); + djui_popup_create_auto_scaling(popupText, 1); +} + +void network_send_kick(u8 localIndex, enum KickReasonType kickReason, char* reason) { if (localIndex == 0) { LOG_ERROR("Trying to send kick to myself?"); return; @@ -17,6 +49,15 @@ void network_send_kick(u8 localIndex, enum KickReasonType kickReason) { packet_init(&p, PACKET_KICK, true, PLMT_NONE); p.keepSendingAfterDisconnect = (kickReason == EKT_REJOIN); packet_write(&p, &kickReasonType, sizeof(u8)); + if (reason) { + u16 reasonLength = strlen(reason); + if (reasonLength >= MAX_REASON_LENGTH) reasonLength = MAX_REASON_LENGTH - 1; + packet_write(&p, &reasonLength, sizeof(u16)); + packet_write(&p, reason, sizeof(u8) * reasonLength); + } else { + u16 reasonLength = 0; + packet_write(&p, &reasonLength, sizeof(u16)); + } network_send_to(localIndex, &p); } @@ -32,18 +73,18 @@ void network_receive_kick(struct Packet* p) { } u8 kickReasonType; + u16 reasonLength = 0; + char reason[MAX_REASON_LENGTH] = { 0 }; packet_read(p, &kickReasonType, sizeof(u8)); enum KickReasonType kickReason = kickReasonType; + packet_read(p, &reasonLength, sizeof(u16)); + if (reasonLength >= MAX_REASON_LENGTH) reasonLength = MAX_REASON_LENGTH - 1; + if (reasonLength > 0) packet_read(p, reason, sizeof(u8) * reasonLength); 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; - } + network_create_kick_popup(kickReasonType, reason); } if (kickReason == EKT_REJOIN) { diff --git a/src/pc/network/packets/packet_moderation.c b/src/pc/network/packets/packet_moderation.c index ccabb4324..d19f521bf 100644 --- a/src/pc/network/packets/packet_moderation.c +++ b/src/pc/network/packets/packet_moderation.c @@ -10,7 +10,7 @@ bool sValidActions[MODERATION_ACTION_COUNT] = { void network_send_moderation_action(u8 action, u8 localIndex, char* reason, bool permanent) { SOFT_ASSERT(gNetworkType != NT_SERVER); - if (!gNetworkPlayerLocal->moderator) { + if (!gNetworkPlayerLocal->moderator && localIndex != 0) { LOG_ERROR("Tried to send moderation action as a non-moderator!"); return; } @@ -18,7 +18,7 @@ void network_send_moderation_action(u8 action, u8 localIndex, char* reason, bool if (!np->connected) { LOG_ERROR("Moderator tried to perform moderation on a disconnected player!"); } - if (np->moderator) { + if (np->moderator && localIndex != 0) { LOG_ERROR("Moderator tried to perform moderation on another moderator!"); return; } @@ -32,16 +32,17 @@ void network_send_moderation_action(u8 action, u8 localIndex, char* reason, bool packet_init(&p, PACKET_MODERATION_ACTION, false, PLMT_NONE); packet_write(&p, &action, sizeof(u8)); packet_write(&p, &np->globalIndex, sizeof(u8)); + u16 reasonLength = 0; if (reason) { u16 reasonLength = strlen(reason); packet_write(&p, &reasonLength, sizeof(u16)); - packet_write(&p, &reason, sizeof(u8) * reasonLength); + packet_write(&p, reason, sizeof(u8) * reasonLength); } else { - packet_write(&p, 0, sizeof(u16)); + packet_write(&p, &reasonLength, sizeof(u16)); } packet_write(&p, &permanent, sizeof(bool)); - network_send_to(gNetworkPlayerServer->globalIndex, &p); + network_send_to(gNetworkPlayerServer->localIndex, &p); } void network_receive_moderation_action(struct Packet* p) { @@ -50,7 +51,7 @@ void network_receive_moderation_action(struct Packet* p) { enum ModerationActions action = MODERATION_ACTION_COUNT; u8 globalIndex = 0; u16 reasonLength = 0; - char* reason = NULL; + char reason[MAX_REASON_LENGTH] = { 0 }; bool permanent = false; packet_read(p, &action, sizeof(u8)); @@ -71,7 +72,8 @@ void network_receive_moderation_action(struct Packet* p) { } packet_read(p, &reasonLength, sizeof(u16)); - packet_read(p, &reason, sizeof(u8) * reasonLength); + if (reasonLength >= MAX_REASON_LENGTH) reasonLength = MAX_REASON_LENGTH - 1; + packet_read(p, reason, sizeof(u8) * reasonLength); packet_read(p, &permanent, sizeof(bool)); switch (action) { diff --git a/src/pc/network/packets/packet_player.c b/src/pc/network/packets/packet_player.c index 3131136bb..7c79bf8ca 100644 --- a/src/pc/network/packets/packet_player.c +++ b/src/pc/network/packets/packet_player.c @@ -252,7 +252,7 @@ void network_receive_player(struct Packet* p) { construct_player_popup(np, DLANG(NOTIF, DEBUG_FLY), NULL); } #else - network_send_kick(np->localIndex, EKT_KICKED); + network_send_kick(np->localIndex, EKT_KICKED, NULL); network_player_disconnected(np->globalIndex); return; #endif