diff --git a/.github/en.md b/.github/en.md index 02744e9..c9f512a 100644 --- a/.github/en.md +++ b/.github/en.md @@ -358,6 +358,36 @@ This plugin uses content from the following projects: ## 馃搵 Changelog +
+v1.2.2.b7 + +- #### General: + - ###### Updated the gamedata signatures for the latest CS2 update (build 24134959) and bumped CounterStrikeSharp to 1.0.371. [by: [@ByDexterTR](https://github.com/ByDexterTR)] + - ###### Fixed a NullReferenceException thrown on weapon pickup when the weapon data pointed at freed schema memory. [by: [@ByDexterTR](https://github.com/ByDexterTR)] + - ###### FullFoceUpdate has been changed to an optional setting in the configuration. Enabling it reduces the risk of PVS errors, but places a significant load on the server (disabled by default). [by: [@Juzlus](https://github.com/Juzlus)] + +- #### Skill improvements: + - ###### Ghost: + - ###### Can now plant the C4. [by: [@ByDexterTR](https://github.com/ByDexterTR)] + - ###### Free Planter / Short Bomb: + - ###### The on-screen bomb countdown now matches the configured detonation time (`mp_c4timer` is set at round start instead of only the blow time being overridden after the plant). [by: [@ByDexterTR](https://github.com/ByDexterTR)] + - ###### Rich Boy: + - ###### The money bonus now respects `mp_maxmoney` instead of being capped at a hardcoded 16000. [by: [@ByDexterTR](https://github.com/ByDexterTR)] + - ###### Second Chance: + - ###### Now reliably respawns at base on a lethal hit, including fall damage (the revive moved to a pre-damage hook so the death is cancelled before it is committed). [by: [@ByDexterTR](https://github.com/ByDexterTR)] + - ###### Shadow: + - ###### Fixed a false "no suitable area" when teleporting behind a target at close range. [by: [@ByDexterTR](https://github.com/ByDexterTR)] + - ###### Radar Hack: + - ###### Now reveals enemies only to the skill holder instead of the holder's whole team, and no longer reveals invisible or camouflaged enemies. [by: [@ByDexterTR](https://github.com/ByDexterTR)] + - ###### C4 Camouflage: + - ###### The disguised bomb carrier no longer appears on the enemy radar. [by: [@ByDexterTR](https://github.com/ByDexterTR)] + - ###### Hot Bomb: + - ###### The "hot bomb" hint no longer shows in a round where nobody has the skill. [by: [@ByDexterTR](https://github.com/ByDexterTR)] + - ###### Jester: + - ###### Fixes crashes that occur when a skill is being disabled. [by: [@Juzlus](https://github.com/Juzlus)] + +
+
v1.2.2.b6 diff --git a/.github/pl.md b/.github/pl.md index 2c8dfee..7f6ca06 100644 --- a/.github/pl.md +++ b/.github/pl.md @@ -356,6 +356,36 @@ Plugin korzysta z zawarto艣ci nast臋puj膮cych projekt贸w: ## 馃搵 Lista Zmian +
+v1.2.2.b7 + +- #### Og贸lne: + - ###### Zaktualizowano sygnatury gamedata do najnowszej aktualizacji CS2 (build 24134959) oraz zaktualizowano CounterStrikeSharp do wersji 1.0.371. [by: [@ByDexterTR](https://github.com/ByDexterTR)] + - ###### FNaprawiono `NullReferenceException` wyst臋puj膮cy podczas podnoszenia broni, gdy dane broni wskazywa艂y na zwolnion膮 pami臋膰 schematu. [by: [@ByDexterTR](https://github.com/ByDexterTR)] + - ###### `FullForceUpdate` zosta艂 zmieniony na opcjonalne ustawienie w konfiguracji. Jego w艂膮czenie zmniejsza ryzyko b艂臋d贸w PVS, ale powoduje znaczne obci膮偶enie serwera (domy艣lnie wy艂膮czone). [by: [@Juzlus](https://github.com/Juzlus)] + +- #### Poprawki mocy: + - ###### Duszek: + - ###### Teraz mo偶e podk艂ada膰 C4. [by: [@ByDexterTR](https://github.com/ByDexterTR)] + - ###### Samowolka / Kr贸tka Bomba: + - ###### Odliczanie bomby wy艣wietlane na ekranie jest teraz zgodne z ustawionym czasem detonacji (`mp_c4timer`). [by: [@ByDexterTR](https://github.com/ByDexterTR)] + - ###### Bogacz: + - ###### Bonus za pieni膮dze teraz respektuje `mp_maxmoney` zamiast by膰 ograniczony na sztywno do warto艣ci 16000. [by: [@ByDexterTR](https://github.com/ByDexterTR)] + - ###### Druga Szansa: + - ###### Teraz niezawodnie odradza gracza w bazie po otrzymaniu 艣miertelnego obra偶enia, r贸wnie偶 od obra偶e艅 od upadku. [by: [@ByDexterTR](https://github.com/ByDexterTR)] + - ###### Cie艅: + - ###### Naprawiono fa艂szywy komunikat "brak miejsca" podczas teleportacji za cel z bliskiej odleg艂o艣ci. [by: [@ByDexterTR](https://github.com/ByDexterTR)] + - ###### Radarowiec: + - ###### Teraz ujawnia przeciwnik贸w tylko posiadaczowi mocy zamiast ca艂ej jego dru偶ynie oraz nie ujawnia ju偶 niewidzialnych ani zamaskowanych przeciwnik贸w. [by: [@ByDexterTR](https://github.com/ByDexterTR)] + - ###### C4 Kamufla偶: + - ###### Niewidzialny gracz nios膮cy bombe nie pojawia si臋 ju偶 na radarze przeciwnik贸w. [by: [@ByDexterTR](https://github.com/ByDexterTR)] + - ###### Gor膮ca bomba: + - ###### Podpowied藕 dotycz膮ca "gor膮cej bomby" nie pojawia si臋 ju偶 w rundach, w kt贸rych nikt nie posiada tej mocy. [by: [@ByDexterTR](https://github.com/ByDexterTR)] + - ###### B艂azen: + - ###### Naprawiono crashe wyst臋puj膮ce podczas wy艂膮czania mocy. [by: [@Juzlus](https://github.com/Juzlus)] + +
+
v1.2.2.b6 diff --git a/jRandomSkills - SRC Files/src/gamedata/jRandomSkills.gamedata.json b/jRandomSkills - SRC Files/src/gamedata/jRandomSkills.gamedata.json index b4a5330..3e0bc0e 100644 --- a/jRandomSkills - SRC Files/src/gamedata/jRandomSkills.gamedata.json +++ b/jRandomSkills - SRC Files/src/gamedata/jRandomSkills.gamedata.json @@ -26,5 +26,23 @@ "windows": "48 89 5C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 55 48 8D 6C 24 ? 48 81 EC ? ? ? ? 48 8B DA 48 8B F1 48 8D 55", "linux": "55 48 89 E5 41 57 49 89 FF 48 89 F7 41 56 41 55 41 54 53 48 81 EC ? ? ? ? E8" } + }, + "CServerSideClient_m_nDeltaTick": { + "offsets": { + "windows": 348, + "linux": 348 + } + }, + "INetworkGameServer_Slots": { + "offsets": { + "windows": 584, + "linux": 584 + } + }, + "INetworkServerService_GetIGameServer": { + "offsets": { + "windows": 23, + "linux": 24 + } } } diff --git a/jRandomSkills - SRC Files/src/jRandomSkills.cs b/jRandomSkills - SRC Files/src/jRandomSkills.cs index 5dd9c62..c6d5062 100644 --- a/jRandomSkills - SRC Files/src/jRandomSkills.cs +++ b/jRandomSkills - SRC Files/src/jRandomSkills.cs @@ -28,7 +28,7 @@ namespace src public static readonly ConcurrentDictionary ActiveSkillsThisRound = new(); public override string ModuleName => "[CS2] [ jRandomSkills ]"; - public override string ModuleAuthor => "D3X, Juzlus"; + public override string ModuleAuthor => "D3X (Original), Juzlus (Modifier), ByDexterTR (Contributor)"; public override string ModuleDescription => "Plugin adds random skills every round for CS2 by D3X. Modified by Juzlus."; public override string ModuleVersion => "1.2.2.b7"; diff --git a/jRandomSkills - SRC Files/src/player/PlayerEvents.cs b/jRandomSkills - SRC Files/src/player/PlayerEvents.cs index 377474d..28cc95a 100644 --- a/jRandomSkills - SRC Files/src/player/PlayerEvents.cs +++ b/jRandomSkills - SRC Files/src/player/PlayerEvents.cs @@ -1093,24 +1093,31 @@ namespace src.player if (randomSkill.Skill == Skills.Illiterate) Illiterate.Enable(); + var playerIndex = player.Index; Instance?.AddTimer(.2f, () => { - if (player == null || !player.IsValid) return; + var playerTarget = Utilities.GetPlayerFromIndex((int)playerIndex); + if (playerTarget == null || !playerTarget.IsValid) return; if (randomSkill.Display) - SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetSkillName(randomSkill.Skill)}{ChatColors.Lime}: {player.GetSkillDescription(randomSkill.Skill)}", - border: !Utilities.GetPlayers().Any(p => p != null && p.IsValid && p.Team == player.Team && p != player) ? "tb" : "t"); + 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(randomSkill.Skill, "disableOnFreezeTime") && SkillUtils.IsFreezeTime()) Instance?.AddTimer(Config.LoadedConfig.SkillTimeBeforeStart, () => { - if (PlayerManager.GetPlayerByIndex(player!.Index)?.Skill != randomSkill.Skill) return; - Instance?.SkillAction(randomSkill.Skill.ToString(), "EnableSkill", [player]); + var playerTarget = Utilities.GetPlayerFromIndex((int)playerIndex); + if (playerTarget == null || !playerTarget.IsValid) return; + + if (PlayerManager.GetPlayerByIndex(playerTarget!.Index)?.Skill != randomSkill.Skill) return; + Debug.WriteToDebug("Enabling skill after freeze time: " + randomSkill.Skill); + Instance?.SkillAction(randomSkill.Skill.ToString(), "EnableSkill", [playerTarget]); }, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE); else { - if (PlayerManager.GetPlayerByIndex(player!.Index)?.Skill != randomSkill.Skill) return; - Instance?.SkillAction(randomSkill.Skill.ToString(), "EnableSkill", [player]); + if (PlayerManager.GetPlayerByIndex(playerTarget!.Index)?.Skill != randomSkill.Skill) return; + Debug.WriteToDebug("Enabling skill: " + randomSkill.Skill); + Instance?.SkillAction(randomSkill.Skill.ToString(), "EnableSkill", [playerTarget]); } }, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE); diff --git a/jRandomSkills - SRC Files/src/player/skills/C4Camouflage.cs b/jRandomSkills - SRC Files/src/player/skills/C4Camouflage.cs index 3f8bd4f..09041f3 100644 --- a/jRandomSkills - SRC Files/src/player/skills/C4Camouflage.cs +++ b/jRandomSkills - SRC Files/src/player/skills/C4Camouflage.cs @@ -159,6 +159,8 @@ namespace src.player.skills invisiblePlayers.TryAdd(player.Index, 0); SkillUtils.SetPlayerInvisibility(player, .5f); + + SkillUtils.ForceFullUpdateToAll(); } public static void DisableSkill(CCSPlayerController player) diff --git a/jRandomSkills - SRC Files/src/player/skills/Ghost.cs b/jRandomSkills - SRC Files/src/player/skills/Ghost.cs index 5535eb3..96f7b56 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Ghost.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Ghost.cs @@ -106,6 +106,8 @@ namespace src.player.skills if (EntityManager.GetPlayerEntities(player.Index, "empty_prop").Count == 0) CreatePlayerPosProp(player); + + SkillUtils.ForceFullUpdateToAll(); } private static void CreatePlayerPosProp(CCSPlayerController player) diff --git a/jRandomSkills - SRC Files/src/player/skills/Glaz.cs b/jRandomSkills - SRC Files/src/player/skills/Glaz.cs index 82901ca..aa5fccd 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Glaz.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Glaz.cs @@ -111,6 +111,8 @@ namespace src.player.skills SkillUtils.TryGiveWeapon(player, CsItem.SmokeGrenade); SkillUtils.UpdateGrenadeCount(player, CsItem.SmokeGrenade, grenadeLimit); + + SkillUtils.ForceFullUpdateToAll(); } public static void DisableSkill(CCSPlayerController player) diff --git a/jRandomSkills - SRC Files/src/player/skills/Jackal.cs b/jRandomSkills - SRC Files/src/player/skills/Jackal.cs index 716d1c6..ae83c78 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Jackal.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Jackal.cs @@ -145,6 +145,8 @@ namespace src.player.skills mainSkillTimer ??= Instance.AddTimer(2.5f, () => UpdateAllTrails(), CounterStrikeSharp.API.Modules.Timers.TimerFlags.REPEAT | CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE); + + SkillUtils.ForceFullUpdateToAll(); } private static void UpdateAllTrails() diff --git a/jRandomSkills - SRC Files/src/player/skills/Jester.cs b/jRandomSkills - SRC Files/src/player/skills/Jester.cs index 2a23f48..7f2d029 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Jester.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Jester.cs @@ -53,18 +53,17 @@ namespace src.player.skills var jester = GetJesterInfo(player.Index); if (jester == null) return; + jester.Generation++; jester.Active = false; - if (jester.Timer != null) - { - jester.Timer.Kill(); - jester.Timer = null; - } + jester.Timer = null; + + jesters.TryRemove(player.Index, out _); Server.NextWorldUpdate(() => { SkillUtils.ResetPrintHTML(player); SetPlayerColor(player, true); - jesters.TryRemove(player.Index, out _); + }); } @@ -154,23 +153,37 @@ namespace src.player.skills uint playerIndex = player.Index; - jesters.TryAdd(playerIndex, new JesterInfo { - PlayerIndex = playerIndex, - Active = false, - Timer = Instance.AddTimer(wait, () => ChangeMode(playerIndex), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE) - }); + if (!jesters.TryGetValue(playerIndex, out var jester)) + { + jester = new JesterInfo + { + PlayerIndex = playerIndex, + Active = false, + Generation = 0 + }; + + jesters[playerIndex] = jester; + } + + jester.Generation++; + int generation = jester.Generation; + + jester.Timer = Instance.AddTimer(wait, () => + { + if (!jesters.TryGetValue(playerIndex, out var info)) + return; + + if (info.Generation != generation) + return; + + ChangeMode(playerIndex); + }, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE); } private static void ChangeMode(uint playerIndex, bool? forceActive = null) { if (!jesters.TryGetValue(playerIndex, out var jester)) return; - if (jester.Timer != null) - { - jester.Timer?.Kill(); - jester.Timer = null; - } - var player = Utilities.GetPlayerFromIndex((int)playerIndex); if (player == null || !player.IsValid || player.LifeState != (byte)LifeState_t.LIFE_ALIVE) { @@ -194,7 +207,19 @@ namespace src.player.skills var maxTime = SkillsInfo.GetValue(skillName, "maxTime"); float wait = (float)Instance.Random.NextDouble() * (maxTime - minTime) + minTime; - jester.Timer = Instance.AddTimer(wait, () => ChangeMode(playerIndex), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE); + jester.Generation++; + int generation = jester.Generation; + + jester.Timer = Instance.AddTimer(wait, () => + { + if (!jesters.TryGetValue(playerIndex, out var info)) + return; + + if (info.Generation != generation) + return; + + ChangeMode(playerIndex); + }, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE); } private static void SetPlayerColor(CCSPlayerController? player, bool forceDisable = false) @@ -205,9 +230,9 @@ namespace src.player.skills if (pawn == null || !pawn.IsValid) return; var jester = GetJesterInfo(player.Index); - if (jester == null) return; + if (!forceDisable && jester == null) return; - var color = jester.Active && !forceDisable ? Color.FromArgb(255, 128, 0, 128) : Color.FromArgb(255, 255, 255, 255); + var color = jester?.Active == true && !forceDisable ? Color.FromArgb(255, 128, 0, 128) : Color.FromArgb(255, 255, 255, 255); pawn.Render = color; Utilities.SetStateChanged(pawn, "CBaseModelEntity", "m_clrRender"); } @@ -249,6 +274,7 @@ namespace src.player.skills { public required uint PlayerIndex { get; set; } public bool Active { get; set; } = false; + public int Generation { get; set; } = 0; public Timer? Timer { get; set; } = null; } diff --git a/jRandomSkills - SRC Files/src/player/skills/Ninja.cs b/jRandomSkills - SRC Files/src/player/skills/Ninja.cs index 8c133d4..6e0c8a0 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Ninja.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Ninja.cs @@ -132,6 +132,8 @@ namespace src.player.skills if (EntityManager.GetPlayerEntities(player.Index, "empty_prop").Count == 0) CreatePlayerPosProp(player); + + SkillUtils.ForceFullUpdateToAll(); } public static void DisableSkill(CCSPlayerController player) diff --git a/jRandomSkills - SRC Files/src/player/skills/ThrowingKnife.cs b/jRandomSkills - SRC Files/src/player/skills/ThrowingKnife.cs index 4b0fd36..2f99271 100644 --- a/jRandomSkills - SRC Files/src/player/skills/ThrowingKnife.cs +++ b/jRandomSkills - SRC Files/src/player/skills/ThrowingKnife.cs @@ -127,6 +127,7 @@ namespace src.player.skills public static void EnableSkill(CCSPlayerController _) { Event.EnableTransmit(); + SkillUtils.ForceFullUpdateToAll(); } public static void DisableSkill(CCSPlayerController player) diff --git a/jRandomSkills - SRC Files/src/player/skills/Wallhack.cs b/jRandomSkills - SRC Files/src/player/skills/Wallhack.cs index 361498f..6aa0681 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Wallhack.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Wallhack.cs @@ -115,6 +115,8 @@ namespace src.player.skills if (glows.IsEmpty) SetGlowEffectForAll(); + + SkillUtils.ForceFullUpdateToAll(); } public static void DisableSkill(CCSPlayerController player) diff --git a/jRandomSkills - SRC Files/src/utils/CServerSideClient.cs b/jRandomSkills - SRC Files/src/utils/CServerSideClient.cs new file mode 100644 index 0000000..02701b6 --- /dev/null +++ b/jRandomSkills - SRC Files/src/utils/CServerSideClient.cs @@ -0,0 +1,107 @@ +// https://discord.com/channels/1160907911501991946/1508172390863994910/1508180670659166348 + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Memory; + +namespace jRandomSkills.src.utils +{ + + #region Native Structs + + [StructLayout(LayoutKind.Sequential)] + public unsafe struct CUtlMemory where T : unmanaged + { + public T* m_pMemory; + public int m_nAllocationCount; + public int m_nGrowSize; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe struct CUtlVector where T : unmanaged + { + public int m_Size; + public CUtlMemory m_Memory; + + public int Count => m_Size; + + public ref T Element(int index) + { + if (index < 0 || index >= m_Size) + throw new IndexOutOfRangeException(); + + if (m_Memory.m_pMemory == null) + throw new NullReferenceException("m_pMemory is null. The vector is empty or the GameData offset is outdated."); + + return ref m_Memory.m_pMemory[index]; + } + } + + #endregion + + #region Network Services + + public class INetworkServerService : NativeObject + { + private readonly VirtualFunctionWithReturn GetIGameServerFunc; + + public INetworkServerService() : base(NativeAPI.GetValveInterface(0, "NetworkServerService_001")) + { + GetIGameServerFunc = new VirtualFunctionWithReturn(Handle, GameData.GetOffset("INetworkServerService_GetIGameServer")); + } + + public INetworkGameServer GetIGameServer() + { + return new INetworkGameServer(GetIGameServerFunc.Invoke(Handle)); + } + } + + public unsafe class INetworkGameServer(nint ptr) : NativeObject(ptr) + { + private static readonly int SlotsOffset = GameData.GetOffset("INetworkGameServer_Slots"); + + private ref CUtlVector Slots => ref Unsafe.AsRef>((void*)(Handle + SlotsOffset)); + + public CServerSideClient? GetClientBySlot(int slot) + { + if (Handle == nint.Zero) + return null; + + if (slot < 0 || slot >= Slots.Count || Slots.m_Memory.m_pMemory == null) + return null; + + var ptr = Slots.Element(slot); + + if (ptr == nint.Zero) + return null; + + return new CServerSideClient(ptr); + } + } + + #endregion + + #region CServerSideClient + + public unsafe class CServerSideClient(nint ptr) : NativeObject(ptr) + { + private static readonly int m_nDeltaTick = GameData.GetOffset("CServerSideClient_m_nDeltaTick"); + + private ref T Field(int offset) where T : unmanaged + { + return ref Unsafe.AsRef((void*)(Handle + offset)); + } + + public int DeltaTick + { + get => Field(m_nDeltaTick); + set => Field(m_nDeltaTick) = value; + } + + public void ForceFullUpdate() => DeltaTick = -1; + } + + #endregion +} \ No newline at end of file diff --git a/jRandomSkills - SRC Files/src/utils/Config.cs b/jRandomSkills - SRC Files/src/utils/Config.cs index 9104558..21e54b1 100644 --- a/jRandomSkills - SRC Files/src/utils/Config.cs +++ b/jRandomSkills - SRC Files/src/utils/Config.cs @@ -79,6 +79,7 @@ namespace src.utils public bool SummaryAfterTheRound { get; set; } public bool EnableBotSkills { get; set; } public bool EnableBotKickDebug { get; set; } + public bool EnableFullForceUpdate { get; set; } public bool DebugMode { get; set; } public bool PerfMode { get; set; } public string? AlternativeSkillButton { get; set; } @@ -107,6 +108,7 @@ namespace src.utils SummaryAfterTheRound = true; EnableBotSkills = true; EnableBotKickDebug = false; + EnableFullForceUpdate = false; DebugMode = false; PerfMode = false; AlternativeSkillButton = null; diff --git a/jRandomSkills - SRC Files/src/utils/SkillUtils.cs b/jRandomSkills - SRC Files/src/utils/SkillUtils.cs index 32ffaee..a1de499 100644 --- a/jRandomSkills - SRC Files/src/utils/SkillUtils.cs +++ b/jRandomSkills - SRC Files/src/utils/SkillUtils.cs @@ -333,6 +333,79 @@ namespace src.utils return EntityManager.CreateTrackedTrigger(ownerPlayerIndex, name, radius, pos); } + public static void ForceFullUpdate(CCSPlayerController player, List<(uint PlayerIndex, QAngle LastAngle)>? batchList = null, INetworkGameServer? networkGameServer = null) + { + if (!Config.LoadedConfig.EnableFullForceUpdate) return; + if (player == null || !player.IsValid) return; + + var pawn = player.PlayerPawn?.Value; + if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) return; + + QAngle lastAngle = new(pawn.V_angle.X, pawn.V_angle.Y, pawn.V_angle.Z); + + networkGameServer ??= new INetworkServerService().GetIGameServer(); + + var client = networkGameServer.GetClientBySlot(player.Slot); + if (client == null) return; + + client.ForceFullUpdate(); + // Only skip the angle restore when the captured view is a spawn-time (0,0,0) placeholder; + // a genuine angle with a single zero component (e.g. yaw exactly 0) must still be restored. + if (lastAngle.X == 0 && lastAngle.Y == 0 && lastAngle.Z == 0) return; + + uint playerIndex = player.Index; + + if (batchList != null) + { + batchList.Add((playerIndex, lastAngle)); + return; + } + + jRandomSkills.Instance.AddTickTimer(3, () => + { + var target = Utilities.GetPlayerFromIndex((int)playerIndex); + if (target == null || !target.IsValid) return; + + var targetPawn = target.PlayerPawn?.Value; + if (targetPawn == null || !targetPawn.IsValid || targetPawn.AbsOrigin == null) return; + + targetPawn.Look(lastAngle); + }); + } + + private static int lastForceFullUpdateAll = int.MinValue; + + public static void ForceFullUpdateToAll() + { + if (!Config.LoadedConfig.EnableFullForceUpdate) return; + + int tickCount = Server.TickCount; + if (tickCount == lastForceFullUpdateAll) return; + + lastForceFullUpdateAll = tickCount; + var playersToRestore = new List<(uint PlayerIndex, QAngle LastAngle)>(); + + INetworkGameServer networkGameServer = new INetworkServerService().GetIGameServer(); + foreach (var player in Utilities.GetPlayers()) + ForceFullUpdate(player, playersToRestore, networkGameServer); + + if (playersToRestore.Count <= 0) return; + + jRandomSkills.Instance.AddTickTimer(3, () => + { + foreach (var item in playersToRestore) + { + var target = Utilities.GetPlayerFromIndex((int)item.PlayerIndex); + if (target == null || !target.IsValid) continue; + + var targetPawn = target.PlayerPawn?.Value; + if (targetPawn == null || !targetPawn.IsValid || targetPawn.AbsOrigin == null) continue; + + targetPawn.Look(item.LastAngle); + } + }); + } + public static bool SetHealth(CCSPlayerPawn? pawn, int newHealth, int? maxHealth = null) { if (pawn == null || !pawn.IsValid) diff --git a/jRandomSkills - Server Files/gamedata/jRandomSkills.gamedata.json b/jRandomSkills - Server Files/gamedata/jRandomSkills.gamedata.json index b4a5330..3e0bc0e 100644 --- a/jRandomSkills - Server Files/gamedata/jRandomSkills.gamedata.json +++ b/jRandomSkills - Server Files/gamedata/jRandomSkills.gamedata.json @@ -26,5 +26,23 @@ "windows": "48 89 5C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 55 48 8D 6C 24 ? 48 81 EC ? ? ? ? 48 8B DA 48 8B F1 48 8D 55", "linux": "55 48 89 E5 41 57 49 89 FF 48 89 F7 41 56 41 55 41 54 53 48 81 EC ? ? ? ? E8" } + }, + "CServerSideClient_m_nDeltaTick": { + "offsets": { + "windows": 348, + "linux": 348 + } + }, + "INetworkGameServer_Slots": { + "offsets": { + "windows": 584, + "linux": 584 + } + }, + "INetworkServerService_GetIGameServer": { + "offsets": { + "windows": 23, + "linux": 24 + } } } diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/WASDMenuAPI.dll b/jRandomSkills - Server Files/plugins/jRandomSkills/WASDMenuAPI.dll index a22e1b0..86a4447 100644 Binary files a/jRandomSkills - Server Files/plugins/jRandomSkills/WASDMenuAPI.dll and b/jRandomSkills - Server Files/plugins/jRandomSkills/WASDMenuAPI.dll differ diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/configs/config.json b/jRandomSkills - Server Files/plugins/jRandomSkills/configs/config.json index a343a83..03dbe24 100644 --- a/jRandomSkills - Server Files/plugins/jRandomSkills/configs/config.json +++ b/jRandomSkills - Server Files/plugins/jRandomSkills/configs/config.json @@ -7,6 +7,7 @@ "SummaryAfterTheRound": true, "EnableBotSkills": true, "EnableBotKickDebug": false, + "EnableFullForceUpdate": false, "DebugMode": false, "PerfMode": false, "AlternativeSkillButton": null, diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/jRandomSkills.dll b/jRandomSkills - Server Files/plugins/jRandomSkills/jRandomSkills.dll index cfc8ef6..8e09384 100644 Binary files a/jRandomSkills - Server Files/plugins/jRandomSkills/jRandomSkills.dll and b/jRandomSkills - Server Files/plugins/jRandomSkills/jRandomSkills.dll differ