v1.2.3.b8 - fix: startup crash with PerfMode/DebugMode enabled

- #### Fixes
    - **Startup crash** - Servers with `PerfMode` or `DebugMode` enabled crashed during boot at the first skill. `PerfLog.Sample` called `IsServerIdle()` on every `SkillAction`, which scanned the player list while the server was still booting. Our own regression, introduced with the idle-logging gate in b8.
    - **PlayerManager** - Added a `serverActive` flag set on map start; `IsServerIdle`, `GetTickPlayers` and `GetTickBomb` no longer touch the game before the map is up.
    - **Commands** - `zmienmape` and one Chinese alias were listed twice, registering the same command name natively two times. Cleaned up the defaults and added a duplicate guard so user configs are covered too.

- #### Chat
    - **Skill announcement** - Teammate skills are now shown first and your own skill description last, so it stays at the bottom of the chat instead of scrolling away.
    - **SetRandomSkill** - No longer leaves the chat box unclosed when the player has teammates.
This commit is contained in:
ByDexter 2026-08-21 10:34:22 +03:00
parent 11f51b596c
commit b95c52028e
8 changed files with 33 additions and 15 deletions

View file

@ -62,8 +62,9 @@ namespace src.command
foreach (var commandPair in commands) foreach (var commandPair in commands)
foreach (var command in commandPair.Key) foreach (var command in commandPair.Key)
{ {
Instance.AddCommand($"css_{command}", commandPair.Value.description, commandPair.Value.handler); string name = $"css_{command}";
oldCommands.TryAdd($"css_{command}", commandPair.Value.handler); if (!oldCommands.TryAdd(name, commandPair.Value.handler)) continue;
Instance.AddCommand(name, commandPair.Value.description, commandPair.Value.handler);
} }
} }
} }

View file

@ -42,6 +42,7 @@ namespace src.player
private static void OnMapStart(string mapName) private static void OnMapStart(string mapName)
{ {
PlayerManager.SetServerActive(true);
Instance.GameRules = null; Instance.GameRules = null;
Event.OnMapChange(); Event.OnMapChange();
BotManager.Initialize(); BotManager.Initialize();
@ -52,6 +53,7 @@ namespace src.player
PerfLog.Info("===== MAP END (clean map change) ====="); PerfLog.Info("===== MAP END (clean map change) =====");
Debug.WriteToDebug("===== MAP END (clean map change) ====="); Debug.WriteToDebug("===== MAP END (clean map change) =====");
BotManager.Stop(); BotManager.Stop();
PlayerManager.SetServerActive(false);
} }
public static void InitializeGameRules() public static void InitializeGameRules()

View file

@ -550,10 +550,6 @@ namespace src.player
var playerTarget = Utilities.GetPlayerFromIndex((int)playerIndex); var playerTarget = Utilities.GetPlayerFromIndex((int)playerIndex);
if (playerTarget == null || !playerTarget.IsValid) return; if (playerTarget == null || !playerTarget.IsValid) return;
if (randomSkill.Display)
SkillUtils.PrintToChat(playerTarget, $"{ChatColors.DarkRed}{playerTarget.GetSkillName(randomSkill.Skill)}{ChatColors.Lime}: {playerTarget.GetSkillDescription(randomSkill.Skill)}",
border: !Utilities.GetPlayers().Any(p => p != null && p.IsValid && p.Team == playerTarget.Team && p != playerTarget) ? "tb" : "t");
if (SkillsInfo.GetValue<bool>(randomSkill.Skill, "disableOnFreezeTime") && SkillUtils.IsFreezeTime()) if (SkillsInfo.GetValue<bool>(randomSkill.Skill, "disableOnFreezeTime") && SkillUtils.IsFreezeTime())
Instance?.AddTimer(Config.LoadedConfig.SkillTimeBeforeStart, () => Instance?.AddTimer(Config.LoadedConfig.SkillTimeBeforeStart, () =>
{ {
@ -575,9 +571,18 @@ namespace src.player
Debug.WriteToDebug($"Player {skillPlayer.PlayerName} has got the skill \"{SkillNames.Get(randomSkill.Skill)}\".", DebugCategory.Skill); Debug.WriteToDebug($"Player {skillPlayer.PlayerName} has got the skill \"{SkillNames.Get(randomSkill.Skill)}\".", DebugCategory.Skill);
UpdateSkillHudExpired(skillPlayer, randomSkill.Skill); UpdateSkillHudExpired(skillPlayer, randomSkill.Skill);
if (randomSkill.Display)
Instance?.AddTimer(.6f, () =>
{
var descTarget = Utilities.GetPlayerFromIndex((int)playerIndex);
if (descTarget == null || !descTarget.IsValid) return;
SkillUtils.PrintToChat(descTarget, $"{ChatColors.DarkRed}{descTarget.GetSkillName(randomSkill.Skill)}{ChatColors.Lime}: {descTarget.GetSkillDescription(randomSkill.Skill)}", border: "tb");
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
if (Config.LoadedConfig.TeamMateSkillChatInfo) if (Config.LoadedConfig.TeamMateSkillChatInfo)
{ {
Instance?.AddTimer(.6f, () => Instance?.AddTimer(.2f, () =>
{ {
if (player == null || !player.IsValid) return; if (player == null || !player.IsValid) return;
@ -597,6 +602,7 @@ namespace src.player
foreach (string text in teammateSkills.Split("\n")) foreach (string text in teammateSkills.Split("\n"))
if (!string.IsNullOrEmpty(text)) if (!string.IsNullOrEmpty(text))
SkillUtils.PrintToChat(player, text, title: player.GetTranslationWithoutIlliterate("teammate_skills"), border: ""); SkillUtils.PrintToChat(player, text, title: player.GetTranslationWithoutIlliterate("teammate_skills"), border: "");
if (!randomSkill.Display)
SkillUtils.PrintToChat(player, string.Empty, title: player.GetTranslationWithoutIlliterate("teammate_skills"), border: "b"); SkillUtils.PrintToChat(player, string.Empty, title: player.GetTranslationWithoutIlliterate("teammate_skills"), border: "b");
} }
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE); }, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
@ -700,8 +706,7 @@ namespace src.player
skillPlayer.SpecialSkill = Skills.None; skillPlayer.SpecialSkill = Skills.None;
if (randomSkill.Display && Config.LoadedConfig.YourSkillChatInfo) if (randomSkill.Display && Config.LoadedConfig.YourSkillChatInfo)
SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetSkillName(randomSkill.Skill)}{ChatColors.Lime}: {player.GetSkillDescription(randomSkill.Skill)}", SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetSkillName(randomSkill.Skill)}{ChatColors.Lime}: {player.GetSkillDescription(randomSkill.Skill)}", border: "tb");
border: !Utilities.GetPlayers().Any(p => p != null && p.IsValid && p.Team == player.Team && p != player) ? "tb" : "t");
if (randomSkill.Skill == Skills.Illiterate) if (randomSkill.Skill == Skills.Illiterate)
Illiterate.Enable(); Illiterate.Enable();

View file

@ -273,13 +273,13 @@ namespace src.utils
ChangeLanguageCommand = new NormalCommand("lang, language, changelang, change_lang, jezyk, język", ""), ChangeLanguageCommand = new NormalCommand("lang, language, changelang, change_lang, jezyk, język", ""),
ReloadCommand = new NormalCommand("reload, refresh", "@jRandomSkills/admin"), ReloadCommand = new NormalCommand("reload, refresh", "@jRandomSkills/admin"),
NextCommand = new NormalCommand("next_skill", "@jRandomSkills/admin"), NextCommand = new NormalCommand("next_skill", "@jRandomSkills/admin"),
CheckEntityCommand = new NormalCommand("ent, entity, checkentity, check_entity, sprawdzencje, checkent, check_ent, 检查实体, 检查实体", "@jRandomSkills/owner"), CheckEntityCommand = new NormalCommand("ent, entity, checkentity, check_entity, sprawdzencje, checkent, check_ent, 检查实体", "@jRandomSkills/owner"),
}; };
VotingCommands = new VotingCommands VotingCommands = new VotingCommands
{ {
StartGameCommand = new StartGameCommand(true, "start, go, começar, iniciar, 开始, 启动", "@jRandomSkills/admin", "mp_freezetime 15; mp_forcecamera 0; mp_overtime_enable 1; sv_cheats 0", "mp_freezetime 0; mp_forcecamera 0; mp_overtime_enable 1; sv_cheats 1", 15, 60, 15, 500, 2), StartGameCommand = new StartGameCommand(true, "start, go, começar, iniciar, 开始, 启动", "@jRandomSkills/admin", "mp_freezetime 15; mp_forcecamera 0; mp_overtime_enable 1; sv_cheats 0", "mp_freezetime 0; mp_forcecamera 0; mp_overtime_enable 1; sv_cheats 1", 15, 60, 15, 500, 2),
ChangeMapCommand = new VotingCommand(true, "map, mapa, changemap, zmienmape, zmienmape, mudarMapa, trocarMapa, 更换地图, 更改地图", "@jRandomSkills/admin", 25, 90, 15, 500, 2), ChangeMapCommand = new VotingCommand(true, "map, mapa, changemap, zmienmape, mudarMapa, trocarMapa, 更换地图, 更改地图", "@jRandomSkills/admin", 25, 90, 15, 500, 2),
SwapCommand = new VotingCommand(true, "swap, zmiana, trocar, 交换, 切换", "@jRandomSkills/admin", 15, 90, 15, 20, 2), SwapCommand = new VotingCommand(true, "swap, zmiana, trocar, 交换, 切换", "@jRandomSkills/admin", 15, 90, 15, 20, 2),
ShuffleCommand = new VotingCommand(true, "shuffle, embaralhar, 随机排序, 洗牌", "@jRandomSkills/admin", 15, 90, 15, 20, 2), ShuffleCommand = new VotingCommand(true, "shuffle, embaralhar, 随机排序, 洗牌", "@jRandomSkills/admin", 15, 90, 15, 20, 2),
PauseCommand = new VotingCommand(true, "pause, unpause, pausar, despausar, 暂停, 恢复", "@jRandomSkills/admin", 15, 60, 15, 2, 2), PauseCommand = new VotingCommand(true, "pause, unpause, pausar, despausar, 暂停, 恢复", "@jRandomSkills/admin", 15, 60, 15, 2, 2),

View file

@ -19,8 +19,14 @@ namespace src.utils
private static int cachedIdleTick = int.MinValue; private static int cachedIdleTick = int.MinValue;
private static bool cachedIdle = true; private static bool cachedIdle = true;
private static bool serverActive;
public static void SetServerActive(bool active) => serverActive = active;
public static bool IsServerIdle() public static bool IsServerIdle()
{ {
if (!serverActive) return true;
int tick = Server.TickCount; int tick = Server.TickCount;
if (tick == cachedIdleTick) return cachedIdle; if (tick == cachedIdleTick) return cachedIdle;
cachedIdleTick = tick; cachedIdleTick = tick;
@ -77,12 +83,16 @@ namespace src.utils
public static List<CCSPlayerController> GetTickPlayers() public static List<CCSPlayerController> GetTickPlayers()
{ {
if (!serverActive) return [];
EnsureTickCache(); EnsureTickCache();
return cachedControllers; return cachedControllers;
} }
public static CC4? GetTickBomb() public static CC4? GetTickBomb()
{ {
if (!serverActive) return null;
int tick = Server.TickCount; int tick = Server.TickCount;
if (tick != cachedBombTick) if (tick != cachedBombTick)
{ {

View file

@ -1,4 +1,4 @@
{ {
"ConfigName": "Default", "ConfigName": "Default",
"GameMode": 3, "GameMode": 3,
"YourSkillChatInfo": true, "YourSkillChatInfo": true,
@ -196,7 +196,7 @@
"Permissions": "@jRandomSkills/admin" "Permissions": "@jRandomSkills/admin"
}, },
"CheckEntityCommand": { "CheckEntityCommand": {
"Alias": "ent, entity, checkentity, check_entity, sprawdzencje, checkent, check_ent, 检查实体, 检查实体", "Alias": "ent, entity, checkentity, check_entity, sprawdzencje, checkent, check_ent, 检查实体",
"Permissions": "@jRandomSkills/owner" "Permissions": "@jRandomSkills/owner"
} }
}, },
@ -220,7 +220,7 @@
"TimeToNextVoting": 15.0, "TimeToNextVoting": 15.0,
"TimeToNextSameVoting": 500.0, "TimeToNextSameVoting": 500.0,
"MinimumPlayersToStartVoting": 2, "MinimumPlayersToStartVoting": 2,
"Alias": "map, mapa, changemap, zmienmape, zmienmape, mudarMapa, trocarMapa, 更换地图, 更改地图", "Alias": "map, mapa, changemap, zmienmape, mudarMapa, trocarMapa, 更换地图, 更改地图",
"Permissions": "@jRandomSkills/admin" "Permissions": "@jRandomSkills/admin"
}, },
"SwapCommand": { "SwapCommand": {