mirror of
https://github.com/roflmuffin/CounterStrikeSharp.git
synced 2025-12-08 00:46:34 -08:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
466da1b193 | ||
|
|
ba860a92d2 | ||
|
|
461fc0ea67 | ||
|
|
6349c11d07 | ||
|
|
b2046b21c4 | ||
|
|
3c6be481c5 | ||
|
|
0a6fe0946d | ||
|
|
79297511e3 | ||
|
|
c6d3988902 | ||
|
|
8a063f4fb6 | ||
|
|
6cf124bfb6 | ||
|
|
1f904a52a7 | ||
|
|
32c99b2e49 | ||
|
|
5c9d38b2b0 | ||
|
|
b54f5c3dee | ||
|
|
761380dff6 | ||
|
|
71ae253e5e | ||
|
|
c2f212df51 | ||
|
|
ad7f7bd365 |
1
.github/workflows/publish-docs.yml
vendored
1
.github/workflows/publish-docs.yml
vendored
@@ -2,6 +2,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "48 89 5C 24 ? 48 89 74 24 ? 55 57 41 ? 41 ? 41 ? 48 ? ? ? ? 48 ? ? ? ? ? ? 4D ? ? 48",
|
||||
"linux": "55 48 89 E5 41 57 41 56 49 89 D6 41 55 49 89 CD 41 54 49 89 F4 53 48 89 FB 48 8D 3D"
|
||||
"linux": "55 48 89 E5 41 57 41 56 4D 89 C6 41 55 49 89 D5 41 54 49 89 F4"
|
||||
}
|
||||
},
|
||||
"UTIL_Remove": {
|
||||
@@ -84,10 +84,24 @@
|
||||
"CCSPlayer_WeaponServices_CanUse": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "48 89 5C 24 ? 48 89 6C 24 ? 56 57 41 56 48 83 EC 30 48 8B 01",
|
||||
"windows": "48 89 5C 24 ? 48 89 6C 24 ? 56 57 41 56 48 83 EC ? 48 8B 01 48 8B FA",
|
||||
"linux": "55 48 8D 15 ? ? ? ? 48 89 E5 41 55 49 89 FD 41 54 49 89 F4"
|
||||
}
|
||||
},
|
||||
"CCSPlayer_ItemServices_CanAcquire": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "44 89 44 24 ? 48 89 54 24 ? 48 89 4C 24 ? 55 56 57 41 54 41 55 41 56 41 57 48 8B EC",
|
||||
"linux": "55 48 89 E5 41 57 41 56 41 55 49 89 CD 41 54 53 48 83 EC"
|
||||
}
|
||||
},
|
||||
"GetCSWeaponDataFromKey": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "48 89 5C 24 ? 48 89 74 24 ? 57 48 83 EC ? 48 8B FA 8B F1 48 85 D2 0F 84",
|
||||
"linux": "55 48 89 E5 41 57 41 56 41 89 FE 41 55 41 54 45"
|
||||
}
|
||||
},
|
||||
"CCSPlayer_ItemServices_GiveNamedItem": {
|
||||
"offsets": {
|
||||
"windows": 19,
|
||||
@@ -119,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",
|
||||
@@ -156,8 +176,8 @@
|
||||
},
|
||||
"CBasePlayerPawn_CommitSuicide": {
|
||||
"offsets": {
|
||||
"windows": 377,
|
||||
"linux": 377
|
||||
"windows": 380,
|
||||
"linux": 380
|
||||
}
|
||||
},
|
||||
"CBasePlayerPawn_RemovePlayerItem": {
|
||||
@@ -239,5 +259,11 @@
|
||||
"windows": 2,
|
||||
"linux": 0
|
||||
}
|
||||
},
|
||||
"CheckTransmitPlayerSlot": {
|
||||
"offsets": {
|
||||
"windows": 584,
|
||||
"linux": 584
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,9 @@ The specific subclass of `GameEvent` will provide strongly typed parameters from
|
||||
|
||||
These event properties are mutable so you can update them as normal and they will update in the event instance.
|
||||
|
||||
> [!CAUTION]
|
||||
> `GameEvent` instances and their properties will cease to exist after the event listener function is called, which means that you will encounter errors when accessing properties in timers and functions like `Server.NextFrame()`. You should store the value of properties in variables before calling functions like `Server.NextFrame()` so you can read the data safely.
|
||||
|
||||
## Preventing Broadcast
|
||||
|
||||
You can modify a game event so that it does not get broadcast to clients by modifying the `bool info.DontBroadcast` property. e.g.
|
||||
|
||||
@@ -6,3 +6,6 @@
|
||||
|
||||
- name: Dependency Injection
|
||||
href: dependency-injection.md
|
||||
|
||||
- name: Referencing Players
|
||||
href: referencing-players.md
|
||||
@@ -1,5 +1,2 @@
|
||||
- name: Core Configuration
|
||||
href: core-configuration.md
|
||||
|
||||
- name: Referencing Players
|
||||
href: referencing-players.md
|
||||
href: core-configuration.md
|
||||
2
examples/WithCheckTransmit/README.md
Normal file
2
examples/WithCheckTransmit/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
# With CheckTransmit
|
||||
This example shows how to work with the `CheckTransmit` listener.
|
||||
13
examples/WithCheckTransmit/WithCheckTransmit.csproj
Normal file
13
examples/WithCheckTransmit/WithCheckTransmit.csproj
Normal file
@@ -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
examples/WithCheckTransmit/WithCheckTransmitPlugin.cs
Normal file
115
examples/WithCheckTransmit/WithCheckTransmitPlugin.cs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Submodule libraries/hl2sdk-cs2 updated: fc4b98f1a7...14e77af283
@@ -337,6 +337,16 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
}
|
||||
|
||||
public static float GetGameFrameTime(){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0x97E331CA);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
return (float)ScriptContext.GlobalScriptContext.GetResult(typeof(float));
|
||||
}
|
||||
}
|
||||
|
||||
public static void IssueServerCommand(string command){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
|
||||
@@ -70,8 +70,13 @@ public class CommandManager : ICommandManager
|
||||
|
||||
if (_commandDefinitions.TryGetValue(name, out var handler))
|
||||
{
|
||||
foreach (var command in handler)
|
||||
foreach (var command in handler.ToList())
|
||||
{
|
||||
if (!handler.Contains(command))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var methodInfo = command.Callback?.GetMethodInfo();
|
||||
|
||||
// We do not need to do permission checks on commands executed from the server console.
|
||||
|
||||
@@ -163,5 +163,12 @@ namespace CounterStrikeSharp.API.Core
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,15 +12,17 @@ public partial class CBaseEntity
|
||||
public void Teleport(Vector? position = null, QAngle? angles = null, Vector? velocity = null)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
|
||||
if (position == null && angles == null && velocity == null)
|
||||
throw new ArgumentNullException("No valid argument");
|
||||
|
||||
|
||||
nint _position = position?.Handle ?? 0;
|
||||
nint _angles = angles?.Handle ?? 0;
|
||||
nint _velocity = velocity?.Handle ?? 0;
|
||||
|
||||
VirtualFunction.CreateVoid<IntPtr, IntPtr, IntPtr, IntPtr>(Handle, GameData.GetOffset("CBaseEntity_Teleport"))(Handle, _position, _angles, _velocity);
|
||||
nint _handle = Handle;
|
||||
|
||||
VirtualFunction.CreateVoid<IntPtr, IntPtr, IntPtr, IntPtr>(_handle, GameData.GetOffset("CBaseEntity_Teleport"))(_handle, _position,
|
||||
_angles, _velocity);
|
||||
}
|
||||
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
@@ -28,4 +29,33 @@ public partial class CCSGameRules
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,4 +340,11 @@ public partial class CCSPlayerController
|
||||
NativeAPI.SetClientVoiceFlags(Handle, (Byte)value);
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete(
|
||||
"You are trying to call Teleport on a non-physical player. Maybe you mean Pawn? (See: https://docs.cssharp.dev/docs/guides/referencing-players.html#controllers--pawns)")]
|
||||
public new void Teleport(Vector? position = null, QAngle? angles = null, Vector? velocity = null)
|
||||
{
|
||||
base.Teleport(position, angles, velocity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
@@ -54,4 +56,9 @@ public partial class CCSPlayer_ItemServices
|
||||
|
||||
return (T)Activator.CreateInstance(typeof(T), pointer)!;
|
||||
}
|
||||
|
||||
public AcquireResult CanAcquire(CEconItemView itemView, AcquireMethod method, IntPtr unknown = 0)
|
||||
{
|
||||
return VirtualFunctions.CCSPlayer_ItemServices_CanAcquireFunc.Invoke(this, itemView, method, unknown);
|
||||
}
|
||||
}
|
||||
|
||||
106
managed/CounterStrikeSharp.API/Core/Model/CCheckTransmitInfo.cs
Normal file
106
managed/CounterStrikeSharp.API/Core/Model/CCheckTransmitInfo.cs
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.Runtime.InteropServices;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
// credits: qstage
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe struct CFixedBitVecBase
|
||||
{
|
||||
private const int LOG2_BITS_PER_INT = 5;
|
||||
private const int BITS_PER_INT = 32;
|
||||
|
||||
private readonly uint* m_Ints;
|
||||
|
||||
public void Add(CEntityInstance entityInstance) => Write(entityInstance.Index);
|
||||
|
||||
public void Add(int bitNum) => Write(bitNum);
|
||||
|
||||
public void Add(uint bitNum) => Write(bitNum);
|
||||
|
||||
public void Remove(CEntityInstance entityInstance) => Clear(entityInstance.Index);
|
||||
|
||||
public void Remove(int bitNum) => Clear(bitNum);
|
||||
|
||||
public void Remove(uint bitNum) => Clear(bitNum);
|
||||
|
||||
public bool Contains(CEntityInstance entityInstance) => Contains(entityInstance.Index);
|
||||
|
||||
public bool Contains(uint bitNum) => Contains((int)bitNum);
|
||||
|
||||
private void Clear(uint bitNum) => Clear((int)bitNum);
|
||||
|
||||
private void Write(uint bitNum) => Write((int)bitNum);
|
||||
|
||||
private void Clear(int bitNum)
|
||||
{
|
||||
if (!(bitNum >= 0 && bitNum < Utilities.MaxEdicts))
|
||||
return;
|
||||
|
||||
uint* pInt = m_Ints + BitVec_Int(bitNum);
|
||||
*pInt &= ~(uint)BitVec_Bit(bitNum);
|
||||
}
|
||||
|
||||
private void Write(int bitNum)
|
||||
{
|
||||
if (!(bitNum >= 0 && bitNum < Utilities.MaxEdicts))
|
||||
return;
|
||||
|
||||
uint* pInt = m_Ints + BitVec_Int(bitNum);
|
||||
*pInt |= (uint)BitVec_Bit(bitNum);
|
||||
}
|
||||
|
||||
public bool Contains(int bitNum)
|
||||
{
|
||||
if (!(bitNum >= 0 && bitNum < Utilities.MaxEdicts))
|
||||
return false;
|
||||
|
||||
uint* pInt = m_Ints + BitVec_Int(bitNum);
|
||||
return (*pInt & (uint)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));
|
||||
}
|
||||
}
|
||||
32
managed/CounterStrikeSharp.API/Core/Model/CTakeDamageInfo.cs
Normal file
32
managed/CounterStrikeSharp.API/Core/Model/CTakeDamageInfo.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public partial class CTakeDamageInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves the hitgroup
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Returns a <see cref="HitGroup_t"/> enumeration representing the player's current hit group,
|
||||
/// or <see cref="HitGroup_t.HITGROUP_INVALID"/> if the hit group cannot be determined.
|
||||
/// </returns>
|
||||
public HitGroup_t GetHitGroup()
|
||||
{
|
||||
IntPtr v4 = Marshal.ReadIntPtr(Handle, 0x78);
|
||||
|
||||
if (v4 == nint.Zero)
|
||||
{
|
||||
return HitGroup_t.HITGROUP_INVALID;
|
||||
}
|
||||
|
||||
IntPtr v1 = Marshal.ReadIntPtr(v4, 16);
|
||||
|
||||
if (v1 == nint.Zero)
|
||||
{
|
||||
return HitGroup_t.HITGROUP_GENERIC;
|
||||
}
|
||||
|
||||
return (HitGroup_t)Marshal.ReadInt32(v1, 56);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Commands.Targeting;
|
||||
@@ -12,7 +14,9 @@ public class Target
|
||||
private TargetType Type { get; }
|
||||
private string Raw { get; }
|
||||
private string Slug { get; }
|
||||
|
||||
|
||||
internal CCSGameRulesProxy? _gameRulesEntity = null;
|
||||
|
||||
public static readonly IReadOnlyDictionary<string, TargetType> TargetTypeMap = new Dictionary<string, TargetType>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{ "@all", TargetType.GroupAll },
|
||||
@@ -22,6 +26,7 @@ public class Target
|
||||
{ "@dead", TargetType.GroupDead },
|
||||
{ "@!me", TargetType.GroupNotMe },
|
||||
{ "@me", TargetType.PlayerMe },
|
||||
{ "@aim", TargetType.PlayerAim },
|
||||
{ "@ct", TargetType.TeamCt },
|
||||
{ "@t", TargetType.TeamT },
|
||||
{ "@spec", TargetType.TeamSpec }
|
||||
@@ -49,7 +54,7 @@ public class Target
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
slug = target.TrimStart('#');
|
||||
if (slug.StartsWith("STEAM")) targetType = TargetType.IdSteamEscaped;
|
||||
else if (!ulong.TryParse(slug, out _)) targetType = TargetType.ExplicitName;
|
||||
@@ -80,48 +85,40 @@ public class Target
|
||||
}
|
||||
}
|
||||
|
||||
private bool TargetPredicate(CCSPlayerController player, CCSPlayerController? caller)
|
||||
private bool TargetPredicate(CCSPlayerController player, CCSPlayerController? caller, CCSGameRules? gameRules = null)
|
||||
{
|
||||
switch (Type)
|
||||
return Type switch
|
||||
{
|
||||
case TargetType.TeamCt:
|
||||
return player.TeamNum == (byte)CsTeam.CounterTerrorist;
|
||||
case TargetType.TeamT:
|
||||
return player.TeamNum == (byte)CsTeam.Terrorist;
|
||||
case TargetType.TeamSpec:
|
||||
return player.TeamNum == (byte)CsTeam.Spectator;
|
||||
case TargetType.GroupAll:
|
||||
return !player.IsHLTV;
|
||||
case TargetType.GroupBots:
|
||||
return player.IsBot;
|
||||
case TargetType.GroupHumans:
|
||||
return !player.IsBot && !player.IsHLTV;
|
||||
case TargetType.GroupAlive:
|
||||
return player.PlayerPawn is { IsValid: true, Value.LifeState: (byte)LifeState_t.LIFE_ALIVE };
|
||||
case TargetType.GroupDead:
|
||||
return player.PlayerPawn is { IsValid: true, Value.LifeState: (byte)LifeState_t.LIFE_DEAD or (byte)LifeState_t.LIFE_DYING };
|
||||
case TargetType.GroupNotMe:
|
||||
return player.SteamID != caller?.SteamID;
|
||||
case TargetType.PlayerMe:
|
||||
return player.SteamID == caller?.SteamID;
|
||||
case TargetType.IdUserid:
|
||||
return player.UserId.ToString() == Slug;
|
||||
case TargetType.IdSteamEscaped:
|
||||
return ((SteamID)player.SteamID).SteamId2 == Slug;
|
||||
case TargetType.IdSteam64:
|
||||
return ((SteamID)player.SteamID).SteamId64.ToString() == Slug;
|
||||
case TargetType.ExplicitName:
|
||||
case TargetType.ImplicitName:
|
||||
return player.PlayerName.Contains(Slug, StringComparison.OrdinalIgnoreCase);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
TargetType.PlayerAim => caller != null && player == gameRules!.GetClientAimTarget(caller),
|
||||
TargetType.TeamCt => player.Team == CsTeam.CounterTerrorist,
|
||||
TargetType.TeamT => player.Team == CsTeam.Terrorist,
|
||||
TargetType.TeamSpec => player.Team == CsTeam.Spectator,
|
||||
TargetType.GroupAll => !player.IsHLTV,
|
||||
TargetType.GroupBots => player.IsBot,
|
||||
TargetType.GroupHumans => !player.IsBot && !player.IsHLTV,
|
||||
TargetType.GroupAlive => player.PlayerPawn is { IsValid: true, Value.LifeState: (byte)LifeState_t.LIFE_ALIVE },
|
||||
TargetType.GroupDead => player.PlayerPawn is { IsValid: true, Value.LifeState: (byte)LifeState_t.LIFE_DEAD or (byte)LifeState_t.LIFE_DYING },
|
||||
TargetType.GroupNotMe => player.SteamID != caller?.SteamID,
|
||||
TargetType.PlayerMe => player.SteamID == caller?.SteamID,
|
||||
TargetType.IdUserid => player.UserId.ToString() == Slug,
|
||||
TargetType.IdSteamEscaped => ((SteamID)player.SteamID).SteamId2 == Slug,
|
||||
TargetType.IdSteam64 => ((SteamID)player.SteamID).SteamId64.ToString() == Slug,
|
||||
TargetType.ExplicitName => player.PlayerName.Contains(Slug, StringComparison.OrdinalIgnoreCase),
|
||||
TargetType.ImplicitName => player.PlayerName.Contains(Slug, StringComparison.OrdinalIgnoreCase),
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
public TargetResult GetTarget(CCSPlayerController? caller)
|
||||
{
|
||||
var players = Utilities.GetPlayers().Where(player => TargetPredicate(player, caller)).ToList();
|
||||
|
||||
return new TargetResult() { Players = players };
|
||||
if (Type == TargetType.PlayerAim)
|
||||
{
|
||||
if (_gameRulesEntity == null || !_gameRulesEntity.IsValid)
|
||||
{
|
||||
_gameRulesEntity = Utilities.FindAllEntitiesByDesignerName<CCSGameRulesProxy>("cs_gamerules").FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
return new TargetResult() { Players = Utilities.GetPlayers().Where(player => TargetPredicate(player, caller, _gameRulesEntity?.GameRules)).ToList() };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ public enum TargetType
|
||||
GroupNotMe, // @!me
|
||||
|
||||
PlayerMe, // @me
|
||||
PlayerAim, // @aim
|
||||
|
||||
IdUserid, // #4
|
||||
IdSteamEscaped, // "#STEAM_0:1:8614"
|
||||
@@ -22,4 +23,4 @@ public enum TargetType
|
||||
ExplicitName, // #name
|
||||
ImplicitName, // name
|
||||
Invalid
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
using System.Text.Json;
|
||||
using System.Reflection;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Extensions;
|
||||
|
||||
public static class PluginConfigExtensions
|
||||
{
|
||||
private static readonly JsonSerializerOptions _jsonSerializerOptions = new()
|
||||
{
|
||||
WriteIndented = true
|
||||
};
|
||||
|
||||
public static JsonSerializerOptions JsonSerializerOptions => _jsonSerializerOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configuration file path
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the plugin configuration.</typeparam>
|
||||
/// <param name="_">Current configuration instance</param>
|
||||
public static string GetConfigPath<T>(this T _) where T : BasePluginConfig, new()
|
||||
{
|
||||
string assemblyName = typeof(T).Assembly.GetName().Name ?? string.Empty;
|
||||
return Path.Combine(Server.GameDirectory, "csgo", "addons", "counterstrikesharp", "configs", "plugins", assemblyName, $"{assemblyName}.json");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the configuration file
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the plugin configuration.</typeparam>
|
||||
/// <param name="config">Current configuration instance</param>
|
||||
public static void Update<T>(this T config) where T : BasePluginConfig, new()
|
||||
{
|
||||
var configPath = config.GetConfigPath();
|
||||
|
||||
try
|
||||
{
|
||||
using var stream = new FileStream(configPath, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||
using var writer = new StreamWriter(stream);
|
||||
writer.Write(JsonSerializer.Serialize(config, JsonSerializerOptions));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Failed to update configuration file at '{configPath}'.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reloads the configuration file and updates current configuration instance.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the plugin configuration.</typeparam>
|
||||
/// <param name="config">Current configuration instance</param>
|
||||
public static void Reload<T>(this T config) where T : BasePluginConfig, new()
|
||||
{
|
||||
var configPath = config.GetConfigPath();
|
||||
|
||||
try
|
||||
{
|
||||
if (!File.Exists(configPath))
|
||||
{
|
||||
throw new FileNotFoundException($"Configuration file '{configPath} not found.");
|
||||
}
|
||||
|
||||
var configContent = File.ReadAllText(configPath);
|
||||
|
||||
var newConfig = JsonSerializer.Deserialize<T>(configContent)
|
||||
?? throw new JsonException($"Deserialization failed for configuration file '{configPath}'.");
|
||||
|
||||
foreach (var property in typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public))
|
||||
{
|
||||
if (property.CanWrite)
|
||||
{
|
||||
property.SetValue(config, property.GetValue(newConfig));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Failed to reload configuration file at '{configPath}'.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,7 @@ namespace CounterStrikeSharp.API.Modules.Memory;
|
||||
public static class VirtualFunctions
|
||||
{
|
||||
public static MemoryFunctionVoid<IntPtr, HudDestination, string, IntPtr, IntPtr, IntPtr, IntPtr> ClientPrintFunc =
|
||||
new(
|
||||
GameData.GetSignature("ClientPrint"));
|
||||
new(GameData.GetSignature("ClientPrint"));
|
||||
|
||||
public static Action<IntPtr, HudDestination, string, IntPtr, IntPtr, IntPtr, IntPtr> ClientPrint =
|
||||
ClientPrintFunc.Invoke;
|
||||
@@ -68,7 +67,13 @@ public static class VirtualFunctions
|
||||
|
||||
public static MemoryFunctionWithReturn<CCSPlayer_WeaponServices, CBasePlayerWeapon, bool> CCSPlayer_WeaponServices_CanUseFunc = new(GameData.GetSignature("CCSPlayer_WeaponServices_CanUse"));
|
||||
public static Func<CCSPlayer_WeaponServices, CBasePlayerWeapon, bool> CCSPlayer_WeaponServices_CanUse = CCSPlayer_WeaponServices_CanUseFunc.Invoke;
|
||||
|
||||
|
||||
public static MemoryFunctionWithReturn<int, string, CCSWeaponBaseVData> GetCSWeaponDataFromKeyFunc = new(GameData.GetSignature("GetCSWeaponDataFromKey"));
|
||||
public static Func<int, string, CCSWeaponBaseVData> GetCSWeaponDataFromKey = GetCSWeaponDataFromKeyFunc.Invoke;
|
||||
|
||||
public static MemoryFunctionWithReturn<CCSPlayer_ItemServices, CEconItemView, AcquireMethod, IntPtr, AcquireResult> CCSPlayer_ItemServices_CanAcquireFunc = new(GameData.GetSignature("CCSPlayer_ItemServices_CanAcquire"));
|
||||
public static Func<CCSPlayer_ItemServices, CEconItemView, AcquireMethod, IntPtr, AcquireResult> CCSPlayer_ItemServices_CanAcquire = CCSPlayer_ItemServices_CanAcquireFunc.Invoke;
|
||||
|
||||
public static MemoryFunctionVoid<CCSPlayerPawnBase> CCSPlayerPawnBase_PostThinkFunc = new (GameData.GetSignature("CCSPlayerPawnBase_PostThink"));
|
||||
public static Action<CCSPlayerPawnBase> CCSPlayerPawnBase_PostThink = CCSPlayerPawnBase_PostThinkFunc.Invoke;
|
||||
|
||||
|
||||
@@ -90,6 +90,7 @@ public abstract class BaseMenuInstance : IMenuInstance
|
||||
|
||||
protected bool HasPrevButton => Page > 0;
|
||||
protected bool HasNextButton => Menu.MenuOptions.Count > NumPerPage && CurrentOffset + NumPerPage < Menu.MenuOptions.Count;
|
||||
protected bool HasExitButton => Menu.ExitButton;
|
||||
protected virtual int MenuItemsPerPage => NumPerPage;
|
||||
|
||||
public virtual void Display()
|
||||
@@ -113,7 +114,7 @@ public abstract class BaseMenuInstance : IMenuInstance
|
||||
return;
|
||||
}
|
||||
|
||||
if (key == 9)
|
||||
if (key == 9 && HasExitButton)
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
@@ -174,4 +175,4 @@ public abstract class BaseMenuInstance : IMenuInstance
|
||||
CurrentOffset = PrevPageOffsets.Pop();
|
||||
Display();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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/>. *
|
||||
*/
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
public enum AcquireMethod : int
|
||||
{
|
||||
PickUp = 0,
|
||||
Buy,
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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/>. *
|
||||
*/
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
public enum AcquireResult : int
|
||||
{
|
||||
Allowed = 0,
|
||||
InvalidItem,
|
||||
AlreadyOwned,
|
||||
AlreadyPurchased,
|
||||
ReachedGrenadeTypeLimit,
|
||||
ReachedGrenadeTotalLimit,
|
||||
NotAllowedByTeam,
|
||||
NotAllowedByMap,
|
||||
NotAllowedByMode,
|
||||
NotAllowedForPurchase,
|
||||
NotAllowedByProhibition,
|
||||
};
|
||||
@@ -101,13 +101,11 @@ public class RecipientFilter : IList<CCSPlayerController>, IMarshalToNative
|
||||
public void Add(int slot)
|
||||
{
|
||||
var player = Utilities.GetPlayerFromSlot(slot);
|
||||
if (player == null)
|
||||
if (player != null)
|
||||
{
|
||||
throw new ArgumentException($"Player with slot {slot} not found");
|
||||
_recipients.Add(player);
|
||||
CollectionChanged?.Invoke();
|
||||
}
|
||||
|
||||
_recipients.Add(player);
|
||||
CollectionChanged?.Invoke();
|
||||
}
|
||||
|
||||
public void AddAllPlayers()
|
||||
|
||||
@@ -46,28 +46,33 @@ namespace CounterStrikeSharp.API
|
||||
/// </summary>
|
||||
/// <remarks>Does not increment when server is hibernating</remarks>
|
||||
public static double TickedTime => NativeAPI.GetTickedTime();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current map time in seconds, as an interval of the server's tick interval.
|
||||
/// e.g. 70.046875 would represent 70 seconds of map time and the 4483rd tick of the server (70.046875 / 0.015625).
|
||||
/// </summary>
|
||||
/// <remarks>Increments even when server is hibernating</remarks>
|
||||
public static float CurrentTime => NativeAPI.GetCurrentTime();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current map tick count.
|
||||
/// CS2 is a 64 tick server, so the value will increment by 64 every second.
|
||||
/// </summary>
|
||||
public static int TickCount => NativeAPI.GetTickCount();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the total time the server has been running in seconds.
|
||||
/// </summary>
|
||||
/// <remarks>Increments even when server is hibernating</remarks>
|
||||
public static double EngineTime => NativeAPI.GetEngineTime();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the time spent on last server or client frame
|
||||
/// </summary>
|
||||
public static float FrameTime => NativeAPI.GetGameFrameTime();
|
||||
|
||||
public static void PrecacheModel(string name) => NativeAPI.PrecacheModel(name);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="RunOnTick"/>
|
||||
/// Returns Task that completes once the synchronous task has been completed.
|
||||
@@ -78,7 +83,7 @@ namespace CounterStrikeSharp.API
|
||||
NativeAPI.QueueTaskForFrame(tick, functionReference);
|
||||
return functionReference.CompletionTask;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Queue a task to be executed on the specified tick.
|
||||
/// See <see cref="TickCount"/> to retrieve the current tick.
|
||||
@@ -108,7 +113,7 @@ namespace CounterStrikeSharp.API
|
||||
{
|
||||
NextFrameAsync(task);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="NextWorldUpdate"/>
|
||||
/// Returns Task that completes once the synchronous task has been completed.
|
||||
@@ -119,7 +124,7 @@ namespace CounterStrikeSharp.API
|
||||
NativeAPI.QueueTaskForNextWorldUpdate(functionReference);
|
||||
return functionReference.CompletionTask;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Queue a task to be executed on the next pre world update.
|
||||
/// <remarks>Executes if the server is hibernating.</remarks>
|
||||
@@ -157,4 +162,4 @@ namespace CounterStrikeSharp.API
|
||||
|
||||
public static void PrintToConsole(string s) => NativeAPI.PrintToServerConsole($"{s}\n\0");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.12.35309.182
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestPlugin", "TestPlugin\TestPlugin.csproj", "{57E64289-5D69-4AA1-BEF0-D0D96A55EE8F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CounterStrikeSharp.API", "CounterStrikeSharp.API\CounterStrikeSharp.API.csproj", "{55B47E41-61AA-4D75-9069-CB14328107B7}"
|
||||
@@ -42,6 +45,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithSharedTypesConsumer", "
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithUserMessages", "..\examples\WithUserMessages\WithUserMessages.csproj", "{A14029BA-CADE-4F25-ADC5-48CF14332F61}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithCheckTransmit", "..\examples\WithCheckTransmit\WithCheckTransmit.csproj", "{854B06B5-0E7B-438A-BA51-3F299557F884}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -108,14 +113,14 @@ Global
|
||||
{6FA3107D-42AF-42A0-BF51-2230D13268B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6FA3107D-42AF-42A0-BF51-2230D13268B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6FA3107D-42AF-42A0-BF51-2230D13268B5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4E5289B5-E81D-421C-B340-B98B6FFE09D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4E5289B5-E81D-421C-B340-B98B6FFE09D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4E5289B5-E81D-421C-B340-B98B6FFE09D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4E5289B5-E81D-421C-B340-B98B6FFE09D1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1309954E-FAF7-47A5-9FF9-C7263B33E4E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1309954E-FAF7-47A5-9FF9-C7263B33E4E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1309954E-FAF7-47A5-9FF9-C7263B33E4E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1309954E-FAF7-47A5-9FF9-C7263B33E4E3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4E5289B5-E81D-421C-B340-B98B6FFE09D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4E5289B5-E81D-421C-B340-B98B6FFE09D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4E5289B5-E81D-421C-B340-B98B6FFE09D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4E5289B5-E81D-421C-B340-B98B6FFE09D1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A37676EA-CF2F-424D-85A1-C359D07A679D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A37676EA-CF2F-424D-85A1-C359D07A679D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A37676EA-CF2F-424D-85A1-C359D07A679D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
@@ -128,6 +133,13 @@ Global
|
||||
{A14029BA-CADE-4F25-ADC5-48CF14332F61}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A14029BA-CADE-4F25-ADC5-48CF14332F61}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A14029BA-CADE-4F25-ADC5-48CF14332F61}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{854B06B5-0E7B-438A-BA51-3F299557F884}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{854B06B5-0E7B-438A-BA51-3F299557F884}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{854B06B5-0E7B-438A-BA51-3F299557F884}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{854B06B5-0E7B-438A-BA51-3F299557F884}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{57E64289-5D69-4AA1-BEF0-D0D96A55EE8F} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
@@ -141,10 +153,14 @@ Global
|
||||
{31EABE0B-871F-497B-BF36-37FFC6FAD15F} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
{BB44E08E-CCA8-4E22-A132-11B2F69D1890} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
{6FA3107D-42AF-42A0-BF51-2230D13268B5} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
{4E5289B5-E81D-421C-B340-B98B6FFE09D1} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
{1309954E-FAF7-47A5-9FF9-C7263B33E4E3} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
{4E5289B5-E81D-421C-B340-B98B6FFE09D1} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
{A37676EA-CF2F-424D-85A1-C359D07A679D} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
{76AD7BB0-A096-4336-83E2-B32CAE4E9933} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
{A14029BA-CADE-4F25-ADC5-48CF14332F61} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
{854B06B5-0E7B-438A-BA51-3F299557F884} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {069C4CD4-BACA-446A-A6B8-0194E4F75355}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
@@ -301,6 +302,26 @@ namespace TestPlugin
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// Hide every door (prop_door_rotating) for everyone as a test
|
||||
RegisterListener<Listeners.CheckTransmit>((CCheckTransmitInfoList infoList) =>
|
||||
{
|
||||
IEnumerable<CPropDoorRotating> doors = Utilities.FindAllEntitiesByDesignerName<CPropDoorRotating>("prop_door_rotating");
|
||||
|
||||
if (!doors.Any())
|
||||
return;
|
||||
|
||||
foreach ((CCheckTransmitInfo info, CCSPlayerController? player) in infoList)
|
||||
{
|
||||
if (player == null)
|
||||
continue;
|
||||
|
||||
foreach (CPropDoorRotating door in doors)
|
||||
{
|
||||
info.TransmitEntities.Remove(door);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void SetupCommands()
|
||||
|
||||
@@ -78,6 +78,7 @@ ISmmAPI* ismm = nullptr;
|
||||
CGameEntitySystem* entitySystem = nullptr;
|
||||
CCoreConfig* coreConfig = nullptr;
|
||||
CGameConfig* gameConfig = nullptr;
|
||||
ISource2GameEntities* gameEntities = nullptr;
|
||||
|
||||
// Custom Managers
|
||||
CallbackManager callbackManager;
|
||||
|
||||
@@ -94,6 +94,7 @@ extern ISource2Server* server;
|
||||
extern CGlobalEntityList* globalEntityList;
|
||||
extern EntityListener entityListener;
|
||||
extern CGameEntitySystem* entitySystem;
|
||||
extern ISource2GameEntities* gameEntities;
|
||||
|
||||
extern EventManager eventManager;
|
||||
extern UserMessageManager userMessageManager;
|
||||
|
||||
@@ -24,14 +24,24 @@
|
||||
#include <public/eiface.h>
|
||||
#include "scripting/callback_manager.h"
|
||||
|
||||
SH_DECL_HOOK7_void(ISource2GameEntities, CheckTransmit, SH_NOATTRIB, 0, CCheckTransmitInfo**, int, CBitVec<16384>&, const Entity2Networkable_t**, const uint16*, int, bool);
|
||||
|
||||
namespace counterstrikesharp {
|
||||
|
||||
EntityManager::EntityManager() {}
|
||||
EntityManager::EntityManager()
|
||||
{
|
||||
m_profile_name = "EntityManager";
|
||||
}
|
||||
|
||||
EntityManager::~EntityManager() {}
|
||||
|
||||
CCheckTransmitInfoList::CCheckTransmitInfoList(CCheckTransmitInfo** pInfoInfoList, int nInfoCount) : infoList(pInfoInfoList), infoCount(nInfoCount) {}
|
||||
|
||||
void EntityManager::OnAllInitialized()
|
||||
{
|
||||
SH_ADD_HOOK_MEMFUNC(ISource2GameEntities, CheckTransmit, globals::gameEntities, this, &EntityManager::CheckTransmit, true);
|
||||
|
||||
check_transmit = globals::callbackManager.CreateCallback("CheckTransmit");
|
||||
on_entity_spawned_callback = globals::callbackManager.CreateCallback("OnEntitySpawned");
|
||||
on_entity_created_callback = globals::callbackManager.CreateCallback("OnEntityCreated");
|
||||
on_entity_deleted_callback = globals::callbackManager.CreateCallback("OnEntityDeleted");
|
||||
@@ -75,7 +85,10 @@ void EntityManager::OnShutdown()
|
||||
globals::callbackManager.ReleaseCallback(on_entity_created_callback);
|
||||
globals::callbackManager.ReleaseCallback(on_entity_deleted_callback);
|
||||
globals::callbackManager.ReleaseCallback(on_entity_parent_changed_callback);
|
||||
globals::callbackManager.ReleaseCallback(check_transmit);
|
||||
globals::entitySystem->RemoveListenerEntity(&entityListener);
|
||||
|
||||
SH_REMOVE_HOOK_MEMFUNC(ISource2GameEntities, CheckTransmit, globals::gameEntities, this, &EntityManager::CheckTransmit, true);
|
||||
}
|
||||
|
||||
void CEntityListener::OnEntitySpawned(CEntityInstance* pEntity)
|
||||
@@ -156,6 +169,23 @@ void EntityManager::UnhookEntityOutput(const char* szClassname, const char* szOu
|
||||
}
|
||||
}
|
||||
|
||||
void EntityManager::CheckTransmit(CCheckTransmitInfo** pInfoInfoList, int nInfoCount, CBitVec<16384>& unionTransmitEdicts, const Entity2Networkable_t** pNetworkables, const uint16* pEntityIndicies, int nEntityIndices, bool bEnablePVSBits)
|
||||
{
|
||||
VPROF_BUDGET(m_profile_name.c_str(), "CS# CheckTransmit");
|
||||
|
||||
auto callback = globals::entityManager.check_transmit;
|
||||
|
||||
if (callback && callback->GetFunctionCount()) {
|
||||
CCheckTransmitInfoList* infoList = new CCheckTransmitInfoList(pInfoInfoList, nInfoCount);
|
||||
|
||||
callback->ScriptContext().Reset();
|
||||
callback->ScriptContext().Push(infoList);
|
||||
callback->Execute();
|
||||
|
||||
delete infoList;
|
||||
}
|
||||
}
|
||||
|
||||
void DetourFireOutputInternal(CEntityIOOutput* const pThis, CEntityInstance* pActivator,
|
||||
CEntityInstance* pCaller, const CVariant* const value, float flDelay)
|
||||
{
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
|
||||
#include <variant.h>
|
||||
|
||||
#include "vprof.h"
|
||||
|
||||
namespace counterstrikesharp {
|
||||
class ScriptCallback;
|
||||
|
||||
@@ -39,6 +41,14 @@ class CEntityListener : public IEntityListener {
|
||||
void OnEntityParentChanged(CEntityInstance *pEntity, CEntityInstance *pNewParent) override;
|
||||
};
|
||||
|
||||
class CCheckTransmitInfoList {
|
||||
public:
|
||||
CCheckTransmitInfoList(CCheckTransmitInfo** pInfoInfoList, int nInfoCount);
|
||||
private:
|
||||
CCheckTransmitInfo** infoList;
|
||||
int infoCount;
|
||||
};
|
||||
|
||||
class EntityManager : public GlobalClass {
|
||||
friend CEntityListener;
|
||||
public:
|
||||
@@ -51,10 +61,15 @@ public:
|
||||
CEntityListener entityListener;
|
||||
std::map<OutputKey_t, CallbackPair*> m_pHookMap;
|
||||
private:
|
||||
void CheckTransmit(CCheckTransmitInfo** pInfoInfoList, int nInfoCount, CBitVec<16384>& unionTransmitEdicts, const Entity2Networkable_t** pNetworkables, const uint16* pEntityIndicies, int nEntityIndices, bool bEnablePVSBits);
|
||||
|
||||
ScriptCallback *on_entity_spawned_callback;
|
||||
ScriptCallback *on_entity_created_callback;
|
||||
ScriptCallback *on_entity_deleted_callback;
|
||||
ScriptCallback *on_entity_parent_changed_callback;
|
||||
ScriptCallback *check_transmit;
|
||||
|
||||
std::string m_profile_name;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -102,6 +102,7 @@ bool CounterStrikeSharpMMPlugin::Load(PluginId id, ISmmAPI* ismm, char* error, s
|
||||
GET_V_IFACE_ANY(GetEngineFactory, globals::gameEventSystem, IGameEventSystem, GAMEEVENTSYSTEM_INTERFACE_VERSION);
|
||||
GET_V_IFACE_ANY(GetEngineFactory, globals::engineServiceManager, IEngineServiceMgr, ENGINESERVICEMGR_INTERFACE_VERSION);
|
||||
GET_V_IFACE_ANY(GetEngineFactory, globals::networkMessages, INetworkMessages, NETWORKMESSAGES_INTERFACE_VERSION);
|
||||
GET_V_IFACE_ANY(GetServerFactory, globals::gameEntities, ISource2GameEntities, SOURCE2GAMEENTITIES_INTERFACE_VERSION);
|
||||
|
||||
auto coreconfig_path = std::string(utils::ConfigsDirectory() + "/core");
|
||||
globals::coreConfig = new CCoreConfig(coreconfig_path);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
OnEntitySpawned: entity:pointer
|
||||
OnEntityCreated: entity:pointer
|
||||
OnEntityDeleted: entity:pointer
|
||||
OnEntityParentChanged: entity:pointer, newParent:pointer
|
||||
OnEntityParentChanged: entity:pointer, newParent:pointer
|
||||
CheckTransmit: infoList:pointer
|
||||
|
||||
@@ -245,7 +245,7 @@ REGISTER_NATIVES(engine, {
|
||||
ScriptEngine::RegisterNativeHandler("GET_TICK_INTERVAL", GetTickInterval);
|
||||
ScriptEngine::RegisterNativeHandler("GET_TICK_COUNT", GetTickCount);
|
||||
ScriptEngine::RegisterNativeHandler("GET_CURRENT_TIME", GetCurrentTime);
|
||||
ScriptEngine::RegisterNativeHandler("GET_GAMEFRAME_TIME", GetGameFrameTime);
|
||||
ScriptEngine::RegisterNativeHandler("GET_GAME_FRAME_TIME", GetGameFrameTime);
|
||||
ScriptEngine::RegisterNativeHandler("GET_ENGINE_TIME", GetEngineTime);
|
||||
ScriptEngine::RegisterNativeHandler("GET_MAX_CLIENTS", GetMaxClients);
|
||||
ScriptEngine::RegisterNativeHandler("ISSUE_SERVER_COMMAND", ServerCommand);
|
||||
|
||||
@@ -6,6 +6,7 @@ GET_CURRENT_TIME: -> float
|
||||
GET_TICK_COUNT: -> int
|
||||
GET_ENGINE_TIME: -> double
|
||||
GET_MAX_CLIENTS: -> int
|
||||
GET_GAME_FRAME_TIME: -> float
|
||||
ISSUE_SERVER_COMMAND: command:string -> void
|
||||
PRECACHE_MODEL: name:string -> void
|
||||
PRECACHE_SOUND: name:string, preload:bool -> bool
|
||||
|
||||
Reference in New Issue
Block a user