From 8a87f0ed7d2525c261630cea46edfeee48defc25 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Wed, 27 Aug 2025 20:38:45 -0400 Subject: [PATCH 1/5] Apply gamestochat restrictions to voice --- src/d_clisrv.c | 36 ++++++++++++++++++++++++++++-- src/d_netcmd.h | 1 + src/d_player.h | 2 ++ src/g_game.c | 2 +- src/k_hud.cpp | 4 ++-- src/k_serverstats.c | 53 +++++++++++++++++++++++++++++++++++++++++---- src/k_serverstats.h | 2 ++ src/y_inter.cpp | 2 +- 8 files changed, 92 insertions(+), 10 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 46466b399..c2dc256cf 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -73,6 +73,7 @@ #include "r_fps.h" #include "filesrch.h" // refreshdirmenu #include "k_objects.h" +#include "k_serverstats.h" // updatemutes // cl loading screen #include "v_video.h" @@ -3435,6 +3436,7 @@ static void Got_RemovePlayer(const UINT8 **p, INT32 playernum); static void Got_AddBot(const UINT8 **p, INT32 playernum); static void Got_ServerMutePlayer(const UINT8 **p, INT32 playernum); static void Got_ServerDeafenPlayer(const UINT8 **p, INT32 playernum); +static void Got_ServerTempMutePlayer(const UINT8 **p, INT32 playernum); void Joinable_OnChange(void); void Joinable_OnChange(void) @@ -3490,6 +3492,7 @@ void D_ClientServerInit(void) COM_AddCommand("serverdeafen", Command_ServerDeafen); COM_AddCommand("serverundeafen", Command_ServerUndeafen); RegisterNetXCmd(XD_SERVERMUTEPLAYER, Got_ServerMutePlayer); + RegisterNetXCmd(XD_SERVERTEMPMUTEPLAYER, Got_ServerTempMutePlayer); RegisterNetXCmd(XD_SERVERDEAFENPLAYER, Got_ServerDeafenPlayer); gametic = 0; @@ -3822,6 +3825,7 @@ static void Got_AddPlayer(const UINT8 **p, INT32 playernum) } HU_AddChatText(joinmsg, false); + SV_UpdateTempMutes(); } if (server && multiplayer && motd[0] != '\0') @@ -3913,6 +3917,34 @@ static void Got_ServerMutePlayer(const UINT8 **p, INT32 playernum) } } +// Xcmd XD_SERVERTEMPMUTEPLAYER +static void Got_ServerTempMutePlayer(const UINT8 **p, INT32 playernum) +{ + UINT8 forplayer = READUINT8(*p); + UINT8 muted = READUINT8(*p); + if (playernum != serverplayer) + { + CONS_Alert(CONS_WARNING, M_GetText("Illegal server mute player cmd from %s\n"), player_names[playernum]); + if (server) + { + SendKick(playernum, KICK_MSG_CON_FAIL); + } + } + if (muted && !(players[forplayer].pflags2 & PF2_SERVERTEMPMUTE)) + { + players[forplayer].pflags2 |= PF2_SERVERTEMPMUTE; + if (P_IsMachineLocalPlayer(&players[forplayer])) + HU_AddChatText(va("\x82* You are temporarily muted until you finish more rounds."), false); + } + else if (!muted && players[forplayer].pflags2 & PF2_SERVERTEMPMUTE) + { + players[forplayer].pflags2 &= ~PF2_SERVERTEMPMUTE; + if (P_IsMachineLocalPlayer(&players[forplayer])) + HU_AddChatText(va("\x82* You've now finished enough rounds to use chat."), false); + } +} + + // Xcmd XD_SERVERDEAFENPLAYER static void Got_ServerDeafenPlayer(const UINT8 **p, INT32 playernum) { @@ -5327,7 +5359,7 @@ static void PT_HandleVoiceServer(SINT8 node) } player = &players[playernum]; - if (player->pflags2 & (PF2_SELFMUTE | PF2_SELFDEAFEN | PF2_SERVERMUTE | PF2_SERVERDEAFEN)) + if (player->pflags2 & (PF2_SELFMUTE | PF2_SELFDEAFEN | PF2_SERVERMUTE | PF2_SERVERDEAFEN | PF2_SERVERTEMPMUTE)) { // ignore, they should not be able to broadcast voice return; @@ -7543,7 +7575,7 @@ void NetVoiceUpdate(void) // 1. In a netgame, // 2. Not self-muted by cvar // 3. The consoleplayer is not server or self muted or deafened - if (netgame && !cv_voice_selfmute.value && !(players[consoleplayer].pflags2 & (PF2_SERVERMUTE | PF2_SELFMUTE | PF2_SELFDEAFEN | PF2_SERVERDEAFEN))) + if (netgame && !cv_voice_selfmute.value && !(players[consoleplayer].pflags2 & (PF2_SERVERMUTE | PF2_SELFMUTE | PF2_SERVERTEMPMUTE | PF2_SELFDEAFEN | PF2_SERVERDEAFEN))) { DoVoicePacket(servernode, g_local_opus_frame, encoded, result); S_SetPlayerVoiceActive(consoleplayer); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index c983bdeeb..36a0ed134 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -189,6 +189,7 @@ typedef enum XD_TEAMCHANGE, // 41 XD_SERVERMUTEPLAYER, // 42 XD_SERVERDEAFENPLAYER, // 43 + XD_SERVERTEMPMUTEPLAYER, // 44 MAXNETXCMD } netxcmd_t; diff --git a/src/d_player.h b/src/d_player.h index cdcaa6562..c8323be13 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -148,6 +148,8 @@ typedef enum PF2_BUBBLECONTACT = 1<<7, // ACHTUNG VERY BAD HACK - Don't allow Bubble Shield to contact certain objects unless this is a fresh blowup. PF2_SUPERTRANSFERVFX = 1<<8, // Don't respawn the "super transfer available" VFX. PF2_FASTTUMBLEBOUNCE = 1<<9, // Don't lose speed when tumblebouncing. + + PF2_SERVERTEMPMUTE = 1<<10, // Haven't met gamestochat requirement } pflags2_t; typedef enum diff --git a/src/g_game.c b/src/g_game.c index 487a26116..31cd80b9a 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2407,7 +2407,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) xtralife = players[player].xtralife; pflags = (players[player].pflags & (PF_WANTSTOJOIN|PF_KICKSTARTACCEL|PF_SHRINKME|PF_SHRINKACTIVE|PF_AUTOROULETTE|PF_ANALOGSTICK|PF_AUTORING)); - pflags2 = (players[player].pflags2 & (PF2_SELFMUTE | PF2_SELFDEAFEN | PF2_SERVERMUTE | PF2_SERVERDEAFEN | PF2_STRICTFASTFALL)); + pflags2 = (players[player].pflags2 & (PF2_SELFMUTE | PF2_SELFDEAFEN | PF2_SERVERTEMPMUTE | PF2_SERVERMUTE | PF2_SERVERDEAFEN | PF2_STRICTFASTFALL)); // SRB2kart memcpy(&itemRoulette, &players[player].itemRoulette, sizeof (itemRoulette)); diff --git a/src/k_hud.cpp b/src/k_hud.cpp index dbaf90bfa..9734d8cdc 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -3003,7 +3003,7 @@ void PositionFacesInfo::draw_1p() { voxmic = kp_voice_remotedeafened; } - else if (players[rankplayer[i]].pflags2 & (PF2_SELFMUTE | PF2_SERVERMUTE)) + else if (players[rankplayer[i]].pflags2 & (PF2_SELFMUTE | PF2_SERVERMUTE | PF2_SERVERTEMPMUTE)) { voxmic = kp_voice_remotemuted; } @@ -8283,7 +8283,7 @@ void K_drawKartHUD(void) if (netgame && cv_voice_allowservervoice.value == 1) { - if (players[consoleplayer].pflags2 & (PF2_SELFMUTE | PF2_SERVERMUTE | PF2_SELFDEAFEN | PF2_SERVERDEAFEN)) + if (players[consoleplayer].pflags2 & (PF2_SELFMUTE | PF2_SERVERMUTE | PF2_SERVERTEMPMUTE | PF2_SELFDEAFEN | PF2_SERVERDEAFEN)) { patch_t* micmuted = kp_voice_localmuted; V_DrawFixedPatch(-1 * FRACUNIT, (BASEVIDHEIGHT - 21) << FRACBITS, FRACUNIT, V_SNAPTOBOTTOM|V_SNAPTOLEFT, micmuted, NULL); diff --git a/src/k_serverstats.c b/src/k_serverstats.c index 4556b84a9..32564da9d 100644 --- a/src/k_serverstats.c +++ b/src/k_serverstats.c @@ -20,6 +20,7 @@ #include "k_serverstats.h" #include "z_zone.h" #include "time.h" +#include "d_netcmd.h" // isplayeradmin static serverplayer_t *trackedList; static size_t numtracked = 0; @@ -231,7 +232,7 @@ serverplayer_t *SV_GetStats(player_t *player) // Write clientpowerlevels and timestamps back to matching trackedList entries, then save trackedList to disk // (NB: Stats changes can be made directly to trackedList through other paths, but will only write to disk here) void SV_UpdateStats(void) -{ +{ UINT32 i, j, hash; if (!server) @@ -250,7 +251,7 @@ void SV_UpdateStats(void) hash = quickncasehash((char*)players[i].public_key, PUBKEYLENGTH); for(j = 0; j < numtracked; j++) - { + { if (hash != trackedList[j].hash) // Not crypto magic, just an early out with a faster comparison continue; if (memcmp(&trackedList[j].public_key, players[i].public_key, PUBKEYLENGTH) == 0) @@ -265,6 +266,7 @@ void SV_UpdateStats(void) // so this shouldn't be reachable. } + SV_UpdateTempMutes(); SV_SaveStats(); } @@ -272,6 +274,9 @@ void SV_BumpMatchStats(void) { int i; + if (!server) + return; + for (i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i]) @@ -284,7 +289,7 @@ void SV_BumpMatchStats(void) serverplayer_t *stat = SV_GetStatsByPlayerIndex(i); // It should never be advantageous to idle, only count rounds where the player accomplishes something. - // If you NO CONTESTed, assume no participation... + // If you NO CONTESTed, assume no participation... boolean participated = !(players[i].pflags & PF_NOCONTEST); if (gametyperules & GTR_CIRCUIT) @@ -303,4 +308,44 @@ void SV_BumpMatchStats(void) if (participated) stat->finishedrounds++; } -} \ No newline at end of file + + SV_UpdateTempMutes(); +} + +static void SV_UpdateTempMute(player_t *player, boolean mute) +{ + UINT8 buf[2]; + + buf[0] = player - players; + buf[1] = (UINT8)(mute); + SendNetXCmd(XD_SERVERTEMPMUTEPLAYER, &buf, 2); +} + +void SV_UpdateTempMutes(void) +{ + int i; + + if (!server) + return; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + if (players[i].spectator) + continue; + if (PR_IsKeyGuest(players[i].public_key)) + continue; + + player_t *player = &players[i]; + + serverplayer_t *stat = SV_GetStatsByPlayerIndex(i); + + if (i == serverplayer || IsPlayerAdmin(i)) + SV_UpdateTempMute(player, false); + else if (stat->finishedrounds >= (UINT32)cv_gamestochat.value && player->pflags2 & PF2_SERVERTEMPMUTE) + SV_UpdateTempMute(player, false); + else if (stat->finishedrounds < (UINT32)cv_gamestochat.value && !(player->pflags2 & PF2_SERVERTEMPMUTE)) + SV_UpdateTempMute(player, true); + } +} diff --git a/src/k_serverstats.h b/src/k_serverstats.h index 3273351dc..ab3d5be0e 100644 --- a/src/k_serverstats.h +++ b/src/k_serverstats.h @@ -48,6 +48,8 @@ void SV_UpdateStats(void); void SV_BumpMatchStats(void); +void SV_UpdateTempMutes(void); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/y_inter.cpp b/src/y_inter.cpp index 1f3dffee9..3ed2b82fc 100644 --- a/src/y_inter.cpp +++ b/src/y_inter.cpp @@ -814,7 +814,7 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset) voxxoffs = 1; voxyoffs = -5; } - else if (players[pnum].pflags2 & (PF2_SELFMUTE | PF2_SERVERMUTE)) + else if (players[pnum].pflags2 & (PF2_SELFMUTE | PF2_SERVERMUTE | PF2_SERVERTEMPMUTE)) { voxpat = (patch_t*) W_CachePatchName("VOXCRM", PU_HUDGFX); voxxoffs = 1; From 1d2ec19c6e768d12c69d4fb31764f2211984028f Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Wed, 27 Aug 2025 21:55:46 -0400 Subject: [PATCH 2/5] Voice menu fixups, apply tempmutes to guest/spectator --- src/d_clisrv.c | 13 +++++++++++-- src/k_serverstats.c | 13 +++++++++---- src/menus/options-sound.cpp | 19 ++++++++++++++++--- src/menus/options-voice.cpp | 4 ++++ 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index c2dc256cf..afbdb83ae 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -234,6 +234,8 @@ static textcmdtic_t *textcmds[TEXTCMD_HASH_SIZE] = {NULL}; static tic_t stop_spamming[MAXPLAYERS]; +static boolean IsPlayerGuest(UINT8 player); + // Generate a message for an authenticating client to sign, with some guarantees about who we are. void GenerateChallenge(uint8_t *buf) { @@ -3825,6 +3827,7 @@ static void Got_AddPlayer(const UINT8 **p, INT32 playernum) } HU_AddChatText(joinmsg, false); + SV_UpdateTempMutes(); } @@ -3934,7 +3937,13 @@ static void Got_ServerTempMutePlayer(const UINT8 **p, INT32 playernum) { players[forplayer].pflags2 |= PF2_SERVERTEMPMUTE; if (P_IsMachineLocalPlayer(&players[forplayer])) - HU_AddChatText(va("\x82* You are temporarily muted until you finish more rounds."), false); + { + if (IsPlayerGuest(playernum)) + HU_AddChatText(va("\x82* GUESTs cannot use chat on this server. Create a profile to join in!"), false); + else + HU_AddChatText(va("\x82* You are temporarily muted until you finish more rounds."), false); + } + } else if (!muted && players[forplayer].pflags2 & PF2_SERVERTEMPMUTE) { @@ -4343,7 +4352,7 @@ static size_t TotalTextCmdPerTic(tic_t tic) } #endif -static boolean IsPlayerGuest(int player) +static boolean IsPlayerGuest(UINT8 player) { return PR_IsKeyGuest(players[player].public_key); } diff --git a/src/k_serverstats.c b/src/k_serverstats.c index 32564da9d..5a29dc313 100644 --- a/src/k_serverstats.c +++ b/src/k_serverstats.c @@ -332,13 +332,18 @@ void SV_UpdateTempMutes(void) { if (!playeringame[i]) continue; - if (players[i].spectator) - continue; - if (PR_IsKeyGuest(players[i].public_key)) - continue; player_t *player = &players[i]; + if (PR_IsKeyGuest(player->public_key)) + { + if (cv_gamestochat.value && !(player->pflags2 & PF2_SERVERTEMPMUTE)) + SV_UpdateTempMute(player, false); + continue; + } + + + serverplayer_t *stat = SV_GetStatsByPlayerIndex(i); if (i == serverplayer || IsPlayerAdmin(i)) diff --git a/src/menus/options-sound.cpp b/src/menus/options-sound.cpp index 3d1c29d22..35dc448ac 100644 --- a/src/menus/options-sound.cpp +++ b/src/menus/options-sound.cpp @@ -63,8 +63,11 @@ struct Slider arrows.x(-10 - ofs).text("\x1C"); arrows.x(kWidth + 2 + ofs).text("\x1D"); - Draw::TextElement tx = Draw::TextElement().parse(""); - h.xy(kWidth + 9, -2).text(tx.string()); + if (&volume_ != &cv_voicevolume) + { + Draw::TextElement tx = Draw::TextElement().parse(""); + h.xy(kWidth + 9, -2).text(tx.string()); + } } h = h.y(1); @@ -85,7 +88,7 @@ struct Slider n = std::atoi(volume_.defaultvalue); h.x(1 + shake_ + n + (n / 10)).size(1, 7).fill(35); - if (!toggle_(false)) + if (!toggle_(false) && &volume_ != &cv_voicevolume) { h .x(kWidth / 2) @@ -94,6 +97,16 @@ struct Slider .flags(V_40TRANS) .text("S I L E N T"); } + else if (!toggle_(false)) + { + h + .x(kWidth / 2) + .y(-1) + .font(Draw::Font::kThin) + .align(Draw::Align::kCenter) + .flags(V_20TRANS) + .text("DEAFENED (Voice Options)"); + } } void input(INT32 c) diff --git a/src/menus/options-voice.cpp b/src/menus/options-voice.cpp index 3e7d74d8d..f4be45c30 100644 --- a/src/menus/options-voice.cpp +++ b/src/menus/options-voice.cpp @@ -60,6 +60,10 @@ static void draw_routine() V_DrawFill(x, y, range + 2, 10, 31); V_DrawFill(x + 1, y + 1, (int) last_peak, 8, color); + if (!detected) + V_DrawThinString(x+1, y+1, V_20TRANS, "Not transmitting..."); + else + V_DrawThinString(x+1, y+1, V_20TRANS|V_GREENMAP, "Transmitting"); } static void tick_routine() From 4ae71662e065e65e03770c588737d47ff7fa4faa Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Wed, 27 Aug 2025 22:17:44 -0400 Subject: [PATCH 3/5] Fix GUEST tempmute message never appearing --- src/d_clisrv.c | 2 +- src/k_serverstats.c | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index afbdb83ae..a4cd1941b 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3938,7 +3938,7 @@ static void Got_ServerTempMutePlayer(const UINT8 **p, INT32 playernum) players[forplayer].pflags2 |= PF2_SERVERTEMPMUTE; if (P_IsMachineLocalPlayer(&players[forplayer])) { - if (IsPlayerGuest(playernum)) + if (IsPlayerGuest(forplayer)) HU_AddChatText(va("\x82* GUESTs cannot use chat on this server. Create a profile to join in!"), false); else HU_AddChatText(va("\x82* You are temporarily muted until you finish more rounds."), false); diff --git a/src/k_serverstats.c b/src/k_serverstats.c index 5a29dc313..84bbacb7f 100644 --- a/src/k_serverstats.c +++ b/src/k_serverstats.c @@ -342,8 +342,6 @@ void SV_UpdateTempMutes(void) continue; } - - serverplayer_t *stat = SV_GetStatsByPlayerIndex(i); if (i == serverplayer || IsPlayerAdmin(i)) From 85b2f8f933bf4b9b225958ed9b80bc1b468f71a0 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Wed, 27 Aug 2025 22:18:56 -0400 Subject: [PATCH 4/5] Don't send redundant tempunmutes for admins --- src/k_serverstats.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_serverstats.c b/src/k_serverstats.c index 84bbacb7f..32e2c7074 100644 --- a/src/k_serverstats.c +++ b/src/k_serverstats.c @@ -344,7 +344,7 @@ void SV_UpdateTempMutes(void) serverplayer_t *stat = SV_GetStatsByPlayerIndex(i); - if (i == serverplayer || IsPlayerAdmin(i)) + if (i == serverplayer || IsPlayerAdmin(i) && player->pflags2 & PF2_SERVERTEMPMUTE) SV_UpdateTempMute(player, false); else if (stat->finishedrounds >= (UINT32)cv_gamestochat.value && player->pflags2 & PF2_SERVERTEMPMUTE) SV_UpdateTempMute(player, false); From 814976fbc25a8576c9c4bec689483c1a061c4e24 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Wed, 27 Aug 2025 22:33:38 -0400 Subject: [PATCH 5/5] Sync pflags2, fix voice icon desyncs / other scary speculative desyncs! --- src/k_serverstats.c | 11 +++++++---- src/p_saveg.cpp | 2 ++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/k_serverstats.c b/src/k_serverstats.c index 32e2c7074..6ebbf3594 100644 --- a/src/k_serverstats.c +++ b/src/k_serverstats.c @@ -316,6 +316,9 @@ static void SV_UpdateTempMute(player_t *player, boolean mute) { UINT8 buf[2]; + if (mute == !!(player->pflags2 & PF2_SERVERTEMPMUTE)) + return; + buf[0] = player - players; buf[1] = (UINT8)(mute); SendNetXCmd(XD_SERVERTEMPMUTEPLAYER, &buf, 2); @@ -337,18 +340,18 @@ void SV_UpdateTempMutes(void) if (PR_IsKeyGuest(player->public_key)) { - if (cv_gamestochat.value && !(player->pflags2 & PF2_SERVERTEMPMUTE)) + if (cv_gamestochat.value) SV_UpdateTempMute(player, false); continue; } serverplayer_t *stat = SV_GetStatsByPlayerIndex(i); - if (i == serverplayer || IsPlayerAdmin(i) && player->pflags2 & PF2_SERVERTEMPMUTE) + if (i == serverplayer || IsPlayerAdmin(i)) SV_UpdateTempMute(player, false); - else if (stat->finishedrounds >= (UINT32)cv_gamestochat.value && player->pflags2 & PF2_SERVERTEMPMUTE) + else if (stat->finishedrounds >= (UINT32)cv_gamestochat.value) SV_UpdateTempMute(player, false); - else if (stat->finishedrounds < (UINT32)cv_gamestochat.value && !(player->pflags2 & PF2_SERVERTEMPMUTE)) + else if (stat->finishedrounds < (UINT32)cv_gamestochat.value) SV_UpdateTempMute(player, true); } } diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index d4feebbef..97f497bba 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -245,6 +245,7 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT8(save->p, players[i].playerstate); WRITEUINT32(save->p, players[i].pflags); + WRITEUINT32(save->p, players[i].pflags2); WRITEUINT8(save->p, players[i].panim); WRITEUINT8(save->p, players[i].spectator); WRITEUINT32(save->p, players[i].spectatewait); @@ -977,6 +978,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].playerstate = (playerstate_t)READUINT8(save->p); players[i].pflags = READUINT32(save->p); + players[i].pflags2 = READUINT32(save->p); players[i].panim = (panim_t)READUINT8(save->p); players[i].spectator = READUINT8(save->p); players[i].spectatewait = READUINT32(save->p);