Added chat commands: /kick /ban /permban /players

This commit is contained in:
MysterD 2022-02-12 15:23:05 -08:00
parent 6925db864c
commit b5de854674
17 changed files with 325 additions and 7 deletions

View file

@ -442,6 +442,7 @@
<ClCompile Include="..\src\menu\star_select.c" />
<ClCompile Include="..\src\pc\audio\audio_null.c" />
<ClCompile Include="..\src\pc\audio\audio_sdl.c" />
<ClCompile Include="..\src\pc\chat_commands.c" />
<ClCompile Include="..\src\pc\cheats.c" />
<ClCompile Include="..\src\pc\cliopts.c" />
<ClCompile Include="..\src\pc\configfile.c" />
@ -526,6 +527,7 @@
<ClCompile Include="..\src\pc\lua\smlua_utils.c" />
<ClCompile Include="..\src\pc\mixer.c" />
<ClCompile Include="..\src\pc\mod_list.c" />
<ClCompile Include="..\src\pc\network\ban_list.c" />
<ClCompile Include="..\src\pc\network\discord\activity.c" />
<ClCompile Include="..\src\pc\network\discord\discord.c" />
<ClCompile Include="..\src\pc\network\discord\lobby.c" />
@ -926,6 +928,7 @@
<ClInclude Include="..\src\game\characters.h" />
<ClInclude Include="..\src\game\level_info.h" />
<ClInclude Include="..\src\game\rng_position.h" />
<ClInclude Include="..\src\pc\chat_commands.h" />
<ClInclude Include="..\src\pc\controller\controller_bind_mapping.h" />
<ClInclude Include="..\src\pc\controller\controller_keyboard_debug.h" />
<ClInclude Include="..\src\pc\debuglog.h" />
@ -986,6 +989,7 @@
<ClInclude Include="..\src\pc\lua\smlua_sync_table.h" />
<ClInclude Include="..\src\pc\lua\smlua_utils.h" />
<ClInclude Include="..\src\pc\mod_list.h" />
<ClInclude Include="..\src\pc\network\ban_list.h" />
<ClInclude Include="..\src\pc\network\branch.h" />
<ClInclude Include="..\src\pc\network\discord\activity.h" />
<ClInclude Include="..\src\pc\network\discord\discord.h" />

View file

@ -4884,6 +4884,12 @@
<ClCompile Include="..\src\pc\crash_handler.c">
<Filter>Source Files\src\pc</Filter>
</ClCompile>
<ClCompile Include="..\src\pc\chat_commands.c">
<Filter>Source Files\src\pc</Filter>
</ClCompile>
<ClCompile Include="..\src\pc\network\ban_list.c">
<Filter>Source Files\src\pc\network</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\actors\common0.h">
@ -6043,5 +6049,11 @@
<ClInclude Include="..\src\pc\network\network_utils.h">
<Filter>Source Files\src\pc\network</Filter>
</ClInclude>
<ClInclude Include="..\src\pc\chat_commands.h">
<Filter>Source Files\src\pc</Filter>
</ClInclude>
<ClInclude Include="..\src\pc\network\ban_list.h">
<Filter>Source Files\src\pc\network</Filter>
</ClInclude>
</ItemGroup>
</Project>

189
src/pc/chat_commands.c Normal file
View file

@ -0,0 +1,189 @@
#include "pc/network/network.h"
#include "pc/lua/smlua_hooks.h"
#include "pc/djui/djui_chat_message.h"
#include "chat_commands.h"
#include "pc/network/ban_list.h"
#include "pc/debuglog.h"
enum ChatConfirmCommand {
CCC_NONE,
CCC_KICK,
CCC_BAN,
CCC_PERMBAN,
};
static enum ChatConfirmCommand sConfirming = CCC_NONE;
static u8 sConfirmPlayerIndex = 0;
static struct NetworkPlayer* chat_get_network_player(char* name) {
// check for id
for (int i = 0; i < MAX_PLAYERS; i++) {
if (!gNetworkPlayers[i].connected) { continue; }
char id[16] = { 0 };
snprintf(id, 16, "%d", i);
if (strcmp(id, name) == 0) {
return &gNetworkPlayers[i];
}
}
// check for name
for (int i = 0; i < MAX_PLAYERS; i++) {
if (!gNetworkPlayers[i].connected) { continue; }
if (strcmp(gNetworkPlayers[i].name, name) == 0) {
return &gNetworkPlayers[i];
}
}
return NULL;
}
static bool str_starts_with(const char* pre, char* str) {
return strncmp(pre, str, strlen(pre)) == 0;
}
bool exec_chat_command(char* command) {
enum ChatConfirmCommand ccc = sConfirming;
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 && 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 && ccc == CCC_PERMBAN) {
struct NetworkPlayer* np = &gNetworkPlayers[sConfirmPlayerIndex];
if (!np->connected) { return true; }
char message[256] = { 0 };
snprintf(message, 256, "\\#fff982\\Permanently 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), true);
network_player_disconnected(np->localIndex);
return true;
}
}
if (strcmp("/players", command) == 0) {
char message[600] = { 0 };
char line[128] = { 0 };
strncat(message, "\\#fff982\\Players:\n", 599);
for (int i = 0; i < MAX_PLAYERS; i++) {
struct NetworkPlayer* np = &gNetworkPlayers[i];
if (!np->connected) { continue; }
snprintf(line, 128, "\\#82f9ff\\%u \\#fff982\\- %s%s\n", np->globalIndex, network_get_player_text_color_string(np->localIndex), np->name);
strncat(message, line, 599);
}
djui_chat_message_create(message);
return true;
}
if (str_starts_with("/kick ", 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[6]);
if (np == NULL) {
djui_chat_message_create("Could not find player.");
return true;
}
if (np->localIndex == 0) {
djui_chat_message_create("Can not kick yourself.");
return true;
}
char message[256] = { 0 };
snprintf(message, 256, "\\#fff982\\Are you sure you want to kick '%s%s\\#fff982\\'?\nType '\\#a0ffa0\\/confirm\\#fff982\\' to kick.", network_get_player_text_color_string(np->localIndex), np->name);
djui_chat_message_create(message);
sConfirming = CCC_KICK;
sConfirmPlayerIndex = np->localIndex;
return true;
}
if (str_starts_with("/ban ", 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[5]);
if (np == NULL) {
djui_chat_message_create("Could not find player.");
return true;
}
if (np->localIndex == 0) {
djui_chat_message_create("Can not ban yourself.");
return true;
}
char message[256] = { 0 };
snprintf(message, 256, "\\#fff982\\Are you sure you want to ban '%s%s\\#fff982\\'?\nType '\\#a0ffa0\\/confirm\\#fff982\\' to ban.", network_get_player_text_color_string(np->localIndex), np->name);
djui_chat_message_create(message);
sConfirming = CCC_BAN;
sConfirmPlayerIndex = np->localIndex;
return true;
}
if (str_starts_with("/permban ", 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[9]);
if (np == NULL) {
djui_chat_message_create("Could not find player.");
return true;
}
if (np->localIndex == 0) {
djui_chat_message_create("Can not permanently ban yourself.");
return true;
}
char message[256] = { 0 };
snprintf(message, 256, "\\#fff982\\Are you sure you want to permanently ban '%s%s\\#fff982\\'?\nType '\\#a0ffa0\\/confirm\\#fff982\\' to permanently ban.", network_get_player_text_color_string(np->localIndex), np->name);
djui_chat_message_create(message);
sConfirming = CCC_PERMBAN;
sConfirmPlayerIndex = np->localIndex;
return true;
}
return smlua_call_chat_command_hook(command);
}
void display_chat_commands(void) {
djui_chat_message_create("/players - List all players and their IDs");
if (gNetworkType == NT_SERVER) {
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");
}
if (sConfirming != CCC_NONE) { djui_chat_message_create("/confirm"); }
smlua_display_chat_commands();
}

7
src/pc/chat_commands.h Normal file
View file

@ -0,0 +1,7 @@
#ifndef CHAT_COMMANDS_H
#define CHAT_COMMANDS_H
bool exec_chat_command(char* command);
void display_chat_commands(void);
#endif

View file

@ -14,6 +14,7 @@
#include "controller/controller_api.h"
#include "fs/fs.h"
#include "pc/mod_list.h"
#include "pc/network/ban_list.h"
#define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0]))
@ -317,6 +318,12 @@ void configfile_load(const char *filename) {
continue;
}
// ban list
if (!strcmp(tokens[0], "ban:")) {
ban_list_add(tokens[1], true);
continue;
}
for (unsigned int i = 0; i < ARRAY_LEN(options); i++) {
if (strcmp(tokens[0], options[i].name) == 0) {
option = &options[i];
@ -414,5 +421,13 @@ void configfile_save(const char *filename) {
fprintf(file, "%s %s\n", "enable-mod:", entry->name);
}
// save ban list
for (unsigned int i = 0; i < gBanCount; i++) {
if (gBanAddresses == NULL) { break; }
if (gBanAddresses[i] == NULL) { continue; }
if (!gBanPerm[i]) { continue; }
fprintf(file, "%s %s\n", "ban:", gBanAddresses[i]);
}
fclose(file);
}

View file

@ -2,6 +2,7 @@
#include <string.h>
#include "pc/network/network.h"
#include "pc/lua/smlua_hooks.h"
#include "pc/chat_commands.h"
#include "djui.h"
struct DjuiChatBox* gDjuiChatBox = NULL;
@ -33,9 +34,9 @@ static void djui_chat_box_input_enter(struct DjuiInputbox* chatInput) {
if (strlen(chatInput->buffer) != 0) {
if (chatInput->buffer[0] == '/') {
if (!smlua_call_chat_command_hook(chatInput->buffer)) {
if (!exec_chat_command(chatInput->buffer)) {
djui_chat_message_create("Unrecognized chat command.");
smlua_display_chat_commands();
display_chat_commands();
}
} else {
djui_chat_message_create_from(gNetworkPlayerLocal->globalIndex, chatInput->buffer);

45
src/pc/network/ban_list.c Normal file
View file

@ -0,0 +1,45 @@
#include <stdlib.h>
#include <string.h>
#include "PR/ultratypes.h"
#include "ban_list.h"
#include "pc/debuglog.h"
char** gBanAddresses = NULL;
bool* gBanPerm = NULL;
u16 gBanCount = 0;
void ban_list_add(char* address, bool perm) {
u16 index = gBanCount++;
if (gBanAddresses == NULL) {
gBanAddresses = malloc(sizeof(char*) * gBanCount);
gBanPerm = malloc(sizeof(bool) * gBanCount);
} else {
gBanAddresses = realloc(gBanAddresses, sizeof(char*) * gBanCount);
gBanPerm = realloc(gBanPerm, sizeof(bool) * gBanCount);
}
if (gBanAddresses == NULL) {
LOG_ERROR("Failed to allocate gBanAddresses");
return;
}
if (gBanPerm == NULL) {
LOG_ERROR("Failed to allocate gBanPerm");
return;
}
gBanAddresses[index] = strdup(address);
gBanPerm[index] = perm;
}
bool ban_list_contains(char* address) {
if (gBanAddresses == NULL || address == NULL) {
return false;
}
for (int i = 0; i < gBanCount; i++) {
if (gBanAddresses[i] == NULL) { continue; }
if (strcmp(address, gBanAddresses[i]) == 0) {
return true;
}
}
return false;
}

13
src/pc/network/ban_list.h Normal file
View file

@ -0,0 +1,13 @@
#ifndef BAN_LIST_H
#define BAN_LIST_H
#include <stdbool.h>
extern char** gBanAddresses;
extern bool* gBanPerm;
extern u16 gBanCount;
void ban_list_add(char* address, bool perm);
bool ban_list_contains(char* address);
#endif

View file

@ -222,6 +222,7 @@ static void ns_discord_shutdown(void) {
struct NetworkSystem gNetworkSystemDiscord = {
.initialize = ns_discord_initialize,
.get_id = ns_discord_get_id,
.get_id_str = ns_discord_get_id_str,
.save_id = ns_discord_save_id,
.clear_id = ns_discord_clear_id,
.dup_addr = ns_discord_dup_addr,

View file

@ -44,6 +44,13 @@ s64 ns_discord_get_id(u8 localId) {
return gNetworkUserIds[localId];
}
char* ns_discord_get_id_str(u8 localId) {
if (localId == UNKNOWN_LOCAL_INDEX) { localId = 0; }
static char id_str[22] = { 0 };
snprintf(id_str, 22, "%lld", gNetworkUserIds[localId]);
return id_str;
}
void ns_discord_save_id(u8 localId, s64 networkId) {
assert(localId > 0);
assert(localId < MAX_PLAYERS);

View file

@ -9,6 +9,7 @@ int ns_discord_network_send(u8 localIndex, void* addr, u8* data, u16 dataLength)
void discord_network_on_message(UNUSED void* eventData, int64_t lobbyId, int64_t userId, uint8_t channelId, uint8_t* data, uint32_t dataLength);
void discord_network_flush(void);
s64 ns_discord_get_id(u8 localId);
char* ns_discord_get_id_str(u8 localId);
void ns_discord_save_id(u8 localId, s64 networkId);
void ns_discord_clear_id(u8 localId);
void discord_network_init(int64_t lobbyId);

View file

@ -43,6 +43,7 @@ enum NetworkSystemType {
struct NetworkSystem {
bool (*initialize)(enum NetworkType);
s64 (*get_id)(u8 localIndex);
char* (*get_id_str)(u8 localIndex);
void (*save_id)(u8 localIndex, s64 networkId);
void (*clear_id)(u8 localIndex);
void* (*dup_addr)(u8 localIndex);

View file

@ -1,5 +1,6 @@
#include <stdio.h>
#include "../network.h"
#include "pc/network/ban_list.h"
#include "pc/debuglog.h"
void packet_process(struct Packet* p) {
@ -100,6 +101,15 @@ void packet_receive(struct Packet* p) {
// send an ACK if requested
network_send_ack(p);
// refuse packets from banned players
if (gNetworkType == NT_SERVER) {
if (ban_list_contains(gNetworkSystem->get_id_str(p->localIndex))) {
LOG_INFO("kicking banned player");
network_send_kick(0, EKT_BANNED);
return;
}
}
// refuse packets from unknown servers
if (gNetworkServerAddr != NULL && gNetworkType == NT_CLIENT) {
bool fromServer = (p->localIndex == UNKNOWN_LOCAL_INDEX);
@ -114,7 +124,7 @@ void packet_receive(struct Packet* p) {
if (gNetworkType == NT_SERVER && p->localIndex == UNKNOWN_LOCAL_INDEX && !network_allow_unknown_local_index(packetType)) {
if (packetType != PACKET_PLAYER) {
LOG_INFO("closing connection for packetType: %d", packetType);
network_send_kick(EKT_CLOSE_CONNECTION);
network_send_kick(0, EKT_CLOSE_CONNECTION);
}
LOG_INFO("refusing packet from unknown player, packetType: %d", packetType);
return;

View file

@ -102,6 +102,8 @@ struct Packet {
enum KickReasonType {
EKT_CLOSE_CONNECTION,
EKT_FULL_PARTY,
EKT_KICKED,
EKT_BANNED,
};
struct LSTNetworkType {
@ -215,7 +217,7 @@ void network_send_chat(char* message, u8 globalIndex);
void network_receive_chat(struct Packet* p);
// packet_kick.c
void network_send_kick(enum KickReasonType kickReason);
void network_send_kick(u8 localIndex, enum KickReasonType kickReason);
void network_receive_kick(struct Packet* p);
// packet_keep_alive.c

View file

@ -73,7 +73,7 @@ void network_send_join(struct Packet* joinRequestPacket) {
}
}
if (globalIndex == UNKNOWN_LOCAL_INDEX) {
network_send_kick(EKT_FULL_PARTY);
network_send_kick(0, EKT_FULL_PARTY);
return;
}
}

View file

@ -3,12 +3,12 @@
#include "pc/debuglog.h"
#include "pc/djui/djui.h"
void network_send_kick(enum KickReasonType kickReason) {
void network_send_kick(u8 localIndex, enum KickReasonType kickReason) {
u8 kickReasonType = kickReason;
struct Packet p = { 0 };
packet_init(&p, PACKET_KICK, false, PLMT_NONE);
packet_write(&p, &kickReasonType, sizeof(u8));
network_send_to(0, &p);
network_send_to(localIndex, &p);
}
void network_receive_kick(struct Packet* p) {
@ -28,6 +28,8 @@ void network_receive_kick(struct Packet* p) {
switch (kickReason) {
case EKT_FULL_PARTY: djui_panel_join_message_error("\\#ffa0a0\\Error:\\#c8c8c8\\ The party is full."); break;
case EKT_KICKED: djui_panel_join_message_error("\\#ffa0a0\\Error:\\#c8c8c8\\ The server kicked you."); break;
case EKT_BANNED: djui_panel_join_message_error("\\#ffa0a0\\Error:\\#c8c8c8\\ The server banned you."); break;
default: djui_panel_join_message_error("\\#ffa0a0\\Error:\\#c8c8c8\\ Host has closed the connection."); break;
}
network_shutdown(false);

View file

@ -105,6 +105,13 @@ static s64 ns_socket_get_id(UNUSED u8 localId) {
return 0;
}
static char* ns_socket_get_id_str(u8 localId) {
if (localId == UNKNOWN_LOCAL_INDEX) { localId = 0; }
static char id_str[INET_ADDRSTRLEN] = { 0 };
snprintf(id_str, INET_ADDRSTRLEN, "%s", inet_ntoa(addr[localId].sin_addr));
return id_str;
}
static void ns_socket_save_id(u8 localId, UNUSED s64 networkId) {
assert(localId > 0);
assert(localId < MAX_PLAYERS);
@ -168,6 +175,7 @@ static void ns_socket_shutdown(void) {
struct NetworkSystem gNetworkSystemSocket = {
.initialize = ns_socket_initialize,
.get_id = ns_socket_get_id,
.get_id_str = ns_socket_get_id_str,
.save_id = ns_socket_save_id,
.clear_id = ns_socket_clear_id,
.dup_addr = ns_socket_dup_addr,