From ec215259376c3071bd4e4768df264447af635378 Mon Sep 17 00:00:00 2001 From: Grovre <50428844+Grovre@users.noreply.github.com> Date: Mon, 20 Nov 2023 10:28:27 -0600 Subject: [PATCH 1/6] Tuple syntax --- CS2Internal/Main.cs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/CS2Internal/Main.cs b/CS2Internal/Main.cs index 2e45dd1..c0d8c7c 100644 --- a/CS2Internal/Main.cs +++ b/CS2Internal/Main.cs @@ -27,27 +27,27 @@ public abstract unsafe class Main { "brasL", 14 }, { "handR", 11 }, { "handL", 16 }, - { "cock", 0 }, + { "cock", 0 }, // nice { "kneesR", 23 }, { "kneesL", 26 }, { "feetR", 24 }, { "feetL", 27 } }; - private static readonly List> Connections = new() + private static readonly List<(string Bone1, string Bone2)> Connections = new() { - Tuple.Create("cou", "head"), - Tuple.Create("cou", "shoulderR"), - Tuple.Create("cou", "shoulderL"), - Tuple.Create("brasL", "shoulderL"), - Tuple.Create("brasR", "shoulderR"), - Tuple.Create("brasR", "handR"), - Tuple.Create("brasL", "handL"), - Tuple.Create("cou", "cock"), - Tuple.Create("kneesR", "cock"), - Tuple.Create("kneesL", "cock"), - Tuple.Create("kneesL", "feetL"), - Tuple.Create("kneesR", "feetR") + ("cou", "head"), + ("cou", "shoulderR"), + ("cou", "shoulderL"), + ("brasL", "shoulderL"), + ("brasR", "shoulderR"), + ("brasR", "handR"), + ("brasL", "handL"), + ("cou", "cock"), + ("kneesR", "cock"), + ("kneesL", "cock"), + ("kneesL", "feetL"), + ("kneesR", "feetR") }; [UnmanagedCallersOnly(EntryPoint = "DllMain", CallConvs = new[] { typeof(CallConvStdcall) })] @@ -111,8 +111,8 @@ private static void UserInterface() foreach (var connection in Connections) { - var bone1 = Bones[connection.Item1]; - var bone2 = Bones[connection.Item2]; + var bone1 = Bones[connection.Bone1]; + var bone2 = Bones[connection.Bone2]; var bone1Pos = *(Vector3*)(entity.SceneNode->ModalState.BoneArray + bone1 * 32); var bone2Pos = *(Vector3*)(entity.SceneNode->ModalState.BoneArray + bone2 * 32); From af92262cce300c3c6e7fd5ccab478f282441d9b5 Mon Sep 17 00:00:00 2001 From: Grovre <50428844+Grovre@users.noreply.github.com> Date: Mon, 20 Nov 2023 10:43:00 -0600 Subject: [PATCH 2/6] Decouple boner information from Main.cs --- CS2Internal/Boners.cs | 36 ++++++++++++++++++++++++++++++++++++ CS2Internal/Main.cs | 40 +++------------------------------------- 2 files changed, 39 insertions(+), 37 deletions(-) create mode 100644 CS2Internal/Boners.cs diff --git a/CS2Internal/Boners.cs b/CS2Internal/Boners.cs new file mode 100644 index 0000000..19a386d --- /dev/null +++ b/CS2Internal/Boners.cs @@ -0,0 +1,36 @@ +namespace CS2Internal; + +public static class Boners +{ + public static readonly Dictionary BoneOffsetMap = new() + { + { "head", 6 }, + { "cou", 5 }, + { "shoulderR", 8 }, + { "shoulderL", 13 }, + { "brasR", 9 }, + { "brasL", 14 }, + { "handR", 11 }, + { "handL", 16 }, + { "cock", 0 }, // nice + { "kneesR", 23 }, + { "kneesL", 26 }, + { "feetR", 24 }, + { "feetL", 27 } + }; + + public static readonly (string Bone1, string Bone2)[] BoneConnections = { + ("cou", "head"), + ("cou", "shoulderR"), + ("cou", "shoulderL"), + ("brasL", "shoulderL"), + ("brasR", "shoulderR"), + ("brasR", "handR"), + ("brasL", "handL"), + ("cou", "cock"), + ("kneesR", "cock"), + ("kneesL", "cock"), + ("kneesL", "feetL"), + ("kneesR", "feetR") + }; +} \ No newline at end of file diff --git a/CS2Internal/Main.cs b/CS2Internal/Main.cs index c0d8c7c..302939c 100644 --- a/CS2Internal/Main.cs +++ b/CS2Internal/Main.cs @@ -17,39 +17,6 @@ public abstract unsafe class Main private static readonly bool IsRunning = true; private static readonly List EntityList = new(); - private static readonly Dictionary Bones = new() - { - { "head", 6 }, - { "cou", 5 }, - { "shoulderR", 8 }, - { "shoulderL", 13 }, - { "brasR", 9 }, - { "brasL", 14 }, - { "handR", 11 }, - { "handL", 16 }, - { "cock", 0 }, // nice - { "kneesR", 23 }, - { "kneesL", 26 }, - { "feetR", 24 }, - { "feetL", 27 } - }; - - private static readonly List<(string Bone1, string Bone2)> Connections = new() - { - ("cou", "head"), - ("cou", "shoulderR"), - ("cou", "shoulderL"), - ("brasL", "shoulderL"), - ("brasR", "shoulderR"), - ("brasR", "handR"), - ("brasL", "handL"), - ("cou", "cock"), - ("kneesR", "cock"), - ("kneesL", "cock"), - ("kneesL", "feetL"), - ("kneesR", "feetR") - }; - [UnmanagedCallersOnly(EntryPoint = "DllMain", CallConvs = new[] { typeof(CallConvStdcall) })] private static bool DllMain(IntPtr hModule, uint ulReasonForCall, IntPtr lpReserved) { @@ -66,7 +33,6 @@ private static bool DllMain(IntPtr hModule, uint ulReasonForCall, IntPtr lpReser return true; } - private static void UserInterface() { ImGui.Begin("Overlay", @@ -109,10 +75,10 @@ private static void UserInterface() .AddRect(topLeft, bottomRight, ImGui.ColorConvertFloat4ToU32(new Vector4(0, 0, 1, 1)), 5, ImDrawCornerFlags.All, 1); - foreach (var connection in Connections) + foreach (var connection in Boners.BoneConnections) { - var bone1 = Bones[connection.Bone1]; - var bone2 = Bones[connection.Bone2]; + var bone1 = Boners.BoneOffsetMap[connection.Bone1]; + var bone2 = Boners.BoneOffsetMap[connection.Bone2]; var bone1Pos = *(Vector3*)(entity.SceneNode->ModalState.BoneArray + bone1 * 32); var bone2Pos = *(Vector3*)(entity.SceneNode->ModalState.BoneArray + bone2 * 32); From 20721d2cb67c5e76e4c0bb4b19ba9385a4935baa Mon Sep 17 00:00:00 2001 From: Grovre <50428844+Grovre@users.noreply.github.com> Date: Mon, 20 Nov 2023 11:26:19 -0600 Subject: [PATCH 3/6] Make boner map frozen --- CS2Internal/Boners.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/CS2Internal/Boners.cs b/CS2Internal/Boners.cs index 19a386d..8ce0f10 100644 --- a/CS2Internal/Boners.cs +++ b/CS2Internal/Boners.cs @@ -1,8 +1,11 @@ -namespace CS2Internal; +using System.Collections.Frozen; +using System.Collections.Immutable; + +namespace CS2Internal; public static class Boners { - public static readonly Dictionary BoneOffsetMap = new() + public static readonly FrozenDictionary BoneOffsetMap = new Dictionary() { { "head", 6 }, { "cou", 5 }, @@ -17,9 +20,9 @@ public static class Boners { "kneesL", 26 }, { "feetR", 24 }, { "feetL", 27 } - }; + }.ToFrozenDictionary(); - public static readonly (string Bone1, string Bone2)[] BoneConnections = { + public static readonly ImmutableArray<(string Bone1, string Bone2)> BoneConnections = new[] { ("cou", "head"), ("cou", "shoulderR"), ("cou", "shoulderL"), @@ -32,5 +35,5 @@ public static readonly (string Bone1, string Bone2)[] BoneConnections = { ("kneesL", "cock"), ("kneesL", "feetL"), ("kneesR", "feetR") - }; + }.ToImmutableArray(); } \ No newline at end of file From 9bccab8082f8fc21d9c56c57a821b57b59e581cd Mon Sep 17 00:00:00 2001 From: Grovre <50428844+Grovre@users.noreply.github.com> Date: Mon, 20 Nov 2023 11:29:31 -0600 Subject: [PATCH 4/6] Make MainThread task long running --- CS2Internal/Main.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CS2Internal/Main.cs b/CS2Internal/Main.cs index 302939c..addee94 100644 --- a/CS2Internal/Main.cs +++ b/CS2Internal/Main.cs @@ -26,7 +26,8 @@ private static bool DllMain(IntPtr hModule, uint ulReasonForCall, IntPtr lpReser WinApi.AllocConsole(); Renderer.Init(UserInterface); - Task.Run(MainThread); + new TaskFactory(TaskCreationOptions.LongRunning, 0) + .StartNew(MainThread); break; } From 174f8c4810911b3558f9641099be2aeee5bdad8a Mon Sep 17 00:00:00 2001 From: Grovre <50428844+Grovre@users.noreply.github.com> Date: Mon, 20 Nov 2023 12:02:55 -0600 Subject: [PATCH 5/6] Extract entitylist to class, use DI in DllMain, avoid manual concurency solutions with concurrent collection --- CS2Internal/EntityList.cs | 62 +++++++++++++++++++++++++++++++++ CS2Internal/Main.cs | 73 +++++++-------------------------------- 2 files changed, 75 insertions(+), 60 deletions(-) create mode 100644 CS2Internal/EntityList.cs diff --git a/CS2Internal/EntityList.cs b/CS2Internal/EntityList.cs new file mode 100644 index 0000000..7923763 --- /dev/null +++ b/CS2Internal/EntityList.cs @@ -0,0 +1,62 @@ +using System.Collections; +using System.Collections.Concurrent; +using CS2Internal.Game; + +namespace CS2Internal; + +public class EntityList +{ + private readonly ConcurrentBag _entities = new(); + + public unsafe void UpdateEntityList() + { + if (Main.ModuleBaseClient == IntPtr.Zero) + return; + + var localPlayer = *(IntPtr*)(Main.ModuleBaseClient + client_dll.dwLocalPlayerPawn); + + if (localPlayer != IntPtr.Zero) + // TODO: Pass by ref + Main._localPlayerPawn = *(Entity**)localPlayer; + + _entities.Clear(); + + var tempEntityAddress = *(IntPtr*)(Main.ModuleBaseClient + client_dll.dwEntityList); + if (tempEntityAddress == IntPtr.Zero) return; + for (var i = 0; i < 32; i++) + { + var listEntry = *(IntPtr*)(tempEntityAddress + (((8 * (i & 0x7FFF)) >> 9) + 16)); + + if (listEntry == IntPtr.Zero) continue; + + var player = *(IntPtr*)(listEntry + 120 * (i & 0x1FF)); + + if (player == IntPtr.Zero) continue; + + var playerPawn = *(IntPtr*)(player + CCSPlayerController.m_hPlayerPawn); + + if (playerPawn == IntPtr.Zero) continue; + + var listEntry2 = *(IntPtr*)(tempEntityAddress + 0x8 * ((playerPawn & 0x7FFF) >> 9) + 16); + + if (listEntry2 == IntPtr.Zero) continue; + + var pCsPlayerPawn = *(IntPtr*)(listEntry2 + 120 * (playerPawn & 0x1FF)); + + if (pCsPlayerPawn == IntPtr.Zero) continue; + + var playerEntity = *(Entity*)pCsPlayerPawn; + + if (localPlayer == pCsPlayerPawn) + continue; + + if (playerEntity.Health is > 0 and <= 100) + _entities.Add(playerEntity); + } + } + + public IEnumerator GetEnumerator() + { + return _entities.GetEnumerator(); + } +} \ No newline at end of file diff --git a/CS2Internal/Main.cs b/CS2Internal/Main.cs index addee94..6956c8e 100644 --- a/CS2Internal/Main.cs +++ b/CS2Internal/Main.cs @@ -11,11 +11,10 @@ namespace CS2Internal; public abstract unsafe class Main { - private static readonly IntPtr ModuleBaseClient = WinApi.GetModuleHandle("client.dll"); + public static readonly IntPtr ModuleBaseClient = WinApi.GetModuleHandle("client.dll"); private static readonly IntPtr ModuleBaseEngine = WinApi.GetModuleHandle("engine2.dll"); - private static Entity* _localPlayerPawn; + public static Entity* _localPlayerPawn; private static readonly bool IsRunning = true; - private static readonly List EntityList = new(); [UnmanagedCallersOnly(EntryPoint = "DllMain", CallConvs = new[] { typeof(CallConvStdcall) })] private static bool DllMain(IntPtr hModule, uint ulReasonForCall, IntPtr lpReserved) @@ -23,18 +22,19 @@ private static bool DllMain(IntPtr hModule, uint ulReasonForCall, IntPtr lpReser switch (ulReasonForCall) { case 1: - + var entityList = new EntityList(); + WinApi.AllocConsole(); - Renderer.Init(UserInterface); + Renderer.Init(() => UserInterface(entityList)); new TaskFactory(TaskCreationOptions.LongRunning, 0) - .StartNew(MainThread); + .StartNew(() => MainThread(entityList)); break; } return true; } - private static void UserInterface() + private static void UserInterface(EntityList entityList) { ImGui.Begin("Overlay", ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove | @@ -46,8 +46,10 @@ private static void UserInterface() var viewMatrixPtr = (float*)(ModuleBaseClient + client_dll.dwViewMatrix); if (viewMatrixPtr == null) return; var matrix = new ReadOnlySpan(viewMatrixPtr, 16); - var tempEntityList = new List(EntityList); //copy to new list to avoid concurrency issues - foreach (var entity in tempEntityList) + // var tempEntityList = new List(EntityList); //copy to new list to avoid concurrency issues + // sike use concurrent collection to avoid concurrency issues + // https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentbag-1.getenumerator?view=net-7.0#remarks + foreach (var entity in entityList) { var engineWidth = 1920; var engineHeight = 1080; @@ -96,63 +98,14 @@ private static void UserInterface() //ImGui.GetWindowDrawList().AddRect(topLeft, bottomRight, ImGui.GetColorU32(ImGuiCol.ButtonHovered), 5); } - tempEntityList.Clear(); ImGui.End(); } - private static void UpdateEntityList() - { - if (ModuleBaseClient == IntPtr.Zero) - return; - - var localPlayer = *(IntPtr*)(ModuleBaseClient + client_dll.dwLocalPlayerPawn); - - if (localPlayer != IntPtr.Zero) - _localPlayerPawn = *(Entity**)localPlayer; - - EntityList.Clear(); - - var tempEntityAddress = *(IntPtr*)(ModuleBaseClient + client_dll.dwEntityList); - if (tempEntityAddress == IntPtr.Zero) return; - for (var i = 0; i < 32; i++) - { - var listEntry = *(IntPtr*)(tempEntityAddress + (((8 * (i & 0x7FFF)) >> 9) + 16)); - - if (listEntry == IntPtr.Zero) continue; - - var player = *(IntPtr*)(listEntry + 120 * (i & 0x1FF)); - - if (player == IntPtr.Zero) continue; - - var playerPawn = *(IntPtr*)(player + CCSPlayerController.m_hPlayerPawn); - - if (playerPawn == IntPtr.Zero) continue; - - var listEntry2 = *(IntPtr*)(tempEntityAddress + 0x8 * ((playerPawn & 0x7FFF) >> 9) + 16); - - if (listEntry2 == IntPtr.Zero) continue; - - var pCsPlayerPawn = *(IntPtr*)(listEntry2 + 120 * (playerPawn & 0x1FF)); - - if (pCsPlayerPawn == IntPtr.Zero) continue; - - var playerEntity = *(Entity*)pCsPlayerPawn; - - if (localPlayer == pCsPlayerPawn) - continue; - - if (playerEntity.Health is > 0 and <= 100) - EntityList.Add(playerEntity); - } - } - - - private static void MainThread() + private static void MainThread(EntityList entityList) { while (IsRunning) { - UpdateEntityList(); - + entityList.UpdateEntityList(); Thread.Sleep(100); } } From 2b257c0392321f787e15665a65146a6c8b560fe8 Mon Sep 17 00:00:00 2001 From: Grovre <50428844+Grovre@users.noreply.github.com> Date: Mon, 20 Nov 2023 12:05:15 -0600 Subject: [PATCH 6/6] Pass localPlayerPawn by reference --- CS2Internal/EntityList.cs | 5 ++--- CS2Internal/Main.cs | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/CS2Internal/EntityList.cs b/CS2Internal/EntityList.cs index 7923763..ab4d18b 100644 --- a/CS2Internal/EntityList.cs +++ b/CS2Internal/EntityList.cs @@ -8,7 +8,7 @@ public class EntityList { private readonly ConcurrentBag _entities = new(); - public unsafe void UpdateEntityList() + public unsafe void UpdateEntityList(ref Entity* localPlayerPawn) { if (Main.ModuleBaseClient == IntPtr.Zero) return; @@ -16,8 +16,7 @@ public unsafe void UpdateEntityList() var localPlayer = *(IntPtr*)(Main.ModuleBaseClient + client_dll.dwLocalPlayerPawn); if (localPlayer != IntPtr.Zero) - // TODO: Pass by ref - Main._localPlayerPawn = *(Entity**)localPlayer; + localPlayerPawn = *(Entity**)localPlayer; _entities.Clear(); diff --git a/CS2Internal/Main.cs b/CS2Internal/Main.cs index 6956c8e..ebfa40c 100644 --- a/CS2Internal/Main.cs +++ b/CS2Internal/Main.cs @@ -13,7 +13,7 @@ public abstract unsafe class Main { public static readonly IntPtr ModuleBaseClient = WinApi.GetModuleHandle("client.dll"); private static readonly IntPtr ModuleBaseEngine = WinApi.GetModuleHandle("engine2.dll"); - public static Entity* _localPlayerPawn; + private static Entity* _localPlayerPawn; private static readonly bool IsRunning = true; [UnmanagedCallersOnly(EntryPoint = "DllMain", CallConvs = new[] { typeof(CallConvStdcall) })] @@ -105,7 +105,7 @@ private static void MainThread(EntityList entityList) { while (IsRunning) { - entityList.UpdateEntityList(); + entityList.UpdateEntityList(ref _localPlayerPawn); Thread.Sleep(100); } }