From 7eba09905a55940a57fb03111e1219e6142b94d9 Mon Sep 17 00:00:00 2001 From: Astra Date: Sat, 3 Oct 2026 00:20:33 +0100 Subject: [PATCH] AdminStealth 0.3.14: a team pick closes the team screen for hidden admins Telling only their client they joined spectators left the screen open, so the admin had to unhide to get out. Move them to spectators and back to no team, as css_hide does, with sv_disable_teamselect_menu on around it. --- AdminStealth.cs | 43 +++++++++++++++++++++++++++++++++---------- README.md | 5 +++-- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/AdminStealth.cs b/AdminStealth.cs index 317484c..f4ee8fb 100644 --- a/AdminStealth.cs +++ b/AdminStealth.cs @@ -23,7 +23,7 @@ namespace AdminStealth; public class AdminStealth : BasePlugin { public override string ModuleName => "AdminStealth"; - public override string ModuleVersion => "0.3.13"; + public override string ModuleVersion => "0.3.14"; public override string ModuleAuthor => "astra"; public override string ModuleDescription => "Makes CS2-SimpleAdmin's css_hide look like leaving, and keeps it"; @@ -80,6 +80,7 @@ public class AdminStealth : BasePlugin _hideTyped.Clear(); _restoreScheduled.Clear(); _restoring.Clear(); + _closingTeamScreen.Clear(); }); } @@ -136,20 +137,42 @@ public class AdminStealth : BasePlugin player!.PrintToChat($" You're hidden, so you can't join a team. Type {ChatAlias()} to show yourself."); - // M opens the team screen on the client, where the server can't block it, and a player with - // no team (as hidden admins are) can't close it without picking. Answer the pick by telling - // only their client they joined spectators, with the team screen disabled around it, so - // nobody else ever sees them on a team. + CloseTeamScreen(player); + return HookResult.Stop; + } + + // M opens the team screen on the client, where the server can't block it, and a player with no + // team (as hidden admins are) can't close it without picking. Telling only their client they + // joined spectators doesn't close it (tried live), so do what css_hide does: really move them + // to spectators and back to no team, with the team screen disabled around it. Their team + // events aren't broadcast while hidden (OnPlayerTeam), and our SimpleAdmin fork doesn't unhide + // for spectators. + private readonly HashSet _closingTeamScreen = []; + + private void CloseTeamScreen(CCSPlayerController player) + { var slot = player.Slot; + if (!_closingTeamScreen.Add(slot)) + return; + Server.ExecuteCommand("sv_disable_teamselect_menu 1"); - AddTimer(0.3f, () => + Server.NextFrame(() => { var p = Utilities.GetPlayerFromSlot(slot); - if (p is { IsValid: true } && IsHidden(p)) - SendTeamEvent(p, (int)CsTeam.Spectator, p.TeamNum); + if (p is { IsValid: true } && IsHidden(p) && p.TeamNum == (byte)CsTeam.None) + p.ChangeTeam(CsTeam.Spectator); + }); + AddTimer(0.15f, () => + { + var p = Utilities.GetPlayerFromSlot(slot); + if (p is { IsValid: true } && IsHidden(p) && p.TeamNum == (byte)CsTeam.Spectator) + p.ChangeTeam(CsTeam.None); + }, TimerFlags.STOP_ON_MAPCHANGE); + AddTimer(0.5f, () => + { + _closingTeamScreen.Remove(slot); + Server.ExecuteCommand("sv_disable_teamselect_menu 0"); }, TimerFlags.STOP_ON_MAPCHANGE); - AddTimer(0.6f, () => Server.ExecuteCommand("sv_disable_teamselect_menu 0"), TimerFlags.STOP_ON_MAPCHANGE); - return HookResult.Stop; } private HookResult OnTeamMenu(CCSPlayerController? player, CommandInfo command) diff --git a/README.md b/README.md index 5f82ae0..19b1e81 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,9 @@ admin off the scoreboard. This plugin makes it look like they left the server: the team select screen doesn't open, until the restore hides them again. - The team select screen: the server's "team" menu message (`CS_UM_VGUIMenu`) is never sent to a hidden admin. M opens it on the client regardless, and a player with no team can't close it - without picking, so a pick is answered by telling only that client it joined spectators - (with `sv_disable_teamselect_menu` on around it); nobody else sees a team change. + without picking, so a pick moves them to spectators and straight back to no team, as `css_hide` + does (with `sv_disable_teamselect_menu` on around it), which closes it. The team events aren't + broadcast; for about 0.15 s they're on the spectator list of anyone looking at the scoreboard. - Restoring runs `css_hide` at most once: it toggles, so a second run could unhide them. Needs our SimpleAdmin fork (1.8.2b-zio2 or later), where moving to spectators doesn't unhide a