From 11ee2f6e82760d0c1ab64bb511df9b35b92420e0 Mon Sep 17 00:00:00 2001 From: EmeraldLockdown <86802223+EmeraldLoc@users.noreply.github.com> Date: Fri, 6 Mar 2026 23:27:10 -0600 Subject: [PATCH 01/22] Begin work on a moderation rework - Adds a new ui for moderation - Redoes the entire backend system What still needs to be done: - Polish - Languages - Need to redo some of the code related to ui to make it cleaner and more concise - other stuff probably --- lang/English.ini | 5 ++++ src/pc/chat_commands.c | 17 ++++++++------ src/pc/djui/djui_base.c | 10 ++++---- src/pc/djui/djui_base.h | 2 ++ src/pc/djui/djui_panel_confirm.c | 2 +- src/pc/djui/djui_panel_host.c | 2 ++ src/pc/djui/djui_panel_pause.c | 5 ++++ src/pc/lua/utils/smlua_misc_utils.h | 3 +-- src/pc/network/packets/packet.c | 5 ++-- src/pc/network/packets/packet.h | 7 +++++- src/pc/network/packets/packet_command_mod.c | 23 ++++++++++++------- src/pc/network/packets/packet_join.c | 11 ++++++--- src/pc/network/packets/packet_kick.c | 8 +++++-- .../network/packets/packet_network_players.c | 7 +++--- src/pc/network/packets/packet_player.c | 2 +- src/pc/pc_main.c | 2 ++ 16 files changed, 77 insertions(+), 34 deletions(-) diff --git a/lang/English.ini b/lang/English.ini index 14ec1d462..60553fc5d 100644 --- a/lang/English.ini +++ b/lang/English.ini @@ -58,6 +58,7 @@ MOD_DESC = "/moderator [NAME|ID] - Make this player able to use commands like /k NAMETAGS_DESC = "/nametags [show-tag|show-health] - Change whether or not you see your own nametag and whether or not you see health" UNRECOGNIZED = "Unrecognized chat command." MOD_GRANTED = "\\#fff982\\You are now a Moderator." +MOD_REVOKED = "\\#FE7F7F\\You are no longer a Moderator." [MENU] BACK = "Back" @@ -338,6 +339,9 @@ CONSOLE = "CONSOLE" [MODLIST] MODS = "MODS" +[MODERATOR_MENU] +MODERATOR_MENU_TITLE = "MODERATOR MENU" + [OPTIONS] OPTIONS = "OPTIONS" PLAYER = "Player" @@ -359,6 +363,7 @@ DYNOS_PACKS = "DynOS Packs" OPTIONS = "Options" CHEATS = "Cheats" SERVER_SETTINGS = "Server Settings" +MODERATOR_MENU = "Moderator Menu" RESUME = "Resume" STOP_HOSTING = "Stop Hosting" DISCONNECT = "Disconnect" diff --git a/src/pc/chat_commands.c b/src/pc/chat_commands.c index adb2bec1e..099061cd2 100644 --- a/src/pc/chat_commands.c +++ b/src/pc/chat_commands.c @@ -4,8 +4,7 @@ #include "pc/djui/djui_language.h" #include "pc/djui/djui_chat_message.h" #include "chat_commands.h" -#include "pc/network/ban_list.h" -#include "pc/network/moderator_list.h" +#include "pc/network/moderation.h" #include "pc/debuglog.h" #include "pc/lua/utils/smlua_level_utils.h" #include "pc/mods/mods_utils.h" @@ -75,7 +74,9 @@ bool exec_chat_command(char* command) { chat_construct_player_message(np, DLANG(CHAT, BANNING)); if (gNetworkType == NT_SERVER) { network_send_kick(np->localIndex, EKT_BANNED); - ban_list_add(gNetworkSystem->get_id_str(np->localIndex), false); + + // TODO: Moderation: Allow you to insert a reason + moderation_list_add(MODERATION_LIST_TYPE_BAN, np->localIndex, "", false); network_player_disconnected(np->localIndex); } else { network_send_chat_command(np->globalIndex, CCC_BAN); @@ -86,7 +87,8 @@ 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); - ban_list_add(gNetworkSystem->get_id_str(np->localIndex), true); + // TODO: Moderation: Allow you to insert a reason + moderation_list_add(MODERATION_LIST_TYPE_BAN, np->localIndex, "", true); network_player_disconnected(np->localIndex); return true; } @@ -94,7 +96,8 @@ bool exec_chat_command(char* command) { chat_construct_player_message(np, DLANG(CHAT, ADD_MODERATOR)); np->moderator = true; network_send_moderator(np->localIndex); - moderator_list_add(gNetworkSystem->get_id_str(np->localIndex), true); + // TODO: Moderation: Allow you to insert a reason + moderation_list_add(MODERATION_LIST_TYPE_MODERATOR, np->localIndex, "", true); return true; } } @@ -120,7 +123,7 @@ bool exec_chat_command(char* command) { djui_chat_message_create(DLANG(CHAT, PLAYER_NOT_FOUND)); return true; } - + if (str_starts_with(command, "/kick ")) { if (gNetworkType != NT_SERVER && !npl->moderator) { djui_chat_message_create(DLANG(CHAT, NO_PERMS)); @@ -205,7 +208,7 @@ bool exec_chat_command(char* command) { return true; } - if (str_starts_with(command, "/moderator ")) { + if (str_starts_with(command, "/moderator ")) { if (gNetworkType != NT_SERVER) { djui_chat_message_create(DLANG(CHAT, SERVER_ONLY)); return true; diff --git a/src/pc/djui/djui_base.c b/src/pc/djui/djui_base.c index e4d85a506..7b60017e5 100644 --- a/src/pc/djui/djui_base.c +++ b/src/pc/djui/djui_base.c @@ -391,10 +391,12 @@ void djui_base_destroy(struct DjuiBase* base) { } // deallocate interactable - if (base->interactable != NULL) { - free(base->interactable); - base->interactable = NULL; - } + free(base->interactable); + base->interactable = NULL; + + // deallocate ctag + free(base->cTag); + base->cTag = NULL; // remove from interactable variable if (base == gDjuiHovered) { gDjuiHovered = NULL; } diff --git a/src/pc/djui/djui_base.h b/src/pc/djui/djui_base.h index 60174824e..2864c3875 100644 --- a/src/pc/djui/djui_base.h +++ b/src/pc/djui/djui_base.h @@ -43,7 +43,9 @@ struct DjuiBase { bool abandonAfterChildRenderFail; bool gradient; s64 tag; + u64 uTag; bool bTag; + char* cTag; void (*get_cursor_hover_location)(struct DjuiBase*, f32* x, f32* y); void (*on_child_render)(struct DjuiBase*, struct DjuiBase*); void (*on_render_pre)(struct DjuiBase*, bool*); diff --git a/src/pc/djui/djui_panel_confirm.c b/src/pc/djui/djui_panel_confirm.c index fc9dc948c..ea4fcfe9f 100644 --- a/src/pc/djui/djui_panel_confirm.c +++ b/src/pc/djui/djui_panel_confirm.c @@ -27,4 +27,4 @@ void djui_panel_confirm_create(struct DjuiBase* caller, char* title, char* messa } djui_panel_add(caller, panel, NULL); -} +} \ No newline at end of file diff --git a/src/pc/djui/djui_panel_host.c b/src/pc/djui/djui_panel_host.c index b1b069e4b..e9e3f682b 100644 --- a/src/pc/djui/djui_panel_host.c +++ b/src/pc/djui/djui_panel_host.c @@ -6,6 +6,7 @@ #include "djui_panel_host_settings.h" #include "djui_panel_host_save.h" #include "djui_panel_host_message.h" +#include "djui_panel_moderation_list.h" #include "djui_panel_rules.h" #include "game/save_file.h" #include "pc/network/network.h" @@ -181,6 +182,7 @@ void djui_panel_host_create(struct DjuiBase* caller) { djui_button_create(body, DLANG(HOST, SETTINGS), DJUI_BUTTON_STYLE_NORMAL, djui_panel_host_settings_create); djui_button_create(body, DLANG(HOST, MODS), DJUI_BUTTON_STYLE_NORMAL, djui_panel_host_mods_create); + djui_button_create(body, "Moderation Lists", DJUI_BUTTON_STYLE_NORMAL, djui_panel_moderation_list_create); struct DjuiRect* rect3 = djui_rect_container_create(body, 64); { diff --git a/src/pc/djui/djui_panel_pause.c b/src/pc/djui/djui_panel_pause.c index 9c993053d..4098305b8 100644 --- a/src/pc/djui/djui_panel_pause.c +++ b/src/pc/djui/djui_panel_pause.c @@ -5,6 +5,7 @@ #include "djui_panel_dynos.h" #include "djui_panel_options.h" #include "djui_panel_host.h" +#include "djui_panel_moderator_menu.h" #include "djui_panel_menu.h" #include "djui_panel_confirm.h" #include "djui_panel_mod_menu.h" @@ -72,6 +73,10 @@ void djui_panel_pause_create(struct DjuiBase* caller) { djui_button_create(body, DLANG(PAUSE, SERVER_SETTINGS), DJUI_BUTTON_STYLE_NORMAL, djui_panel_host_create); } + if (gNetworkType == NT_SERVER || network_is_moderator()) { + djui_button_create(body, DLANG(PAUSE, MODERATOR_MENU), DJUI_BUTTON_STYLE_NORMAL, djui_panel_moderator_menu_create); + } + struct Mod* addedMods[MAX_HOOKED_MOD_MENU_ELEMENTS] = { 0 }; int modCount = 0; for (int i = 0; i < gHookedModMenuElementsCount; i++) { diff --git a/src/pc/lua/utils/smlua_misc_utils.h b/src/pc/lua/utils/smlua_misc_utils.h index e4bb3c1eb..c9f07b98b 100644 --- a/src/pc/lua/utils/smlua_misc_utils.h +++ b/src/pc/lua/utils/smlua_misc_utils.h @@ -37,7 +37,7 @@ enum ActSelectHudPart { ACT_SELECT_HUD_ACT_NAME = 1 << 3, ACT_SELECT_HUD_STAR_NUM = 1 << 4, ACT_SELECT_HUD_PLAYERS_IN_LEVEL = 1 << 5, - + ACT_SELECT_HUD_NONE = 0, ACT_SELECT_HUD_ALL = ACT_SELECT_HUD_SCORE | ACT_SELECT_HUD_LEVEL_NAME | ACT_SELECT_HUD_COURSE_NUM | ACT_SELECT_HUD_ACT_NAME |ACT_SELECT_HUD_STAR_NUM | ACT_SELECT_HUD_PLAYERS_IN_LEVEL }; @@ -51,7 +51,6 @@ struct DateTime { s32 second; }; - /* |description|Gets the current area's networked timer|descriptionEnd| */ u32 get_network_area_timer(void); /* |description|Gets the area update counter incremented when objects are updated|descriptionEnd| */ diff --git a/src/pc/network/packets/packet.c b/src/pc/network/packets/packet.c index 199a88e5a..b2ccc9596 100644 --- a/src/pc/network/packets/packet.c +++ b/src/pc/network/packets/packet.c @@ -1,7 +1,7 @@ #include #include #include "../network.h" -#include "pc/network/ban_list.h" +#include "pc/network/moderation.h" #include "pc/debuglog.h" static u32 sCompBufferLen = 0; @@ -93,6 +93,7 @@ void packet_process(struct Packet* p) { case PACKET_KICK: network_receive_kick(p); break; case PACKET_COMMAND: network_receive_chat_command(p); break; case PACKET_MODERATOR: network_receive_moderator(p); break; + case PACKET_MODERATION_ACTION: network_receive_moderation_action(p); break; case PACKET_KEEP_ALIVE: network_receive_keep_alive(p); break; case PACKET_LEAVING: network_receive_leaving(p); break; case PACKET_SAVE_FILE: network_receive_save_file(p); break; @@ -153,7 +154,7 @@ void packet_receive(struct Packet* p) { // refuse packets from banned players if (gNetworkType == NT_SERVER) { - if (ban_list_contains(gNetworkSystem->get_id_str(p->localIndex))) { + 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); return; diff --git a/src/pc/network/packets/packet.h b/src/pc/network/packets/packet.h index 5ab859760..9b2427fd7 100644 --- a/src/pc/network/packets/packet.h +++ b/src/pc/network/packets/packet.h @@ -74,9 +74,10 @@ enum PacketType { PACKET_LUA_CUSTOM, PACKET_LUA_CUSTOM_BYTESTRING, - + PACKET_COMMAND, PACKET_MODERATOR, + PACKET_MODERATION_ACTION, /// PACKET_CUSTOM = 255, @@ -248,6 +249,10 @@ void network_receive_chat_command(struct Packet* p); void network_send_moderator(u8 localIndex); void network_receive_moderator(struct Packet* p); +// packet_moderation.c +void network_send_moderation_action(u8 action, u8 localIndex, char* reason, bool permanent); +void network_receive_moderation_action(struct Packet* p); + // packet_keep_alive.c void network_send_keep_alive(u8 localIndex); void network_receive_keep_alive(struct Packet* p); diff --git a/src/pc/network/packets/packet_command_mod.c b/src/pc/network/packets/packet_command_mod.c index ce3fa0ba2..58cdba588 100644 --- a/src/pc/network/packets/packet_command_mod.c +++ b/src/pc/network/packets/packet_command_mod.c @@ -2,8 +2,7 @@ #include "../network.h" #include "pc/djui/djui_language.h" #include "pc/djui/djui_chat_message.h" -#include "pc/network/ban_list.h" -#include "pc/network/moderator_list.h" +#include "pc/network/moderation.h" #include "pc/debuglog.h" void network_send_chat_command(u8 globalIndex, enum ChatConfirmCommand ccc) { @@ -23,7 +22,7 @@ void network_receive_chat_command(struct Packet *p) { return; } - if (!moderator_list_contains(gNetworkSystem->get_id_str(p->localIndex))) { + if (!moderation_list_contains(MODERATION_LIST_TYPE_MODERATOR, gNetworkSystem->get_id_str(p->localIndex))) { LOG_ERROR("recieved moderator command from non moderator"); return; } @@ -48,21 +47,29 @@ void network_receive_chat_command(struct Packet *p) { } if (CCC == CCC_BAN) { network_send_kick(np->localIndex, EKT_BANNED); - ban_list_add(gNetworkSystem->get_id_str(np->localIndex), false); + // 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); } - network_player_disconnected(np->localIndex); + network_player_disconnected(np->globalIndex); djui_chat_message_create(message); } void network_send_moderator(u8 localIndex) { struct Packet p = { 0 }; packet_init(&p, PACKET_MODERATOR, false, PLMT_NONE); + packet_write(&p, &gNetworkPlayerLocal[localIndex].moderator, sizeof(bool)); network_send_to(localIndex, &p); } void network_receive_moderator(struct Packet *p) { - if (gNetworkPlayers[0].moderator || (network_player_any_connected() && gNetworkPlayers[p->localIndex].type != NPT_SERVER)) return; - gNetworkPlayers[0].moderator = true; - djui_chat_message_create(DLANG(CHAT, MOD_GRANTED)); + if (network_player_any_connected() && gNetworkPlayers[p->localIndex].type != NPT_SERVER) return; + bool moderator; + packet_read(p, &moderator, sizeof(bool)); + if (gNetworkPlayers[0].moderator == moderator) { + LOG_ERROR("Server moderator is telling me to be what I already am! Ignoring..."); + return; + } + gNetworkPlayers[0].moderator = moderator; + djui_chat_message_create(moderator ? DLANG(CHAT, MOD_GRANTED) : DLANG(CHAT, MOD_REVOKED)); } diff --git a/src/pc/network/packets/packet_join.c b/src/pc/network/packets/packet_join.c index 0f1602352..4d643452d 100644 --- a/src/pc/network/packets/packet_join.c +++ b/src/pc/network/packets/packet_join.c @@ -48,9 +48,12 @@ void network_send_join_request(void) { snprintf(version, MAX_VERSION_LENGTH, "%s", get_version()); packet_write(&p, &version, sizeof(u8) * MAX_VERSION_LENGTH); - packet_write(&p, &configPlayerModel, sizeof(u8)); - packet_write(&p, &configPlayerPalette, sizeof(struct PlayerPalette)); - packet_write(&p, &configPlayerName, sizeof(u8) * MAX_CONFIG_STRING); + packet_write(&p, &configPlayerModel, sizeof(u8)); + packet_write(&p, &configPlayerPalette, sizeof(struct PlayerPalette)); + packet_write(&p, &configPlayerName, sizeof(u8) * MAX_CONFIG_STRING); + char discordId[64]; + snprintf(discordId, 64, "%s", get_local_discord_id()); + packet_write(&p, &discordId, sizeof(u8) * 64); network_send_to((gNetworkPlayerServer != NULL) ? gNetworkPlayerServer->localIndex : 0, &p); LOG_INFO("sending join request"); @@ -66,10 +69,12 @@ void network_receive_join_request(struct Packet* p) { packet_read(p, &sJoinRequestPlayerModel, sizeof(u8)); packet_read(p, &sJoinRequestPlayerPalette, sizeof(struct PlayerPalette)); packet_read(p, &sJoinRequestPlayerName, sizeof(u8) * MAX_CONFIG_STRING); + packet_read(p, &sJoinRequestDiscordId, sizeof(u8) * 64); } else { sJoinRequestPlayerModel = 0; sJoinRequestPlayerPalette = DEFAULT_MARIO_PALETTE; snprintf(sJoinRequestPlayerName, MAX_CONFIG_STRING, "%s", "Player"); + snprintf(sJoinRequestDiscordId, 64, "%s", "0"); } network_send_join(p); diff --git a/src/pc/network/packets/packet_kick.c b/src/pc/network/packets/packet_kick.c index 9f2d006d2..b39bd591c 100644 --- a/src/pc/network/packets/packet_kick.c +++ b/src/pc/network/packets/packet_kick.c @@ -8,6 +8,10 @@ f32 sLastReconnectTime = -9999999; f32 sLastNotifyTime = -9999999; void network_send_kick(u8 localIndex, enum KickReasonType kickReason) { + if (localIndex == 0) { + LOG_ERROR("Trying to send kick to myself?"); + return; + } u8 kickReasonType = kickReason; struct Packet p = { 0 }; packet_init(&p, PACKET_KICK, true, PLMT_NONE); @@ -22,8 +26,8 @@ void network_receive_kick(struct Packet* p) { return; } - if (network_player_any_connected() && gNetworkPlayers[p->localIndex].type != NPT_SERVER) { - LOG_ERROR("Kick came from non-server... refuse!"); + if (network_player_any_connected() && gNetworkPlayers[p->localIndex].type != NPT_SERVER && !gNetworkPlayers[p->localIndex].moderator) { + LOG_ERROR("Kick came from non-server and non-moderator... refuse!"); return; } diff --git a/src/pc/network/packets/packet_network_players.c b/src/pc/network/packets/packet_network_players.c index c9e57e298..b8ac19e8f 100644 --- a/src/pc/network/packets/packet_network_players.c +++ b/src/pc/network/packets/packet_network_players.c @@ -5,7 +5,7 @@ #include "game/behavior_actions.h" #include "pc/debuglog.h" #include "pc/configfile.h" -#include "pc/network/moderator_list.h" +#include "pc/network/moderation.h" static void network_send_to_network_players(u8 sendToLocalIndex) { SOFT_ASSERT(gNetworkType == NT_SERVER); @@ -60,8 +60,9 @@ void network_receive_network_players_request(struct Packet* p) { } network_send_to_network_players(localIndex); - if (moderator_list_contains(gNetworkSystem->get_id_str(p->localIndex))) { - LOG_INFO("sending moderator packet to localIndex: %d", p->localIndex); + if (moderation_list_contains(MODERATION_LIST_TYPE_MODERATOR, gNetworkSystem->get_id_str(p->localIndex))) { + LOG_INFO("sending moderator packet to localIndex: %d", localIndex); + gNetworkPlayers[localIndex].moderator = true; network_send_moderator(p->localIndex); } } diff --git a/src/pc/network/packets/packet_player.c b/src/pc/network/packets/packet_player.c index 793def2db..3131136bb 100644 --- a/src/pc/network/packets/packet_player.c +++ b/src/pc/network/packets/packet_player.c @@ -253,7 +253,7 @@ void network_receive_player(struct Packet* p) { } #else network_send_kick(np->localIndex, EKT_KICKED); - network_player_disconnected(np->localIndex); + network_player_disconnected(np->globalIndex); return; #endif } diff --git a/src/pc/pc_main.c b/src/pc/pc_main.c index 91f689b6d..9af6c0ec1 100644 --- a/src/pc/pc_main.c +++ b/src/pc/pc_main.c @@ -42,6 +42,7 @@ #include "pc/network/version.h" #include "pc/network/socket/socket.h" #include "pc/network/network_player.h" +#include "pc/network/moderation.h" #include "pc/update_checker.h" #include "pc/djui/djui.h" #include "pc/djui/djui_unicode.h" @@ -510,6 +511,7 @@ int main(int argc, char *argv[]) { #endif configfile_load(); + moderation_list_load(); legacy_folder_handler(); From c753cfe3f0b763bc261708566d93a1777ff266f8 Mon Sep 17 00:00:00 2001 From: EmeraldLockdown <86802223+EmeraldLoc@users.noreply.github.com> Date: Sat, 7 Mar 2026 20:02:48 -0600 Subject: [PATCH 02/22] Forgot to add 99% of the files. Well did a lot Lots of polish, cleanup, crash fixes, etc. Added languages for the most part Discord Id's now exist Other stuff, idrk Just have to allow you to inspect currently connected players and I should be good to draft this sucker --- autogen/lua_definitions/functions.lua | 7 + docs/lua/functions-5.md | 23 +++ docs/lua/functions.md | 1 + lang/English.ini | 31 +++- src/pc/djui/djui_panel.c | 19 +++ src/pc/djui/djui_panel.h | 1 + src/pc/djui/djui_panel_host.c | 4 +- .../djui_panel_moderation_confirm_action.c | 146 ++++++++++++++++ .../djui_panel_moderation_confirm_action.h | 5 + src/pc/djui/djui_panel_moderation_list.c | 97 +++++++++++ src/pc/djui/djui_panel_moderation_list.h | 5 + .../djui_panel_moderation_list_inspector.c | 115 +++++++++++++ .../djui_panel_moderation_list_inspector.h | 4 + src/pc/djui/djui_panel_moderator_menu.c | 104 +++++++++++ src/pc/djui/djui_panel_moderator_menu.h | 5 + src/pc/lua/smlua_functions_autogen.c | 18 ++ src/pc/network/moderation.c | 83 +++++++++ src/pc/network/moderation.h | 19 +++ src/pc/network/moderation_list.c | 161 ++++++++++++++++++ src/pc/network/moderation_list.h | 43 +++++ src/pc/network/network_utils.c | 8 + src/pc/network/network_utils.h | 2 + src/pc/network/packets/packet_moderation.c | 87 ++++++++++ 23 files changed, 986 insertions(+), 2 deletions(-) create mode 100644 src/pc/djui/djui_panel_moderation_confirm_action.c create mode 100644 src/pc/djui/djui_panel_moderation_confirm_action.h create mode 100644 src/pc/djui/djui_panel_moderation_list.c create mode 100644 src/pc/djui/djui_panel_moderation_list.h create mode 100644 src/pc/djui/djui_panel_moderation_list_inspector.c create mode 100644 src/pc/djui/djui_panel_moderation_list_inspector.h create mode 100644 src/pc/djui/djui_panel_moderator_menu.c create mode 100644 src/pc/djui/djui_panel_moderator_menu.h create mode 100644 src/pc/network/moderation.c create mode 100644 src/pc/network/moderation.h create mode 100644 src/pc/network/moderation_list.c create mode 100644 src/pc/network/moderation_list.h create mode 100644 src/pc/network/packets/packet_moderation.c diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua index f7b0aa63d..468a38f0a 100644 --- a/autogen/lua_definitions/functions.lua +++ b/autogen/lua_definitions/functions.lua @@ -7910,6 +7910,13 @@ function network_get_player_text_color_string(localIndex) -- ... end +--- @param localIndex integer +--- @return string +--- Gets the complete player name, including the player's starting hex code. +function network_get_complete_player_name(localIndex) + -- ... +end + --- @return boolean --- Checks if the game can currently be paused in singleplayer function network_check_singleplayer_pause() diff --git a/docs/lua/functions-5.md b/docs/lua/functions-5.md index 507dd7401..7dbb0c90f 100644 --- a/docs/lua/functions-5.md +++ b/docs/lua/functions-5.md @@ -2758,6 +2758,29 @@ Gets the DJUI hex color code string for the player corresponding to `localIndex`
+## [network_get_complete_player_name](#network_get_complete_player_name) + +### Description +Gets the complete player name, including the player's starting hex code. + +### Lua Example +`local stringValue = network_get_complete_player_name(localIndex)` + +### Parameters +| Field | Type | +| ----- | ---- | +| localIndex | `integer` | + +### Returns +- `string` + +### C Prototype +`const char* network_get_complete_player_name(u8 localIndex);` + +[:arrow_up_small:](#) + +
+ ## [network_check_singleplayer_pause](#network_check_singleplayer_pause) ### Description diff --git a/docs/lua/functions.md b/docs/lua/functions.md index e2f94b46b..da30dd6f9 100644 --- a/docs/lua/functions.md +++ b/docs/lua/functions.md @@ -1432,6 +1432,7 @@ - [network_is_server](functions-5.md#network_is_server) - [network_is_moderator](functions-5.md#network_is_moderator) - [network_get_player_text_color_string](functions-5.md#network_get_player_text_color_string) + - [network_get_complete_player_name](functions-5.md#network_get_complete_player_name) - [network_check_singleplayer_pause](functions-5.md#network_check_singleplayer_pause) - [network_discord_id_from_local_index](functions-5.md#network_discord_id_from_local_index) diff --git a/lang/English.ini b/lang/English.ini index 60553fc5d..e85f9890e 100644 --- a/lang/English.ini +++ b/lang/English.ini @@ -266,6 +266,7 @@ SAVE_SLOT = "Save Slot" SETTINGS = "Settings" MODS = "Mods" ROMHACKS = "Rom-Hacks" +MODERATION_LISTS = "Moderation Lists" APPLY = "Apply" HOST = "Host" @@ -339,8 +340,36 @@ CONSOLE = "CONSOLE" [MODLIST] MODS = "MODS" -[MODERATOR_MENU] +[MODERATION] MODERATOR_MENU_TITLE = "MODERATOR MENU" +MODERATION_LISTS_TITLE = "MODERATION LISTS" +MODERATION_LISTS = "Moderation Lists" +NO_PLAYERS_CONNECTED = "No players connected." +KICK = "Kick" +BAN = "Ban" +UNBAN = "Unban" +MOD = "Mod" +UNMOD = "Unmod" +KICK_PLAYER_TITLE = "KICK PLAYER" +BAN_PLAYER_TITLE = "BAN PLAYER" +UNBAN_PLAYER_TITLE = "UNBAN PLAYER" +MOD_PLAYER_TITLE = "MOD PLAYER" +UNMOD_PLAYER_TITLE = "UNMOD PLAYER" +KICK_CONFIRM = "Are you sure you want to kick @\\#dcdcdc\\?" +BAN_CONFIRM = "Are you sure you want to ban @\\#dcdcdc\\?" +UNBAN_CONFIRM = "Are you sure you want to unban @\\#dcdcdc\\?" +MOD_CONFIRM = "Are you sure you want to mod @\\#dcdcdc\\?" +UNMOD_CONFIRM = "Are you sure you want to unmod @\\#dcdcdc\\?" +BAN_LIST = "Ban List" +MODERATOR_LIST = "Moderator List" +LIST = "List" +NO_PLAYERS_IN_LIST = "No players in list." +INSPECT = "Inspect" +INSPECTOR_TITLE = "INSPECTOR" +DATE = "Added: @" +DISCORD_ID = "Discord ID: @" +REASON = "Reason: @" +PERMANENT = "Permanent" [OPTIONS] OPTIONS = "OPTIONS" diff --git a/src/pc/djui/djui_panel.c b/src/pc/djui/djui_panel.c index b25a251d2..1619ecb0a 100644 --- a/src/pc/djui/djui_panel.c +++ b/src/pc/djui/djui_panel.c @@ -11,6 +11,7 @@ static struct DjuiPanel* sPanelList = NULL; static struct DjuiPanel* sPanelRemoving = NULL; +static int sPanelBackQueue = 0; static f32 sMoveAmount = 0; bool gDjuiPanelDisableBack = false; @@ -136,6 +137,19 @@ void djui_panel_back(void) { gDjuiPanelJoinMessageVisible = false; } +void djui_panel_back_by(int amount) { + if (amount <= 0) { return; } + if (sPanelList == NULL) { return; } + if (gDjuiPanelDisableBack) { return; } + + sPanelBackQueue = amount - 1; + + if (sPanelRemoving == NULL) { + sPanelRemoving--; + djui_panel_back(); + } +} + void djui_panel_update(void) { if (sPanelList == NULL) { return; } if (sPanelList->base == NULL) { return; } @@ -175,6 +189,10 @@ void djui_panel_update(void) { djui_base_destroy(removingBase); free(panel); removingBase = NULL; + if (sPanelBackQueue > 0) { + sPanelBackQueue--; + djui_panel_back(); + } return; } } @@ -220,6 +238,7 @@ void djui_panel_shutdown(void) { sPanelList = NULL; sPanelRemoving = NULL; sMoveAmount = 0; + sPanelBackQueue = 0; gInteractableOverridePad = false; gDjuiPanelJoinMessageVisible = false; gDjuiPanelMainCreated = false; diff --git a/src/pc/djui/djui_panel.h b/src/pc/djui/djui_panel.h index dd752c953..71e61b879 100644 --- a/src/pc/djui/djui_panel.h +++ b/src/pc/djui/djui_panel.h @@ -19,5 +19,6 @@ extern bool gDjuiPanelDisableBack; bool djui_panel_is_active(void); struct DjuiPanel* djui_panel_add(struct DjuiBase* caller, struct DjuiThreePanel* threePanel, struct DjuiBase* defaultElementBase); void djui_panel_back(void); +void djui_panel_back_by(int amount); void djui_panel_update(void); void djui_panel_shutdown(void); diff --git a/src/pc/djui/djui_panel_host.c b/src/pc/djui/djui_panel_host.c index e9e3f682b..28a3fb663 100644 --- a/src/pc/djui/djui_panel_host.c +++ b/src/pc/djui/djui_panel_host.c @@ -182,7 +182,9 @@ void djui_panel_host_create(struct DjuiBase* caller) { djui_button_create(body, DLANG(HOST, SETTINGS), DJUI_BUTTON_STYLE_NORMAL, djui_panel_host_settings_create); djui_button_create(body, DLANG(HOST, MODS), DJUI_BUTTON_STYLE_NORMAL, djui_panel_host_mods_create); - djui_button_create(body, "Moderation Lists", DJUI_BUTTON_STYLE_NORMAL, djui_panel_moderation_list_create); + if (gDjuiInMainMenu) { + djui_button_create(body, DLANG(HOST, MODERATION_LISTS), DJUI_BUTTON_STYLE_NORMAL, djui_panel_moderation_list_create); + } struct DjuiRect* rect3 = djui_rect_container_create(body, 64); { diff --git a/src/pc/djui/djui_panel_moderation_confirm_action.c b/src/pc/djui/djui_panel_moderation_confirm_action.c new file mode 100644 index 000000000..a61dab4d3 --- /dev/null +++ b/src/pc/djui/djui_panel_moderation_confirm_action.c @@ -0,0 +1,146 @@ +#include +#include "djui.h" +#include "djui_panel.h" +#include "djui_panel_menu.h" +#include "djui_panel_confirm.h" +#include "djui_panel_moderation_list.h" +#include "pc/network/network.h" +#include "pc/network/moderation.h" + +static char* sReason = NULL; +void (*sOnYesClick)(struct DjuiBase*) = NULL; + +static void djui_panel_moderation_call_action(struct DjuiBase* caller) { + u8 player = caller->tag; + u8 action = caller->uTag; + char* address = caller->cTag; + + switch(action) { + case MODERATION_ACTION_KICK: + network_kick_player(player, sReason); + break; + case MODERATION_ACTION_BAN: + network_ban_player(player, sReason, false); + break; + case MODERATION_ACTION_UNBAN: + network_unban_player(address); + break; + case MODERATION_ACTION_MOD: + network_mod_player(player, sReason, true); + break; + case MODERATION_ACTION_UNMOD: + network_unmod_player(address); + break; + default: + break; + } + + free(sReason); + sReason = NULL; + djui_panel_menu_back(caller); + + if (sOnYesClick) sOnYesClick(caller); +} + +static void djui_panel_moderation_confirm_reason_text_change(struct DjuiBase* caller) { + struct DjuiInputbox* inputbox = (struct DjuiInputbox*)caller; + if (inputbox) { + sReason = strdup(inputbox->buffer); + } +} + +static void djui_panel_moderation_confirm_set_title_and_message(u8 action, char** title, char* message, char* playerName) { + switch (action) { + case MODERATION_ACTION_KICK: + *title = djui_language_get("MODERATION", "KICK_PLAYER_TITLE"); + djui_language_replace(DLANG(MODERATION, KICK_CONFIRM), message, 256, '@', playerName); + break; + case MODERATION_ACTION_BAN: + *title = djui_language_get("MODERATION", "BAN_PLAYER_TITLE"); + djui_language_replace(DLANG(MODERATION, BAN_CONFIRM), message, 256, '@', playerName); + break; + case MODERATION_ACTION_UNBAN: + *title = djui_language_get("MODERATION", "UNBAN_PLAYER_TITLE"); + djui_language_replace(DLANG(MODERATION, UNBAN_CONFIRM), message, 256, '@', playerName); + break; + case MODERATION_ACTION_MOD: + *title = djui_language_get("MODERATION", "MOD_PLAYER_TITLE"); + djui_language_replace(DLANG(MODERATION, MOD_CONFIRM), message, 256, '@', playerName); + break; + case MODERATION_ACTION_UNMOD: + *title = djui_language_get("MODERATION", "UNMOD_PLAYER_TITLE"); + djui_language_replace(DLANG(MODERATION, UNMOD_CONFIRM), message, 256, '@', playerName); + break; + default: + return; + } +} + +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*)) { + struct DjuiThreePanel* panel = djui_panel_menu_create(title, false); + struct DjuiBase* body = djui_three_panel_get_body(panel); + { + 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) { + struct DjuiRect* rect1 = djui_rect_container_create(body, 32); + { + struct DjuiText* text1 = djui_text_create(&rect1->base, "Reason:"); + djui_base_set_size_type(&text1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_color(&text1->base, 220, 220, 220, 255); + djui_base_set_size(&text1->base, 0.585f, 64); + djui_base_set_alignment(&text1->base, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP); + djui_text_set_drop_shadow(text1, 64, 64, 64, 100); + + struct DjuiInputbox* inputbox1 = djui_inputbox_create(&rect1->base, 256); + djui_base_set_size_type(&inputbox1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&inputbox1->base, 0.45f, 32); + djui_base_set_alignment(&inputbox1->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP); + djui_interactable_hook_value_change(&inputbox1->base, djui_panel_moderation_confirm_reason_text_change); + } + } + + djui_base_set_size(&text->base, 1.0f, 64); + djui_base_compute_tree(&text->base); + u16 lines = djui_text_count_lines(text, 12); + f32 textHeight = 32 * 0.8125f * lines + 8; + djui_base_set_size(&text->base, 1.0f, textHeight); + + djui_base_set_color(&text->base, 220, 220, 220, 255); + djui_text_set_alignment(text, DJUI_HALIGN_CENTER, DJUI_VALIGN_TOP); + + struct DjuiRect* rect2 = djui_rect_container_create(body, 64); + { + djui_button_left_create(&rect2->base, DLANG(MENU, NO), DJUI_BUTTON_STYLE_NORMAL, djui_panel_menu_back); + struct DjuiButton* yesButton = djui_button_right_create(&rect2->base, DLANG(MENU, YES), DJUI_BUTTON_STYLE_NORMAL, djui_panel_moderation_call_action); + yesButton->base.tag = localIndex; + yesButton->base.uTag = action; + yesButton->base.bTag = permanent; + yesButton->base.cTag = strdup(address); + sOnYesClick = on_yes_click; + } + } + + djui_panel_add(caller, panel, NULL); +} + +void djui_panel_moderation_confirm_create(struct DjuiBase* caller, u8 action, u8 localIndex, bool permanent, void (*on_yes_click)(struct DjuiBase*)) { + if (localIndex >= MAX_PLAYERS) return; + char* title = NULL; + char message[256] = { 0 }; + djui_panel_moderation_confirm_set_title_and_message(action, &title, message, (char*)network_get_complete_player_name(localIndex)); + djui_panel_moderation_confirm_create_body(caller, title, message, localIndex, action, permanent, gNetworkSystem->get_id_str(localIndex), on_yes_click); +} + +void djui_panel_moderation_confirm_create_using_list(struct DjuiBase* caller, u8 action, u8 listType, u16 listIndex, void (*on_yes_click)(struct DjuiBase*)) { + struct ModerationEntry* entry = moderation_list_get_list_by_type(listType)->list[listIndex]; + char* title = NULL; + char message[256] = { 0 }; + char colorString[10] = { 0 }; + snprintf(colorString, 10, "\\#%02x%02x%02x\\", entry->playerColor[0], entry->playerColor[1], entry->playerColor[2]); + char playerName[256] = { 0 }; + snprintf(playerName, 256, "%s%s", colorString, entry->playerName); + djui_panel_moderation_confirm_set_title_and_message(action, &title, message, playerName); + djui_panel_moderation_confirm_create_body(caller, title, message, 0, action, false, entry->address, on_yes_click); +} \ No newline at end of file diff --git a/src/pc/djui/djui_panel_moderation_confirm_action.h b/src/pc/djui/djui_panel_moderation_confirm_action.h new file mode 100644 index 000000000..e8fa14008 --- /dev/null +++ b/src/pc/djui/djui_panel_moderation_confirm_action.h @@ -0,0 +1,5 @@ +#pragma once +#include "djui.h" + +void djui_panel_moderation_confirm_create(struct DjuiBase* caller, u8 action, u8 localIndex, bool permanent, void (*on_yes_click)(struct DjuiBase*)); +void djui_panel_moderation_confirm_create_using_list(struct DjuiBase* caller, u8 action, u8 listType, u16 listIndex, void (*on_yes_click)(struct DjuiBase*)); \ No newline at end of file diff --git a/src/pc/djui/djui_panel_moderation_list.c b/src/pc/djui/djui_panel_moderation_list.c new file mode 100644 index 000000000..c4bd0f18c --- /dev/null +++ b/src/pc/djui/djui_panel_moderation_list.c @@ -0,0 +1,97 @@ + +#include +#include "djui.h" +#include "djui_panel.h" +#include "djui_panel_menu.h" +#include "djui_panel_moderator_menu.h" +#include "djui_panel_moderation_list.h" +#include "djui_panel_moderation_list_inspector.h" +#include "pc/network/network.h" +#include "pc/network/moderation.h" + +static struct DjuiFlowLayout* sLayout = NULL; +static struct DjuiPaginated* sPaginated = NULL; +static unsigned int sSelectedList = MODERATION_LIST_TYPE_BAN; + +static void djui_panel_moderation_list_inspect_player(struct DjuiBase* caller) { + djui_panel_moderation_list_inspect_create(caller); +} + +static void djui_panel_moderation_list_populate_list(struct DjuiBase* layoutBase) { + struct ModerationList* list = moderation_list_get_list_by_type(sSelectedList); + if (list->count == 0) { + struct DjuiText* text = djui_text_create(layoutBase, DLANG(MODERATION, NO_PLAYERS_IN_LIST)); + 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_text_set_drop_shadow(text, 64, 64, 64, 100); + return; + } + for (int i = 0; i < list->count; i++) { + struct ModerationEntry* entry = list->list[i]; + struct DjuiRect* rectContainer = djui_rect_container_create(layoutBase, 32); + { + struct DjuiText* text = djui_text_create(&rectContainer->base, entry->playerName); + djui_text_set_alignment(text, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP); + djui_text_set_drop_shadow(text, 64, 64, 64, 100); + djui_base_set_color(&text->base, entry->playerColor[0], entry->playerColor[1], entry->playerColor[2], 255); + djui_base_set_alignment(&text->base, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP); + djui_base_set_size_type(&text->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&text->base, 0.5, 32); + + struct DjuiButton* button = djui_button_create(&rectContainer->base, DLANG(MODERATION, INSPECT), DJUI_BUTTON_STYLE_NORMAL, djui_panel_moderation_list_inspect_player); + djui_base_set_alignment(&button->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP); + djui_base_set_size_type(&button->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&button->base, 0.45, 32); + button->base.tag = sSelectedList; + button->base.uTag = i; + } + } +} + +void djui_panel_moderation_list_reload() { + if (!sLayout || !sPaginated) return; + djui_base_destroy_children(&sLayout->base); + djui_panel_moderation_list_populate_list(&sLayout->base); + djui_paginated_calculate_height(sPaginated); +} + +static void djui_panel_moderation_list_destroy(struct DjuiBase* base) { + struct DjuiThreePanel* threePanel = (struct DjuiThreePanel*)base; + free(threePanel); + sLayout = NULL; + sPaginated = NULL; +} + +static bool djui_panel_moderation_list_on_back(UNUSED struct DjuiBase* base) { + if (!gDjuiInMainMenu) { + djui_panel_moderator_menu_reload(); + } + return false; +} + +void djui_panel_moderation_list_create(struct DjuiBase* caller) { + struct DjuiThreePanel* panel = djui_panel_menu_create(DLANG(MODERATION, MODERATION_LISTS_TITLE), true); + + struct DjuiBase* body = djui_three_panel_get_body(panel); + { + char* choices[MODERATION_LIST_TYPE_COUNT] = { + DLANG(MODERATION, BAN_LIST), + DLANG(MODERATION, MODERATOR_LIST), + }; + djui_selectionbox_create(body, DLANG(MODERATION, LIST), choices, MODERATION_LIST_TYPE_COUNT, &sSelectedList, djui_panel_moderation_list_reload); + + struct DjuiPaginated* paginated = djui_paginated_create(body, 8); + paginated->showMaxCount = true; + sLayout = paginated->layout; + djui_panel_moderation_list_populate_list(&paginated->layout->base); + djui_paginated_calculate_height(paginated); + sPaginated = paginated; + + djui_button_create(body, DLANG(MENU, BACK), DJUI_BUTTON_STYLE_BACK, djui_panel_menu_back); + } + + panel->on_back = djui_panel_moderation_list_on_back; + panel->base.destroy = djui_panel_moderation_list_destroy; + djui_panel_add(caller, panel, NULL); +} \ No newline at end of file diff --git a/src/pc/djui/djui_panel_moderation_list.h b/src/pc/djui/djui_panel_moderation_list.h new file mode 100644 index 000000000..4903b01cf --- /dev/null +++ b/src/pc/djui/djui_panel_moderation_list.h @@ -0,0 +1,5 @@ +#pragma once +#include "djui.h" + +void djui_panel_moderation_list_reload(); +void djui_panel_moderation_list_create(struct DjuiBase* caller); \ No newline at end of file diff --git a/src/pc/djui/djui_panel_moderation_list_inspector.c b/src/pc/djui/djui_panel_moderation_list_inspector.c new file mode 100644 index 000000000..277d077d1 --- /dev/null +++ b/src/pc/djui/djui_panel_moderation_list_inspector.c @@ -0,0 +1,115 @@ +#include +#include "djui.h" +#include "djui_panel.h" +#include "djui_panel_menu.h" +#include "djui_panel_moderator_menu.h" +#include "djui_panel_moderation_list.h" +#include "djui_panel_moderation_confirm_action.h" +#include "djui_panel_confirm.h" +#include "pc/network/network.h" +#include "pc/network/moderation.h" +#include "pc/debuglog.h" + +static u16 sListType = 0; +static u16 sListIndex = 0; + +static void djui_panel_moderation_list_inspect_action_exit(UNUSED struct DjuiBase* caller) { + djui_panel_back_by(2); + djui_panel_moderation_list_reload(); +} + +static void djui_panel_moderation_list_action_button_click(struct DjuiBase* caller) { + djui_panel_moderation_confirm_create_using_list(caller, caller->tag, sListType, sListIndex, djui_panel_moderation_list_inspect_action_exit); +} + +static void djui_panel_moderation_list_inspect_destroy(struct DjuiBase* base) { + struct DjuiThreePanel* threePanel = (struct DjuiThreePanel*)base; + free(threePanel); +} + +void djui_panel_moderation_list_inspect_create(struct DjuiBase* caller) { + struct ModerationList* list = moderation_list_get_list_by_type(caller->tag); + if (!list) return; + struct ModerationEntry* entry = list->list[caller->uTag]; + if (!entry) return; + struct tm* localTime = localtime(&entry->time); + + sListType = caller->tag; + sListIndex = caller->uTag; + + struct DjuiThreePanel* panel = djui_panel_menu_create(DLANG(MODERATION, INSPECTOR), true); + struct DjuiBase* body = djui_three_panel_get_body(panel); + { + char playerName[MAX_CONFIG_STRING + 128]; + snprintf(playerName, MAX_CONFIG_STRING + 128, "%s \\#fff982\\- \\#82f9ff\\%s", entry->playerName, entry->address); + struct DjuiText* playerText = djui_text_create(body, playerName); + djui_base_set_color(&playerText->base, entry->playerColor[0], entry->playerColor[1], entry->playerColor[2], 255); + djui_base_set_size_type(&playerText->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&playerText->base, 1, 32); + djui_text_set_alignment(playerText, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP); + djui_text_set_drop_shadow(playerText, 64, 64, 64, 100); + + char date[128]; + strftime(date, sizeof(date), "%m/%d/%Y %I:%M:%S %p", localTime); + char dateStr[256]; + djui_language_replace(DLANG(MODERATION, DATE), dateStr, 256, '@', date); + struct DjuiText* dateText = djui_text_create(body, dateStr); + djui_base_set_color(&dateText->base, 220, 220, 220, 255); + djui_base_set_size_type(&dateText->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&dateText->base, 1, 32); + djui_text_set_alignment(dateText, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP); + djui_text_set_drop_shadow(dateText, 64, 64, 64, 100); + + if (entry->discordId && strcmp(entry->discordId, "") != 0 && strcmp(entry->discordId, "0") != 0) { + char discordIdStr[128]; + djui_language_replace(DLANG(MODERATION, DISCORD_ID), discordIdStr, 128, '@', entry->discordId); + struct DjuiText* discordIdText = djui_text_create(body, discordIdStr); + djui_base_set_color(&discordIdText->base, 220, 220, 220, 255); + djui_base_set_size_type(&discordIdText->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&discordIdText->base, 1, 32); + djui_text_set_alignment(discordIdText, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP); + 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), reasonStr, 512, '@', 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); + djui_base_set_size(&reasonText->base, 1, 32); + djui_base_compute_tree(&reasonText->base); + u16 reasonLines = djui_text_count_lines(reasonText, 12); + f32 reasonHeight = 32 * reasonLines; + printf("Reason lines is %u, with reason height being %f\n", reasonLines, reasonHeight); + djui_base_set_size(&reasonText->base, 1, reasonHeight); + djui_text_set_alignment(reasonText, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP); + djui_text_set_drop_shadow(reasonText, 64, 64, 64, 100); + } + + djui_checkbox_create(body, DLANG(MODERATION, PERMANENT), &entry->permanent, moderation_list_save); + + for (u8 i = 0; i < MODERATION_ACTION_COUNT; i++) { + if (!list->actions[i]) continue; + switch (i) { + case MODERATION_ACTION_UNBAN: { + struct DjuiButton* button = djui_button_create(body, DLANG(MODERATION, UNBAN), DJUI_BUTTON_STYLE_NORMAL, djui_panel_moderation_list_action_button_click); + button->base.tag = MODERATION_ACTION_UNBAN; + break; + } + case MODERATION_ACTION_UNMOD: { + struct DjuiButton* button = djui_button_create(body, DLANG(MODERATION, UNMOD), DJUI_BUTTON_STYLE_NORMAL, djui_panel_moderation_list_action_button_click); + button->base.tag = MODERATION_ACTION_UNMOD; + break; + } + default: + break; + } + } + } + + djui_button_create(body, DLANG(MENU, BACK), DJUI_BUTTON_STYLE_BACK, djui_panel_menu_back); + + panel->base.destroy = djui_panel_moderation_list_inspect_destroy; + djui_panel_add(caller, panel, NULL); +} \ No newline at end of file diff --git a/src/pc/djui/djui_panel_moderation_list_inspector.h b/src/pc/djui/djui_panel_moderation_list_inspector.h new file mode 100644 index 000000000..248b36f18 --- /dev/null +++ b/src/pc/djui/djui_panel_moderation_list_inspector.h @@ -0,0 +1,4 @@ +#pragma once +#include "djui.h" + +void djui_panel_moderation_list_inspect_create(struct DjuiBase* caller); \ No newline at end of file diff --git a/src/pc/djui/djui_panel_moderator_menu.c b/src/pc/djui/djui_panel_moderator_menu.c new file mode 100644 index 000000000..98a552c79 --- /dev/null +++ b/src/pc/djui/djui_panel_moderator_menu.c @@ -0,0 +1,104 @@ + +#include +#include "djui.h" +#include "djui_panel.h" +#include "djui_panel_menu.h" +#include "djui_panel_moderator_menu.h" +#include "djui_panel_moderation_list.h" +#include "djui_panel_moderation_confirm_action.h" +#include "pc/network/network.h" +#include "pc/network/moderation.h" + +static struct DjuiFlowLayout* sLayout = NULL; +static struct DjuiPaginated* sPaginated = NULL; + +static void djui_panel_moderator_menu_action_button_click(struct DjuiBase* caller) { + djui_panel_moderation_confirm_create(caller, caller->uTag, caller->tag, false, djui_panel_moderator_menu_reload); +} + +static void djui_panel_moderator_add_players(struct DjuiBase* layoutBase) { + bool isPlayerConnected = false; + for (int i = 1; i < MAX_PLAYERS; i++) { + struct NetworkPlayer* np = &gNetworkPlayers[i]; + if (!np->connected) continue; + if (gNetworkPlayerLocal->moderator && np->moderator) continue; + if (gNetworkPlayerLocal->moderator && np->globalIndex == 0) continue; + + isPlayerConnected = true; + + struct DjuiFlowLayout* flowLayout = djui_flow_layout_create(layoutBase); + djui_flow_layout_set_flow_direction(flowLayout, DJUI_FLOW_DIR_RIGHT); + djui_base_set_color(&flowLayout->base, 0, 0, 0, 0); + djui_base_set_size_type(&flowLayout->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&flowLayout->base, 1, 32); + { + struct DjuiButton* playerButton = djui_button_create(&flowLayout->base, np->name, DJUI_BUTTON_STYLE_NORMAL, NULL); + u8 playerColor[3]; + memcpy(playerColor, network_get_player_text_color(i), 3); + djui_base_set_color(&playerButton->text->base, playerColor[0], playerColor[1], playerColor[2], 255); + djui_base_set_size_type(&playerButton->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&playerButton->base, 0.4, 32); + + struct DjuiButton* kickButton = djui_button_create(&flowLayout->base, DLANG(MODERATION, KICK), DJUI_BUTTON_STYLE_NORMAL, djui_panel_moderator_menu_action_button_click); + djui_base_set_size_type(&kickButton->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&kickButton->base, 0.333, 32); + kickButton->base.uTag = MODERATION_ACTION_KICK; + kickButton->base.tag = i; + + struct DjuiButton* banButton = djui_button_create(&flowLayout->base, DLANG(MODERATION, BAN), DJUI_BUTTON_STYLE_NORMAL, djui_panel_moderator_menu_action_button_click); + djui_base_set_size_type(&banButton->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&banButton->base, 0.5, 32); + banButton->base.uTag = MODERATION_ACTION_BAN; + banButton->base.tag = i; + + struct DjuiButton* modButton = djui_button_create(&flowLayout->base, np->moderator ? DLANG(MODERATION, UNMOD) : DLANG(MODERATION, MOD), DJUI_BUTTON_STYLE_NORMAL, djui_panel_moderator_menu_action_button_click); + djui_base_set_size_type(&modButton->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&modButton->base, 1.0, 32); + djui_base_set_enabled(&modButton->base, gNetworkType == NT_SERVER); + modButton->base.uTag = np->moderator ? MODERATION_ACTION_UNMOD : MODERATION_ACTION_MOD; + modButton->base.tag = i; + } + } + + if (!isPlayerConnected) { + struct DjuiText* text = djui_text_create(layoutBase, DLANG(MODERATION, NO_PLAYERS_CONNECTED)); + 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_text_set_drop_shadow(text, 64, 64, 64, 100); + } +} + +static void djui_panel_moderator_menu_destroy(struct DjuiBase* base) { + struct DjuiThreePanel* threePanel = (struct DjuiThreePanel*)base; + free(threePanel); + sLayout = NULL; + sPaginated = NULL; +} + +void djui_panel_moderator_menu_reload() { + if (!sLayout || !sPaginated) return; + djui_base_destroy_children(&sLayout->base); + djui_panel_moderator_add_players(&sLayout->base); + djui_paginated_calculate_height(sPaginated); +} + +void djui_panel_moderator_menu_create(struct DjuiBase* caller) { + struct DjuiThreePanel* panel = djui_panel_menu_create(DLANG(MODERATION, MODERATOR_MENU_TITLE), true); + + struct DjuiBase* body = djui_three_panel_get_body(panel); + { + struct DjuiPaginated* paginated = djui_paginated_create(body, 8); + paginated->showMaxCount = true; + sLayout = paginated->layout; + djui_panel_moderator_add_players(&paginated->layout->base); + djui_paginated_calculate_height(paginated); + sPaginated = paginated; + + if (gNetworkType == NT_SERVER) djui_button_create(body, DLANG(MODERATION, MODERATION_LISTS), DJUI_BUTTON_STYLE_NORMAL, djui_panel_moderation_list_create); + djui_button_create(body, DLANG(MENU, BACK), DJUI_BUTTON_STYLE_BACK, djui_panel_menu_back); + } + + panel->base.destroy = djui_panel_moderator_menu_destroy; + djui_panel_add(caller, panel, NULL); +} \ No newline at end of file diff --git a/src/pc/djui/djui_panel_moderator_menu.h b/src/pc/djui/djui_panel_moderator_menu.h new file mode 100644 index 000000000..648441d15 --- /dev/null +++ b/src/pc/djui/djui_panel_moderator_menu.h @@ -0,0 +1,5 @@ +#pragma once +#include "djui.h" + +void djui_panel_moderator_menu_reload(); +void djui_panel_moderator_menu_create(struct DjuiBase* caller); \ No newline at end of file diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c index c4b6b4f0e..b0424d43f 100644 --- a/src/pc/lua/smlua_functions_autogen.c +++ b/src/pc/lua/smlua_functions_autogen.c @@ -23452,6 +23452,23 @@ int smlua_func_network_get_player_text_color_string(lua_State* L) { return 1; } +int smlua_func_network_get_complete_player_name(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_get_complete_player_name", 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_get_complete_player_name"); return 0; } + + lua_pushstring(L, network_get_complete_player_name(localIndex)); + + return 1; +} + int smlua_func_network_check_singleplayer_pause(UNUSED lua_State* L) { if (L == NULL) { return 0; } @@ -37997,6 +38014,7 @@ void smlua_bind_functions_autogen(void) { smlua_bind_function(L, "network_is_server", smlua_func_network_is_server); smlua_bind_function(L, "network_is_moderator", smlua_func_network_is_moderator); smlua_bind_function(L, "network_get_player_text_color_string", smlua_func_network_get_player_text_color_string); + smlua_bind_function(L, "network_get_complete_player_name", smlua_func_network_get_complete_player_name); smlua_bind_function(L, "network_check_singleplayer_pause", smlua_func_network_check_singleplayer_pause); smlua_bind_function(L, "network_discord_id_from_local_index", smlua_func_network_discord_id_from_local_index); diff --git a/src/pc/network/moderation.c b/src/pc/network/moderation.c new file mode 100644 index 000000000..6792ddc07 --- /dev/null +++ b/src/pc/network/moderation.c @@ -0,0 +1,83 @@ +#include +#include +#include +#include "moderation.h" +#include "pc/debuglog.h" +#include "pc/ini.h" + +void network_kick_player(u8 localIndex, char* reason) { + if (gNetworkPlayerLocal->moderator) { + network_send_moderation_action(MODERATION_ACTION_KICK, localIndex, reason, false); + return; + } + if (gNetworkType != NT_SERVER) { + LOG_ERROR("Tried to kick player as non-server!"); + return; + } + struct NetworkPlayer* np = &gNetworkPlayers[localIndex]; + if (!np->connected) { + LOG_ERROR("Tried to perform moderation on disconnected player!"); + return; + } + network_send_kick(np->localIndex, EKT_KICKED); + network_player_disconnected(np->globalIndex); +} + +void network_ban_player(u8 localIndex, char* reason, bool permanent) { + if (gNetworkPlayerLocal->moderator) { + network_send_moderation_action(MODERATION_ACTION_BAN, localIndex, reason, permanent); + return; + } + if (gNetworkType != NT_SERVER) { + LOG_ERROR("Tried to ban player as non-server!"); + return; + } + struct NetworkPlayer* np = &gNetworkPlayers[localIndex]; + if (!np->connected) { + LOG_ERROR("Tried to perform moderation on disconnected player!"); + return; + } + moderation_list_add(MODERATION_LIST_TYPE_BAN, localIndex, reason, permanent); + network_send_kick(np->localIndex, EKT_BANNED); + network_player_disconnected(np->globalIndex); +} + +void network_unban_player(char* address) { + if (gNetworkType != NT_SERVER) { + LOG_ERROR("Tried to unban player as non-server!"); + return; + } + moderation_list_remove(MODERATION_LIST_TYPE_BAN, address); +} + +void network_mod_player(u8 localIndex, char* reason, bool permanent) { + if (gNetworkType != NT_SERVER) { + LOG_ERROR("Tried to mod player as non-server!"); + return; + } + struct NetworkPlayer* np = &gNetworkPlayers[localIndex]; + if (!np->connected) { + LOG_ERROR("Tried to perform moderation on disconnected player!"); + return; + } + np->moderator = true; + network_send_moderator(np->localIndex); + moderation_list_add(MODERATION_LIST_TYPE_MODERATOR, localIndex, reason, permanent); +} + +void network_unmod_player(char* address) { + if (gNetworkType != NT_SERVER) { + LOG_ERROR("Tried to unmod player as non-server!"); + return; + } + if (gNetworkSystem != NT_NONE) { + // loop thru to see if moderator is in the lobby + for (u8 i = 0; i < MAX_PLAYERS; i++) { + struct NetworkPlayer* np = &gNetworkPlayers[i]; + if (!np->connected || !np->moderator || !address || strcmp(gNetworkSystem->get_id_str(np->localIndex), address) != 0) continue; + np->moderator = false; + network_send_moderator(np->localIndex); + } + } + moderation_list_remove(MODERATION_LIST_TYPE_MODERATOR, address); +} \ No newline at end of file diff --git a/src/pc/network/moderation.h b/src/pc/network/moderation.h new file mode 100644 index 000000000..c3216b61f --- /dev/null +++ b/src/pc/network/moderation.h @@ -0,0 +1,19 @@ +#pragma once +#include + +enum ModerationActions { + MODERATION_ACTION_KICK, + MODERATION_ACTION_BAN, + MODERATION_ACTION_UNBAN, + MODERATION_ACTION_MOD, + MODERATION_ACTION_UNMOD, + MODERATION_ACTION_COUNT, +}; + +void network_kick_player(u8 localIndex, char* reason); +void network_ban_player(u8 localIndex, char* reason, bool permanent); +void network_unban_player(char* address); +void network_mod_player(u8 localIndex, char* reason, bool permanent); +void network_unmod_player(char* address); + +#include "moderation_list.h" \ No newline at end of file diff --git a/src/pc/network/moderation_list.c b/src/pc/network/moderation_list.c new file mode 100644 index 000000000..fa5d36e69 --- /dev/null +++ b/src/pc/network/moderation_list.c @@ -0,0 +1,161 @@ +#include +#include +#include +#include "moderation.h" +#include "pc/debuglog.h" +#include "pc/ini.h" + +struct ModerationLists gModerationLists = { + .banList = { + .actions = { + [MODERATION_ACTION_UNBAN] = true + } + }, + .moderatorList = { + .actions = { + [MODERATION_ACTION_UNMOD] = true + } + } +}; + +static const char* safe_ini_get(ini_t* ini, const char* section, const char* key) { + const char* str = ini_get(ini, section, key); + return str ? str : ""; +} + +struct ModerationList* moderation_list_get_list_by_type(enum ModerationListType type) { + if (type == MODERATION_LIST_TYPE_BAN) return &gModerationLists.banList; + if (type == MODERATION_LIST_TYPE_MODERATOR) return &gModerationLists.moderatorList; + LOG_ERROR("Type %u is not a valid type", type); + return NULL; +} + +void moderation_list_save() { + FILE* file = fopen(fs_get_write_path(MODERATION_LIST_FILEPATH), "w"); + if (!file) return; + + for (u8 type = 0; type < MODERATION_LIST_TYPE_COUNT; type++) { + struct ModerationList* list = moderation_list_get_list_by_type(type); + if (!list) continue; + + fprintf(file, "[Type %u]\n", type); + fprintf(file, "count = %u\n\n", list->count); + + for (u16 i = 0; i < list->count; i++) { + struct ModerationEntry* entry = list->list[i]; + if (!entry) continue; + fprintf(file, "[Entry %u for %u]\n", i, type); + fprintf(file, "time = %ld\n", entry->time); + fprintf(file, "playerName = %s\n", entry->playerName); + fprintf(file, "playerColorR = %d\n", entry->playerColor[0]); + fprintf(file, "playerColorG = %d\n", entry->playerColor[1]); + fprintf(file, "playerColorB = %d\n", entry->playerColor[2]); + fprintf(file, "address = %s\n", entry->address); + fprintf(file, "discordId = %s\n", entry->discordId); + fprintf(file, "reason = %s\n", entry->reason); + fprintf(file, "permanent = %d\n\n", entry->permanent ? 1 : 0); + } + } + fclose(file); +} + +void moderation_list_load() { + ini_t* iniFile = ini_load(fs_get_write_path(MODERATION_LIST_FILEPATH)); + if (!iniFile) return; + + for (u8 type = 0; type < MODERATION_LIST_TYPE_COUNT; type++) { + struct ModerationList* list = moderation_list_get_list_by_type(type); + if (!list) continue; + + char typeSection[16]; + snprintf(typeSection, 16, "Type %u", type); + u16 totalInIni = strtol(safe_ini_get(iniFile, typeSection, "count"), NULL, 0); + + for (u16 i = 0; i < totalInIni && list->count < MAX_MODERATION_LIST_ENTRIES; i++) { + char entrySection[32]; + snprintf(entrySection, 32, "Entry %u for %u", i, type); + + struct ModerationEntry* entry = malloc(sizeof(struct ModerationEntry)); + if (!entry) continue; + + entry->permanent = (strtol(safe_ini_get(iniFile, entrySection, "permanent"), NULL, 0) != 0); + + if (!entry->permanent) { + free(entry); + continue; + } + + entry->time = strtol(safe_ini_get(iniFile, entrySection, "time"), NULL, 0); + entry->playerName = strdup(safe_ini_get(iniFile, entrySection, "playerName")); + entry->playerColor[0] = strtol(safe_ini_get(iniFile, entrySection, "playerColorR"), NULL, 0); + entry->playerColor[1] = strtol(safe_ini_get(iniFile, entrySection, "playerColorG"), NULL, 0); + 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")); + + list->list[list->count++] = entry; + } + } + ini_free(iniFile); + // wipe non-permanent players from list + moderation_list_save(); +} + +void moderation_list_add(enum ModerationListType type, u8 localIndex, char* reason, bool permanent) { + struct ModerationList* list = moderation_list_get_list_by_type(type); + if (!list || list->count >= MAX_MODERATION_LIST_ENTRIES) return; + + struct NetworkPlayer* np = &gNetworkPlayers[localIndex]; + struct ModerationEntry* entry = malloc(sizeof(struct ModerationEntry)); + if (!entry) return; + + entry->playerName = strdup(np->name); + 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 : ""); + entry->permanent = permanent; + time(&entry->time); + + list->list[list->count++] = entry; + moderation_list_save(); +} + +void moderation_list_remove(enum ModerationListType type, char* address) { + struct ModerationList* list = moderation_list_get_list_by_type(type); + if (!list || !address) return; + + for (u16 i = 0; i < list->count; i++) { + if (list->list[i] && strcmp(list->list[i]->address, address) == 0) { + 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++) { + list->list[j] = list->list[j + 1]; + } + + list->count--; + list->list[list->count] = NULL; + + moderation_list_save(); + return; + } + } + LOG_ERROR("Address %s not found in list %u", address, type); +} + +bool moderation_list_contains(enum ModerationListType type, char* address) { + struct ModerationList* list = moderation_list_get_list_by_type(type); + if (!list || !address) return false; + + for (u16 i = 0; i < list->count; i++) { + if (list->list[i] && strcmp(list->list[i]->address, address) == 0) { + return true; + } + } + return false; +} \ No newline at end of file diff --git a/src/pc/network/moderation_list.h b/src/pc/network/moderation_list.h new file mode 100644 index 000000000..cbbdd9be7 --- /dev/null +++ b/src/pc/network/moderation_list.h @@ -0,0 +1,43 @@ +#pragma once +#include +#include +#include "moderation.h" + +#define MODERATION_LIST_FILEPATH "moderation_list.ini" +#define MAX_MODERATION_LIST_ENTRIES 1024 + +enum ModerationListType { + MODERATION_LIST_TYPE_BAN, + MODERATION_LIST_TYPE_MODERATOR, + MODERATION_LIST_TYPE_COUNT +}; + +struct ModerationEntry { + time_t time; + char* playerName; + u8 playerColor[3]; + char* address; + char* discordId; + char* reason; + bool permanent; +}; + +struct ModerationList { + struct ModerationEntry* list[MAX_MODERATION_LIST_ENTRIES]; + u16 count; + bool actions[MODERATION_ACTION_COUNT]; +}; + +struct ModerationLists { + struct ModerationList banList; + struct ModerationList moderatorList; +}; + +extern struct ModerationLists gModerationLists; + +struct ModerationList* moderation_list_get_list_by_type(enum ModerationListType type); +void moderation_list_save(); +void moderation_list_load(); +void moderation_list_add(enum ModerationListType type, u8 localIndex, char* reason, bool permanent); +void moderation_list_remove(enum ModerationListType type, char* address); +bool moderation_list_contains(enum ModerationListType type, char* address); \ No newline at end of file diff --git a/src/pc/network/network_utils.c b/src/pc/network/network_utils.c index a461d6534..bbfc7f5fd 100644 --- a/src/pc/network/network_utils.c +++ b/src/pc/network/network_utils.c @@ -53,6 +53,14 @@ const char* network_get_player_text_color_string(u8 localIndex) { return sColorString; } +const char* network_get_complete_player_name(u8 localIndex) { + if (localIndex >= MAX_PLAYERS) { localIndex = 0; } + static char buffer[MAX_CONFIG_STRING + 10]; + const char* colorString = network_get_player_text_color_string(localIndex); + snprintf(buffer, MAX_CONFIG_STRING + 10, "%s%s", colorString, gNetworkPlayers[localIndex].name); + return buffer; +} + extern s16 gMenuMode; bool network_check_singleplayer_pause(void) { return ((gMenuMode != -1) || (gCameraMovementFlags & CAM_MOVE_PAUSE_SCREEN)) && diff --git a/src/pc/network/network_utils.h b/src/pc/network/network_utils.h index a44b58674..d33658520 100644 --- a/src/pc/network/network_utils.h +++ b/src/pc/network/network_utils.h @@ -17,6 +17,8 @@ bool network_is_moderator(void); u8* network_get_player_text_color(u8 localIndex); /* |description|Gets the DJUI hex color code string for the player corresponding to `localIndex`'s cap color|descriptionEnd| */ const char* network_get_player_text_color_string(u8 localIndex); +/* |description|Gets the complete player name, including the player's starting hex code.|descriptionEnd| */ +const char* network_get_complete_player_name(u8 localIndex); /* |description|Checks if the game can currently be paused in singleplayer|descriptionEnd| */ bool network_check_singleplayer_pause(void); diff --git a/src/pc/network/packets/packet_moderation.c b/src/pc/network/packets/packet_moderation.c new file mode 100644 index 000000000..ccabb4324 --- /dev/null +++ b/src/pc/network/packets/packet_moderation.c @@ -0,0 +1,87 @@ +#include +#include "../network.h" +#include "../moderation.h" +#include "pc/debuglog.h" + +bool sValidActions[MODERATION_ACTION_COUNT] = { + [MODERATION_ACTION_KICK] = true, + [MODERATION_ACTION_BAN] = true +}; + +void network_send_moderation_action(u8 action, u8 localIndex, char* reason, bool permanent) { + SOFT_ASSERT(gNetworkType != NT_SERVER); + if (!gNetworkPlayerLocal->moderator) { + LOG_ERROR("Tried to send moderation action as a non-moderator!"); + return; + } + struct NetworkPlayer* np = &gNetworkPlayers[localIndex]; + if (!np->connected) { + LOG_ERROR("Moderator tried to perform moderation on a disconnected player!"); + } + if (np->moderator) { + LOG_ERROR("Moderator tried to perform moderation on another moderator!"); + return; + } + + if (!sValidActions[action]) { + LOG_ERROR("Tried to send unimplemented action to the server!"); + return; + } + + struct Packet p = { 0 }; + packet_init(&p, PACKET_MODERATION_ACTION, false, PLMT_NONE); + packet_write(&p, &action, sizeof(u8)); + packet_write(&p, &np->globalIndex, sizeof(u8)); + if (reason) { + u16 reasonLength = strlen(reason); + packet_write(&p, &reasonLength, sizeof(u16)); + packet_write(&p, &reason, sizeof(u8) * reasonLength); + } else { + packet_write(&p, 0, sizeof(u16)); + } + packet_write(&p, &permanent, sizeof(bool)); + + network_send_to(gNetworkPlayerServer->globalIndex, &p); +} + +void network_receive_moderation_action(struct Packet* p) { + SOFT_ASSERT(gNetworkType == NT_SERVER); + + enum ModerationActions action = MODERATION_ACTION_COUNT; + u8 globalIndex = 0; + u16 reasonLength = 0; + char* reason = NULL; + bool permanent = false; + + packet_read(p, &action, sizeof(u8)); + if (!sValidActions[action]) { + LOG_ERROR("Received an invalid moderation action from a moderator!"); + return; + } + + packet_read(p, &globalIndex, sizeof(u8)); + if (globalIndex >= MAX_PLAYERS) { + LOG_ERROR("Received an out of range global index from a moderator!"); + return; + } + struct NetworkPlayer* np = network_player_from_global_index(globalIndex); + if (!np->connected) { + LOG_ERROR("Network player received from moderator is not connected!"); + return; + } + + packet_read(p, &reasonLength, sizeof(u16)); + packet_read(p, &reason, sizeof(u8) * reasonLength); + packet_read(p, &permanent, sizeof(bool)); + + switch (action) { + case MODERATION_ACTION_KICK: + network_kick_player(np->localIndex, reason); + break; + case MODERATION_ACTION_BAN: + network_ban_player(np->localIndex, reason, permanent); + break; + default: + break; + } +} \ No newline at end of file From 65dfee6e9a7d886ec5c0cf1e6723ddb44d1a69a4 Mon Sep 17 00:00:00 2001 From: EmeraldLockdown <86802223+EmeraldLoc@users.noreply.github.com> Date: Sun, 8 Mar 2026 18:35:11 -0500 Subject: [PATCH 03/22] Add in inspect panels, even more polish, just need to do refreshing iirc --- lang/English.ini | 5 +- .../djui_panel_moderation_confirm_action.c | 24 ++++++-- .../djui_panel_moderation_list_inspector.c | 3 +- src/pc/djui/djui_panel_moderator_menu.c | 7 ++- .../djui/djui_panel_moderator_menu_inspect.c | 55 +++++++++++++++++++ .../djui/djui_panel_moderator_menu_inspect.h | 4 ++ src/pc/network/socket/socket.c | 6 +- 7 files changed, 91 insertions(+), 13 deletions(-) create mode 100644 src/pc/djui/djui_panel_moderator_menu_inspect.c create mode 100644 src/pc/djui/djui_panel_moderator_menu_inspect.h diff --git a/lang/English.ini b/lang/English.ini index e85f9890e..c22da1361 100644 --- a/lang/English.ini +++ b/lang/English.ini @@ -341,7 +341,7 @@ CONSOLE = "CONSOLE" MODS = "MODS" [MODERATION] -MODERATOR_MENU_TITLE = "MODERATOR MENU" +MODERATOR_MENU_TITLE = "MODERATION" MODERATION_LISTS_TITLE = "MODERATION LISTS" MODERATION_LISTS = "Moderation Lists" NO_PLAYERS_CONNECTED = "No players connected." @@ -368,7 +368,8 @@ INSPECT = "Inspect" INSPECTOR_TITLE = "INSPECTOR" DATE = "Added: @" DISCORD_ID = "Discord ID: @" -REASON = "Reason: @" +REASON_INFO = "Reason: @" +REASON = "Reason" PERMANENT = "Permanent" [OPTIONS] diff --git a/src/pc/djui/djui_panel_moderation_confirm_action.c b/src/pc/djui/djui_panel_moderation_confirm_action.c index a61dab4d3..25cb8616a 100644 --- a/src/pc/djui/djui_panel_moderation_confirm_action.c +++ b/src/pc/djui/djui_panel_moderation_confirm_action.c @@ -8,6 +8,7 @@ #include "pc/network/moderation.h" static char* sReason = NULL; +static bool sPermanent = false; void (*sOnYesClick)(struct DjuiBase*) = NULL; static void djui_panel_moderation_call_action(struct DjuiBase* caller) { @@ -20,13 +21,13 @@ static void djui_panel_moderation_call_action(struct DjuiBase* caller) { network_kick_player(player, sReason); break; case MODERATION_ACTION_BAN: - network_ban_player(player, sReason, false); + network_ban_player(player, sReason, sPermanent); break; case MODERATION_ACTION_UNBAN: network_unban_player(address); break; case MODERATION_ACTION_MOD: - network_mod_player(player, sReason, true); + network_mod_player(player, sReason, sPermanent); break; case MODERATION_ACTION_UNMOD: network_unmod_player(address); @@ -37,6 +38,7 @@ static void djui_panel_moderation_call_action(struct DjuiBase* caller) { free(sReason); sReason = NULL; + sPermanent = false; djui_panel_menu_back(caller); if (sOnYesClick) sOnYesClick(caller); @@ -76,7 +78,17 @@ static void djui_panel_moderation_confirm_set_title_and_message(u8 action, char* } } +static void djui_panel_moderation_confirm_destroy(struct DjuiBase* base) { + struct DjuiThreePanel* threePanel = (struct DjuiThreePanel*)base; + free(threePanel); + free(sReason); + sReason = NULL; + sPermanent = false; +} + 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*)) { + sPermanent = permanent; + struct DjuiThreePanel* panel = djui_panel_menu_create(title, false); struct DjuiBase* body = djui_three_panel_get_body(panel); { @@ -86,7 +98,7 @@ void djui_panel_moderation_confirm_create_body(struct DjuiBase* caller, char* ti if (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, "Reason:"); + struct DjuiText* text1 = djui_text_create(&rect1->base, DLANG(MODERATION, REASON)); djui_base_set_size_type(&text1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); djui_base_set_color(&text1->base, 220, 220, 220, 255); djui_base_set_size(&text1->base, 0.585f, 64); @@ -99,6 +111,8 @@ void djui_panel_moderation_confirm_create_body(struct DjuiBase* caller, char* ti djui_base_set_alignment(&inputbox1->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP); djui_interactable_hook_value_change(&inputbox1->base, djui_panel_moderation_confirm_reason_text_change); } + + djui_checkbox_create(body, DLANG(MODERATION, PERMANENT), &sPermanent, NULL); } djui_base_set_size(&text->base, 1.0f, 64); @@ -116,17 +130,19 @@ void djui_panel_moderation_confirm_create_body(struct DjuiBase* caller, char* ti struct DjuiButton* yesButton = djui_button_right_create(&rect2->base, DLANG(MENU, YES), DJUI_BUTTON_STYLE_NORMAL, djui_panel_moderation_call_action); yesButton->base.tag = localIndex; yesButton->base.uTag = action; - yesButton->base.bTag = permanent; yesButton->base.cTag = strdup(address); sOnYesClick = on_yes_click; } } + panel->base.destroy = djui_panel_moderation_confirm_destroy; djui_panel_add(caller, panel, NULL); } void djui_panel_moderation_confirm_create(struct DjuiBase* caller, u8 action, u8 localIndex, bool permanent, void (*on_yes_click)(struct DjuiBase*)) { if (localIndex >= MAX_PLAYERS) return; + struct NetworkPlayer* np = &gNetworkPlayers[localIndex]; + if (!np->connected) return; char* title = NULL; char message[256] = { 0 }; djui_panel_moderation_confirm_set_title_and_message(action, &title, message, (char*)network_get_complete_player_name(localIndex)); diff --git a/src/pc/djui/djui_panel_moderation_list_inspector.c b/src/pc/djui/djui_panel_moderation_list_inspector.c index 277d077d1..d6ffd2003 100644 --- a/src/pc/djui/djui_panel_moderation_list_inspector.c +++ b/src/pc/djui/djui_panel_moderation_list_inspector.c @@ -73,7 +73,7 @@ void djui_panel_moderation_list_inspect_create(struct DjuiBase* caller) { if (entry->reason && strcmp(entry->reason, "") != 0) { char reasonStr[512]; - djui_language_replace(DLANG(MODERATION, REASON), reasonStr, 512, '@', entry->reason); + djui_language_replace(DLANG(MODERATION, REASON_INFO), reasonStr, 512, '@', 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); @@ -81,7 +81,6 @@ void djui_panel_moderation_list_inspect_create(struct DjuiBase* caller) { djui_base_compute_tree(&reasonText->base); u16 reasonLines = djui_text_count_lines(reasonText, 12); f32 reasonHeight = 32 * reasonLines; - printf("Reason lines is %u, with reason height being %f\n", reasonLines, reasonHeight); djui_base_set_size(&reasonText->base, 1, reasonHeight); djui_text_set_alignment(reasonText, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP); djui_text_set_drop_shadow(reasonText, 64, 64, 64, 100); diff --git a/src/pc/djui/djui_panel_moderator_menu.c b/src/pc/djui/djui_panel_moderator_menu.c index 98a552c79..638cb7c1a 100644 --- a/src/pc/djui/djui_panel_moderator_menu.c +++ b/src/pc/djui/djui_panel_moderator_menu.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" @@ -13,7 +14,7 @@ static struct DjuiFlowLayout* sLayout = NULL; static struct DjuiPaginated* sPaginated = NULL; static void djui_panel_moderator_menu_action_button_click(struct DjuiBase* caller) { - djui_panel_moderation_confirm_create(caller, caller->uTag, caller->tag, false, djui_panel_moderator_menu_reload); + djui_panel_moderation_confirm_create(caller, caller->uTag, caller->tag, caller->bTag, djui_panel_moderator_menu_reload); } static void djui_panel_moderator_add_players(struct DjuiBase* layoutBase) { @@ -32,12 +33,13 @@ static void djui_panel_moderator_add_players(struct DjuiBase* layoutBase) { djui_base_set_size_type(&flowLayout->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); djui_base_set_size(&flowLayout->base, 1, 32); { - struct DjuiButton* playerButton = djui_button_create(&flowLayout->base, np->name, DJUI_BUTTON_STYLE_NORMAL, NULL); + struct DjuiButton* playerButton = djui_button_create(&flowLayout->base, np->name, DJUI_BUTTON_STYLE_NORMAL, djui_panel_moderator_menu_inspector_create); u8 playerColor[3]; memcpy(playerColor, network_get_player_text_color(i), 3); djui_base_set_color(&playerButton->text->base, playerColor[0], playerColor[1], playerColor[2], 255); djui_base_set_size_type(&playerButton->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); djui_base_set_size(&playerButton->base, 0.4, 32); + playerButton->base.tag = i; struct DjuiButton* kickButton = djui_button_create(&flowLayout->base, DLANG(MODERATION, KICK), DJUI_BUTTON_STYLE_NORMAL, djui_panel_moderator_menu_action_button_click); djui_base_set_size_type(&kickButton->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); @@ -56,6 +58,7 @@ static void djui_panel_moderator_add_players(struct DjuiBase* layoutBase) { djui_base_set_size(&modButton->base, 1.0, 32); djui_base_set_enabled(&modButton->base, gNetworkType == NT_SERVER); modButton->base.uTag = np->moderator ? MODERATION_ACTION_UNMOD : MODERATION_ACTION_MOD; + modButton->base.bTag = true; modButton->base.tag = i; } } diff --git a/src/pc/djui/djui_panel_moderator_menu_inspect.c b/src/pc/djui/djui_panel_moderator_menu_inspect.c new file mode 100644 index 000000000..46ec8665e --- /dev/null +++ b/src/pc/djui/djui_panel_moderator_menu_inspect.c @@ -0,0 +1,55 @@ + +#include +#include "djui.h" +#include "djui_panel.h" +#include "djui_panel_menu.h" +#include "djui_panel_moderator_menu.h" +#include "djui_panel_moderation_list.h" +#include "djui_panel_moderation_confirm_action.h" +#include "pc/network/network.h" +#include "pc/network/moderation.h" + +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); +} + +static void djui_panel_moderator_menu_inspector_destroy(struct DjuiBase* base) { + struct DjuiThreePanel* threePanel = (struct DjuiThreePanel*)base; + free(threePanel); +} + +void djui_panel_moderator_menu_inspector_create(struct DjuiBase* caller) { + if (caller->tag <= 0 || caller->tag >= MAX_PLAYERS) return; + struct NetworkPlayer* np = &gNetworkPlayers[caller->tag]; + if (!np->connected) return; + struct DjuiThreePanel* panel = djui_panel_menu_create(DLANG(MODERATION, MODERATOR_MENU_TITLE), true); + + struct DjuiBase* body = djui_three_panel_get_body(panel); + { + char playerName[MAX_CONFIG_STRING + 128]; + snprintf(playerName, MAX_CONFIG_STRING + 128, "%s \\#fff982\\- \\#82f9ff\\%s \\#fff982\\- \\#82f9ff\\%u", network_get_complete_player_name(np->localIndex), gNetworkSystem->get_id_str(np->localIndex), np->localIndex); + struct DjuiText* playerText = djui_text_create(body, playerName); + djui_base_set_size_type(&playerText->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&playerText->base, 1, 32); + djui_text_set_alignment(playerText, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP); + djui_text_set_drop_shadow(playerText, 64, 64, 64, 100); + + struct DjuiButton* kickButton = djui_button_create(body, DLANG(MODERATION, KICK), DJUI_BUTTON_STYLE_NORMAL, djui_panel_moderator_menu_action_button_click); + kickButton->base.uTag = MODERATION_ACTION_KICK; + kickButton->base.tag = np->localIndex; + + struct DjuiButton* banButton = djui_button_create(body, DLANG(MODERATION, BAN), DJUI_BUTTON_STYLE_NORMAL, djui_panel_moderator_menu_action_button_click); + banButton->base.uTag = MODERATION_ACTION_BAN; + banButton->base.tag = np->localIndex; + + struct DjuiButton* modButton = djui_button_create(body, np->moderator ? DLANG(MODERATION, UNMOD) : DLANG(MODERATION, MOD), DJUI_BUTTON_STYLE_NORMAL, djui_panel_moderator_menu_action_button_click); + modButton->base.uTag = np->moderator ? MODERATION_ACTION_UNMOD : MODERATION_ACTION_MOD; + modButton->base.bTag = true; + modButton->base.tag = np->localIndex; + + djui_button_create(body, DLANG(MENU, BACK), DJUI_BUTTON_STYLE_BACK, djui_panel_menu_back); + } + + panel->base.destroy = djui_panel_moderator_menu_inspector_destroy; + djui_panel_add(caller, panel, NULL); +} \ No newline at end of file diff --git a/src/pc/djui/djui_panel_moderator_menu_inspect.h b/src/pc/djui/djui_panel_moderator_menu_inspect.h new file mode 100644 index 000000000..983a431cc --- /dev/null +++ b/src/pc/djui/djui_panel_moderator_menu_inspect.h @@ -0,0 +1,4 @@ +#pragma once +#include "djui.h" + +void djui_panel_moderator_menu_inspector_create(struct DjuiBase* caller); \ No newline at end of file diff --git a/src/pc/network/socket/socket.c b/src/pc/network/socket/socket.c index edf179202..1791812e0 100644 --- a/src/pc/network/socket/socket.c +++ b/src/pc/network/socket/socket.c @@ -148,7 +148,7 @@ static bool ns_socket_initialize(enum NetworkType networkType, UNUSED bool recon int reuse = 1; if (setsockopt(sCurSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) < 0) { LOG_ERROR("setsockopt(SO_REUSEADDR) failed"); - } + } #ifdef SO_REUSEPORT if (setsockopt(sCurSocket, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuse, sizeof(reuse)) < 0) { @@ -157,9 +157,9 @@ static bool ns_socket_initialize(enum NetworkType networkType, UNUSED bool recon #endif // bind the socket to any address and the specified port. int rc = socket_bind(sCurSocket, port); - if (rc != NO_ERROR) { + if (rc != NO_ERROR) { LOG_ERROR("bind returned an error."); - return false; + return false; } LOG_INFO("bound to port %u", port); } else if (networkType == NT_CLIENT) { From 2102da9f565eabc0b22e96ad688b1e3f995081f7 Mon Sep 17 00:00:00 2001 From: EmeraldLockdown <86802223+EmeraldLoc@users.noreply.github.com> Date: Sun, 8 Mar 2026 21:48:26 -0500 Subject: [PATCH 04/22] ive lost track --- src/pc/djui/djui_panel_moderation_confirm_action.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pc/djui/djui_panel_moderation_confirm_action.c b/src/pc/djui/djui_panel_moderation_confirm_action.c index 25cb8616a..b2507abe2 100644 --- a/src/pc/djui/djui_panel_moderation_confirm_action.c +++ b/src/pc/djui/djui_panel_moderation_confirm_action.c @@ -101,13 +101,13 @@ void djui_panel_moderation_confirm_create_body(struct DjuiBase* caller, char* ti struct DjuiText* text1 = djui_text_create(&rect1->base, DLANG(MODERATION, REASON)); djui_base_set_size_type(&text1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); djui_base_set_color(&text1->base, 220, 220, 220, 255); - djui_base_set_size(&text1->base, 0.585f, 64); + djui_base_set_size(&text1->base, 0.4f, 64); djui_base_set_alignment(&text1->base, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP); djui_text_set_drop_shadow(text1, 64, 64, 64, 100); struct DjuiInputbox* inputbox1 = djui_inputbox_create(&rect1->base, 256); djui_base_set_size_type(&inputbox1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); - djui_base_set_size(&inputbox1->base, 0.45f, 32); + djui_base_set_size(&inputbox1->base, 0.75, 32); djui_base_set_alignment(&inputbox1->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP); djui_interactable_hook_value_change(&inputbox1->base, djui_panel_moderation_confirm_reason_text_change); } From 537eabc3bd68c0c70ebcebd962543b03d7684bb8 Mon Sep 17 00:00:00 2001 From: EmeraldLockdown <86802223+EmeraldLoc@users.noreply.github.com> Date: Sun, 8 Mar 2026 22:14:55 -0500 Subject: [PATCH 05/22] Add reloading, do last changes (hopefully, can't wait to review and make pr message (it requires search pr to be merged (again (i'm so cooked (its gg))))) --- .../djui/djui_panel_moderator_menu_inspect.c | 22 +++++++++++++++---- .../djui/djui_panel_moderator_menu_inspect.h | 1 + src/pc/network/moderation.c | 9 ++++++++ src/pc/network/moderation.h | 1 + src/pc/network/network_player.c | 9 ++++++++ 5 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/pc/djui/djui_panel_moderator_menu_inspect.c b/src/pc/djui/djui_panel_moderator_menu_inspect.c index 46ec8665e..50a4e0830 100644 --- a/src/pc/djui/djui_panel_moderator_menu_inspect.c +++ b/src/pc/djui/djui_panel_moderator_menu_inspect.c @@ -9,6 +9,9 @@ #include "pc/network/network.h" #include "pc/network/moderation.h" +struct DjuiButton* sModButton = NULL; +static u8 sSelectedIndex = 0; + 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); } @@ -16,12 +19,23 @@ static void djui_panel_moderator_menu_action_button_click(struct DjuiBase* calle static void djui_panel_moderator_menu_inspector_destroy(struct DjuiBase* base) { struct DjuiThreePanel* threePanel = (struct DjuiThreePanel*)base; free(threePanel); + sModButton = NULL; +} + +void djui_panel_moderator_inspector_reload() { + if (!sModButton) return; + if (sSelectedIndex >= MAX_PLAYERS) return; + struct NetworkPlayer* np = &gNetworkPlayers[sSelectedIndex]; + + djui_text_set_text(sModButton->text, np->moderator ? DLANG(MODERATION, UNMOD) : DLANG(MODERATION, MOD)); + sModButton->base.uTag = np->moderator ? MODERATION_ACTION_UNMOD : MODERATION_ACTION_MOD; } void djui_panel_moderator_menu_inspector_create(struct DjuiBase* caller) { if (caller->tag <= 0 || caller->tag >= MAX_PLAYERS) return; struct NetworkPlayer* np = &gNetworkPlayers[caller->tag]; if (!np->connected) return; + sSelectedIndex = caller->tag; struct DjuiThreePanel* panel = djui_panel_menu_create(DLANG(MODERATION, MODERATOR_MENU_TITLE), true); struct DjuiBase* body = djui_three_panel_get_body(panel); @@ -42,10 +56,10 @@ void djui_panel_moderator_menu_inspector_create(struct DjuiBase* caller) { banButton->base.uTag = MODERATION_ACTION_BAN; banButton->base.tag = np->localIndex; - struct DjuiButton* modButton = djui_button_create(body, np->moderator ? DLANG(MODERATION, UNMOD) : DLANG(MODERATION, MOD), DJUI_BUTTON_STYLE_NORMAL, djui_panel_moderator_menu_action_button_click); - modButton->base.uTag = np->moderator ? MODERATION_ACTION_UNMOD : MODERATION_ACTION_MOD; - modButton->base.bTag = true; - modButton->base.tag = np->localIndex; + sModButton = djui_button_create(body, np->moderator ? DLANG(MODERATION, UNMOD) : DLANG(MODERATION, MOD), DJUI_BUTTON_STYLE_NORMAL, djui_panel_moderator_menu_action_button_click); + sModButton->base.uTag = np->moderator ? MODERATION_ACTION_UNMOD : MODERATION_ACTION_MOD; + sModButton->base.bTag = true; + sModButton->base.tag = np->localIndex; djui_button_create(body, DLANG(MENU, BACK), DJUI_BUTTON_STYLE_BACK, djui_panel_menu_back); } diff --git a/src/pc/djui/djui_panel_moderator_menu_inspect.h b/src/pc/djui/djui_panel_moderator_menu_inspect.h index 983a431cc..e0be57523 100644 --- a/src/pc/djui/djui_panel_moderator_menu_inspect.h +++ b/src/pc/djui/djui_panel_moderator_menu_inspect.h @@ -1,4 +1,5 @@ #pragma once #include "djui.h" +void djui_panel_moderator_inspector_reload(); void djui_panel_moderator_menu_inspector_create(struct DjuiBase* caller); \ No newline at end of file diff --git a/src/pc/network/moderation.c b/src/pc/network/moderation.c index 6792ddc07..32dad08ae 100644 --- a/src/pc/network/moderation.c +++ b/src/pc/network/moderation.c @@ -2,9 +2,18 @@ #include #include #include "moderation.h" +#include "pc/djui/djui_panel_moderator_menu.h" +#include "pc/djui/djui_panel_moderation_list.h" +#include "pc/djui/djui_panel_moderator_menu_inspect.h" #include "pc/debuglog.h" #include "pc/ini.h" +void djui_reload_moderation_panels() { + djui_panel_moderator_menu_reload(); + djui_panel_moderation_list_reload(); + djui_panel_moderator_inspector_reload(); +} + void network_kick_player(u8 localIndex, char* reason) { if (gNetworkPlayerLocal->moderator) { network_send_moderation_action(MODERATION_ACTION_KICK, localIndex, reason, false); diff --git a/src/pc/network/moderation.h b/src/pc/network/moderation.h index c3216b61f..64bec5986 100644 --- a/src/pc/network/moderation.h +++ b/src/pc/network/moderation.h @@ -10,6 +10,7 @@ enum ModerationActions { MODERATION_ACTION_COUNT, }; +void djui_reload_moderation_panels(); void network_kick_player(u8 localIndex, char* reason); void network_ban_player(u8 localIndex, char* reason, bool permanent); void network_unban_player(char* address); diff --git a/src/pc/network/network_player.c b/src/pc/network/network_player.c index 5e1661bbb..3f25b020f 100644 --- a/src/pc/network/network_player.c +++ b/src/pc/network/network_player.c @@ -17,6 +17,7 @@ #endif #include "game/mario.h" #include "pc/djui/djui_unicode.h" +#include "moderation.h" struct NetworkPlayer gNetworkPlayers[MAX_PLAYERS] = { 0 }; struct NetworkPlayer *gNetworkPlayerLocal = NULL; @@ -292,6 +293,8 @@ u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex, u8 mode np->palette = *palette; network_player_update_model(localIndex); + djui_reload_moderation_panels(); + snprintf(np->name, MAX_CONFIG_STRING, "%s", name); return localIndex; } @@ -327,6 +330,9 @@ u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex, u8 mode snprintf(np->discordId, 64, "%s", discordId); + // update moderation panels + djui_reload_moderation_panels(); + // clear networking fields np->lastReceived = clock_elapsed(); np->lastSent = clock_elapsed(); @@ -421,6 +427,9 @@ u8 network_player_disconnected(u8 globalIndex) { // reset mario state init_mario_single_from_save_file(&gMarioStates[i], i); + // reload moderation panels + djui_reload_moderation_panels(); + return i; } return UNKNOWN_GLOBAL_INDEX; 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 06/22] 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 From ceac1bbb27723ea764ad48082de8665ee2371980 Mon Sep 17 00:00:00 2001 From: EmeraldLockdown <86802223+EmeraldLoc@users.noreply.github.com> Date: Mon, 9 Mar 2026 22:36:18 -0500 Subject: [PATCH 07/22] Fix windows compilation (hopefully) --- src/pc/djui/djui_panel_moderation_list.c | 4 ++-- src/pc/djui/djui_panel_moderation_list.h | 2 +- src/pc/djui/djui_panel_moderation_list_inspector.c | 2 +- src/pc/djui/djui_panel_moderator_menu.c | 2 +- src/pc/djui/djui_panel_moderator_menu.h | 2 +- src/pc/djui/djui_panel_moderator_menu_inspect.c | 10 +++++----- src/pc/djui/djui_panel_moderator_menu_inspect.h | 2 +- src/pc/network/moderation.c | 6 +++--- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/pc/djui/djui_panel_moderation_list.c b/src/pc/djui/djui_panel_moderation_list.c index c4bd0f18c..93c24fcc9 100644 --- a/src/pc/djui/djui_panel_moderation_list.c +++ b/src/pc/djui/djui_panel_moderation_list.c @@ -49,7 +49,7 @@ static void djui_panel_moderation_list_populate_list(struct DjuiBase* layoutBase } } -void djui_panel_moderation_list_reload() { +void djui_panel_moderation_list_reload(UNUSED struct DjuiBase* caller) { if (!sLayout || !sPaginated) return; djui_base_destroy_children(&sLayout->base); djui_panel_moderation_list_populate_list(&sLayout->base); @@ -65,7 +65,7 @@ static void djui_panel_moderation_list_destroy(struct DjuiBase* base) { static bool djui_panel_moderation_list_on_back(UNUSED struct DjuiBase* base) { if (!gDjuiInMainMenu) { - djui_panel_moderator_menu_reload(); + djui_panel_moderator_menu_reload(NULL); } return false; } diff --git a/src/pc/djui/djui_panel_moderation_list.h b/src/pc/djui/djui_panel_moderation_list.h index 4903b01cf..646119d9c 100644 --- a/src/pc/djui/djui_panel_moderation_list.h +++ b/src/pc/djui/djui_panel_moderation_list.h @@ -1,5 +1,5 @@ #pragma once #include "djui.h" -void djui_panel_moderation_list_reload(); +void djui_panel_moderation_list_reload(UNUSED struct DjuiBase* caller); void djui_panel_moderation_list_create(struct DjuiBase* caller); \ No newline at end of file diff --git a/src/pc/djui/djui_panel_moderation_list_inspector.c b/src/pc/djui/djui_panel_moderation_list_inspector.c index 2fec5ca0a..078fe70bd 100644 --- a/src/pc/djui/djui_panel_moderation_list_inspector.c +++ b/src/pc/djui/djui_panel_moderation_list_inspector.c @@ -15,7 +15,7 @@ static u16 sListIndex = 0; static void djui_panel_moderation_list_inspect_action_exit(UNUSED struct DjuiBase* caller) { djui_panel_back_by(2); - djui_panel_moderation_list_reload(); + djui_panel_moderation_list_reload(NULL); } static void djui_panel_moderation_list_action_button_click(struct DjuiBase* caller) { diff --git a/src/pc/djui/djui_panel_moderator_menu.c b/src/pc/djui/djui_panel_moderator_menu.c index 638cb7c1a..783b092d0 100644 --- a/src/pc/djui/djui_panel_moderator_menu.c +++ b/src/pc/djui/djui_panel_moderator_menu.c @@ -79,7 +79,7 @@ static void djui_panel_moderator_menu_destroy(struct DjuiBase* base) { sPaginated = NULL; } -void djui_panel_moderator_menu_reload() { +void djui_panel_moderator_menu_reload(UNUSED struct DjuiBase* caller) { if (!sLayout || !sPaginated) return; djui_base_destroy_children(&sLayout->base); djui_panel_moderator_add_players(&sLayout->base); diff --git a/src/pc/djui/djui_panel_moderator_menu.h b/src/pc/djui/djui_panel_moderator_menu.h index 648441d15..63b26b3fc 100644 --- a/src/pc/djui/djui_panel_moderator_menu.h +++ b/src/pc/djui/djui_panel_moderator_menu.h @@ -1,5 +1,5 @@ #pragma once #include "djui.h" -void djui_panel_moderator_menu_reload(); +void djui_panel_moderator_menu_reload(UNUSED struct DjuiBase* caller); void djui_panel_moderator_menu_create(struct DjuiBase* caller); \ No newline at end of file diff --git a/src/pc/djui/djui_panel_moderator_menu_inspect.c b/src/pc/djui/djui_panel_moderator_menu_inspect.c index 10f76c523..77f9a8463 100644 --- a/src/pc/djui/djui_panel_moderator_menu_inspect.c +++ b/src/pc/djui/djui_panel_moderator_menu_inspect.c @@ -13,7 +13,7 @@ struct DjuiButton* sModButton = NULL; static u8 sSelectedIndex = 0; -static void djui_panel_moderator_inspector_validate_and_reload(); +static void djui_panel_moderator_inspector_validate_and_reload(UNUSED struct DjuiBase* caller); 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_inspector_validate_and_reload); @@ -25,15 +25,15 @@ 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(); +static void djui_panel_moderator_inspector_validate_and_reload(UNUSED struct DjuiBase* caller) { + djui_panel_moderator_menu_reload(caller); 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(); + djui_panel_moderator_inspector_reload(NULL); } -void djui_panel_moderator_inspector_reload() { +void djui_panel_moderator_inspector_reload(UNUSED struct DjuiBase* caller) { if (!sModButton) return; if (sSelectedIndex >= MAX_PLAYERS) return; struct NetworkPlayer* np = &gNetworkPlayers[sSelectedIndex]; diff --git a/src/pc/djui/djui_panel_moderator_menu_inspect.h b/src/pc/djui/djui_panel_moderator_menu_inspect.h index e0be57523..779abd968 100644 --- a/src/pc/djui/djui_panel_moderator_menu_inspect.h +++ b/src/pc/djui/djui_panel_moderator_menu_inspect.h @@ -1,5 +1,5 @@ #pragma once #include "djui.h" -void djui_panel_moderator_inspector_reload(); +void djui_panel_moderator_inspector_reload(UNUSED struct DjuiBase* caller); void djui_panel_moderator_menu_inspector_create(struct DjuiBase* caller); \ No newline at end of file diff --git a/src/pc/network/moderation.c b/src/pc/network/moderation.c index e63b603a9..6277b6667 100644 --- a/src/pc/network/moderation.c +++ b/src/pc/network/moderation.c @@ -11,9 +11,9 @@ u8 gQueuedDisconnect = QUEUED_DISCONNECT_NONE; void djui_reload_moderation_panels() { - djui_panel_moderator_menu_reload(); - djui_panel_moderation_list_reload(); - djui_panel_moderator_inspector_reload(); + djui_panel_moderator_menu_reload(NULL); + djui_panel_moderation_list_reload(NULL); + djui_panel_moderator_inspector_reload(NULL); } void network_kick_player(u8 localIndex, char* reason) { From b74e486a2d33b8108d432370e4a387eb30fc3e0b Mon Sep 17 00:00:00 2001 From: EmeraldLockdown <86802223+EmeraldLoc@users.noreply.github.com> Date: Mon, 9 Mar 2026 22:45:41 -0500 Subject: [PATCH 08/22] Fix windows (again (please work)) --- src/pc/djui/djui_panel_moderation_list_inspector.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pc/djui/djui_panel_moderation_list_inspector.c b/src/pc/djui/djui_panel_moderation_list_inspector.c index 078fe70bd..f12c97117 100644 --- a/src/pc/djui/djui_panel_moderation_list_inspector.c +++ b/src/pc/djui/djui_panel_moderation_list_inspector.c @@ -13,6 +13,11 @@ static u16 sListType = 0; static u16 sListIndex = 0; +// I don't like you Windows +static void djui_panel_moderation_list_inspect_save(UNUSED struct DjuiBase* caller) { + moderation_list_save(); +} + static void djui_panel_moderation_list_inspect_action_exit(UNUSED struct DjuiBase* caller) { djui_panel_back_by(2); djui_panel_moderation_list_reload(NULL); @@ -87,7 +92,7 @@ void djui_panel_moderation_list_inspect_create(struct DjuiBase* caller) { djui_text_set_drop_shadow(reasonText, 64, 64, 64, 100); } - djui_checkbox_create(body, DLANG(MODERATION, PERMANENT), &entry->permanent, moderation_list_save); + djui_checkbox_create(body, DLANG(MODERATION, PERMANENT), &entry->permanent, djui_panel_moderation_list_inspect_save); for (u8 i = 0; i < MODERATION_ACTION_COUNT; i++) { if (!list->actions[i]) continue; From 6036bada409a9069382e28ab55aca9ae13866204 Mon Sep 17 00:00:00 2001 From: EmeraldLockdown <86802223+EmeraldLoc@users.noreply.github.com> Date: Mon, 9 Mar 2026 23:47:48 -0500 Subject: [PATCH 09/22] please windows.... no warnings, I'm doing a cast just for you, I need this windows :pray: --- src/pc/network/moderation_list.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pc/network/moderation_list.c b/src/pc/network/moderation_list.c index 3471f40a6..7b4222e80 100644 --- a/src/pc/network/moderation_list.c +++ b/src/pc/network/moderation_list.c @@ -45,7 +45,8 @@ void moderation_list_save() { struct ModerationEntry* entry = list->list[i]; if (!entry) continue; fprintf(file, "[Entry %u for %u]\n", i, type); - fprintf(file, "time = %ld\n", entry->time); + // windows <3 + fprintf(file, "time = %lld\n", (long long)entry->time); fprintf(file, "playerName = %s\n", entry->playerName); fprintf(file, "playerColorR = %d\n", entry->playerColor[0]); fprintf(file, "playerColorG = %d\n", entry->playerColor[1]); From 65dbb835e7107311f77cb8c45296e45bc2d81359 Mon Sep 17 00:00:00 2001 From: EmeraldLockdown <86802223+EmeraldLoc@users.noreply.github.com> Date: Tue, 10 Mar 2026 14:25:32 -0500 Subject: [PATCH 10/22] Make popups text length not include hex --- src/pc/djui/djui_popup.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pc/djui/djui_popup.c b/src/pc/djui/djui_popup.c index 01673ae64..7d629b36a 100644 --- a/src/pc/djui/djui_popup.c +++ b/src/pc/djui/djui_popup.c @@ -37,7 +37,7 @@ static void djui_popup_destroy(struct DjuiBase* base) { free(popup); } -static void djui_popup_create_interal(const char* message, int lines, int paddingLines) { +static void djui_popup_create_internal(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)); @@ -67,16 +67,16 @@ static void djui_popup_create_interal(const char* message, int lines, int paddin } void djui_popup_create(const char* message, int lines) { - int linesReq = (int)ceilf(djui_hud_measure_text(message) / DJUI_POPUP_WIDTH); + int linesReq = (int)ceilf(djui_hud_measure_text(str_remove_color_codes(message)) / DJUI_POPUP_WIDTH); if (linesReq < 1) linesReq = 1; if (linesReq > lines) linesReq = lines; - djui_popup_create_interal(message, linesReq, lines - linesReq); + djui_popup_create_internal(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); + int linesReq = (int)ceilf(djui_hud_measure_text(str_remove_color_codes(message)) / DJUI_POPUP_WIDTH); if (linesReq < 1) linesReq = 1; - djui_popup_create_interal(message, linesReq, paddingLines); + djui_popup_create_internal(message, linesReq, paddingLines); } void djui_popup_update(void) { From eb6ef89b76c2c9068be20fb871d4245def540c0e Mon Sep 17 00:00:00 2001 From: EmeraldLockdown <86802223+EmeraldLoc@users.noreply.github.com> Date: Tue, 10 Mar 2026 15:04:44 -0500 Subject: [PATCH 11/22] Fix crash with cursor use after free --- src/pc/djui/djui_base.c | 5 +++++ src/pc/djui/djui_cursor.c | 8 ++++---- src/pc/djui/djui_cursor.h | 2 ++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/pc/djui/djui_base.c b/src/pc/djui/djui_base.c index 7b60017e5..46e81d405 100644 --- a/src/pc/djui/djui_base.c +++ b/src/pc/djui/djui_base.c @@ -353,6 +353,11 @@ void djui_base_destroy(struct DjuiBase* base) { gInteractableBinding = NULL; } + // remove input controlled base + if (gInputControlledBase == base) { + gInputControlledBase = NULL; + } + // remove myself from parent's linked list if (base->parent != NULL) { struct DjuiBaseChild* child = base->parent->child; diff --git a/src/pc/djui/djui_cursor.c b/src/pc/djui/djui_cursor.c index 7c336a74f..4c7dfde7f 100644 --- a/src/pc/djui/djui_cursor.c +++ b/src/pc/djui/djui_cursor.c @@ -12,7 +12,7 @@ extern ALIGNED8 u8 gd_texture_hand_closed[]; struct DjuiImage* sMouseCursor = NULL; static bool sCursorMouseControlled = false; -static struct DjuiBase* sInputControlledBase = NULL; +struct DjuiBase* gInputControlledBase = NULL; static f32 sSavedMouseX = 0; static f32 sSavedMouseY = 0; @@ -55,7 +55,7 @@ static void djui_cursor_base_hover_location(struct DjuiBase* base, f32* x, f32* void djui_cursor_input_controlled_center(struct DjuiBase* base) { if (!sCursorMouseControlled && (!base || (base && base->interactable && base->interactable->enabled))) { - sInputControlledBase = base; + gInputControlledBase = base; djui_cursor_set_visible(base != NULL); } } @@ -147,8 +147,8 @@ static void djui_cursor_update_position(void) { if (sCursorMouseControlled) { gCursorX = mouse_window_x / djui_gfx_get_scale(); gCursorY = mouse_window_y / djui_gfx_get_scale(); - } else if (sInputControlledBase != NULL) { - djui_cursor_base_hover_location(sInputControlledBase, &gCursorX, &gCursorY); + } else if (gInputControlledBase != NULL) { + djui_cursor_base_hover_location(gInputControlledBase, &gCursorX, &gCursorY); } // set cursor position diff --git a/src/pc/djui/djui_cursor.h b/src/pc/djui/djui_cursor.h index bc9374060..e485ba4b2 100644 --- a/src/pc/djui/djui_cursor.h +++ b/src/pc/djui/djui_cursor.h @@ -5,6 +5,8 @@ extern f32 gCursorX; extern f32 gCursorY; +extern struct DjuiBase* gInputControlledBase; + void djui_cursor_set_visible(bool visible); bool djui_cursor_inside_base(struct DjuiBase* base); void djui_cursor_input_controlled_center(struct DjuiBase* base); From a12386fbf31b590ed66a824186555c3d596a6b9e Mon Sep 17 00:00:00 2001 From: EmeraldLockdown <86802223+EmeraldLoc@users.noreply.github.com> Date: Tue, 10 Mar 2026 15:11:08 -0500 Subject: [PATCH 12/22] Remove mod button if not server in mod menu inspect --- src/pc/djui/djui_panel_moderator_menu_inspect.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/pc/djui/djui_panel_moderator_menu_inspect.c b/src/pc/djui/djui_panel_moderator_menu_inspect.c index 77f9a8463..e32446d5f 100644 --- a/src/pc/djui/djui_panel_moderator_menu_inspect.c +++ b/src/pc/djui/djui_panel_moderator_menu_inspect.c @@ -67,10 +67,12 @@ void djui_panel_moderator_menu_inspector_create(struct DjuiBase* caller) { banButton->base.uTag = MODERATION_ACTION_BAN; banButton->base.tag = np->localIndex; - sModButton = djui_button_create(body, np->moderator ? DLANG(MODERATION, UNMOD) : DLANG(MODERATION, MOD), DJUI_BUTTON_STYLE_NORMAL, djui_panel_moderator_menu_action_button_click); - sModButton->base.uTag = np->moderator ? MODERATION_ACTION_UNMOD : MODERATION_ACTION_MOD; - sModButton->base.bTag = true; - sModButton->base.tag = np->localIndex; + if (gNetworkType == NT_SERVER) { + sModButton = djui_button_create(body, np->moderator ? DLANG(MODERATION, UNMOD) : DLANG(MODERATION, MOD), DJUI_BUTTON_STYLE_NORMAL, djui_panel_moderator_menu_action_button_click); + sModButton->base.uTag = np->moderator ? MODERATION_ACTION_UNMOD : MODERATION_ACTION_MOD; + sModButton->base.bTag = true; + sModButton->base.tag = np->localIndex; + } djui_button_create(body, DLANG(MENU, BACK), DJUI_BUTTON_STYLE_BACK, djui_panel_menu_back); } From 34ab46bdb42a08271ab930b5ef984cddcfb99d3e Mon Sep 17 00:00:00 2001 From: EmeraldLockdown <86802223+EmeraldLoc@users.noreply.github.com> Date: Tue, 10 Mar 2026 15:29:35 -0500 Subject: [PATCH 13/22] Make moderators perma baninng players not allowed --- src/pc/djui/djui_panel_moderation_confirm_action.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pc/djui/djui_panel_moderation_confirm_action.c b/src/pc/djui/djui_panel_moderation_confirm_action.c index a6c65ecae..630f1957f 100644 --- a/src/pc/djui/djui_panel_moderation_confirm_action.c +++ b/src/pc/djui/djui_panel_moderation_confirm_action.c @@ -113,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); } - if (action != MODERATION_ACTION_KICK) djui_checkbox_create(body, DLANG(MODERATION, PERMANENT), &sPermanent, NULL); + if (action != MODERATION_ACTION_KICK && gNetworkType == NT_SERVER) djui_checkbox_create(body, DLANG(MODERATION, PERMANENT), &sPermanent, NULL); } djui_base_set_size(&text->base, 1.0f, 64); From fabc152c586e7bd334ae6283bc24e0fe1e8283fc Mon Sep 17 00:00:00 2001 From: EmeraldLockdown <86802223+EmeraldLoc@users.noreply.github.com> Date: Tue, 10 Mar 2026 16:25:35 -0500 Subject: [PATCH 14/22] im so tired --- src/pc/network/packets/packet_kick.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/pc/network/packets/packet_kick.c b/src/pc/network/packets/packet_kick.c index 5fb933546..53cf12fc2 100644 --- a/src/pc/network/packets/packet_kick.c +++ b/src/pc/network/packets/packet_kick.c @@ -40,10 +40,6 @@ void network_create_kick_popup(enum KickReasonType kickReason, char* reason) { } void network_send_kick(u8 localIndex, enum KickReasonType kickReason, char* reason) { - if (localIndex == 0) { - LOG_ERROR("Trying to send kick to myself?"); - return; - } u8 kickReasonType = kickReason; struct Packet p = { 0 }; packet_init(&p, PACKET_KICK, true, PLMT_NONE); From 9f61eee788db89c17cdc95c3b041ffb879937afa Mon Sep 17 00:00:00 2001 From: EmeraldLockdown <86802223+EmeraldLoc@users.noreply.github.com> Date: Tue, 10 Mar 2026 16:35:30 -0500 Subject: [PATCH 15/22] cool stuff --- src/pc/network/packets/packet.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/pc/network/packets/packet.c b/src/pc/network/packets/packet.c index 38e837158..b625e01a6 100644 --- a/src/pc/network/packets/packet.c +++ b/src/pc/network/packets/packet.c @@ -155,8 +155,17 @@ void packet_receive(struct Packet* p) { // refuse packets from banned players if (gNetworkType == NT_SERVER) { if (moderation_list_contains(MODERATION_LIST_TYPE_BAN, gNetworkSystem->get_id_str(p->localIndex))) { + char* reason = NULL; + struct ModerationList* list = moderation_list_get_list_by_type(MODERATION_LIST_TYPE_BAN); + for (u16 i = 0; i < list->count; i++) { + struct ModerationEntry* entry = list->list[i]; + if (strcmp(entry->address, gNetworkSystem->get_id_str(p->localIndex)) == 0) { + reason = entry->reason; + break; + } + } LOG_INFO("kicking banned player"); - network_send_kick(0, EKT_BANNED, NULL); + network_send_kick(0, EKT_BANNED, reason); return; } } From 98193bac956c834d315bc16faf13ba9bb112ae81 Mon Sep 17 00:00:00 2001 From: EmeraldLockdown <86802223+EmeraldLoc@users.noreply.github.com> Date: Tue, 10 Mar 2026 16:44:11 -0500 Subject: [PATCH 16/22] Send reason over command packet --- src/pc/chat_commands.c | 7 ++----- src/pc/network/packets/packet.h | 2 +- src/pc/network/packets/packet_command_mod.c | 22 ++++++++++++++++----- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/pc/chat_commands.c b/src/pc/chat_commands.c index 9646182bb..11fdaeb05 100644 --- a/src/pc/chat_commands.c +++ b/src/pc/chat_commands.c @@ -68,7 +68,7 @@ bool exec_chat_command(char* command) { network_send_kick(np->localIndex, EKT_KICKED, reason); network_player_disconnected(np->localIndex); } else { - network_send_chat_command(np->globalIndex, CCC_KICK); + network_send_chat_command(np->globalIndex, CCC_KICK, reason); } return true; } @@ -79,11 +79,10 @@ bool exec_chat_command(char* command) { if (gNetworkType == NT_SERVER) { 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, reason, false); network_player_disconnected(np->localIndex); } else { - network_send_chat_command(np->globalIndex, CCC_BAN); + network_send_chat_command(np->globalIndex, CCC_BAN, reason); } return true; } @@ -91,7 +90,6 @@ 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, reason); - // TODO: Moderation: Allow you to insert a reason moderation_list_add(MODERATION_LIST_TYPE_BAN, np->localIndex, reason, true); network_player_disconnected(np->localIndex); return true; @@ -100,7 +98,6 @@ bool exec_chat_command(char* command) { chat_construct_player_message(np, DLANG(CHAT, ADD_MODERATOR)); 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, reason, true); return true; } diff --git a/src/pc/network/packets/packet.h b/src/pc/network/packets/packet.h index 56b48e07d..c72418119 100644 --- a/src/pc/network/packets/packet.h +++ b/src/pc/network/packets/packet.h @@ -243,7 +243,7 @@ void network_send_kick(u8 localIndex, enum KickReasonType kickReason, char* reas void network_receive_kick(struct Packet* p); // packet_command_mod.c -void network_send_chat_command(u8 localIndex, enum ChatConfirmCommand CCC); +void network_send_chat_command(u8 localIndex, enum ChatConfirmCommand CCC, char* reason); void network_receive_chat_command(struct Packet* p); // packet_moderator.c diff --git a/src/pc/network/packets/packet_command_mod.c b/src/pc/network/packets/packet_command_mod.c index 55acf9e14..628b15a93 100644 --- a/src/pc/network/packets/packet_command_mod.c +++ b/src/pc/network/packets/packet_command_mod.c @@ -5,7 +5,7 @@ #include "pc/network/moderation.h" #include "pc/debuglog.h" -void network_send_chat_command(u8 globalIndex, enum ChatConfirmCommand ccc) { +void network_send_chat_command(u8 globalIndex, enum ChatConfirmCommand ccc, char* reason) { if (!gNetworkPlayers[0].moderator) return; u8 cccType = ccc; struct Packet p = { 0 }; @@ -13,6 +13,14 @@ void network_send_chat_command(u8 globalIndex, enum ChatConfirmCommand ccc) { packet_init(&p, PACKET_COMMAND, false, PLMT_NONE); packet_write(&p, &globalIndex, sizeof(u8)); packet_write(&p, &cccType, sizeof(u8)); + u16 reasonLength = 0; + if (reason) { + u16 reasonLength = strlen(reason); + packet_write(&p, &reasonLength, sizeof(u16)); + packet_write(&p, reason, sizeof(u8) * reasonLength); + } else { + packet_write(&p, &reasonLength, sizeof(u16)); + } network_send_to(gNetworkPlayerServer->localIndex, &p); } @@ -27,8 +35,13 @@ void network_receive_chat_command(struct Packet *p) { return; } u8 CCC; u8 player; + u16 reasonLength = 0; + char reason[MAX_REASON_LENGTH] = { 0 }; packet_read(p, &player, sizeof(u8)); packet_read(p, &CCC, sizeof(u8)); + packet_read(p, &reasonLength, sizeof(u16)); + if (reasonLength >= MAX_REASON_LENGTH) reasonLength = MAX_REASON_LENGTH - 1; + packet_read(p, reason, sizeof(u8) * reasonLength); if (CCC != CCC_KICK && CCC != CCC_BAN) { LOG_ERROR("recieved an invalid chat command: %d", CCC); @@ -42,13 +55,12 @@ void network_receive_chat_command(struct Packet *p) { } char message[256] = { 0 }; if (CCC == CCC_KICK) { - network_send_kick(np->localIndex, EKT_KICKED, NULL); + network_send_kick(np->localIndex, EKT_KICKED, reason); 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, NULL); - // TODO: Moderation: Allow you to insert a reason - moderation_list_add(MODERATION_LIST_TYPE_BAN, np->localIndex, "", false); + network_send_kick(np->localIndex, EKT_BANNED, reason); + moderation_list_add(MODERATION_LIST_TYPE_BAN, np->localIndex, reason, false); snprintf(message, 256, "\\#fff982\\Banned '%s%s\\#fff982\\'!", network_get_player_text_color_string(np->localIndex), np->name); } network_player_disconnected(np->globalIndex); From 246cb46bcdc8804996afac0028a0845dd5d75bc0 Mon Sep 17 00:00:00 2001 From: EmeraldLockdown <86802223+EmeraldLoc@users.noreply.github.com> Date: Tue, 10 Mar 2026 18:20:00 -0500 Subject: [PATCH 17/22] Make popups use new func --- src/pc/djui/djui_popup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pc/djui/djui_popup.c b/src/pc/djui/djui_popup.c index 7d629b36a..ce55a912d 100644 --- a/src/pc/djui/djui_popup.c +++ b/src/pc/djui/djui_popup.c @@ -67,14 +67,14 @@ static void djui_popup_create_internal(const char* message, int lines, int paddi } void djui_popup_create(const char* message, int lines) { - int linesReq = (int)ceilf(djui_hud_measure_text(str_remove_color_codes(message)) / DJUI_POPUP_WIDTH); + int linesReq = (int)ceilf(djui_hud_measure_text(djui_text_get_uncolored_string(NULL, strlen(message + 1), message)) / DJUI_POPUP_WIDTH); if (linesReq < 1) linesReq = 1; if (linesReq > lines) linesReq = lines; djui_popup_create_internal(message, linesReq, lines - linesReq); } void djui_popup_create_auto_scaling(const char* message, int paddingLines) { - int linesReq = (int)ceilf(djui_hud_measure_text(str_remove_color_codes(message)) / DJUI_POPUP_WIDTH); + int linesReq = (int)ceilf(djui_hud_measure_text(djui_text_get_uncolored_string(NULL, strlen(message + 1), message)) / DJUI_POPUP_WIDTH); if (linesReq < 1) linesReq = 1; djui_popup_create_internal(message, linesReq, paddingLines); } From fbf3280f09436f4bc9f9c8592cc71c499f04da47 Mon Sep 17 00:00:00 2001 From: EmeraldLockdown <86802223+EmeraldLoc@users.noreply.github.com> Date: Tue, 10 Mar 2026 19:17:11 -0500 Subject: [PATCH 18/22] Make first param of `network_disconnect` optional --- autogen/lua_definitions/functions.lua | 2 +- docs/lua/functions-7.md | 2 +- src/pc/lua/smlua_functions_autogen.c | 11 +++++++---- src/pc/lua/utils/smlua_misc_utils.c | 2 +- src/pc/lua/utils/smlua_misc_utils.h | 2 +- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua index a94676ded..9984b61ce 100644 --- a/autogen/lua_definitions/functions.lua +++ b/autogen/lua_definitions/functions.lua @@ -11586,7 +11586,7 @@ function get_coopnet_id(localIndex) -- ... end ---- @param dcType DisconnectType +--- @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) diff --git a/docs/lua/functions-7.md b/docs/lua/functions-7.md index 8514b8376..46446ddf5 100644 --- a/docs/lua/functions-7.md +++ b/docs/lua/functions-7.md @@ -1858,7 +1858,7 @@ Disconnects the local player with DisconnectType `dcType` (default is DC_LEAVE) - None ### C Prototype -`void network_disconnect(enum DisconnectType dcType, OPTIONAL const char* reason);` +`void network_disconnect(OPTIONAL enum DisconnectType dcType, OPTIONAL const char* reason);` [:arrow_up_small:](#) diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c index e47a26fab..9e0d645e8 100644 --- a/src/pc/lua/smlua_functions_autogen.c +++ b/src/pc/lua/smlua_functions_autogen.c @@ -34145,13 +34145,16 @@ 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); + if (top < 0 || top > 2) { + LOG_LUA_LINE("Improper param count for '%s': Expected between %u and %u, Received %u", "network_disconnect", 0, 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; } + int dcType = (int) 0; + if (top >= 1) { + 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); diff --git a/src/pc/lua/utils/smlua_misc_utils.c b/src/pc/lua/utils/smlua_misc_utils.c index 3f7e53081..cab62e76d 100644 --- a/src/pc/lua/utils/smlua_misc_utils.c +++ b/src/pc/lua/utils/smlua_misc_utils.c @@ -525,7 +525,7 @@ const char* get_coopnet_id(UNUSED s8 localIndex) { /// -void network_disconnect(enum DisconnectType dcType, OPTIONAL const char* reason) { +void network_disconnect(OPTIONAL enum DisconnectType dcType, OPTIONAL const char* reason) { switch (dcType) { case DC_KICK: if (gNetworkType == NT_SERVER) { diff --git a/src/pc/lua/utils/smlua_misc_utils.h b/src/pc/lua/utils/smlua_misc_utils.h index 5fce12641..28cdd528f 100644 --- a/src/pc/lua/utils/smlua_misc_utils.h +++ b/src/pc/lua/utils/smlua_misc_utils.h @@ -226,7 +226,7 @@ const char* get_local_discord_id(void); 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); +void network_disconnect(OPTIONAL enum DisconnectType dcType, OPTIONAL const char* reason); /* |description|Gets the master volume level|descriptionEnd| */ f32 get_volume_master(void); From f1b3917adca27be916832ff0c2c3cc8e1b5144a1 Mon Sep 17 00:00:00 2001 From: EmeraldLockdown <86802223+EmeraldLoc@users.noreply.github.com> Date: Wed, 11 Mar 2026 18:54:32 -0500 Subject: [PATCH 19/22] Move `network_disconnect` to `network.h` --- autogen/lua_definitions/constants.lua | 9 ----- autogen/lua_definitions/functions.lua | 14 ++++---- docs/lua/constants.md | 8 ----- docs/lua/functions-5.md | 24 +++++++++++++ docs/lua/functions-7.md | 24 ------------- docs/lua/functions.md | 2 +- src/pc/lua/smlua_constants_autogen.c | 3 -- src/pc/lua/smlua_functions_autogen.c | 52 +++++++++++++-------------- src/pc/lua/utils/smlua_misc_utils.c | 24 ------------- src/pc/lua/utils/smlua_misc_utils.h | 9 ----- src/pc/network/network_utils.c | 25 +++++++++++++ src/pc/network/network_utils.h | 9 +++++ 12 files changed, 92 insertions(+), 111 deletions(-) diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua index 011e6a73d..8e160c5d4 100644 --- a/autogen/lua_definitions/constants.lua +++ b/autogen/lua_definitions/constants.lua @@ -8275,15 +8275,6 @@ 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 9984b61ce..9e982734e 100644 --- a/autogen/lua_definitions/functions.lua +++ b/autogen/lua_definitions/functions.lua @@ -7962,6 +7962,13 @@ function network_discord_id_from_local_index(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 + --- Resets Yoshi as being alive function set_yoshi_as_not_dead() -- ... @@ -11586,13 +11593,6 @@ 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 2b82d0596..0c92edac9 100644 --- a/docs/lua/constants.md +++ b/docs/lua/constants.md @@ -84,7 +84,6 @@ - [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) @@ -3547,13 +3546,6 @@ ## [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-5.md b/docs/lua/functions-5.md index d7d4a8757..b6ac6cd85 100644 --- a/docs/lua/functions-5.md +++ b/docs/lua/functions-5.md @@ -2825,6 +2825,30 @@ Gets a Discord ID corresponding to the network player with `localIndex`
+## [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(OPTIONAL enum DisconnectType dcType, OPTIONAL const char* reason);` + +[:arrow_up_small:](#) + +
+ --- # functions from obj_behaviors.c diff --git a/docs/lua/functions-7.md b/docs/lua/functions-7.md index 46446ddf5..207edbb6f 100644 --- a/docs/lua/functions-7.md +++ b/docs/lua/functions-7.md @@ -1840,30 +1840,6 @@ 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(OPTIONAL 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 cd6fc83a9..b3af65380 100644 --- a/docs/lua/functions.md +++ b/docs/lua/functions.md @@ -1439,6 +1439,7 @@ - [network_get_complete_player_name](functions-5.md#network_get_complete_player_name) - [network_check_singleplayer_pause](functions-5.md#network_check_singleplayer_pause) - [network_discord_id_from_local_index](functions-5.md#network_discord_id_from_local_index) + - [network_disconnect](functions-5.md#network_disconnect)
@@ -2061,7 +2062,6 @@ - [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/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c index dbd9b83cd..d7ca5a882 100644 --- a/src/pc/lua/smlua_constants_autogen.c +++ b/src/pc/lua/smlua_constants_autogen.c @@ -3549,9 +3549,6 @@ 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 9e0d645e8..ebc32f885 100644 --- a/src/pc/lua/smlua_functions_autogen.c +++ b/src/pc/lua/smlua_functions_autogen.c @@ -23591,6 +23591,31 @@ int smlua_func_network_discord_id_from_local_index(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 < 0 || top > 2) { + LOG_LUA_LINE("Improper param count for '%s': Expected between %u and %u, Received %u", "network_disconnect", 0, 2, top); + return 0; + } + + int dcType = (int) 0; + if (top >= 1) { + 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; +} + ///////////////////// // obj_behaviors.c // ///////////////////// @@ -34141,31 +34166,6 @@ 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 < 0 || top > 2) { - LOG_LUA_LINE("Improper param count for '%s': Expected between %u and %u, Received %u", "network_disconnect", 0, 2, top); - return 0; - } - - int dcType = (int) 0; - if (top >= 1) { - 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; } @@ -38136,6 +38136,7 @@ void smlua_bind_functions_autogen(void) { smlua_bind_function(L, "network_get_complete_player_name", smlua_func_network_get_complete_player_name); smlua_bind_function(L, "network_check_singleplayer_pause", smlua_func_network_check_singleplayer_pause); smlua_bind_function(L, "network_discord_id_from_local_index", smlua_func_network_discord_id_from_local_index); + smlua_bind_function(L, "network_disconnect", smlua_func_network_disconnect); // obj_behaviors.c smlua_bind_function(L, "set_yoshi_as_not_dead", smlua_func_set_yoshi_as_not_dead); @@ -38736,7 +38737,6 @@ 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 cab62e76d..7c1064e40 100644 --- a/src/pc/lua/utils/smlua_misc_utils.c +++ b/src/pc/lua/utils/smlua_misc_utils.c @@ -525,30 +525,6 @@ const char* get_coopnet_id(UNUSED s8 localIndex) { /// -void network_disconnect(OPTIONAL 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 28cdd528f..c9f07b98b 100644 --- a/src/pc/lua/utils/smlua_misc_utils.h +++ b/src/pc/lua/utils/smlua_misc_utils.h @@ -5,12 +5,6 @@ #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, @@ -225,9 +219,6 @@ 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(OPTIONAL 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/network_utils.c b/src/pc/network/network_utils.c index bbfc7f5fd..0b279a485 100644 --- a/src/pc/network/network_utils.c +++ b/src/pc/network/network_utils.c @@ -1,9 +1,12 @@ #include #include "network_utils.h" +#include "moderation.h" #include "game/camera.h" #include "game/level_update.h" #include "game/mario_misc.h" #include "pc/mods/mods.h" +#include "pc/debuglog.h" +#include "pc/lua/smlua.h" u8 network_global_index_from_local(u8 localIndex) { if (gNetworkType == NT_SERVER) { return localIndex; } @@ -71,3 +74,25 @@ const char* network_discord_id_from_local_index(u8 localIndex) { if (localIndex >= MAX_PLAYERS) { return "0"; } return gNetworkPlayers[localIndex].discordId; } + +void network_disconnect(OPTIONAL 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; + } +} diff --git a/src/pc/network/network_utils.h b/src/pc/network/network_utils.h index d33658520..4b0ef0c05 100644 --- a/src/pc/network/network_utils.h +++ b/src/pc/network/network_utils.h @@ -4,6 +4,12 @@ #include #include "network.h" +enum DisconnectType { + DC_LEAVE, + DC_KICK, + DC_BAN +}; + /* |description|Gets a player's global index from their local index|descriptionEnd| */ u8 network_global_index_from_local(u8 localIndex); /* |description|Gets a player's local index from their global index|descriptionEnd| */ @@ -26,4 +32,7 @@ bool network_check_singleplayer_pause(void); /* |description|Gets a Discord ID corresponding to the network player with `localIndex`|descriptionEnd| */ const char* network_discord_id_from_local_index(u8 localIndex); +/* |description|Disconnects the local player with DisconnectType `dcType` (default is DC_LEAVE) because of `reason` (optional).|descriptionEnd| */ +void network_disconnect(OPTIONAL enum DisconnectType dcType, OPTIONAL const char* reason); + #endif \ No newline at end of file From 5008c635af3a547010936877d9968a0551b81168 Mon Sep 17 00:00:00 2001 From: EmeraldLockdown <86802223+EmeraldLoc@users.noreply.github.com> Date: Wed, 11 Mar 2026 18:59:26 -0500 Subject: [PATCH 20/22] Readd DC constants (oops) --- autogen/convert_constants.py | 1 + autogen/lua_definitions/constants.lua | 9 +++++++++ docs/lua/constants.md | 15 +++++++++++++++ src/pc/lua/smlua_constants_autogen.c | 3 +++ 4 files changed, 28 insertions(+) diff --git a/autogen/convert_constants.py b/autogen/convert_constants.py index 409cd8427..31758cc82 100644 --- a/autogen/convert_constants.py +++ b/autogen/convert_constants.py @@ -20,6 +20,7 @@ in_files = [ "src/game/characters.h", "src/pc/network/network.h", "src/pc/network/network_player.h", + "src/pc/network/network_utils.h", "include/PR/os_cont.h", "src/game/interaction.c", "src/game/interaction.h", diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua index 8e160c5d4..c4c0f4564 100644 --- a/autogen/lua_definitions/constants.lua +++ b/autogen/lua_definitions/constants.lua @@ -4777,6 +4777,15 @@ NPT_CLIENT = 3 --- @type NetworkPlayerType --- | `NPT_SERVER` --- | `NPT_CLIENT` +DC_LEAVE = 0 --- @type DisconnectType +DC_KICK = 1 --- @type DisconnectType +DC_BAN = 2 --- @type DisconnectType + +--- @alias DisconnectType +--- | `DC_LEAVE` +--- | `DC_KICK` +--- | `DC_BAN` + --- @type integer OBJ_COL_FLAG_GROUNDED = (1 << 0) diff --git a/docs/lua/constants.md b/docs/lua/constants.md index 0c92edac9..b62956e03 100644 --- a/docs/lua/constants.md +++ b/docs/lua/constants.md @@ -64,6 +64,8 @@ - [enum PvpType](#enum-PvpType) - [network_player.h](#network_playerh) - [enum NetworkPlayerType](#enum-NetworkPlayerType) +- [network_utils.h](#network_utilsh) + - [enum DisconnectType](#enum-DisconnectType) - [obj_behaviors.c](#obj_behaviorsc) - [obj_behaviors_2.h](#obj_behaviors_2h) - [object_constants.h](#object_constantsh) @@ -2257,6 +2259,19 @@
+## [network_utils.h](#network_utils.h) + +### [enum DisconnectType](#DisconnectType) +| Identifier | Value | +| :--------- | :---- | +| DC_LEAVE | 0 | +| DC_KICK | 1 | +| DC_BAN | 2 | + +[:arrow_up_small:](#) + +
+ ## [obj_behaviors.c](#obj_behaviors.c) - OBJ_COL_FLAG_GROUNDED - OBJ_COL_FLAG_HIT_WALL diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c index d7ca5a882..e5b0b33eb 100644 --- a/src/pc/lua/smlua_constants_autogen.c +++ b/src/pc/lua/smlua_constants_autogen.c @@ -2346,6 +2346,9 @@ char gSmluaConstants[] = "" "NPT_LOCAL=1\n" "NPT_SERVER=2\n" "NPT_CLIENT=3\n" +"DC_LEAVE=0\n" +"DC_KICK=1\n" +"DC_BAN=2\n" "OBJ_COL_FLAG_GROUNDED=(1 << 0)\n" "OBJ_COL_FLAG_HIT_WALL=(1 << 1)\n" "OBJ_COL_FLAG_UNDERWATER=(1 << 2)\n" From 88869cce6af16a1d20c63d4976b6d284a68cf09c Mon Sep 17 00:00:00 2001 From: EmeraldLockdown <86802223+EmeraldLoc@users.noreply.github.com> Date: Wed, 11 Mar 2026 19:03:15 -0500 Subject: [PATCH 21/22] Formatting fixes, remove unecessary default's in switches --- src/pc/djui/djui_panel_moderation_confirm_action.c | 4 +--- src/pc/network/packets/packet_moderation.c | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/pc/djui/djui_panel_moderation_confirm_action.c b/src/pc/djui/djui_panel_moderation_confirm_action.c index 630f1957f..2c4725eac 100644 --- a/src/pc/djui/djui_panel_moderation_confirm_action.c +++ b/src/pc/djui/djui_panel_moderation_confirm_action.c @@ -16,7 +16,7 @@ static void djui_panel_moderation_call_action(struct DjuiBase* caller) { u8 action = caller->uTag; char* address = caller->cTag; - switch(action) { + switch (action) { case MODERATION_ACTION_KICK: network_kick_player(player, sReason); break; @@ -32,8 +32,6 @@ static void djui_panel_moderation_call_action(struct DjuiBase* caller) { case MODERATION_ACTION_UNMOD: network_unmod_player(address); break; - default: - break; } free(sReason); diff --git a/src/pc/network/packets/packet_moderation.c b/src/pc/network/packets/packet_moderation.c index d19f521bf..394c1996c 100644 --- a/src/pc/network/packets/packet_moderation.c +++ b/src/pc/network/packets/packet_moderation.c @@ -83,7 +83,5 @@ void network_receive_moderation_action(struct Packet* p) { case MODERATION_ACTION_BAN: network_ban_player(np->localIndex, reason, permanent); break; - default: - break; } } \ No newline at end of file From 095e7d0e1c16c5cd430b06b536dc785b50aee6dc Mon Sep 17 00:00:00 2001 From: EmeraldLockdown <86802223+EmeraldLoc@users.noreply.github.com> Date: Wed, 11 Mar 2026 19:18:51 -0500 Subject: [PATCH 22/22] Add some nicer formatting (thanks oolio), add german translation, luigi was wrong about default (like hes wrong about falling off of rr) --- lang/English.ini | 2 +- lang/German.ini | 37 +++++++++++++++++++ .../djui_panel_moderation_confirm_action.c | 21 +++-------- src/pc/network/packets/packet_moderation.c | 9 ++--- 4 files changed, 47 insertions(+), 22 deletions(-) diff --git a/lang/English.ini b/lang/English.ini index bd415a766..2813d6cbb 100644 --- a/lang/English.ini +++ b/lang/English.ini @@ -395,7 +395,7 @@ DYNOS_PACKS = "DynOS Packs" OPTIONS = "Options" CHEATS = "Cheats" SERVER_SETTINGS = "Server Settings" -MODERATOR_MENU = "Moderator Menu" +MODERATOR_MENU = "Moderation" RESUME = "Resume" STOP_HOSTING = "Stop Hosting" DISCONNECT = "Disconnect" diff --git a/lang/German.ini b/lang/German.ini index 46d0bf440..a6734b6f6 100644 --- a/lang/German.ini +++ b/lang/German.ini @@ -9,7 +9,9 @@ DISCORD_ERROR = "Discord hat einen Fehler verursacht.\nUm dies zu vermeiden, ver DISCORD_DETECT = "\\#ffa0a0\\Fehlermeldung:\\#dcdcdc\\ Discord konnte nicht gefunden werden.\n\\#a0a0a0\\Versuche, das Spiel zu schließen, Discord neu zu starten und dann das Spiel anschließend wieder zu öffnen." DISCONNECT_FULL = "\\#ffa0a0\\Verbindung getrennt:\\#dcdcdc\\ Der Server ist bereits voll." DISCONNECT_KICK = "\\#ffa0a0\\Verbindung getrennt:\\#dcdcdc\\ Du wurdest vom Server gekickt." +DISCONNECT_KICK_REASON = "\\#ffa0a0\\Verbindung getrennt:\\#dcdcdc\\ Du wurdest gekickt. Grund: @" DISCONNECT_BAN = "\\#ffa0a0\\Verbindung getrennt:\\#dcdcdc\\ Der Bann-Hammer hat gesprochen." +DISCONNECT_BAN_REASON = "\\#ffa0a0\\Verbindung getrennt:\\#dcdcdc\\ Du wurdest gebannt. Grund: @" DISCONNECT_REJOIN = "\\#ffa0a0\\Verbindung getrennt:\\#dcdcdc\\ Erneut verbinden..." DISCONNECT_CLOSED = "\\#ffa0a0\\Verbindung getrennt:\\#dcdcdc\\ Der Hoster hat den Server geschlosse." DISCONNECT_BIG_MOD = "Es konnte keine Verbindung hergestellt werden, da zu viele oder zu große Mods auf dem Server vorhanden sind!" @@ -58,12 +60,14 @@ MOD_DESC = "/moderator [NAME|ID] - Gebe einem Spieler Moderator rechte wie /kick NAMETAGS_DESC = "/nametags [show-tag|show-health] - Sichtbarkeit von Spielernamen sowie der KP/Kraft aktivieren oder deaktivieren " UNRECOGNIZED = "Unbekannter Befehl!" MOD_GRANTED = "\\#fff982\\Du bist jetzt ein Moderator." +MOD_REVOKED = "\\#FE7F7F\\Du bist kein Moderator mehr." [MENU] BACK = "Zurück" CANCEL = "Abbrechen" NO = "Nein" YES = "Ja" +MODERATION_LISTS = "Moderationslisten" [CAMERA] CAMERA = "KAMERA" @@ -338,6 +342,38 @@ CONSOLE = "KONSOLE" [MODLIST] MODS = "MODS" +[MODERATION] +MODERATOR_MENU_TITLE = "MODERATION" +MODERATION_LISTS_TITLE = "MODERATIONSLISTEN" +MODERATION_LISTS = "Moderationslisten" +NO_PLAYERS_CONNECTED = "Keine Spieler verbunden." +KICK = "Kicken" +BAN = "Bannen" +UNBAN = "Entbannen" +MOD = "Mod geben" +UNMOD = "Mod entfernen" +KICK_PLAYER_TITLE = "SPIELER KICKEN" +BAN_PLAYER_TITLE = "SPIELER BANNEN" +UNBAN_PLAYER_TITLE = "SPIELER ENTBANNEN" +MOD_PLAYER_TITLE = "SPIELER ZUM MODERATOR MACHEN" +UNMOD_PLAYER_TITLE = "MODERATOR ENTFERNEN" +KICK_CONFIRM = "Bist du sicher, dass du @\\#dcdcdc\\ kicken möchtest?" +BAN_CONFIRM = "Bist du sicher, dass du @\\#dcdcdc\\ bannen möchtest?" +UNBAN_CONFIRM = "Bist du sicher, dass du @\\#dcdcdc\\ entbannen möchtest?" +MOD_CONFIRM = "Bist du sicher, dass du @\\#dcdcdc\\ zum Moderator machen möchtest?" +UNMOD_CONFIRM = "Bist du sicher, dass du @\\#dcdcdc\\ als Moderator entfernen möchtest?" +BAN_LIST = "Bann-Liste" +MODERATOR_LIST = "Moderatorliste" +LIST = "Liste" +NO_PLAYERS_IN_LIST = "Keine Spieler in der Liste." +INSPECT = "Untersuchen" +INSPECTOR_TITLE = "INSPEKTOR" +DATE = "Hinzugefügt: @" +DISCORD_ID = "Discord-ID: @" +REASON_INFO = "Grund: @" +REASON = "Grund" +PERMANENT = "Permanent" + [OPTIONS] OPTIONS = "OPTIONEN" PLAYER = "Spieler" @@ -364,6 +400,7 @@ STOP_HOSTING = "Hosting beenden" DISCONNECT = "Verbindung trennen" MOD_MENU = "Mod-Menü" MOD_MENU_TITLE = "MOD-MENÜ" +MODERATOR_MENU = "Moderator-Menü" [PLAYER] PLAYER_TITLE = "SPIELER" diff --git a/src/pc/djui/djui_panel_moderation_confirm_action.c b/src/pc/djui/djui_panel_moderation_confirm_action.c index 2c4725eac..cd5b9944a 100644 --- a/src/pc/djui/djui_panel_moderation_confirm_action.c +++ b/src/pc/djui/djui_panel_moderation_confirm_action.c @@ -17,21 +17,12 @@ static void djui_panel_moderation_call_action(struct DjuiBase* caller) { char* address = caller->cTag; switch (action) { - case MODERATION_ACTION_KICK: - network_kick_player(player, sReason); - break; - case MODERATION_ACTION_BAN: - network_ban_player(player, sReason, sPermanent); - break; - case MODERATION_ACTION_UNBAN: - network_unban_player(address); - break; - case MODERATION_ACTION_MOD: - network_mod_player(player, sReason, sPermanent); - break; - case MODERATION_ACTION_UNMOD: - network_unmod_player(address); - break; + case MODERATION_ACTION_KICK: network_kick_player(player, sReason); break; + case MODERATION_ACTION_BAN: network_ban_player(player, sReason, sPermanent); break; + case MODERATION_ACTION_UNBAN: network_unban_player(address); break; + case MODERATION_ACTION_MOD: network_mod_player(player, sReason, sPermanent); break; + case MODERATION_ACTION_UNMOD: network_unmod_player(address); break; + default: break; } free(sReason); diff --git a/src/pc/network/packets/packet_moderation.c b/src/pc/network/packets/packet_moderation.c index 394c1996c..821d24c1f 100644 --- a/src/pc/network/packets/packet_moderation.c +++ b/src/pc/network/packets/packet_moderation.c @@ -77,11 +77,8 @@ void network_receive_moderation_action(struct Packet* p) { packet_read(p, &permanent, sizeof(bool)); switch (action) { - case MODERATION_ACTION_KICK: - network_kick_player(np->localIndex, reason); - break; - case MODERATION_ACTION_BAN: - network_ban_player(np->localIndex, reason, permanent); - break; + case MODERATION_ACTION_KICK: network_kick_player(np->localIndex, reason); break; + case MODERATION_ACTION_BAN: network_ban_player(np->localIndex, reason, permanent); break; + default: break; } } \ No newline at end of file