Skip to content

Commit

Permalink
Merge branch 'main' into virtual-hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
KillStr3aK authored Nov 6, 2024
2 parents 96489f3 + 1f904a5 commit 8d60cf9
Show file tree
Hide file tree
Showing 18 changed files with 499 additions and 49 deletions.
12 changes: 12 additions & 0 deletions configs/addons/counterstrikesharp/gamedata/gamedata.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,12 @@
"linux": "55 48 89 E5 41 57 41 56 41 55 41 54 49 89 FC 53 48 81 EC 88 00 00 00 48 8D 05 ? ? ? ?"
}
},
"CCSGameRules_FindPickerEntity": {
"offsets": {
"windows": 27,
"linux": 28
}
},
"UTIL_CreateEntityByName": {
"signatures": {
"library": "server",
Expand Down Expand Up @@ -253,5 +259,11 @@
"windows": 2,
"linux": 0
}
},
"CheckTransmitPlayerSlot": {
"offsets": {
"windows": 584,
"linux": 584
}
}
}
2 changes: 2 additions & 0 deletions examples/WithCheckTransmit/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# With CheckTransmit
This example shows how to work with the `CheckTransmit` listener.
13 changes: 13 additions & 0 deletions examples/WithCheckTransmit/WithCheckTransmit.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\managed\CounterStrikeSharp.API\CounterStrikeSharp.API.csproj" />
</ItemGroup>

</Project>
115 changes: 115 additions & 0 deletions examples/WithCheckTransmit/WithCheckTransmitPlugin.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes;

namespace WithCheckTransmit;

[MinimumApiVersion(276)]
public class WithCheckTransmitPlugin : BasePlugin
{
public override string ModuleName => "Example: With CheckTransmit";
public override string ModuleVersion => "1.0.0";
public override string ModuleAuthor => "CounterStrikeSharp & Contributors";
public override string ModuleDescription => "A simple plugin that uses the CheckTransmit listener!";

private Dictionary<int, bool> ShouldSeeDoors = new Dictionary<int, bool>();

public override void Load(bool hotReload)
{
// This command is related to the following example.
AddCommand("nodoors", "Toggle door transmit", (player, info) =>
{
if (player == null)
return;

if (ShouldSeeDoors.ContainsKey(player.Slot))
{
ShouldSeeDoors[player.Slot] = !ShouldSeeDoors[player.Slot];
} else
{
ShouldSeeDoors.Add(player.Slot, false);
}

info.ReplyToCommand($"You should {(ShouldSeeDoors[player.Slot] ? "see" : "not see")} doors");
});

// In this example, we will hide every door for players that have enabled the option with the command 'nodoors'
RegisterListener<Listeners.CheckTransmit>((CCheckTransmitInfoList infoList) =>
{
// Get the list of the currently available doors (prop_door_rotating)
IEnumerable<CPropDoorRotating> doors = Utilities.FindAllEntitiesByDesignerName<CPropDoorRotating>("prop_door_rotating");

// Do nothing if there is none.
if (!doors.Any())
return;

// Go through every received info
foreach ((CCheckTransmitInfo info, CCSPlayerController? player) in infoList)
{
// If no player is found, we can continue
if (player == null)
continue;

// Otherwise, lets do the work:

// Check if we should clear or not:

// If we have no data saved for this player, then we should not continue
if (!ShouldSeeDoors.ContainsKey(player.Slot))
continue;

// If this value is true, then this player should see doors
if (ShouldSeeDoors[player.Slot])
continue;

// Otherwise, lets remove the door entity indexes from the info list so they won't be transmitted
foreach (CPropDoorRotating door in doors)
{
info.TransmitEntities.Remove(door);
}

// NOTE: this is a barebone example, saving data and doing sanity checks is up to you.
}
});

// In this example, we will hide other players in the same team as the player.
// NOTE: 'Hiding' players requires extra work to do, killing non-transmitted players results in crash.
RegisterListener<Listeners.CheckTransmit>((CCheckTransmitInfoList infoList) =>
{
// Get the list of the current players, we only work with this value later on
List<CCSPlayerController> players = Utilities.GetPlayers();

// Go through every received info
foreach ((CCheckTransmitInfo info, CCSPlayerController? player) in infoList)
{
// If no player is found, we can continue
if (player == null)
continue;

// Otherwise, lets do the work:

// as an example, lets hide everyone for this player who is in the same team.
IEnumerable<CCSPlayerController> targetPlayers = players.Where(p =>
// is the player and its pawn valid
p.IsValid && p.Pawn.IsValid &&

// we shouldn't hide ourselves
p.Slot != player.Slot &&

// is the player is in the same team
p.Team == player.Team &&

// is alive
p.PlayerPawn.Value?.LifeState == (byte)LifeState_t.LIFE_ALIVE
);

foreach (CCSPlayerController targetPlayer in targetPlayers)
{
// Calling 'Remove' will clear the entity index of the target player pawn from the transmission list
// so it won't be transmitted for the 'player'.
info.TransmitEntities.Remove(targetPlayer.Pawn);
}
}
});
}
}
9 changes: 8 additions & 1 deletion managed/CounterStrikeSharp.API/Core/Listeners.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,5 +163,12 @@ public class Listeners {
/// <param name="manifest">Resource Manifest</param>
[ListenerName("OnServerPrecacheResources")]
public delegate void OnServerPrecacheResources(ResourceManifest manifest);

/// <summary>
/// Called when checking transmit on entities.
/// </summary>
/// <param name="infoList">Transmit info list</param>
[ListenerName("CheckTransmit")]
public delegate void CheckTransmit([CastFrom(typeof(nint))]CCheckTransmitInfoList infoList);
}
}
}
32 changes: 31 additions & 1 deletion managed/CounterStrikeSharp.API/Core/Model/CCSGameRules.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

using CounterStrikeSharp.API.Modules.Entities.Constants;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Utils;

namespace CounterStrikeSharp.API.Core;

Expand All @@ -28,4 +29,33 @@ public void TerminateRound(float delay, RoundEndReason roundEndReason)
{
VirtualFunctions.TerminateRound(Handle, roundEndReason, delay, 0, 0);
}
}

public T? FindPickerEntity<T>(CBasePlayerController player)
where T : CBaseEntity
{
VirtualFunctionWithReturn<CCSGameRules, CBasePlayerController, CBaseEntity?> CCSGameRules_FindPickerEntity = new (Handle, GameData.GetOffset("CCSGameRules_FindPickerEntity"));
CBaseEntity? entity = CCSGameRules_FindPickerEntity.Invoke(this, player);

if (entity == null || !entity.IsValid)
{
return null;
}

return entity.As<T>();
}

public CCSPlayerController? GetClientAimTarget(CCSPlayerController player)
{
CCSPlayerPawn? pawn = FindPickerEntity<CCSPlayerPawn>(player);

if (pawn == null || !pawn.IsValid)
return null;

if (pawn.DesignerName == "player")
{
return pawn.OriginalController.Value;
}

return null;
}
}
106 changes: 106 additions & 0 deletions managed/CounterStrikeSharp.API/Core/Model/CCheckTransmitInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* This file is part of CounterStrikeSharp.
* CounterStrikeSharp is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CounterStrikeSharp is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
*/

using System.Collections;
using System.Runtime.InteropServices;

namespace CounterStrikeSharp.API.Core
{
[StructLayout(LayoutKind.Explicit)]
public struct CCheckTransmitInfo
{
/// <summary>
/// Entity n is already marked for transmission
/// </summary>
[FieldOffset(0x0)]
public CFixedBitVecBase TransmitEntities;

/// <summary>
/// Entity n is always send even if not in PVS (HLTV and Replay only)
/// </summary>
[FieldOffset(0x8)]
public CFixedBitVecBase TransmitAlways;
};

public sealed class CCheckTransmitInfoList : NativeObject, IReadOnlyList<(CCheckTransmitInfo info, CCSPlayerController? player)>
{
private int CheckTransmitPlayerSlotOffset = GameData.GetOffset("CheckTransmitPlayerSlot");

private unsafe nint* Inner => (nint*)base.Handle;

public unsafe int Count { get => (int)(*(this.Inner + 1)); }

public unsafe CCheckTransmitInfoList(IntPtr pointer) : base(pointer)
{ }

/// <summary>
/// Get transmit info for the given index.
/// </summary>
/// <param name="index">Index of the info you want to retrieve from the list, should be between 0 and '<see cref="Count"/>' - 1</param>
/// <returns></returns>
public (CCheckTransmitInfo info, CCSPlayerController? player) this[int index]
{
get
{
var (transmit, slot) = this.Get(index);
CCSPlayerController? player = Utilities.GetPlayerFromSlot(slot);
return (transmit, player);
}
}

/// <summary>
/// Get transmit info for the given index.
/// </summary>
/// <param name="index">Index of the info you want to retrieve from the list, should be between 0 and '<see cref="Count"/>' - 1</param>
/// <returns></returns>
private unsafe (CCheckTransmitInfo, int) Get(int index)
{
if (index < 0 || index >= this.Count)
{
throw new ArgumentOutOfRangeException("index");
}

// 'base.Handle' holds the pointer for our 'CCheckTransmitInfoList' wrapper class

// Get the pointer to the array of 'CCheckTransmitInfo'
nint* infoListPtr = *(nint**)this.Inner; // Dereference 'Inner' to get the pointer to the array

// Access the specific 'CCheckTransmitInfo*'
nint infoPtr = *(infoListPtr + index);

// Retrieve the 'CCheckTransmitInfo' from the pointer
CCheckTransmitInfo info = Marshal.PtrToStructure<CCheckTransmitInfo>(infoPtr);

// Get player slot from the 'infoPtr' using the 'CheckTransmitPlayerSlotOffset' offset
int playerSlot = *(int*)((byte*)infoPtr + CheckTransmitPlayerSlotOffset);

return (info, playerSlot);
}

public IEnumerator<(CCheckTransmitInfo, CCSPlayerController?)> GetEnumerator()
{
for (int i = 0; i < this.Count; i++)
{
yield return this[i];
}
}

IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
}
Loading

0 comments on commit 8d60cf9

Please sign in to comment.