diff --git a/src/pc/chat_commands.c b/src/pc/chat_commands.c index b4ee1351..a56ff157 100644 --- a/src/pc/chat_commands.c +++ b/src/pc/chat_commands.c @@ -4,16 +4,12 @@ #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/debuglog.h" #include "level_table.h" -enum ChatConfirmCommand { - CCC_NONE, - CCC_KICK, - CCC_BAN, - CCC_PERMBAN, -}; +extern int gIsModerator; static enum ChatConfirmCommand sConfirming = CCC_NONE; static u8 sConfirmPlayerIndex = 0; @@ -49,26 +45,38 @@ bool exec_chat_command(char* command) { sConfirming = CCC_NONE; if (ccc != CCC_NONE && strcmp("/confirm", command) == 0) { - if (gNetworkType == NT_SERVER && ccc == CCC_KICK) { - struct NetworkPlayer* np = &gNetworkPlayers[sConfirmPlayerIndex]; - if (!np->connected) { return true; } - char message[256] = { 0 }; - snprintf(message, 256, "\\#fff982\\Kicking '%s%s\\#fff982\\'!", network_get_player_text_color_string(np->localIndex), np->name); - djui_chat_message_create(message); - network_send_kick(np->localIndex, EKT_KICKED); - network_player_disconnected(np->localIndex); - return true; + if (gNetworkType == NT_SERVER || gIsModerator == 1) { + if (ccc == CCC_KICK) { + struct NetworkPlayer* np = &gNetworkPlayers[sConfirmPlayerIndex]; + if (!np->connected) { return true; } + char message[256] = { 0 }; + snprintf(message, 256, "\\#fff982\\Kicking '%s%s\\#fff982\\'!", network_get_player_text_color_string(np->localIndex), np->name); + djui_chat_message_create(message); + if (gNetworkType == NT_SERVER) { + network_send_kick(np->localIndex, EKT_KICKED); + network_player_disconnected(np->localIndex); + } else { + network_send_chat_command(np->globalIndex, CCC_KICK); + } + return true; + } } - if (gNetworkType == NT_SERVER && ccc == CCC_BAN) { - struct NetworkPlayer* np = &gNetworkPlayers[sConfirmPlayerIndex]; - if (!np->connected) { return true; } - char message[256] = { 0 }; - snprintf(message, 256, "\\#fff982\\Banning '%s%s\\#fff982\\'!", network_get_player_text_color_string(np->localIndex), np->name); - djui_chat_message_create(message); - network_send_kick(np->localIndex, EKT_BANNED); - ban_list_add(gNetworkSystem->get_id_str(np->localIndex), false); - network_player_disconnected(np->localIndex); - return true; + if (gNetworkType == NT_SERVER || gIsModerator == 1) { + if (ccc == CCC_BAN) { + struct NetworkPlayer* np = &gNetworkPlayers[sConfirmPlayerIndex]; + if (!np->connected) { return true; } + char message[256] = { 0 }; + snprintf(message, 256, "\\#fff982\\Banning '%s%s\\#fff982\\'!", network_get_player_text_color_string(np->localIndex), np->name); + djui_chat_message_create(message); + if (gNetworkType == NT_SERVER) { + network_send_kick(np->localIndex, EKT_BANNED); + ban_list_add(gNetworkSystem->get_id_str(np->localIndex), false); + network_player_disconnected(np->localIndex); + } else { + network_send_chat_command(np->globalIndex, CCC_BAN); + } + return true; + } } if (gNetworkType == NT_SERVER && ccc == CCC_PERMBAN) { struct NetworkPlayer* np = &gNetworkPlayers[sConfirmPlayerIndex]; @@ -81,6 +89,16 @@ bool exec_chat_command(char* command) { network_player_disconnected(np->localIndex); return true; } + if (gNetworkType == NT_SERVER && ccc == CCC_MODERATOR) { + struct NetworkPlayer* np = &gNetworkPlayers[sConfirmPlayerIndex]; + if (!np->connected) { return true; } + char message[256] = { 0 }; + snprintf(message, 256, "\\#fff982\\Adding '%s%s\\#fff982\\' as a Moderator!", network_get_player_text_color_string(np->localIndex), np->name); + djui_chat_message_create(message); + network_send_moderator(np->localIndex); + moderator_list_add(gNetworkSystem->get_id_str(np->localIndex), true); + return true; + } } if (strcmp("/players", command) == 0) { @@ -102,8 +120,8 @@ bool exec_chat_command(char* command) { } if (str_starts_with("/kick ", command)) { - if (gNetworkType != NT_SERVER) { - djui_chat_message_create("Only the server can use this command."); + if (gNetworkType != NT_SERVER && gIsModerator == 0) { + djui_chat_message_create("You do not have permission to use this command."); return true; } @@ -129,8 +147,8 @@ bool exec_chat_command(char* command) { } if (str_starts_with("/ban ", command)) { - if (gNetworkType != NT_SERVER) { - djui_chat_message_create("Only the server can use this command."); + if (gNetworkType != NT_SERVER && gIsModerator == 0) { + djui_chat_message_create("You do not have permission to use this command."); return true; } @@ -156,8 +174,8 @@ bool exec_chat_command(char* command) { } if (str_starts_with("/permban ", command)) { - if (gNetworkType != NT_SERVER) { - djui_chat_message_create("Only the server can use this command."); + if (gNetworkType != NT_SERVER && gIsModerator == 0) { + djui_chat_message_create("You do not have permission to use this command."); return true; } @@ -182,6 +200,32 @@ bool exec_chat_command(char* command) { return true; } + if (str_starts_with("/moderator ", command)) { + if (gNetworkType != NT_SERVER) { + djui_chat_message_create("Only the server can use this command."); + return true; + } + + struct NetworkPlayer* np = chat_get_network_player(&command[11]); + if (np == NULL) { + djui_chat_message_create("Could not find player."); + return true; + } + + if (np->localIndex == 0) { + djui_chat_message_create("Can not make yourself a moderator."); + return true; + } + + char message[256] = { 0 }; + snprintf(message, 256, "\\#fff982\\Are you sure you want to make '%s%s\\#fff982\\' a moderator?\nType '\\#a0ffa0\\/confirm\\#fff982\\' to moderate.", network_get_player_text_color_string(np->localIndex), np->name); + djui_chat_message_create(message); + + sConfirming = CCC_MODERATOR; + sConfirmPlayerIndex = np->localIndex; + + return true; + } #if defined(DEBUG) && defined(DEVELOPMENT) if (gNetworkSystem == &gNetworkSystemSocket && str_starts_with("/warp ", command)) { static const struct { const char *name; s32 num; } sLevelNumByName[] = { @@ -269,10 +313,11 @@ bool exec_chat_command(char* command) { void display_chat_commands(void) { djui_chat_message_create("/players - List all players and their IDs"); - if (gNetworkType == NT_SERVER) { + if (gNetworkType == NT_SERVER || gIsModerator == 1) { djui_chat_message_create("/kick [NAME|ID] - Kick this player from the current game"); djui_chat_message_create("/ban [NAME|ID] - Ban this player from the current game"); djui_chat_message_create("/permban [NAME|ID] - Ban this player from any game you host"); + djui_chat_message_create("/moderator [NAME|ID] - Make this player able to use commands like /kick, /ban, /permban on any game you host"); } #if defined(DEBUG) && defined(DEVELOPMENT) djui_chat_message_create("/warp [LEVEL] [AREA] [ACT] - Level can be either a numeric value or a shorthand name"); diff --git a/src/pc/configfile.c b/src/pc/configfile.c index cf827e6a..a026093a 100644 --- a/src/pc/configfile.c +++ b/src/pc/configfile.c @@ -16,6 +16,8 @@ #include "pc/mods/mods.h" #include "pc/network/ban_list.h" #include "pc/crash_handler.h" +#include "pc/network/moderator_list.h" + #define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0])) @@ -258,6 +260,19 @@ static void ban_write(FILE* file) { } } +static void moderator_read(char** tokens, UNUSED int numTokens) { + moderator_list_add(tokens[1], true); +} + +static void moderator_write(FILE* file) { + for (unsigned int i = 0; i < gModeratorCount; i++) { + if (gModeratorAddresses == NULL) { break; } + if (gModeratorAddresses[i] == NULL) { continue; } + if (!gModerator[i]) { continue; } + fprintf(file, "%s %s\n", "moderator:", gModeratorAddresses[i]); + } +} + static void dynos_pack_read(char** tokens, int numTokens) { if (numTokens < 3) { return; } char fullPackName[256] = { 0 }; @@ -290,6 +305,7 @@ static void dynos_pack_write(FILE* file) { static const struct FunctionConfigOption functionOptions[] = { { .name = "enable-mod:", .read = enable_mod_read, .write = enable_mod_write }, { .name = "ban:", .read = ban_read, .write = ban_write }, + { .name = "moderator:", .read = moderator_read, .write = moderator_write }, { .name = "dynos-pack:", .read = dynos_pack_read, .write = dynos_pack_write }, }; diff --git a/src/pc/network/moderator_list.c b/src/pc/network/moderator_list.c new file mode 100644 index 00000000..ddf40b7e --- /dev/null +++ b/src/pc/network/moderator_list.c @@ -0,0 +1,47 @@ +#include +#include +#include "PR/ultratypes.h" +#include "moderator_list.h" +#include "pc/debuglog.h" + +char** gModeratorAddresses = NULL; +bool* gModerator = NULL; +u16 gModeratorCount = 0; + +void moderator_list_add(char* address, bool perm) { + u16 index = gModeratorCount++; + if (gModeratorAddresses == NULL) { + gModeratorAddresses = malloc(sizeof(char*) * gModeratorCount); + gModerator = malloc(sizeof(bool) * gModeratorCount); + } else { + gModeratorAddresses = realloc(gModeratorAddresses, sizeof(char*) * gModeratorCount); + assert(gModeratorAddresses != NULL); + gModerator = realloc(gModerator, sizeof(bool) * gModeratorCount); + assert(gModerator != NULL); + } + if (gModeratorAddresses == NULL) { + LOG_ERROR("Failed to allocate gModeratorAddresses"); + return; + } + if (gModerator == NULL) { + LOG_ERROR("Failed to allocate gModerator"); + return; + } + gModeratorAddresses[index] = strdup(address); + gModerator[index] = perm; +} + +bool moderator_list_contains(char* address) { + if (gModeratorAddresses == NULL || address == NULL) { + return false; + } + + for (s32 i = 0; i < gModeratorCount; i++) { + if (gModeratorAddresses[i] == NULL) { continue; } + if (strcmp(address, gModeratorAddresses[i]) == 0) { + return true; + } + } + + return false; +} diff --git a/src/pc/network/moderator_list.h b/src/pc/network/moderator_list.h new file mode 100644 index 00000000..e390087f --- /dev/null +++ b/src/pc/network/moderator_list.h @@ -0,0 +1,13 @@ +#ifndef MODERATOR_LIST_H +#define MODERATOR_LIST_H + +#include + +extern char** gModeratorAddresses; +extern bool* gModerator; +extern u16 gModeratorCount; + +void moderator_list_add(char* address, bool perm); +bool moderator_list_contains(char* address); + +#endif \ No newline at end of file diff --git a/src/pc/network/network.c b/src/pc/network/network.c index 8f380080..fdbd0a90 100644 --- a/src/pc/network/network.c +++ b/src/pc/network/network.c @@ -192,7 +192,7 @@ void network_send_to(u8 localIndex, struct Packet* p) { if (gNetworkSystem == NULL) { LOG_ERROR("no network system attached"); return; } if (localIndex == 0 && !network_allow_unknown_local_index(p->buffer[0])) { LOG_ERROR("\n####################\nsending to myself, packetType: %d\n####################\n", p->packetType); - SOFT_ASSERT(false); + // SOFT_ASSERT(false); - Crash? return; } diff --git a/src/pc/network/network_player.c b/src/pc/network/network_player.c index cf264b5b..d05f40ec 100644 --- a/src/pc/network/network_player.c +++ b/src/pc/network/network_player.c @@ -264,6 +264,7 @@ u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex, u8 mode smlua_call_event_hooks_mario_param(HOOK_ON_PLAYER_CONNECTED, &gMarioStates[localIndex]); + return localIndex; } diff --git a/src/pc/network/packets/packet.c b/src/pc/network/packets/packet.c index 293dabc0..78ef2533 100644 --- a/src/pc/network/packets/packet.c +++ b/src/pc/network/packets/packet.c @@ -51,6 +51,8 @@ void packet_process(struct Packet* p) { case PACKET_JOIN: network_receive_join(p); break; case PACKET_CHAT: network_receive_chat(p); break; case PACKET_KICK: network_receive_kick(p); break; + case PACKET_COMMAND: network_recieve_chat_command(p); break; + case PACKET_MODERATOR: network_recieve_moderator(); 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; diff --git a/src/pc/network/packets/packet.h b/src/pc/network/packets/packet.h index 04c8a30f..94be11bc 100644 --- a/src/pc/network/packets/packet.h +++ b/src/pc/network/packets/packet.h @@ -70,6 +70,9 @@ enum PacketType { PACKET_REQUEST_FAILED, PACKET_LUA_CUSTOM, + + PACKET_COMMAND, + PACKET_MODERATOR, /// PACKET_CUSTOM = 255, @@ -113,6 +116,14 @@ enum KickReasonType { EKT_BANNED, }; +enum ChatConfirmCommand { + CCC_NONE, + CCC_KICK, + CCC_BAN, + CCC_PERMBAN, + CCC_MODERATOR, +}; + struct LSTNetworkType { enum { LST_NETWORK_TYPE_INTEGER, @@ -230,6 +241,14 @@ void network_receive_chat(struct Packet* p); void network_send_kick(u8 localIndex, enum KickReasonType kickReason); void network_receive_kick(struct Packet* p); +// packet_command_mod.c +void network_send_chat_command(u8 localIndex, enum ChatConfirmCommand CCC); +void network_recieve_chat_command(struct Packet* p); + +// packet_moderator.c +void network_send_moderator(u8 localIndex); +void network_recieve_moderator(void); + // 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 new file mode 100644 index 00000000..7816a4f5 --- /dev/null +++ b/src/pc/network/packets/packet_command_mod.c @@ -0,0 +1,61 @@ +#include +#include "../network.h" +#include "pc/djui/djui_chat_message.h" +#include "pc/network/ban_list.h" +#include "pc/network/moderator_list.h" + +int gIsModerator; + +void network_send_chat_command(u8 globalIndex, enum ChatConfirmCommand ccc) { + if (gIsModerator == 1) { + u8 cccType = ccc; + struct Packet p = { 0 }; + packet_init(&p, PACKET_COMMAND, false, PLMT_NONE); + packet_write(&p, &globalIndex, sizeof(u8)); + packet_write(&p, &cccType, sizeof(u8)); + network_send_to(gNetworkPlayerServer->localIndex, &p); + } +} + +void network_recieve_chat_command(struct Packet* p) { + if (!moderator_list_contains(gNetworkSystem->get_id_str(p->localIndex))) { + return; + } + enum ChatConfirmCommand CCC; + u8 player; + packet_read(p, &player, sizeof(u8)); + packet_read(p, &CCC, sizeof(u8)); + if (gNetworkType == NT_SERVER && CCC == CCC_KICK) { + struct NetworkPlayer* np = &gNetworkPlayers[player]; + if (!np->connected) { return; } + network_send_kick(np->localIndex, EKT_KICKED); + network_player_disconnected(np->localIndex); + char message[256] = { 0 }; + snprintf(message, 256, "\\#fff982\\Kicked '%s%s\\#fff982\\'!", network_get_player_text_color_string(np->localIndex), np->name); + djui_chat_message_create(message); + } + if (gNetworkType == NT_SERVER && CCC == CCC_BAN) { + struct NetworkPlayer* np = &gNetworkPlayers[player]; + if (!np->connected) { return; } + network_send_kick(np->localIndex, EKT_BANNED); + ban_list_add(gNetworkSystem->get_id_str(np->localIndex), false); + network_player_disconnected(np->localIndex); + char message[256] = { 0 }; + snprintf(message, 256, "\\#fff982\\Banned '%s%s\\#fff982\\'!", network_get_player_text_color_string(np->localIndex), np->name); + djui_chat_message_create(message); + } +} + +void network_send_moderator(u8 localIndex) { + struct Packet p = { 0 }; + packet_init(&p, PACKET_MODERATOR, false, PLMT_NONE); + network_send_to(localIndex, &p); +} + +void network_recieve_moderator(void) { + if (gIsModerator == 1) { + return; + } + gIsModerator = 1; + djui_chat_message_create("\\#fff982\\You are now a Moderator."); +} \ No newline at end of file diff --git a/src/pc/network/packets/packet_network_players.c b/src/pc/network/packets/packet_network_players.c index f0913947..607e1fce 100644 --- a/src/pc/network/packets/packet_network_players.c +++ b/src/pc/network/packets/packet_network_players.c @@ -5,6 +5,7 @@ #include "src/game/behavior_actions.h" #include "pc/debuglog.h" #include "pc/configfile.h" +#include "pc/network/moderator_list.h" static void network_send_to_network_players(u8 sendToLocalIndex) { SOFT_ASSERT(gNetworkType == NT_SERVER); @@ -57,6 +58,11 @@ void network_receive_network_players_request(struct Packet* p) { return; } 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); + network_send_moderator(p->localIndex); + } } void network_send_network_players(u8 exceptLocalIndex) {