From 8739fd3ac0370370464ce92ec5785d2eca5b14c6 Mon Sep 17 00:00:00 2001 From: Kamal Tufekcic Date: Fri, 10 Jul 2026 03:01:19 +0300 Subject: [PATCH] CS# bug bypass for survival mode --- Directory.Build.props | 2 +- Outnumbered/Engine/RespawnFix.cs | 23 +++++++++++++++++++++++ Outnumbered/Outnumbered.cs | 2 +- Outnumbered/Survival.cs | 4 ++-- Outnumbered/outnumbered.csproj | 2 +- README.md | 2 +- 6 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 Outnumbered/Engine/RespawnFix.cs diff --git a/Directory.Build.props b/Directory.Build.props index 9e53fec..71ab1b2 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -5,6 +5,6 @@ enable latest true - 1.0.3 + 1.0.4 diff --git a/Outnumbered/Engine/RespawnFix.cs b/Outnumbered/Engine/RespawnFix.cs new file mode 100644 index 0000000..a9ddde8 --- /dev/null +++ b/Outnumbered/Engine/RespawnFix.cs @@ -0,0 +1,23 @@ +using System; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Memory; + +namespace Outnumbered.Engine; + +// Workaround, pending a CounterStrikeSharp release carrying the upstream fix: CSSharp's +// CCSPlayerController.Respawn() calls SetPawn(PlayerPawn.Value), which faults inside the engine's +// network-state change code when the pawn is DEAD — the usual respawn case — on the current CS2 +// build (a bot respawned right after we kill it took the crash). This mirrors CSSharp's Respawn() +// but only re-attaches a LIVE pawn; the CCSPlayerController_Respawn vfunc re-creates the pawn on +// respawn regardless. Revert every SafeRespawn() call back to .Respawn() once the fixed package ships. +internal static class RespawnFix +{ + public static void SafeRespawn(this CCSPlayerController controller) + { + if (controller is not { IsValid: true }) return; + var pawn = controller.PlayerPawn.Value; + if (pawn == null) return; + if (pawn is { IsValid: true, Health: > 0 }) controller.SetPawn(pawn); + VirtualFunction.CreateVoid(controller.Handle, GameData.GetOffset("CCSPlayerController_Respawn"))(controller.Handle); + } +} diff --git a/Outnumbered/Outnumbered.cs b/Outnumbered/Outnumbered.cs index dc8fe2d..e5c18a0 100644 --- a/Outnumbered/Outnumbered.cs +++ b/Outnumbered/Outnumbered.cs @@ -10,7 +10,7 @@ namespace Outnumbered; public sealed partial class OutnumberedPlugin : BasePlugin, IPluginConfig { public override string ModuleName => "Outnumbered"; - public override string ModuleVersion => "1.0.3-modes"; + public override string ModuleVersion => "1.0.4-modes"; public override string ModuleAuthor => "snake"; public override string ModuleDescription => "Players-vs-bots RPG / perk mod."; diff --git a/Outnumbered/Survival.cs b/Outnumbered/Survival.cs index c44d965..cb5829e 100644 --- a/Outnumbered/Survival.cs +++ b/Outnumbered/Survival.cs @@ -657,7 +657,7 @@ public sealed class SurvivalDriver : IMatchDriver, IDraftDriver // Respawn a pool bot, then (deferred a frame so the spawn settles) teleport it to a spawn point away from the squad. private void DirectedBotSpawn(CCSPlayerController bot) { - bot.Respawn(); + bot.SafeRespawn(); // CSSharp's Respawn() faults in SetPawn on a dead controller (see RespawnFix) if (_spawnPoints.Count == 0 || _p.Config.Survival.MinSpawnDistance <= 0) return; // no candidates / disabled -> plain respawn int slot = bot.Slot; OutnumberedPlugin.NextFrameForSlot(slot, b => @@ -923,7 +923,7 @@ public sealed partial class OutnumberedPlugin if (p is not { IsValid: true } || p.IsBot || p.PawnIsAlive || p.Team != CsTeam.CounterTerrorist) return; if (p.AuthorizedSteamID?.SteamId64 is not { } sid) return; var dest = teleportTo is { } t ? new Vector(t.X, t.Y, t.Z) : null; - p.Respawn(); + p.SafeRespawn(); // CSSharp's Respawn() faults in SetPawn on a dead controller (see RespawnFix) NextFrameForSlot(p.Slot, sid, pl => { var pd = PdOf(pl); var pawn = pl.PlayerPawn.Value; diff --git a/Outnumbered/outnumbered.csproj b/Outnumbered/outnumbered.csproj index 768d96b..f768397 100644 --- a/Outnumbered/outnumbered.csproj +++ b/Outnumbered/outnumbered.csproj @@ -12,7 +12,7 @@ - + diff --git a/README.md b/README.md index 6a43d6b..b0ea153 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ servers. ## Requirements - A CS2 dedicated server with **Metamod:Source** and **CounterStrikeSharp** installed - (compiled against CounterStrikeSharp API **1.0.369**; use a matching or newer server build). + (compiled against CounterStrikeSharp API **1.0.370**; use a matching or newer server build). - **.NET 10** — provided by the CounterStrikeSharp runtime; only needed as an SDK if you build from source. - **Linux** — the damage path, native grenade spawning, and launch-flag parsing are Linux-specific. - A database: **PostgreSQL** for production (recommended, required for multi-instance), or **SQLite** for single-server/dev.