Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
qstage authored Sep 15, 2024
1 parent b9887e8 commit 84d2a1b
Show file tree
Hide file tree
Showing 7 changed files with 348 additions and 0 deletions.
85 changes: 85 additions & 0 deletions ForceFullUpdate.cs
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;
}
}
23 changes: 23 additions & 0 deletions HidePlayers.csproj
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>
177 changes: 177 additions & 0 deletions Plugin.cs
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;
}
}
11 changes: 11 additions & 0 deletions PluginConfig.cs
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;
}
40 changes: 40 additions & 0 deletions gamedata/HidePlayers.json
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
}
}
}
6 changes: 6 additions & 0 deletions lang/en.json
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!"
}
6 changes: 6 additions & 0 deletions lang/ru.json
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} скрытие игроков!"
}

0 comments on commit 84d2a1b

Please sign in to comment.