-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
348 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
using System.Runtime.InteropServices; | ||
using CounterStrikeSharp.API; | ||
using CounterStrikeSharp.API.Core; | ||
using CounterStrikeSharp.API.Modules.Memory; | ||
|
||
namespace HidePlayers; | ||
|
||
[StructLayout(LayoutKind.Sequential)] | ||
struct CUtlMemory | ||
{ | ||
public unsafe nint* m_pMemory; | ||
public int m_nAllocationCount; | ||
public int m_nGrowSize; | ||
} | ||
|
||
[StructLayout(LayoutKind.Sequential)] | ||
struct CUtlVector | ||
{ | ||
public unsafe nint this[int index] | ||
{ | ||
get => this.m_Memory.m_pMemory[index]; | ||
set => this.m_Memory.m_pMemory[index] = value; | ||
} | ||
|
||
public int m_iSize; | ||
public CUtlMemory m_Memory; | ||
|
||
public nint Element(int index) => this[index]; | ||
} | ||
|
||
// thx https://discord.com/channels/1160907911501991946/1175947333880524962/1231712355784851497 | ||
|
||
class INetworkServerService : NativeObject | ||
{ | ||
private readonly VirtualFunctionWithReturn<nint, nint> GetIGameServerFunc; | ||
|
||
public INetworkServerService() : base(NativeAPI.GetValveInterface(0, "NetworkServerService_001")) | ||
{ | ||
this.GetIGameServerFunc = new VirtualFunctionWithReturn<nint, nint>(this.Handle, GameData.GetOffset("INetworkServerService_GetIGameServer")); | ||
} | ||
|
||
public INetworkGameServer GetIGameServer() | ||
{ | ||
return new INetworkGameServer(this.GetIGameServerFunc.Invoke(this.Handle)); | ||
} | ||
} | ||
|
||
public class INetworkGameServer : NativeObject | ||
{ | ||
private static int SlotsOffset = GameData.GetOffset("INetworkGameServer_Slots"); | ||
|
||
private CUtlVector Slots; | ||
|
||
public INetworkGameServer(nint ptr) : base(ptr) | ||
{ | ||
this.Slots = Marshal.PtrToStructure<CUtlVector>(base.Handle + SlotsOffset); | ||
} | ||
|
||
public CServerSideClient? GetClientBySlot(int playerSlot) | ||
{ | ||
if (playerSlot >= 0 && playerSlot < this.Slots.m_iSize) | ||
return this.Slots[playerSlot] == IntPtr.Zero ? null : new CServerSideClient(this.Slots[playerSlot]); | ||
|
||
return null; | ||
} | ||
} | ||
|
||
public class CServerSideClient : NativeObject | ||
{ | ||
private static int m_nForceWaitForTick = GameData.GetOffset("CServerSideClient_m_nForceWaitForTick"); | ||
|
||
public unsafe int ForceWaitForTick | ||
{ | ||
get { return *(int*)(base.Handle + m_nForceWaitForTick); } | ||
set { *(int*)(base.Handle + m_nForceWaitForTick) = value; } | ||
} | ||
|
||
public CServerSideClient(nint ptr) : base(ptr) | ||
{ } | ||
|
||
public void ForceFullUpdate() | ||
{ | ||
this.ForceWaitForTick = -1; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net8.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.264" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<Content Include="lang\**"> | ||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
</Content> | ||
<Content Include="gamedata\**"> | ||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
</Content> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
using System.Runtime.InteropServices; | ||
using CounterStrikeSharp.API; | ||
using CounterStrikeSharp.API.Core; | ||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace HidePlayers; | ||
|
||
public sealed class Plugin : BasePlugin, IPluginConfig<PluginConfig> | ||
{ | ||
public override string ModuleName => "HidePlayers"; | ||
public override string ModuleAuthor => "xstage"; | ||
public override string ModuleVersion => "1.0.0"; | ||
public override string ModuleDescription => "Plugin uses code borrowed from CS2Fixes / cs2kz-metamod / hl2sdk"; | ||
|
||
public PluginConfig Config { get; set; } = new(); | ||
private readonly bool[] _hide = new bool[65]; | ||
private readonly CSPlayerState[] _oldPlayerState = new CSPlayerState[65]; | ||
private readonly INetworkServerService networkServerService = new(); | ||
|
||
private static readonly MemoryFunctionVoid<nint, nint, int, nint, int, short, int, bool> CheckTransmit = new(GameData.GetSignature("CheckTransmit")); | ||
private static readonly MemoryFunctionVoid<nint, CSPlayerState> StateTransition = new(GameData.GetSignature("StateTransition")); | ||
|
||
#region CCheckTransmitInfo | ||
[StructLayout(LayoutKind.Sequential)] | ||
public struct CCheckTransmitInfo | ||
{ | ||
public CFixedBitVecBase m_pTransmitEntity; | ||
}; | ||
|
||
[StructLayout(LayoutKind.Sequential)] | ||
public unsafe struct CFixedBitVecBase | ||
{ | ||
private const int LOG2_BITS_PER_INT = 5; | ||
private const int MAX_EDICT_BITS = 14; | ||
private const int BITS_PER_INT = 32; | ||
private const int MAX_EDICTS = 1 << MAX_EDICT_BITS; | ||
|
||
private uint* m_Ints; | ||
|
||
public void Clear(int bitNum) | ||
{ | ||
if (!(bitNum >= 0 && bitNum < MAX_EDICTS)) | ||
return; | ||
|
||
uint* pInt = m_Ints + BitVec_Int(bitNum); | ||
*pInt &= ~(uint)BitVec_Bit(bitNum); | ||
} | ||
|
||
public bool IsBitSet(int bitNum) | ||
{ | ||
if (!(bitNum >= 0 && bitNum < MAX_EDICTS)) | ||
return false; | ||
|
||
uint* pInt = m_Ints + BitVec_Int(bitNum); | ||
return ( *pInt & BitVec_Bit( bitNum ) ) != 0 ; | ||
} | ||
|
||
private int BitVec_Int(int bitNum) => bitNum >> LOG2_BITS_PER_INT; | ||
private int BitVec_Bit(int bitNum) => 1 << ((bitNum) & (BITS_PER_INT - 1)); | ||
} | ||
#endregion | ||
|
||
public override void Load(bool hotReload) | ||
{ | ||
StateTransition.Hook(Hook_StateTransition, HookMode.Post); | ||
CheckTransmit.Hook(Hook_CheckTransmit, HookMode.Post); | ||
|
||
RegisterEventHandler<EventPlayerDisconnect>((@event, info) => | ||
{ | ||
if (@event.Userid is {Index: uint index}) | ||
{ | ||
_hide[index] = false; | ||
} | ||
|
||
return HookResult.Continue; | ||
}); | ||
|
||
AddCommand(Config.Command, "Hide players models", (player, info) => | ||
{ | ||
player?.PrintToChat(Localizer["Player.Hide", Localizer["Plugin.Tag"], (_hide[player.Index] ^= true) ? Localizer["Plugin.Enable"] : Localizer["Plugin.Disable"]]); | ||
}); | ||
} | ||
|
||
public override void Unload(bool hotReload) | ||
{ | ||
StateTransition.Unhook(Hook_StateTransition, HookMode.Post); | ||
CheckTransmit.Unhook(Hook_CheckTransmit, HookMode.Post); | ||
} | ||
|
||
private void ForceFullUpdate(CCSPlayerController? player) | ||
{ | ||
if (player is null || !player.IsValid) return; | ||
|
||
var networkGameServer = networkServerService.GetIGameServer(); | ||
networkGameServer.GetClientBySlot(player.Slot)?.ForceFullUpdate(); | ||
|
||
player.PlayerPawn.Value?.Teleport(null, player.PlayerPawn.Value.EyeAngles, null); | ||
} | ||
|
||
private unsafe HookResult Hook_CheckTransmit(DynamicHook hook) | ||
{ | ||
nint* ppInfoList = (nint*)hook.GetParam<nint>(1); | ||
int infoCount = hook.GetParam<int>(2); | ||
|
||
for (int i = 0; i < infoCount; i++) | ||
{ | ||
nint pInfo = ppInfoList[i]; | ||
byte slot = *(byte*)(pInfo + GameData.GetOffset("CheckTransmitPlayerSlot")); | ||
|
||
var player = Utilities.GetPlayerFromSlot(slot); | ||
var info = Marshal.PtrToStructure<CCheckTransmitInfo>(pInfo); | ||
|
||
if (player == null || player.PlayerPawn.Value == null || player.IsHLTV) | ||
continue; | ||
|
||
foreach (var target in Utilities.GetPlayers() | ||
.Where(p => p != null && p.PlayerPawn.Value != null)) | ||
{ | ||
var pawn = target.PlayerPawn.Value!; | ||
|
||
#region fix client crash | ||
if (target.Slot == slot && ((LifeState_t)pawn.LifeState != LifeState_t.LIFE_DEAD || pawn.PlayerState.HasFlag(CSPlayerState.STATE_DEATH_ANIM))) | ||
continue; | ||
|
||
if (player.PlayerPawn.Value.PlayerState.HasFlag(CSPlayerState.STATE_DORMANT) && target.Slot != slot) | ||
continue; | ||
|
||
if ((LifeState_t)pawn.LifeState != LifeState_t.LIFE_ALIVE) | ||
{ | ||
info.m_pTransmitEntity.Clear((int)pawn.Index); | ||
continue; | ||
} | ||
#endregion | ||
|
||
if (_hide[player.Index] && (Config.Hidden.Equals("@enemy") && player.Team != target.Team || Config.Hidden.Equals("@team") && player.Team == target.Team || Config.Hidden.Equals("@all"))) | ||
{ | ||
info.m_pTransmitEntity.Clear((int)pawn.Index); | ||
} | ||
} | ||
} | ||
|
||
return HookResult.Continue; | ||
} | ||
|
||
private HookResult Hook_StateTransition(DynamicHook hook) | ||
{ | ||
var pawn = new CCSPlayerPawn(hook.GetParam<nint>(0)); | ||
|
||
if (!pawn.IsValid) return HookResult.Continue; | ||
|
||
var player = pawn.OriginalController.Value; | ||
var state = hook.GetParam<CSPlayerState>(1); | ||
|
||
if (player is null) return HookResult.Continue; | ||
|
||
if (_oldPlayerState[player.Index] != CSPlayerState.STATE_OBSERVER_MODE && state == CSPlayerState.STATE_OBSERVER_MODE || | ||
_oldPlayerState[player.Index] == CSPlayerState.STATE_OBSERVER_MODE && state != CSPlayerState.STATE_OBSERVER_MODE) | ||
{ | ||
ForceFullUpdate(player); | ||
} | ||
|
||
_oldPlayerState[player.Index] = state; | ||
|
||
return HookResult.Continue; | ||
} | ||
|
||
public void OnConfigParsed(PluginConfig config) | ||
{ | ||
if (config.Version < Config.Version) | ||
{ | ||
Logger.LogWarning("Update plugin config. New version: {Version}", Config.Version); | ||
} | ||
|
||
Config = config; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
using CounterStrikeSharp.API.Core; | ||
|
||
namespace HidePlayers; | ||
|
||
public class PluginConfig : IBasePluginConfig | ||
{ | ||
public string Command { get; set; } = "css_hidemodels"; | ||
public string Hidden { get; set; } = "@all"; | ||
|
||
public int Version { get; set; } = 1; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
{ | ||
"StateTransition": { | ||
"signatures": { | ||
"library": "server", | ||
"windows": "48 83 EC 28 48 89 74 24 ? 8B F2 48 89 7C 24 ? 48 8B F9", | ||
"linux": "55 48 89 E5 41 55 41 54 41 89 F4 53 48 89 FB 48 83 EC ? 39 B7" | ||
} | ||
}, | ||
"CheckTransmit": { | ||
"signatures": { | ||
"library": "server", | ||
"windows": "48 8B C4 4C 89 48 ? 44 89 40 ? 48 89 50 ? 48 89 48 ? 55", | ||
"linux": "55 48 89 E5 41 57 49 89 CF 41 56 41 55 41 54 53 48 81 EC" | ||
} | ||
}, | ||
"CheckTransmitPlayerSlot": { | ||
"offsets": { | ||
"windows": 584, | ||
"linux": 584 | ||
} | ||
}, | ||
"INetworkServerService_GetIGameServer": { | ||
"offsets": { | ||
"windows": 23, | ||
"linux": 24 | ||
} | ||
}, | ||
"INetworkGameServer_Slots": { | ||
"offsets": { | ||
"windows": 624, | ||
"linux": 640 | ||
} | ||
}, | ||
"CServerSideClient_m_nForceWaitForTick": { | ||
"offsets": { | ||
"windows": 308, | ||
"linux": 324 | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"Plugin.Tag": "[Hide]", | ||
"Plugin.Enable": "{Green}turned on{Default}", | ||
"Plugin.Disable": "{LightRed}turned off{Default}", | ||
"Player.Hide": "{Gold}{0}{Default} You {1} hide players!" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"Plugin.Tag": "[Hide]", | ||
"Plugin.Enable": "{Green}включили{Default}", | ||
"Plugin.Disable": "{LightRed}выключили{Default}", | ||
"Player.Hide": "{Gold}{0}{Default} Вы {1} скрытие игроков!" | ||
} |