Compare commits
3 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b1dd58850e | |||
| a74e9946c8 | |||
| 7eba09905a |
3 changed files with 57 additions and 15 deletions
|
|
@ -23,7 +23,7 @@ namespace AdminStealth;
|
||||||
public class AdminStealth : BasePlugin
|
public class AdminStealth : BasePlugin
|
||||||
{
|
{
|
||||||
public override string ModuleName => "AdminStealth";
|
public override string ModuleName => "AdminStealth";
|
||||||
public override string ModuleVersion => "0.3.13";
|
public override string ModuleVersion => "0.3.15";
|
||||||
public override string ModuleAuthor => "astra";
|
public override string ModuleAuthor => "astra";
|
||||||
public override string ModuleDescription => "Makes CS2-SimpleAdmin's css_hide look like leaving, and keeps it";
|
public override string ModuleDescription => "Makes CS2-SimpleAdmin's css_hide look like leaving, and keeps it";
|
||||||
|
|
||||||
|
|
@ -66,6 +66,9 @@ public class AdminStealth : BasePlugin
|
||||||
// without ever calling us.
|
// without ever calling us.
|
||||||
foreach (var alias in _hideAliases)
|
foreach (var alias in _hideAliases)
|
||||||
AddCommandListener(alias, (player, command) => OnHideTyped(player, command));
|
AddCommandListener(alias, (player, command) => OnHideTyped(player, command));
|
||||||
|
// Players who may not hide get no reply at all, not even CSS's missing-permission one. Only
|
||||||
|
// a catch-all listener runs before every plugin's command handler, for console and chat.
|
||||||
|
AddCommandListener(null, OnAnyCommand);
|
||||||
// Joining any team, spectators included, would make SimpleAdmin unhide them.
|
// Joining any team, spectators included, would make SimpleAdmin unhide them.
|
||||||
AddCommandListener("jointeam", (player, command) => OnJoinTeam(player, command));
|
AddCommandListener("jointeam", (player, command) => OnJoinTeam(player, command));
|
||||||
AddCommandListener("spectate", (player, command) => OnJoinTeam(player, command));
|
AddCommandListener("spectate", (player, command) => OnJoinTeam(player, command));
|
||||||
|
|
@ -80,6 +83,7 @@ public class AdminStealth : BasePlugin
|
||||||
_hideTyped.Clear();
|
_hideTyped.Clear();
|
||||||
_restoreScheduled.Clear();
|
_restoreScheduled.Clear();
|
||||||
_restoring.Clear();
|
_restoring.Clear();
|
||||||
|
_closingTeamScreen.Clear();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,6 +126,14 @@ public class AdminStealth : BasePlugin
|
||||||
return player == null ? HookResult.Continue : HookResult.Stop;
|
return player == null ? HookResult.Continue : HookResult.Stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private HookResult OnAnyCommand(CCSPlayerController? player, CommandInfo command)
|
||||||
|
{
|
||||||
|
if (player is not { IsValid: true, IsBot: false } ||
|
||||||
|
!_hideAliases.Contains(command.GetArg(0), StringComparer.OrdinalIgnoreCase) || CanHide(player))
|
||||||
|
return HookResult.Continue;
|
||||||
|
return HookResult.Stop;
|
||||||
|
}
|
||||||
|
|
||||||
private HookResult OnHideTyped(CCSPlayerController? player, CommandInfo command)
|
private HookResult OnHideTyped(CCSPlayerController? player, CommandInfo command)
|
||||||
{
|
{
|
||||||
if (player is { IsValid: true } && !_restoring.Contains(player.Slot))
|
if (player is { IsValid: true } && !_restoring.Contains(player.Slot))
|
||||||
|
|
@ -136,20 +148,42 @@ public class AdminStealth : BasePlugin
|
||||||
|
|
||||||
player!.PrintToChat($" You're hidden, so you can't join a team. Type {ChatAlias()} to show yourself.");
|
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
|
CloseTeamScreen(player);
|
||||||
// no team (as hidden admins are) can't close it without picking. Answer the pick by telling
|
return HookResult.Stop;
|
||||||
// only their client they joined spectators, with the team screen disabled around it, so
|
}
|
||||||
// nobody else ever sees them on a team.
|
|
||||||
|
// 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<int> _closingTeamScreen = [];
|
||||||
|
|
||||||
|
private void CloseTeamScreen(CCSPlayerController player)
|
||||||
|
{
|
||||||
var slot = player.Slot;
|
var slot = player.Slot;
|
||||||
|
if (!_closingTeamScreen.Add(slot))
|
||||||
|
return;
|
||||||
|
|
||||||
Server.ExecuteCommand("sv_disable_teamselect_menu 1");
|
Server.ExecuteCommand("sv_disable_teamselect_menu 1");
|
||||||
AddTimer(0.3f, () =>
|
Server.NextFrame(() =>
|
||||||
{
|
{
|
||||||
var p = Utilities.GetPlayerFromSlot(slot);
|
var p = Utilities.GetPlayerFromSlot(slot);
|
||||||
if (p is { IsValid: true } && IsHidden(p))
|
if (p is { IsValid: true } && IsHidden(p) && p.TeamNum == (byte)CsTeam.None)
|
||||||
SendTeamEvent(p, (int)CsTeam.Spectator, p.TeamNum);
|
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);
|
}, 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)
|
private HookResult OnTeamMenu(CCSPlayerController? player, CommandInfo command)
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,12 @@ 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 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
|
- 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
|
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
|
without picking, so a pick moves them to spectators and straight back to no team, as `css_hide`
|
||||||
(with `sv_disable_teamselect_menu` on around it); nobody else sees a team change.
|
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.
|
- Restoring runs `css_hide` at most once: it toggles, so a second run could unhide them.
|
||||||
|
- Players who may not use `css_hide` (or its aliases) get no reply at all when they try it, from
|
||||||
|
console or chat: not even CSS's missing-permission message.
|
||||||
|
|
||||||
Needs our SimpleAdmin fork (1.8.2b-zio2 or later), where moving to spectators doesn't unhide a
|
Needs our SimpleAdmin fork (1.8.2b-zio2 or later), where moving to spectators doesn't unhide a
|
||||||
hidden admin. Never hot-reload CS2-SimpleAdmin while this runs: CSS keeps handing out the old
|
hidden admin. Never hot-reload CS2-SimpleAdmin while this runs: CSS keeps handing out the old
|
||||||
|
|
|
||||||
11
release.sh
11
release.sh
|
|
@ -19,8 +19,13 @@ set -euo pipefail
|
||||||
FORGEJO_URL="https://git.zio.sh"
|
FORGEJO_URL="https://git.zio.sh"
|
||||||
OWNER="cs2"
|
OWNER="cs2"
|
||||||
REPO="AdminStealth"
|
REPO="AdminStealth"
|
||||||
# Placeholder - replace, or set FORGEJO_TOKEN in the environment instead. Needs write:repository.
|
# Needs write:repository. Only uploading needs it; downloading from the public repo doesn't. From the
|
||||||
# Only uploading needs it; downloading from the public repo doesn't. Don't commit a real token.
|
# environment, or else from .forgejo-token in the cs2/ folder above every plugin (outside their
|
||||||
|
# repos, mode 600). Don't commit a real token.
|
||||||
|
TOKEN_FILE="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/../../.forgejo-token"
|
||||||
|
if [[ -z "${FORGEJO_TOKEN:-}" && -r "$TOKEN_FILE" ]]; then
|
||||||
|
FORGEJO_TOKEN="$(tr -d '[:space:]' < "$TOKEN_FILE")"
|
||||||
|
fi
|
||||||
FORGEJO_TOKEN="${FORGEJO_TOKEN:-CHANGE_ME_FORGEJO_TOKEN}"
|
FORGEJO_TOKEN="${FORGEJO_TOKEN:-CHANGE_ME_FORGEJO_TOKEN}"
|
||||||
|
|
||||||
# Only these ship from the publish output. CounterStrikeSharp.API.dll is excluded by the csproj
|
# Only these ship from the publish output. CounterStrikeSharp.API.dll is excluded by the csproj
|
||||||
|
|
@ -37,7 +42,7 @@ if [[ -z "$TAG" ]]; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [[ "$FORGEJO_TOKEN" == "CHANGE_ME_FORGEJO_TOKEN" ]]; then
|
if [[ "$FORGEJO_TOKEN" == "CHANGE_ME_FORGEJO_TOKEN" ]]; then
|
||||||
echo "Set FORGEJO_TOKEN (edit release.sh or export it) first." >&2
|
echo "Set FORGEJO_TOKEN (export it, or put it in $TOKEN_FILE) first." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
for tool in podman jq curl git; do
|
for tool in podman jq curl git; do
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue