diff --git a/CS2-SimpleAdmin/CS2-SimpleAdmin.cs b/CS2-SimpleAdmin/CS2-SimpleAdmin.cs index 78a93ee..086fdc4 100644 --- a/CS2-SimpleAdmin/CS2-SimpleAdmin.cs +++ b/CS2-SimpleAdmin/CS2-SimpleAdmin.cs @@ -22,7 +22,7 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig "CS2-SimpleAdmin" + (Helper.IsDebugBuild ? " (DEBUG)" : " (RELEASE)"); public override string ModuleDescription => "Simple admin plugin for Counter-Strike 2 :)"; public override string ModuleAuthor => "daffyy"; - public override string ModuleVersion => "1.8.2b-zio3"; + public override string ModuleVersion => "1.8.2b-zio5"; public override void Load(bool hotReload) { diff --git a/CS2-SimpleAdmin/Commands/basechat.cs b/CS2-SimpleAdmin/Commands/basechat.cs index 9b0662e..a301a9b 100644 --- a/CS2-SimpleAdmin/Commands/basechat.cs +++ b/CS2-SimpleAdmin/Commands/basechat.cs @@ -29,13 +29,23 @@ public partial class CS2_SimpleAdmin foreach (var player in Helper.GetValidPlayers() .Where(p => AdminManager.PlayerHasPermissions(new SteamID(p.SteamID), "@css/chat"))) { - if (_localizer != null) - player.PrintToChat(_localizer["sa_adminchat_template_admin", - caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName, - utf8String]); + if (_localizer == null) continue; + // zio: players get ">[STAFF][RANK] name: message", like @ in team chat. + player.PrintToChat(caller == null + ? _localizer["sa_adminchat_template_admin", _localizer["sa_console"], utf8String] + : StaffChatMessage(caller, utf8String.Trim())); } } + // zio: staff chat line, ">[STAFF][RANK] name: message" ("[RANK]" left out without a rank group). + private static string StaffChatMessage(CCSPlayerController sender, string message) + { + var rank = Helper.GetStaffRankTag(sender); + return rank == null + ? _localizer!["sa_staffchat_template_norank", sender.PlayerName, message] + : _localizer!["sa_staffchat_template", rank, sender.PlayerName, message]; + } + /// /// Sends a custom chat message to all players with color tags processed. /// @@ -73,6 +83,20 @@ public partial class CS2_SimpleAdmin var utf8String = Encoding.UTF8.GetString(utf8BytesString); Helper.LogCommand(caller, command); + + // zio: from a player (including @ in all chat), ">[RANK] name: message" with no [SA] prefix. + // Console (and the web panel over RCON) keeps the plain "ADMIN:" message. + if (caller != null && _localizer != null) + { + var rank = Helper.GetStaffRankTag(caller); + var line = rank == null + ? _localizer["sa_staffsay_template_norank", caller.PlayerName, utf8String.Trim()] + : _localizer["sa_staffsay_template", rank, caller.PlayerName, utf8String.Trim()]; + foreach (var player in Helper.GetValidPlayers()) + player.PrintToChat(line); + return; + } + foreach (var player in Helper.GetValidPlayers()) { player.SendLocalizedMessage(_localizer, diff --git a/CS2-SimpleAdmin/Events.cs b/CS2-SimpleAdmin/Events.cs index 58e95f9..bd0a404 100644 --- a/CS2-SimpleAdmin/Events.cs +++ b/CS2-SimpleAdmin/Events.cs @@ -376,7 +376,8 @@ public partial class CS2_SimpleAdmin StringBuilder sb = new(); if (AdminManager.PlayerHasPermissions(new SteamID(player.SteamID), "@css/chat")) { - sb.Append(_localizer!["sa_adminchat_template_admin", player.PlayerName, info.GetArg(1).Remove(0, 1)]); + // zio: ">[STAFF][RANK] name: message". + sb.Append(StaffChatMessage(player, info.GetArg(1).Remove(0, 1))); foreach (var p in Utilities.GetPlayers().Where(p => p.IsValid && p is { IsBot: false, IsHLTV: false } && AdminManager.PlayerHasPermissions(new SteamID(p.SteamID), "@css/chat"))) { p.PrintToChat(sb.ToString()); @@ -384,7 +385,8 @@ public partial class CS2_SimpleAdmin } else { - sb.Append(_localizer!["sa_adminchat_template_player", player.PlayerName, info.GetArg(1).Remove(0, 1)]); + // zio: ">[PLAYER] name: message". + sb.Append(_localizer!["sa_staffchat_template_player", player.PlayerName, info.GetArg(1).Remove(0, 1)]); player.PrintToChat(sb.ToString()); foreach (var p in Utilities.GetPlayers().Where(p => p is { IsValid: true, IsBot: false, IsHLTV: false } && AdminManager.PlayerHasPermissions(new SteamID(p.SteamID), "@css/chat"))) { diff --git a/CS2-SimpleAdmin/Helper.cs b/CS2-SimpleAdmin/Helper.cs index 833b200..226ff47 100644 --- a/CS2-SimpleAdmin/Helper.cs +++ b/CS2-SimpleAdmin/Helper.cs @@ -144,6 +144,30 @@ internal static class Helper return string.Join(" ", parts); } + // zio: staff rank groups, highest first, and their chat abbreviations. + private static readonly (string Group, string Tag)[] StaffRanks = + [ + ("#rank/owner", "O"), + ("#rank/senioradmin", "SA"), + ("#rank/admin", "A"), + ("#rank/trialadmin", "TA"), + ("#rank/mod", "M"), + ("#rank/trialmod", "TM"), + ("#rank/helper", "H"), + ]; + + // zio: the player's highest staff rank abbreviation, or null if they're in no rank group. + internal static string? GetStaffRankTag(CCSPlayerController player) + { + var groups = AdminManager.GetPlayerAdminData(new SteamID(player.SteamID))?.Groups; + if (groups == null) return null; + foreach (var (group, tag) in StaffRanks) + { + if (groups.Contains(group)) return tag; + } + return null; + } + public static void KickPlayer(int userId, NetworkDisconnectionReason reason = NetworkDisconnectionReason.NETWORK_DISCONNECT_KICKED, int delay = 0) { var player = Utilities.GetPlayerFromUserid(userId); diff --git a/CS2-SimpleAdmin/lang/en.json b/CS2-SimpleAdmin/lang/en.json index af18974..6ff4800 100644 --- a/CS2-SimpleAdmin/lang/en.json +++ b/CS2-SimpleAdmin/lang/en.json @@ -141,6 +141,11 @@ "sa_admin_voice_unlisten_all": "{Default}You no longer hear {lime}all {default}players", "sa_adminsay_prefix": "{RED}ADMIN: {lightred}{0}{default}", "sa_adminchat_template_admin": "{LIME}(ADMIN) {lightred}{0}{default}: {lightred}{1}{default}", + "sa_staffchat_template": "{default}>[{yellow}STAFF{default}][{lime}{0}{default}] {yellow}{1}{default}: {grey}{2}{default}", + "sa_staffchat_template_norank": "{default}>[{yellow}STAFF{default}] {yellow}{0}{default}: {grey}{1}{default}", + "sa_staffchat_template_player": "{default}>[{lightblue}PLAYER{default}] {lightblue}{0}{default}: {grey}{1}{default}", + "sa_staffsay_template": "{default}>[{lime}{0}{default}] {yellow}{1}{default}: {grey}{2}{default}", + "sa_staffsay_template_norank": "{default}> {yellow}{0}{default}: {grey}{1}{default}", "sa_adminchat_template_player": "{SILVER}(PLAYER) {lightred}{0}{default}: {lightred}{1}{default}", "sa_discord_log_command": "**{0}** issued command `{1}` on server `HOSTNAME`", "sa_menu_pluginsmanager_loaded": "{lime}Enabled {default}plugin {lime}{0}", diff --git a/README.md b/README.md index ed6660b..9f870af 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,21 @@ from the `build-1.8.2b` tag). Changes, all marked `zio:` in the code: - New permission `@css/bypassipban`, for players or groups: IP bans, and bans on other accounts that share their IP, don't apply to them; a ban on their own SteamID still does. `@css/root` has it, as it has every `@css` flag. +- Staff chat shows the sender's rank, from their highest `#rank/...` group (O, SA, A, TA, M, TM, + H for helper): `@message` in team chat and `css_asay` read `>[STAFF][RANK] name: message`, and + `@message` in all chat and `css_say` from a player read `>[RANK] name: message` (`> name: message` + if they have no rank group). A non-staff player's `@message` in team chat reads + `>[PLAYER] name: message`. Console's `css_say`/`css_asay` are unchanged. New lang keys + `sa_staffchat_template`, `sa_staffsay_template`, their `_norank` variants and + `sa_staffchat_template_player`, in `lang/en.json` only. Publish with `FORGEJO_TOKEN=... ./release.sh v` (ModuleVersion is `1.8.2b-zioN`). It rebuilds from the committed source and uploads `SimpleAdmin-.tar.gz` to git.zio.sh -(`cs2/SimpleAdmin`), holding only `CS2-SimpleAdmin.dll` and `lang/en.json`. The rest of the plugin -and `shared/CS2-SimpleAdminApi` stay as the upstream 1.8.2b release installed them. +(`cs2/SimpleAdmin`), laid out like `game/csgo/`. It holds the whole plugin (its libraries, all lang +files and migrations) and `shared/CS2-SimpleAdminApi`, as upstream's own release does, so nothing +from upstream needs installing first. It leaves out the optional FunCommands and StealthModule +modules and `configs/`. The NickFox007 dependencies (AnyBaseLib, PlayerSettings, MenuManager) aren't +in it either; the server's `pre.sh` installs them from their own releases. --- diff --git a/release.sh b/release.sh index c2b1e46..0ccb29b 100755 --- a/release.sh +++ b/release.sh @@ -10,11 +10,13 @@ # Asset: SimpleAdmin-.tar.gz, laid out like the server's game/csgo/ - extract it there: # tar -xzf SimpleAdmin-.tar.gz -C /srv/cs2/game/csgo # -# addons/counterstrikesharp/plugins/CS2-SimpleAdmin/ CS2-SimpleAdmin.dll, lang/en.json +# addons/counterstrikesharp/plugins/CS2-SimpleAdmin/ the plugin, its libraries, lang/, migrations +# addons/counterstrikesharp/shared/CS2-SimpleAdminApi/ the shared API (unchanged from upstream) # -# Only the files the fork changes. Everything else in the plugin folder (its libraries, other lang -# files, migrations) and shared/CS2-SimpleAdminApi stay as the upstream 1.8.2b release installed -# them; the fork doesn't touch the API. No configs/: those stay the server's. +# The whole plugin, packaged the way upstream's CI packages its release, so nothing of upstream's +# has to be installed first. Not included: the optional FunCommands and StealthModule modules, and +# the NickFox007 dependencies (AnyBaseLib, PlayerSettings, MenuManager), which pre.sh installs from +# their own releases. No configs/: the database settings stay the server's. set -euo pipefail FORGEJO_URL="https://git.zio.sh" @@ -24,13 +26,6 @@ REPO="SimpleAdmin" # Only uploading needs it; downloading from the public repo doesn't. Don't commit a real token. FORGEJO_TOKEN="${FORGEJO_TOKEN:-CHANGE_ME_FORGEJO_TOKEN}" -# Only these ship from the publish output. CounterStrikeSharp.API.dll is excluded by the csproj -# (ExcludeAssets=runtime); the server already provides it. -PLUGIN_FILES=( - CS2-SimpleAdmin.dll - lang/en.json -) - TAG="${1:-}" if [[ -z "$TAG" ]]; then echo "usage: $0 (e.g. v1.8.2b-zio1)" >&2 @@ -60,24 +55,24 @@ fi # --- Build ----------------------------------------------------------------------- OUT="release-out" -PUBLISH="$OUT/publish" -rm -rf CS2-SimpleAdmin/bin CS2-SimpleAdmin/obj CS2-SimpleAdminApi/bin CS2-SimpleAdminApi/obj "$OUT" -# dotnet build, as upstream's CI does: the csproj sets PublishTrimmed, which a plugin mustn't be. -podman run --rm -v "$(pwd)":/src:Z -w /src \ - mcr.microsoft.com/dotnet/sdk:10.0 dotnet build CS2-SimpleAdmin/CS2-SimpleAdmin.csproj -c Release -o "/src/$PUBLISH" - -# --- Stage (layout of game/csgo/) and package ---------------------------------------- STAGE="$OUT/stage" -PLUGIN_DIR="$STAGE/addons/counterstrikesharp/plugins/CS2-SimpleAdmin" -mkdir -p "$PLUGIN_DIR" -for file in "${PLUGIN_FILES[@]}"; do - mkdir -p "$PLUGIN_DIR/$(dirname "$file")" - cp "$PUBLISH/$file" "$PLUGIN_DIR/$file" -done +CSS_DIR="addons/counterstrikesharp" +rm -rf CS2-SimpleAdmin/bin CS2-SimpleAdmin/obj CS2-SimpleAdminApi/bin CS2-SimpleAdminApi/obj "$OUT" +# dotnet build straight into the staged layout, as upstream's CI does: the csproj sets +# PublishTrimmed, which a plugin mustn't be. CounterStrikeSharp.API.dll is kept out by the csproj +# (ExcludeAssets=runtime); the server already provides it. +podman run --rm -v "$(pwd)":/src:Z -w /src mcr.microsoft.com/dotnet/sdk:10.0 sh -c " + dotnet build CS2-SimpleAdmin/CS2-SimpleAdmin.csproj -c Release -o '/src/$STAGE/$CSS_DIR/plugins/CS2-SimpleAdmin' && + dotnet build CS2-SimpleAdminApi/CS2-SimpleAdminApi.csproj -c Release -o '/src/$STAGE/$CSS_DIR/shared/CS2-SimpleAdminApi'" +if find "$STAGE" -name CounterStrikeSharp.API.dll | grep -q .; then + echo "CounterStrikeSharp.API.dll ended up in the build output; not packaging it." >&2 + exit 1 +fi +# --- Package (layout of game/csgo/) ------------------------------------------------ TARBALL="SimpleAdmin-${TAG}.tar.gz" tar -czf "$OUT/$TARBALL" -C "$STAGE" addons -rm -rf "$STAGE" "$PUBLISH" +rm -rf "$STAGE" echo "Packaged:" tar -tzf "$OUT/$TARBALL"