mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2026-04-26 12:01:43 +00:00
Merge 095e7d0e1c into 8afe062be1
This commit is contained in:
commit
fefa85a7cf
54 changed files with 1529 additions and 70 deletions
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -4194,6 +4194,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`
|
||||
|
|
@ -7955,6 +7962,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()
|
||||
|
|
@ -7968,6 +7982,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()
|
||||
-- ...
|
||||
|
|
|
|||
|
|
@ -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 @@
|
|||
|
||||
<br />
|
||||
|
||||
## [network_utils.h](#network_utils.h)
|
||||
|
||||
### [enum DisconnectType](#DisconnectType)
|
||||
| Identifier | Value |
|
||||
| :--------- | :---- |
|
||||
| DC_LEAVE | 0 |
|
||||
| DC_KICK | 1 |
|
||||
| DC_BAN | 2 |
|
||||
|
||||
[:arrow_up_small:](#)
|
||||
|
||||
<br />
|
||||
|
||||
## [obj_behaviors.c](#obj_behaviors.c)
|
||||
- OBJ_COL_FLAG_GROUNDED
|
||||
- OBJ_COL_FLAG_HIT_WALL
|
||||
|
|
|
|||
|
|
@ -3966,6 +3966,30 @@ Gets the header hex color code from a `DJUI_RAINBOW_COLOR_*` constant
|
|||
<br />
|
||||
|
||||
|
||||
## [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:](#)
|
||||
|
||||
<br />
|
||||
|
||||
## [djui_popup_create](#djui_popup_create)
|
||||
|
||||
### Description
|
||||
|
|
|
|||
|
|
@ -2758,6 +2758,29 @@ Gets the DJUI hex color code string for the player corresponding to `localIndex`
|
|||
|
||||
<br />
|
||||
|
||||
## [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:](#)
|
||||
|
||||
<br />
|
||||
|
||||
## [network_check_singleplayer_pause](#network_check_singleplayer_pause)
|
||||
|
||||
### Description
|
||||
|
|
@ -2802,6 +2825,30 @@ Gets a Discord ID corresponding to the network player with `localIndex`
|
|||
|
||||
<br />
|
||||
|
||||
## [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:](#)
|
||||
|
||||
<br />
|
||||
|
||||
---
|
||||
# functions from obj_behaviors.c
|
||||
|
||||
|
|
|
|||
|
|
@ -815,6 +815,7 @@
|
|||
<br />
|
||||
|
||||
- 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)
|
||||
|
||||
<br />
|
||||
|
|
@ -1438,8 +1439,10 @@
|
|||
- [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)
|
||||
- [network_disconnect](functions-5.md#network_disconnect)
|
||||
|
||||
<br />
|
||||
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
|
|
@ -58,6 +60,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"
|
||||
|
|
@ -266,6 +269,7 @@ SAVE_SLOT = "Save Slot"
|
|||
SETTINGS = "Settings"
|
||||
MODS = "Mods"
|
||||
ROMHACKS = "Rom-Hacks"
|
||||
MODERATION_LISTS = "Moderation Lists"
|
||||
APPLY = "Apply"
|
||||
HOST = "Host"
|
||||
|
||||
|
|
@ -339,6 +343,38 @@ CONSOLE = "CONSOLE"
|
|||
[MODLIST]
|
||||
MODS = "MODS"
|
||||
|
||||
[MODERATION]
|
||||
MODERATOR_MENU_TITLE = "MODERATION"
|
||||
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_INFO = "Reason: @"
|
||||
REASON = "Reason"
|
||||
PERMANENT = "Permanent"
|
||||
|
||||
[OPTIONS]
|
||||
OPTIONS = "OPTIONS"
|
||||
PLAYER = "Player"
|
||||
|
|
@ -360,6 +396,7 @@ DYNOS_PACKS = "DynOS Packs"
|
|||
OPTIONS = "Options"
|
||||
CHEATS = "Cheats"
|
||||
SERVER_SETTINGS = "Server Settings"
|
||||
MODERATOR_MENU = "Moderation"
|
||||
RESUME = "Resume"
|
||||
STOP_HOSTING = "Stop Hosting"
|
||||
DISCONNECT = "Disconnect"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -339,6 +343,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"
|
||||
|
|
@ -365,6 +401,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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -55,17 +54,21 @@ 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);
|
||||
network_send_chat_command(np->globalIndex, CCC_KICK, reason);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -74,19 +77,20 @@ 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);
|
||||
ban_list_add(gNetworkSystem->get_id_str(np->localIndex), false);
|
||||
network_send_kick(np->localIndex, EKT_BANNED, 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;
|
||||
}
|
||||
}
|
||||
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);
|
||||
network_send_kick(np->localIndex, EKT_BANNED, reason);
|
||||
moderation_list_add(MODERATION_LIST_TYPE_BAN, np->localIndex, reason, true);
|
||||
network_player_disconnected(np->localIndex);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -94,7 +98,7 @@ 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);
|
||||
moderation_list_add(MODERATION_LIST_TYPE_MODERATOR, np->localIndex, reason, true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -120,7 +124,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 +209,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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -391,10 +396,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; }
|
||||
|
|
|
|||
|
|
@ -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*);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -27,4 +27,4 @@ void djui_panel_confirm_create(struct DjuiBase* caller, char* title, char* messa
|
|||
}
|
||||
|
||||
djui_panel_add(caller, panel, NULL);
|
||||
}
|
||||
}
|
||||
|
|
@ -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,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);
|
||||
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);
|
||||
{
|
||||
|
|
|
|||
152
src/pc/djui/djui_panel_moderation_confirm_action.c
Normal file
152
src/pc/djui/djui_panel_moderation_confirm_action.c
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
#include <stdio.h>
|
||||
#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;
|
||||
static bool sPermanent = false;
|
||||
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, 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);
|
||||
sReason = NULL;
|
||||
sPermanent = false;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static void djui_panel_moderation_confirm_destroy(struct DjuiBase* base) {
|
||||
struct DjuiThreePanel* threePanel = (struct DjuiThreePanel*)base;
|
||||
free(threePanel);
|
||||
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*)) {
|
||||
sPermanent = permanent;
|
||||
|
||||
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_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));
|
||||
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.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.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);
|
||||
}
|
||||
|
||||
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);
|
||||
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.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));
|
||||
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);
|
||||
}
|
||||
5
src/pc/djui/djui_panel_moderation_confirm_action.h
Normal file
5
src/pc/djui/djui_panel_moderation_confirm_action.h
Normal file
|
|
@ -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*));
|
||||
97
src/pc/djui/djui_panel_moderation_list.c
Normal file
97
src/pc/djui/djui_panel_moderation_list.c
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#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(UNUSED struct DjuiBase* caller) {
|
||||
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(NULL);
|
||||
}
|
||||
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);
|
||||
}
|
||||
5
src/pc/djui/djui_panel_moderation_list.h
Normal file
5
src/pc/djui/djui_panel_moderation_list.h
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
#include "djui.h"
|
||||
|
||||
void djui_panel_moderation_list_reload(UNUSED struct DjuiBase* caller);
|
||||
void djui_panel_moderation_list_create(struct DjuiBase* caller);
|
||||
120
src/pc/djui/djui_panel_moderation_list_inspector.c
Normal file
120
src/pc/djui/djui_panel_moderation_list_inspector.c
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
#include <stdio.h>
|
||||
#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;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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[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);
|
||||
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;
|
||||
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, djui_panel_moderation_list_inspect_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);
|
||||
}
|
||||
4
src/pc/djui/djui_panel_moderation_list_inspector.h
Normal file
4
src/pc/djui/djui_panel_moderation_list_inspector.h
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
#pragma once
|
||||
#include "djui.h"
|
||||
|
||||
void djui_panel_moderation_list_inspect_create(struct DjuiBase* caller);
|
||||
107
src/pc/djui/djui_panel_moderator_menu.c
Normal file
107
src/pc/djui/djui_panel_moderator_menu.c
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include "djui.h"
|
||||
#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"
|
||||
#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, caller->bTag, 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, 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);
|
||||
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.bTag = true;
|
||||
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(UNUSED struct DjuiBase* caller) {
|
||||
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);
|
||||
}
|
||||
5
src/pc/djui/djui_panel_moderator_menu.h
Normal file
5
src/pc/djui/djui_panel_moderator_menu.h
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
#include "djui.h"
|
||||
|
||||
void djui_panel_moderator_menu_reload(UNUSED struct DjuiBase* caller);
|
||||
void djui_panel_moderator_menu_create(struct DjuiBase* caller);
|
||||
82
src/pc/djui/djui_panel_moderator_menu_inspect.c
Normal file
82
src/pc/djui/djui_panel_moderator_menu_inspect.c
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include "djui.h"
|
||||
#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"
|
||||
#include "pc/network/moderation.h"
|
||||
|
||||
struct DjuiButton* sModButton = NULL;
|
||||
static u8 sSelectedIndex = 0;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static void djui_panel_moderator_menu_inspector_destroy(struct DjuiBase* base) {
|
||||
struct DjuiThreePanel* threePanel = (struct DjuiThreePanel*)base;
|
||||
free(threePanel);
|
||||
sModButton = NULL;
|
||||
}
|
||||
|
||||
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(NULL);
|
||||
}
|
||||
|
||||
void djui_panel_moderator_inspector_reload(UNUSED struct DjuiBase* caller) {
|
||||
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);
|
||||
{
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
panel->base.destroy = djui_panel_moderator_menu_inspector_destroy;
|
||||
djui_panel_add(caller, panel, NULL);
|
||||
}
|
||||
5
src/pc/djui/djui_panel_moderator_menu_inspect.h
Normal file
5
src/pc/djui/djui_panel_moderator_menu_inspect.h
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
#include "djui.h"
|
||||
|
||||
void djui_panel_moderator_inspector_reload(UNUSED struct DjuiBase* caller);
|
||||
void djui_panel_moderator_menu_inspector_create(struct DjuiBase* caller);
|
||||
|
|
@ -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++) {
|
||||
|
|
|
|||
|
|
@ -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_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));
|
||||
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(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(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);
|
||||
}
|
||||
|
||||
void djui_popup_update(void) {
|
||||
struct DjuiPopupList* node = sPopupListHead;
|
||||
struct DjuiPopupList* last = NULL;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -13191,6 +13191,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; }
|
||||
|
||||
|
|
@ -23576,6 +23595,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; }
|
||||
|
||||
|
|
@ -23608,6 +23644,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 //
|
||||
/////////////////////
|
||||
|
|
@ -37578,6 +37639,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
|
||||
|
|
@ -38146,8 +38208,10 @@ 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);
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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| */
|
||||
|
|
|
|||
94
src/pc/network/moderation.c
Normal file
94
src/pc/network/moderation.c
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <PR/ultratypes.h>
|
||||
#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"
|
||||
|
||||
u8 gQueuedDisconnect = QUEUED_DISCONNECT_NONE;
|
||||
|
||||
void djui_reload_moderation_panels() {
|
||||
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) {
|
||||
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, reason);
|
||||
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, reason);
|
||||
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);
|
||||
}
|
||||
24
src/pc/network/moderation.h
Normal file
24
src/pc/network/moderation.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
#include <stdbool.h>
|
||||
|
||||
#define QUEUED_DISCONNECT_NONE 255
|
||||
|
||||
enum ModerationActions {
|
||||
MODERATION_ACTION_KICK,
|
||||
MODERATION_ACTION_BAN,
|
||||
MODERATION_ACTION_UNBAN,
|
||||
MODERATION_ACTION_MOD,
|
||||
MODERATION_ACTION_UNMOD,
|
||||
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);
|
||||
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"
|
||||
161
src/pc/network/moderation_list.c
Normal file
161
src/pc/network/moderation_list.c
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <PR/ultratypes.h>
|
||||
#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);
|
||||
// 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]);
|
||||
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"));
|
||||
snprintf(entry->reason, MAX_REASON_LENGTH, "%s", 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));
|
||||
snprintf(entry->reason, MAX_REASON_LENGTH, "%s", 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]);
|
||||
|
||||
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;
|
||||
}
|
||||
44
src/pc/network/moderation_list.h
Normal file
44
src/pc/network/moderation_list.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <PR/ultratypes.h>
|
||||
#include "moderation.h"
|
||||
|
||||
#define MODERATION_LIST_FILEPATH "moderation_list.ini"
|
||||
#define MAX_MODERATION_LIST_ENTRIES 1024
|
||||
#define MAX_REASON_LENGTH 256
|
||||
|
||||
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[MAX_REASON_LENGTH];
|
||||
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);
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
#include <stdio.h>
|
||||
#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; }
|
||||
|
|
@ -53,6 +56,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)) &&
|
||||
|
|
@ -63,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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,12 @@
|
|||
#include <stdbool.h>
|
||||
#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| */
|
||||
|
|
@ -17,6 +23,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);
|
||||
|
|
@ -24,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
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#include <stdio.h>
|
||||
#include <zlib.h>
|
||||
#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,9 +154,18 @@ 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))) {
|
||||
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);
|
||||
network_send_kick(0, EKT_BANNED, reason);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -178,7 +188,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;
|
||||
|
|
|
|||
|
|
@ -74,9 +74,10 @@ enum PacketType {
|
|||
|
||||
PACKET_LUA_CUSTOM,
|
||||
PACKET_LUA_CUSTOM_BYTESTRING,
|
||||
|
||||
|
||||
PACKET_COMMAND,
|
||||
PACKET_MODERATOR,
|
||||
PACKET_MODERATION_ACTION,
|
||||
|
||||
///
|
||||
PACKET_CUSTOM = 255,
|
||||
|
|
@ -237,17 +238,22 @@ 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
|
||||
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
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -2,11 +2,10 @@
|
|||
#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) {
|
||||
void network_send_chat_command(u8 globalIndex, enum ChatConfirmCommand ccc, char* reason) {
|
||||
if (!gNetworkPlayers[0].moderator) return;
|
||||
|
||||
u8 cccType = ccc; struct Packet p = { 0 };
|
||||
|
|
@ -14,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);
|
||||
}
|
||||
|
||||
|
|
@ -23,13 +30,18 @@ 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;
|
||||
}
|
||||
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);
|
||||
|
|
@ -43,26 +55,33 @@ 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, 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);
|
||||
ban_list_add(gNetworkSystem->get_id_str(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->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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -91,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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include <stdio.h>
|
||||
#include "../network.h"
|
||||
#include "../moderation.h"
|
||||
#include "pc/debuglog.h"
|
||||
#include "pc/djui/djui.h"
|
||||
#include "pc/utils/misc.h"
|
||||
|
|
@ -7,12 +8,52 @@
|
|||
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) {
|
||||
u8 kickReasonType = kickReason;
|
||||
struct Packet p = { 0 };
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -22,24 +63,24 @@ 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;
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
|
|||
84
src/pc/network/packets/packet_moderation.c
Normal file
84
src/pc/network/packets/packet_moderation.c
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
#include <stdio.h>
|
||||
#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 && localIndex != 0) {
|
||||
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 && localIndex != 0) {
|
||||
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));
|
||||
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));
|
||||
}
|
||||
packet_write(&p, &permanent, sizeof(bool));
|
||||
|
||||
network_send_to(gNetworkPlayerServer->localIndex, &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[MAX_REASON_LENGTH] = { 0 };
|
||||
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));
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -252,8 +252,8 @@ 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_player_disconnected(np->localIndex);
|
||||
network_send_kick(np->localIndex, EKT_KICKED, NULL);
|
||||
network_player_disconnected(np->globalIndex);
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue