fix: Update CS# for Patch 19388062 (#958)

This commit is contained in:
Michael Wilson
2025-08-04 09:28:05 +10:00
committed by GitHub
parent af3bb528d7
commit 1f30e5619f
41 changed files with 1700 additions and 282 deletions

2
.gitmodules vendored
View File

@@ -1,6 +1,6 @@
[submodule "libraries/hl2sdk-cs2"]
path = libraries/hl2sdk-cs2
url = https://github.com/alliedmodders/hl2sdk
url = https://github.com/roflmuffin/hl2sdk
branch = cs2
[submodule "libraries/metamod-source"]
path = libraries/metamod-source

View File

@@ -77,6 +77,7 @@ set(SOURCE_FILES
src/core/managers/con_command_manager.cpp
src/core/managers/con_command_manager.h
src/scripting/natives/natives_commands.cpp
src/scripting/natives/natives_convars.cpp
src/core/memory_module.h
src/core/memory_module.cpp
src/core/cs2_sdk/interfaces/cgameresourceserviceserver.h

View File

@@ -10,7 +10,7 @@
"signatures": {
"library": "server",
"windows": "48 85 C9 0F 84 ? ? ? ? 48 89 5C 24 ? 55",
"linux": "55 48 8D 05 F8 5F 8B 00"
"linux": "55 48 8D 05 ? ? ? ? 48 89 E5 41 57 41 89 F7 31 F6"
}
},
"CCSPlayerController_SwitchTeam": {
@@ -22,14 +22,14 @@
},
"CCSPlayerController_ChangeTeam": {
"offsets": {
"windows": 100,
"linux": 99
"windows": 107,
"linux": 106
}
},
"CCSPlayerController_Respawn": {
"offsets": {
"windows": 257,
"linux": 259
"windows": 276,
"linux": 278
}
},
"CBasePlayerController_SetPawn": {
@@ -67,13 +67,6 @@
"linux": "48 89 FE 48 85 FF 74 ? 48 8D 05 ? ? ? ? 48"
}
},
"Host_Say": {
"signatures": {
"library": "server",
"windows": "44 89 4C 24 20 44 88 44 24 18",
"linux": "55 48 89 E5 41 57 49 89 F7 41 56 41 55 41 54 4D 89 C4"
}
},
"CBaseModelEntity_SetModel": {
"signatures": {
"library": "server",
@@ -98,8 +91,8 @@
"GetCSWeaponDataFromKey": {
"signatures": {
"library": "server",
"windows": "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 48 83 EC ? 33 ED 48 8B FA 8B F1",
"linux": "55 48 89 E5 41 54 53 48 81 EC 10 01 00 00 48 85 FF"
"windows": "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 48 83 EC 20 33 ED 48 8B FA 8B F1",
"linux": "55 31 D2 48 89 E5 53 89 FB"
}
},
"CCSPlayer_ItemServices_GiveNamedItem": {
@@ -135,8 +128,8 @@
},
"CCSGameRules_FindPickerEntity": {
"offsets": {
"windows": 27,
"linux": 28
"windows": 25,
"linux": 26
}
},
"UTIL_CreateEntityByName": {
@@ -163,7 +156,7 @@
"CEntityInstance_AcceptInput": {
"signatures": {
"library": "server",
"windows": "E8 ? ? ? ? F6 44 24 ? ? 74 ? 48 8B 05 ? ? ? ? 48 8B 54 24 ? 48 8B 08 48 8B 01 FF 50 ? 48 83 C4 ? 5B C3 CC CC CC 48 89 5C 24",
"windows": "89 5C 24 ? 48 89 74 24 ? 57 48 83 EC ? 49 8B F0 48 8B D9 48 8B 0D",
"linux": "55 48 89 F0 48 89 E5 41 57 49 89 FF 41 56 48 8D 7D C0"
}
},
@@ -183,8 +176,8 @@
},
"CBasePlayerPawn_CommitSuicide": {
"offsets": {
"windows": 380,
"linux": 380
"windows": 404,
"linux": 404
}
},
"CBasePlayerPawn_RemovePlayerItem": {
@@ -196,8 +189,8 @@
},
"CBaseEntity_Teleport": {
"offsets": {
"windows": 157,
"linux": 156
"windows": 166,
"linux": 165
}
},
"CBaseEntity_TakeDamageOld": {
@@ -237,14 +230,14 @@
"signatures": {
"library": "server",
"windows": "4C 89 4C 24 ? 48 89 4C 24 ? 53 56",
"linux": "55 48 89 E5 41 57 41 56 41 55 41 54 49 89 D4 53 48 89 F3 48 83 EC 58"
"linux": "55 48 89 E5 41 57 49 89 FF 41 56 41 55 41 54 49 89 D4 53 48 89 F3"
}
},
"IGameSystem_InitAllSystems_pFirst": {
"signatures": {
"library": "server",
"windows": "48 89 5C 24 ? 55 56 57 48 83 EC ? 48 8D 05",
"linux": "4C 8B 35 ? ? ? ? 4D 85 F6 75 ? E9"
"windows": "48 8B 1D ? ? ? ? 48 85 DB 0F 84 ? ? ? ? BD",
"linux": "4C 8B 35 ? ? ? ? 4D 85 F6 75"
}
},
"CEntityResourceManifest_AddResource": {
@@ -262,8 +255,27 @@
},
"CheckTransmitPlayerSlot": {
"offsets": {
"windows": 584,
"linux": 584
"windows": 576,
"linux": 576
}
},
"NetworkStateChanged": {
"signatures": {
"library": "server",
"windows": "4C 8B C2 48 8B D1 48 8B 09",
"linux": "48 8B 07 48 85 C0 74 ? 48 8B 50 10"
}
},
"SetStateChanged": {
"offsets": {
"windows": 25,
"linux": 26
}
},
"ISource2GameEntities::CheckTransmit": {
"offsets": {
"windows": 12,
"linux": 13
}
}
}

View File

@@ -218,6 +218,156 @@ namespace CounterStrikeSharp.API.Core
}
}
public static void SetConvarFlags(ushort convar, ulong flags){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(convar);
ScriptContext.GlobalScriptContext.Push(flags);
ScriptContext.GlobalScriptContext.SetIdentifier(0xB2BDCCBF);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static ulong GetConvarFlags(ushort convar){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(convar);
ScriptContext.GlobalScriptContext.SetIdentifier(0x94829E2B);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (ulong)ScriptContext.GlobalScriptContext.GetResult(typeof(ulong));
}
}
public static short GetConvarType(ushort convar){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(convar);
ScriptContext.GlobalScriptContext.SetIdentifier(0xB6E0E54C);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (short)ScriptContext.GlobalScriptContext.GetResult(typeof(short));
}
}
public static string GetConvarName(ushort convar){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(convar);
ScriptContext.GlobalScriptContext.SetIdentifier(0xB6F0E2F3);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (string)ScriptContext.GlobalScriptContext.GetResult(typeof(string));
}
}
public static string GetConvarHelpText(ushort convar){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(convar);
ScriptContext.GlobalScriptContext.SetIdentifier(0x341D1F67);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (string)ScriptContext.GlobalScriptContext.GetResult(typeof(string));
}
}
public static ushort GetConvarAccessIndexByName(string name){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(name);
ScriptContext.GlobalScriptContext.SetIdentifier(0x6288420D);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (ushort)ScriptContext.GlobalScriptContext.GetResult(typeof(ushort));
}
}
public static T GetConvarValue<T>(ushort convar){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(convar);
ScriptContext.GlobalScriptContext.SetIdentifier(0x935B2E9F);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (T)ScriptContext.GlobalScriptContext.GetResult(typeof(T));
}
}
public static string GetConvarValueAsString(ushort convar){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(convar);
ScriptContext.GlobalScriptContext.SetIdentifier(0x5CC184F8);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (string)ScriptContext.GlobalScriptContext.GetResult(typeof(string));
}
}
public static IntPtr GetConvarValueAddress(ushort convar){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(convar);
ScriptContext.GlobalScriptContext.SetIdentifier(0xECC4CC16);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (IntPtr)ScriptContext.GlobalScriptContext.GetResult(typeof(IntPtr));
}
}
public static void SetConvarValueAsString(ushort convar, string value){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(convar);
ScriptContext.GlobalScriptContext.Push(value);
ScriptContext.GlobalScriptContext.SetIdentifier(0x5EF52D6C);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static void SetConvarValue<T>(ushort convar, T value){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(convar);
ScriptContext.GlobalScriptContext.Push(value);
ScriptContext.GlobalScriptContext.SetIdentifier(0xB3DDAA0B);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static ushort CreateConvar<T>(string name, short type, string helptext, ulong flags, bool hasmin, bool hasmax, T defaultvalue, T minvalue, T maxvalue){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(name);
ScriptContext.GlobalScriptContext.Push(type);
ScriptContext.GlobalScriptContext.Push(helptext);
ScriptContext.GlobalScriptContext.Push(flags);
ScriptContext.GlobalScriptContext.Push(hasmin);
ScriptContext.GlobalScriptContext.Push(hasmax);
ScriptContext.GlobalScriptContext.Push(defaultvalue);
ScriptContext.GlobalScriptContext.Push(minvalue);
ScriptContext.GlobalScriptContext.Push(maxvalue);
ScriptContext.GlobalScriptContext.SetIdentifier(0xF22079B9);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (ushort)ScriptContext.GlobalScriptContext.GetResult(typeof(ushort));
}
}
public static void DeleteConvar(ushort convar){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(convar);
ScriptContext.GlobalScriptContext.SetIdentifier(0xFC28F444);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static string GetStringFromSymbolLarge(IntPtr pointer){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
@@ -725,6 +875,18 @@ namespace CounterStrikeSharp.API.Core
}
}
public static void ClientPrint(int slot, int huddestination, string msg){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(slot);
ScriptContext.GlobalScriptContext.Push(huddestination);
ScriptContext.GlobalScriptContext.Push(msg);
ScriptContext.GlobalScriptContext.SetIdentifier(0x8F03FA72);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static IntPtr GetEntityFromIndex(int index){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
@@ -1393,6 +1555,32 @@ namespace CounterStrikeSharp.API.Core
}
}
public static void SchemaSetStateChanged(IntPtr instance, uint offset, uint arrayindex, uint pathindex){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(instance);
ScriptContext.GlobalScriptContext.Push(offset);
ScriptContext.GlobalScriptContext.Push(arrayindex);
ScriptContext.GlobalScriptContext.Push(pathindex);
ScriptContext.GlobalScriptContext.SetIdentifier(0x7D697B7C);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static void SchemaNetworkStateChanged(IntPtr instance, uint offset, uint arrayindex, uint pathindex){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(instance);
ScriptContext.GlobalScriptContext.Push(offset);
ScriptContext.GlobalScriptContext.Push(arrayindex);
ScriptContext.GlobalScriptContext.Push(pathindex);
ScriptContext.GlobalScriptContext.SetIdentifier(0xBBE9D700);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static IntPtr GetEconItemSystem(){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();

View File

@@ -50,7 +50,7 @@ public partial class CCSPlayerController
{
Guard.IsValidEntity(this);
VirtualFunctions.ClientPrint(Handle, HudDestination.Chat, message, 0, 0, 0, 0);
NativeAPI.ClientPrint(this.Slot, (int)HudDestination.Chat, message);
}
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
@@ -58,7 +58,7 @@ public partial class CCSPlayerController
{
Guard.IsValidEntity(this);
VirtualFunctions.ClientPrint(Handle, HudDestination.Center, message, 0, 0, 0, 0);
NativeAPI.ClientPrint(this.Slot, (int)HudDestination.Center, message);
}
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
@@ -66,7 +66,7 @@ public partial class CCSPlayerController
{
Guard.IsValidEntity(this);
VirtualFunctions.ClientPrint(Handle, HudDestination.Alert, message, 0, 0, 0, 0);
NativeAPI.ClientPrint(this.Slot, (int)HudDestination.Alert, message);
}
public void PrintToCenterHtml(string message) => PrintToCenterHtml(message, 5);

View File

@@ -26,6 +26,11 @@
<None Remove="Modules\Commands\CommandInfo"/>
<None Remove="Modules\Disabled\**"/>
</ItemGroup>
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>NativeTestsPlugin</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
<ItemGroup>
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.4.0"/>
<PackageReference Include="Microsoft.CSharp" Version="4.7.0"/>
@@ -68,6 +73,7 @@
</Exec>
</Target>
<!-- <Target Name="PreBuild" BeforeTargets="PreBuildEvent">-->
<!-- <Exec Command="dotnet run &#45;&#45;project ../../tooling/CodeGen.Natives" />-->
<!-- </Target>-->

View File

@@ -1,30 +1,32 @@
using System;
using System.Runtime.CompilerServices;
using CounterStrikeSharp.API.Core;
using System.Runtime.CompilerServices;
namespace CounterStrikeSharp.API.Modules.Cvars;
public class ConVar
{
public IntPtr Handle { get; }
public ushort AccessIndex { get; protected set; }
public ConVar(IntPtr handle)
public ConVar(ushort accessIndex)
{
Handle = handle;
AccessIndex = accessIndex;
}
public string Name => Utilities.ReadStringUtf8(Handle);
public string Description => Utilities.ReadStringUtf8(Handle + 32);
public string Name => NativeAPI.GetConvarName(AccessIndex);
public string Description => NativeAPI.GetConvarHelpText(AccessIndex);
/// <summary>
/// The underlying data type of the ConVar.
/// </summary>
public unsafe ref ConVarType Type => ref Unsafe.AsRef<ConVarType>((void*)(Handle + 40));
public ConVarType Type => (ConVarType)NativeAPI.GetConvarType(AccessIndex);
/// <summary>
/// The ConVar flags as defined by <see cref="ConVarFlags"/>.
/// </summary>
public unsafe ref ConVarFlags Flags => ref Unsafe.AsRef<ConVarFlags>((void*)(Handle + 48));
public ConVarFlags Flags
{
get => (ConVarFlags)NativeAPI.GetConvarFlags(AccessIndex);
set => NativeAPI.SetConvarFlags(AccessIndex, (ulong)value);
}
/// <summary>
/// Used to access primitive value types, i.e. <see langword="bool"/>, <see langword="float"/>, <see langword="int"/>, etc.
@@ -85,12 +87,18 @@ public class ConVar
throw new InvalidOperationException("Reference types must be accessed using `GetReferenceValue`");
}
return ref Unsafe.AsRef<T>((void*)(Handle + 64));
var address = NativeAPI.GetConvarValueAddress(AccessIndex);
if (address == IntPtr.Zero)
{
throw new InvalidOperationException($"ConVar {Name} is not initialized or does not have a value.");
}
return ref Unsafe.AsRef<T>((void*)address);
}
public void SetValue<T>(T value)
{
GetPrimitiveValue<T>() = value;
NativeAPI.SetConvarValue(AccessIndex, value);
}
/// <summary>
@@ -100,7 +108,7 @@ public class ConVar
/// <returns></returns>
public T GetNativeValue<T>() where T : NativeObject
{
return (T)Activator.CreateInstance(typeof(T), Handle + 64);
return NativeAPI.GetConvarValue<T>(AccessIndex);
}
/// <summary>
@@ -110,26 +118,8 @@ public class ConVar
/// </remarks>
public string StringValue
{
get
{
if (Type != ConVarType.String)
{
throw new InvalidOperationException(
$"ConVar is a {Type} but you are trying to get a string value.");
}
return Utilities.ReadStringUtf8(Handle + 64);
}
set
{
if (Type != ConVarType.String)
{
throw new InvalidOperationException(
$"ConVar is a {Type} but you are trying to get a string value.");
}
NativeAPI.SetConvarStringValue(Handle, value);
}
get => NativeAPI.GetConvarValueAsString(AccessIndex);
set => NativeAPI.SetConvarValueAsString(AccessIndex, value);
}
/// <summary>
@@ -163,9 +153,9 @@ public class ConVar
/// <returns></returns>
public static ConVar? Find(string name)
{
var ptr = NativeAPI.FindConvar(name);
if (ptr == IntPtr.Zero) return null;
var accessIndex = NativeAPI.GetConvarAccessIndexByName(name);
if (accessIndex == 0) return null;
return new ConVar(ptr);
return new ConVar(accessIndex);
}
}

View File

@@ -60,38 +60,48 @@ public static class VirtualFunctions
public static Action<IntPtr, IntPtr> CBaseEntity_DispatchSpawn = CBaseEntity_DispatchSpawnFunc.Invoke;
public static MemoryFunctionVoid<CBasePlayerController, CBasePlayerPawn, bool, bool> CBasePlayerController_SetPawnFunc = new (GameData.GetSignature("CBasePlayerController_SetPawn"));
public static MemoryFunctionVoid<CBasePlayerController, CBasePlayerPawn, bool, bool> CBasePlayerController_SetPawnFunc =
new(GameData.GetSignature("CBasePlayerController_SetPawn"));
public static MemoryFunctionVoid<CEntityInstance, CTakeDamageInfo> CBaseEntity_TakeDamageOldFunc =
new(GameData.GetSignature("CBaseEntity_TakeDamageOld"));
public static MemoryFunctionVoid<CEntityInstance, CTakeDamageInfo> CBaseEntity_TakeDamageOldFunc = new (GameData.GetSignature("CBaseEntity_TakeDamageOld"));
public static Action<CEntityInstance, CTakeDamageInfo> CBaseEntity_TakeDamageOld = CBaseEntity_TakeDamageOldFunc.Invoke;
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<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 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 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 MemoryFunctionVoid<CCSPlayerPawnBase> CCSPlayerPawnBase_PostThinkFunc = new (GameData.GetSignature("CCSPlayerPawnBase_PostThink"));
public static Action<CCSPlayerPawnBase> CCSPlayerPawnBase_PostThink = CCSPlayerPawnBase_PostThinkFunc.Invoke;
public static MemoryFunctionVoid<CBaseTrigger, CBaseEntity> CBaseTrigger_StartTouchFunc = new (GameData.GetSignature("CBaseTrigger_StartTouch"));
public static MemoryFunctionVoid<CBaseTrigger, CBaseEntity> CBaseTrigger_StartTouchFunc =
new(GameData.GetSignature("CBaseTrigger_StartTouch"));
public static Action<CBaseTrigger, CBaseEntity> CBaseTrigger_StartTouch = CBaseTrigger_StartTouchFunc.Invoke;
public static MemoryFunctionVoid<CBaseTrigger, CBaseEntity> CBaseTrigger_EndTouchFunc = new (GameData.GetSignature("CBaseTrigger_EndTouch"));
public static MemoryFunctionVoid<CBaseTrigger, CBaseEntity> CBaseTrigger_EndTouchFunc =
new(GameData.GetSignature("CBaseTrigger_EndTouch"));
public static Action<CBaseTrigger, CBaseEntity> CBaseTrigger_EndTouch = CBaseTrigger_EndTouchFunc.Invoke;
public static MemoryFunctionVoid<IntPtr, IntPtr> RemovePlayerItemFunc =
new(GameData.GetSignature("CBasePlayerPawn_RemovePlayerItem"));
public static Action<IntPtr, IntPtr> RemovePlayerItemVirtual = RemovePlayerItemFunc.Invoke;
public static MemoryFunctionVoid<IntPtr, IntPtr, int, short, int> StateChangedFunc =
new(GameData.GetSignature("StateChanged"));
public static Action<IntPtr, IntPtr, int, short, int> StateChanged = StateChangedFunc.Invoke;
public static MemoryFunctionVoid<IntPtr, int, long> NetworkStateChangedFunc = new(GameData.GetSignature("NetworkStateChanged"));
public static Action<IntPtr, int, long> NetworkStateChanged = NetworkStateChangedFunc.Invoke;
}

View File

@@ -238,11 +238,11 @@ namespace CounterStrikeSharp.API
if (chainOffset != 0)
{
VirtualFunctions.NetworkStateChanged(entity.Handle + chainOffset, offset + extraOffset, 0xFFFFFFFF);
NativeAPI.SchemaNetworkStateChanged(entity.Handle + chainOffset, (uint)(offset + extraOffset), 0xFFFFFFFF, 0xFFFFFFFF);
return;
}
VirtualFunctions.StateChanged(entity.NetworkTransmitComponent.Handle, entity.Handle, offset + extraOffset, -1, -1);
NativeAPI.SchemaSetStateChanged(entity.Handle, (uint)(offset + extraOffset), 0xFFFFFFFF, 0xFFFFFFFF);
entity.LastNetworkChange = Server.CurrentTime;
entity.IsSteadyState.Clear();
@@ -260,6 +260,7 @@ namespace CounterStrikeSharp.API
{
return null;
}
return ptr;
}
}

View File

@@ -2,6 +2,7 @@ using System;
using System.Threading.Tasks;
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Commands;
using Moq;
using Xunit;
@@ -9,34 +10,75 @@ namespace NativeTestsPlugin;
public class CommandTests
{
private readonly Mock<Action> _mock;
private readonly FunctionReference _methodCallback;
public CommandTests()
{
_mock = new Mock<Action>();
_methodCallback = FunctionReference.Create(() => { _mock.Object.Invoke(); });
}
[Fact]
public async Task AddCommandHandler()
{
var mock = new Mock<Action>();
var methodCallback = FunctionReference.Create(() =>
NativeAPI.AddCommand("css_test_native", "description", true, (int)ConCommandFlags.FCVAR_LINKED_CONCOMMAND, _methodCallback);
NativeAPI.IssueServerCommand("css_test_native");
await WaitOneFrame();
_mock.Verify(s => s(), Times.Once);
NativeAPI.RemoveCommand("css_test_native", _methodCallback);
NativeAPI.IssueServerCommand("css_test_native");
await WaitOneFrame();
_mock.Verify(s => s(), Times.Once);
NativeAPI.RemoveCommand("css_test_native", _methodCallback);
}
[Fact]
public async Task CanTriggerCommandsWithPublicChatTrigger()
{
mock.Object.Invoke();
var mock = new Mock<Action<int, IntPtr>>();
var methodCallback = FunctionReference.Create((int playerSlot, IntPtr commandInfo) =>
{
var cmdInfo = new CommandInfo(commandInfo, null);
Assert.Equal("css_test_public_chat", cmdInfo.GetArg(0));
Assert.Equal(4, cmdInfo.ArgCount);
Assert.Equal("1", cmdInfo.GetArg(1));
Assert.Equal("2", cmdInfo.GetArg(2));
Assert.Equal("3", cmdInfo.GetArg(3));
mock.Object.Invoke(playerSlot, commandInfo);
});
NativeAPI.AddCommand("test_native", "description", true, (int)ConCommandFlags.FCVAR_LINKED_CONCOMMAND, methodCallback);
NativeAPI.IssueServerCommand("test_native");
NativeAPI.AddCommand("css_test_public_chat", "description", true, (int)ConCommandFlags.FCVAR_LINKED_CONCOMMAND, methodCallback);
NativeAPI.IssueServerCommand("css_test_public_chat 1 2 3");
await WaitOneFrame();
mock.Verify(s => s(), Times.Once);
mock.Verify(s => s(It.IsAny<int>(), It.IsAny<IntPtr>()), Times.Once);
NativeAPI.RemoveCommand("test_native", methodCallback);
NativeAPI.IssueServerCommand("test_native");
NativeAPI.IssueServerCommand("say \"!test_public_chat 1 2 3\"");
await WaitOneFrame();
mock.Verify(s => s(), Times.Once);
mock.Verify(s => s(It.IsAny<int>(), It.IsAny<IntPtr>()), Times.Exactly(2));
NativeAPI.RemoveCommand("css_test_public_chat", methodCallback);
}
[Fact]
public async Task CanTriggerCommandsWithSilentChatTrigger()
{
NativeAPI.AddCommand("css_test_silent_chat", "description", true, (int)ConCommandFlags.FCVAR_LINKED_CONCOMMAND, _methodCallback);
NativeAPI.IssueServerCommand("css_test_silent_chat");
await WaitOneFrame();
_mock.Verify(s => s(), Times.Once);
NativeAPI.IssueServerCommand("say \"!test_silent_chat\"");
await WaitOneFrame();
_mock.Verify(s => s(), Times.Exactly(2));
NativeAPI.RemoveCommand("css_test_silent_chat", _methodCallback);
}
[Fact]
public async Task IssueServerCommand()
{
bool called = false;
NativeAPI.AddCommandListener("say", FunctionReference.Create(() =>
{
called = true;
}), true);
NativeAPI.AddCommandListener("say", FunctionReference.Create(() => { called = true; }), true);
NativeAPI.IssueServerCommand("say Hello, world!");
await WaitOneFrame();

View File

@@ -0,0 +1,94 @@
using System.Threading.Tasks;
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Modules.Cvars;
using CounterStrikeSharp.API.Modules.Utils;
using Xunit;
namespace NativeTestsPlugin;
public class ConVarTests
{
[Fact]
public async Task BoolConVar()
{
Server.ExecuteCommand("sv_cheats 1");
await WaitOneFrame();
var boolConVar = ConVar.Find("sv_cheats");
Assert.NotNull(boolConVar);
Assert.Equal("sv_cheats", boolConVar.Name);
Assert.Equal(ConVarType.Bool, boolConVar.Type);
Assert.Equal(ConVarFlags.FCVAR_NOTIFY | ConVarFlags.FCVAR_REPLICATED | ConVarFlags.FCVAR_RELEASE, boolConVar.Flags);
Assert.True(boolConVar.GetPrimitiveValue<bool>());
boolConVar.GetPrimitiveValue<bool>() = false;
Assert.False(boolConVar.GetPrimitiveValue<bool>());
Server.ExecuteCommand("sv_cheats 1");
await WaitOneFrame();
}
[Fact]
public async Task IntConVar()
{
Server.ExecuteCommand("mp_td_dmgtokick 300");
await WaitOneFrame();
var intConVar = ConVar.Find("mp_td_dmgtokick");
Assert.NotNull(intConVar);
Assert.Equal("mp_td_dmgtokick", intConVar.Name);
Assert.Equal(ConVarType.Int32, intConVar.Type);
Assert.Equal(300, intConVar.GetPrimitiveValue<int>());
intConVar.GetPrimitiveValue<int>() = 500;
Assert.Equal(500, intConVar.GetPrimitiveValue<int>());
}
[Fact]
public async Task FloatConVar()
{
Server.ExecuteCommand("inferno_damage 40.0");
await WaitOneFrame();
var floatConVar = ConVar.Find("inferno_damage");
Assert.NotNull(floatConVar);
Assert.Equal(ConVarType.Float32, floatConVar.Type);
Assert.Equal(40.0, floatConVar.GetPrimitiveValue<float>());
floatConVar.GetPrimitiveValue<float>() = 50.0f;
Assert.Equal(50.0f, floatConVar.GetPrimitiveValue<float>());
}
[Fact]
public async Task VectorConVar()
{
Server.ExecuteCommand("fog_color -1 -1 -1");
await WaitOneFrame();
var vectorConVar = ConVar.Find("fog_color");
Assert.NotNull(vectorConVar);
Assert.Equal(-1, vectorConVar.GetNativeValue<Vector>().X);
Assert.Equal(-1, vectorConVar.GetNativeValue<Vector>().Y);
Assert.Equal(-1, vectorConVar.GetNativeValue<Vector>().Z);
var vec = vectorConVar.GetNativeValue<Vector>();
vec.X = 0;
vec.Y = 0;
vec.Z = 0;
Assert.Equal(0, vectorConVar.GetNativeValue<Vector>().X);
Assert.Equal(0, vectorConVar.GetNativeValue<Vector>().Y);
Assert.Equal(0, vectorConVar.GetNativeValue<Vector>().Z);
}
[Fact]
public async Task StringConVar()
{
Server.ExecuteCommand("mp_backup_round_file backup");
await WaitOneFrame();
var stringConVar = ConVar.Find("mp_backup_round_file");
Assert.NotNull(stringConVar);
Assert.Equal("backup", stringConVar.StringValue);
stringConVar.StringValue = "new_backup";
}
}

View File

@@ -13,6 +13,7 @@
* 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;
using System.Reflection;
using System.Threading;
@@ -40,6 +41,7 @@ namespace NativeTestsPlugin
gameThreadId = Thread.CurrentThread.ManagedThreadId;
// Loading blocks the game thread, so we use NextFrame to run our tests asynchronously.
Server.NextFrame(() => RunTests());
AddCommand("css_run_tests", "Runs the xUnit tests for the native plugin.", (player, info) => { RunTests(); });
}
async Task RunTests()
@@ -82,6 +84,7 @@ namespace NativeTestsPlugin
public class SourceSynchronizationContext : SynchronizationContext
{
private readonly int _mainThreadId;
public SourceSynchronizationContext(int mainThreadId)
{
_mainThreadId = mainThreadId;

View File

@@ -125,14 +125,18 @@ SchemaKey schema::GetOffset(const char* className, uint32_t classKey, const char
return tableMap->Element(memberIndex);
}
void SetStateChanged(uintptr_t entityInstance, int nOffset)
void NetworkStateChanged(uintptr_t chainEntity, uint32_t offset, uint32_t nArrayIndex, uint32_t nPathIndex)
{
reinterpret_cast<CEntityInstance*>(entityInstance)->NetworkStateChanged(nOffset);
CNetworkStateChangedInfo info(offset, nArrayIndex, nPathIndex);
if (counterstrikesharp::globals::NetworkStateChanged)
counterstrikesharp::globals::NetworkStateChanged(reinterpret_cast<void*>(chainEntity), info);
}
void ChainNetworkStateChanged(uintptr_t networkVarChainer, uint nLocalOffset)
void SetStateChanged(uintptr_t pEntity, uint32_t offset, uint32_t nArrayIndex, uint32_t nPathIndex)
{
CEntityInstance* pEntity = *reinterpret_cast<CEntityInstance**>(networkVarChainer);
if (pEntity && (pEntity->m_pEntity->m_flags & EF_IS_CONSTRUCTION_IN_PROGRESS) == 0)
pEntity->NetworkStateChanged(nLocalOffset, -1, *reinterpret_cast<ChangeAccessorFieldPathIndex_t*>(networkVarChainer + 32));
CNetworkStateChangedInfo info(offset, nArrayIndex, nPathIndex);
static auto fnOffset = counterstrikesharp::globals::gameConfig->GetOffset("SetStateChanged");
CALL_VIRTUAL(void, fnOffset, (void*)pEntity, &info);
}

View File

@@ -24,6 +24,7 @@
#include <stdint.h>
#include "tier0/dbg.h"
#include "utils/virtual.h"
#include "tier1/utlvector.h"
#include <string>
#include <vector>
@@ -36,9 +37,37 @@ struct SchemaKey
bool networked;
};
class Z_CBaseEntity;
void SetStateChanged(uintptr_t pEntity, int offset);
void ChainNetworkStateChanged(uintptr_t pEntity, int offset);
class CBaseEntity;
struct CNetworkStateChangedInfo
{
CNetworkStateChangedInfo() = delete;
CNetworkStateChangedInfo(uint32_t nOffset, uint32_t nArrayIndex, uint32_t nPathIndex)
{
m_vecOffsetData.EnsureCount(1);
m_vecOffsetData[0] = nOffset;
unk_30 = -1;
unk_3c = 0;
m_nArrayIndex = nArrayIndex;
m_nPathIndex = nPathIndex;
}
private:
int m_nSize; // 0x0
CUtlVector<uint32_t> m_vecOffsetData; // 0x8
char* m_pszFieldName{}; // 0x20
char* m_pszFileName{}; // 0x28
uint32_t unk_30 = -1; // 0x30
uint32_t m_nArrayIndex{}; // 0x34
uint32_t m_nPathIndex{}; // 0x38
uint16_t unk_3c{}; // 0x3c
}; // Size: 0x3e
void NetworkStateChanged(uintptr_t chainEntity, uint32_t offset, uint32_t nArrayIndex = -1, uint32_t nPathIndex = -1);
void SetStateChanged(uintptr_t pEntity, uint32_t offset, uint32_t nArrayIndex = -1, uint32_t nPathIndex = -1);
constexpr uint32_t val_32_const = 0x811c9dc5;
constexpr uint32_t prime_32_const = 0x1000193;
@@ -130,7 +159,7 @@ SchemaKey GetOffset(const char* className, uint32_t classKey, const char* member
if (m_chain != 0 && m_key.networked) \
{ \
DevMsg("Found chain offset %d for %s::%s\n", m_chain, ThisClassName, #varName); \
ChainNetworkStateChanged((uintptr_t)(pThisClass) + m_chain, m_key.offset + extra_offset); \
SetStateChanged((uintptr_t)(pThisClass) + m_chain, m_key.offset + extra_offset); \
} \
else if (m_key.networked) \
{ \

View File

@@ -70,7 +70,7 @@ GS_EVENT_MEMBER(CGameSystem, BuildGameSessionManifest)
GS_EVENT_MEMBER(CGameSystem, ServerPreEntityThink)
{
VPROF_BUDGET("CS#::CGameSystem::ServerPreEntityThink", "CS# On Frame");
// VPROF_BUDGET("CS#::CGameSystem::ServerPreEntityThink", "CS# On Frame");
auto callback = counterstrikesharp::globals::serverManager.on_server_pre_entity_think;
if (callback && callback->GetFunctionCount())
@@ -88,7 +88,7 @@ GS_EVENT_MEMBER(CGameSystem, ServerPreEntityThink)
GS_EVENT_MEMBER(CGameSystem, ServerPostEntityThink)
{
VPROF_BUDGET("CS#::CGameSystem::ServerPostEntityThink", "CS# On Frame");
// VPROF_BUDGET("CS#::CGameSystem::ServerPostEntityThink", "CS# On Frame");
auto callback = counterstrikesharp::globals::serverManager.on_server_post_entity_think;
if (callback && callback->GetFunctionCount())

View File

@@ -109,13 +109,21 @@ void Initialize()
modules::schemasystem = modules::GetModuleByName(MODULE_PREFIX "schemasystem" MODULE_EXT);
modules::vscript = modules::GetModuleByName(MODULE_PREFIX "vscript" MODULE_EXT);
interfaces::Initialize();
entitySystem = interfaces::pGameResourceServiceServer->GetGameEntitySystem();
if (!interfaces::pGameResourceServiceServer)
{
CSSHARP_CORE_ERROR("Failed to get CGameResourceServiceServer");
return;
}
GetLegacyGameEventListener = reinterpret_cast<GetLegacyGameEventListener_t*>(
modules::server->FindSignature(globals::gameConfig->GetSignature("LegacyGameEventListener")));
if (GetLegacyGameEventListener == nullptr)
{
CSSHARP_CORE_ERROR("Failed to find signature for \'GetLegacyGameEventListener\'");
return;
}
GameEventManagerInit = reinterpret_cast<GameEventManagerInit_t*>(
modules::server->FindSignature(globals::gameConfig->GetSignature("CGameEventManager_Init")));
@@ -125,6 +133,15 @@ void Initialize()
return;
}
NetworkStateChanged =
reinterpret_cast<NetworkStateChanged_t*>(modules::server->FindSignature(globals::gameConfig->GetSignature("NetworkStateChanged")));
if (NetworkStateChanged == nullptr)
{
CSSHARP_CORE_ERROR("Failed to find signature for \'NetworkStateChanged\'");
return;
}
auto m_hook = funchook_create();
funchook_prepare(m_hook, (void**)&GameEventManagerInit, (void*)&DetourGameEventManagerInit);
funchook_install(m_hook, 0);

View File

@@ -37,6 +37,7 @@ class CounterStrikeSharpMMPlugin;
class CGameEntitySystem;
class IGameEventListener2;
class CSchemaSystem;
class CNetworkStateChangedInfo;
namespace counterstrikesharp {
class EntityListener;
@@ -120,11 +121,13 @@ extern const float engine_fixed_tick_interval;
typedef void GameEventManagerInit_t(IGameEventManager2* gameEventManager);
typedef IGameEventListener2* GetLegacyGameEventListener_t(CPlayerSlot slot);
typedef void* NetworkStateChanged_t(void* chainEntity, CNetworkStateChangedInfo& info);
static void DetourGameEventManagerInit(IGameEventManager2* gameEventManager);
extern bool gameLoopInitialized;
extern GetLegacyGameEventListener_t* GetLegacyGameEventListener;
inline NetworkStateChanged_t* NetworkStateChanged = nullptr;
extern std::thread::id gameThreadId;
void Initialize();

View File

@@ -35,24 +35,11 @@ ChatManager::ChatManager() {}
ChatManager::~ChatManager() {}
void ChatManager::OnAllInitialized()
{
m_pHostSay = reinterpret_cast<HostSay>(modules::server->FindSignature(globals::gameConfig->GetSignature("Host_Say")));
if (m_pHostSay == nullptr)
{
CSSHARP_CORE_ERROR("Failed to find signature for \'Host_Say\'");
return;
}
auto m_hook = funchook_create();
funchook_prepare(m_hook, (void**)&m_pHostSay, (void*)&DetourHostSay);
funchook_install(m_hook, 0);
}
void ChatManager::OnAllInitialized() {}
void ChatManager::OnShutdown() {}
void DetourHostSay(CEntityInstance* pController, CCommand& args, bool teamonly, int unk1, const char* unk2)
bool ChatManager::OnSayCommand(CEntityInstance* pController, const CCommand& args, bool teamonly)
{
if (pController)
{
@@ -71,32 +58,35 @@ void DetourHostSay(CEntityInstance* pController, CCommand& args, bool teamonly,
bool bSilent = globals::coreConfig->IsSilentChatTrigger(args[1], prefix);
bool bCommand = globals::coreConfig->IsPublicChatTrigger(args[1], prefix) || bSilent;
if (!bSilent)
{
m_pHostSay(pController, args, teamonly, unk1, unk2);
}
if (bCommand)
{
char* pszMessage = (char*)(args.ArgS() + prefix.length() + 1);
auto message = std::string(args.ArgS());
// Trailing slashes are only removed if Host_Say has been called.
if (bSilent) pszMessage[V_strlen(pszMessage) - 1] = 0;
// trim quotes off message if they appear, then trim the prefix
// "!foobar" -> foobar
// !foobar -> foobar
if (message.size() >= 2 && message.front() == '"' && message.back() == '"')
{
message = message.substr(1, message.size() - 2);
}
message = message.substr(prefix.size());
CCommand args;
args.Tokenize(pszMessage);
CCommand newArgs;
newArgs.Tokenize(message.c_str());
auto prefixedPhrase = std::string("css_") + args.Arg(0);
auto prefixedPhrase = std::string("css_") + newArgs.Arg(0);
auto bValidWithPrefix = globals::conCommandManager.IsValidValveCommand(prefixedPhrase.c_str());
if (bValidWithPrefix)
{
// Re-tokenize with a `css_` prefix if we have found that its a valid command.
args.Tokenize(("css_" + std::string(pszMessage)).c_str());
newArgs.Tokenize(("css_" + std::string(message)).c_str());
}
globals::chatManager.OnSayCommandPost(pController, args);
globals::chatManager.OnSayCommandPost(pController, newArgs);
}
return bSilent;
}
bool ChatManager::OnSayCommandPre(CEntityInstance* pController, CCommand& command) { return false; }

View File

@@ -54,6 +54,7 @@ class ChatManager : public GlobalClass
bool OnSayCommandPre(CEntityInstance* pController, CCommand& args);
void OnSayCommandPost(CEntityInstance* pController, CCommand& args);
static bool OnSayCommand(CEntityInstance* pController, const CCommand& args, bool teamonly);
private:
void InternalDispatch(CEntityInstance* pPlayerController, const char* szTriggerPhrase, CCommand& pFullCommand);
@@ -62,7 +63,4 @@ class ChatManager : public GlobalClass
std::map<std::string, ChatCommandInfo*> m_cmd_lookup;
};
static void DetourHostSay(CEntityInstance* pController, CCommand& args, bool teamonly, int unk1, const char* unk2);
static HostSay m_pHostSay = nullptr;
} // namespace counterstrikesharp

View File

@@ -31,6 +31,9 @@
#include "core/managers/con_command_manager.h"
#include "chat_manager.h"
#include "entitysystem.h"
#include <nlohmann/json.hpp>
#include <public/eiface.h>
#include <schemasystem.h>
@@ -464,6 +467,24 @@ void ConCommandManager::Hook_DispatchConCommand(ConCommandRef cmd, const CComman
CSSHARP_CORE_TRACE("[ConCommandManager::Hook_DispatchConCommand]: {}", name);
auto slot = ctx.GetPlayerSlot();
bool isSay = V_strcmp(name, "say");
bool isTeamSay = V_strcmp(name, "say_team");
if (isSay || isTeamSay)
{
CEntityInstance* entityInstance = nullptr;
if (globals::entitySystem && (slot != -1))
{
entityInstance = globals::entitySystem->GetEntityInstance(CEntityIndex(slot.Get() + 1));
}
if (globals::chatManager.OnSayCommand(entityInstance, args, isTeamSay))
{
RETURN_META(MRES_SUPERCEDE);
}
}
auto result = ExecuteCommandCallbacks(name, ctx, args, HookMode::Pre, CommandCallingContext::Console);
if (result >= HookResult::Handled)
{

View File

@@ -25,17 +25,18 @@
#include <public/eiface.h>
#include "scripting/callback_manager.h"
SH_DECL_HOOK7_void(ISource2GameEntities,
CheckTransmit,
SH_NOATTRIB,
SH_DECL_MANUALHOOK8_void(CheckTransmit,
0,
CCheckTransmitInfo**,
int,
0,
0,
ISource2GameEntities*,
CCheckTransmitInfoHack**,
uint32_t,
CBitVec<16384>&,
CBitVec<16384>&,
const Entity2Networkable_t**,
const uint16*,
int,
bool);
uint32_t);
namespace counterstrikesharp {
@@ -43,14 +44,17 @@ EntityManager::EntityManager() { m_profile_name = "EntityManager"; }
EntityManager::~EntityManager() {}
CCheckTransmitInfoList::CCheckTransmitInfoList(CCheckTransmitInfo** pInfoInfoList, int nInfoCount)
CCheckTransmitInfoList::CCheckTransmitInfoList(CCheckTransmitInfoHack** pInfoInfoList, int nInfoCount)
: infoList(pInfoInfoList), infoCount(nInfoCount)
{
}
int g_iCheckTransmit = -1;
void EntityManager::OnAllInitialized()
{
SH_ADD_HOOK_MEMFUNC(ISource2GameEntities, CheckTransmit, globals::gameEntities, this, &EntityManager::CheckTransmit, true);
SH_MANUALHOOK_RECONFIGURE(CheckTransmit, globals::gameConfig->GetOffset("ISource2GameEntities::CheckTransmit"), 0, 0);
g_iCheckTransmit = SH_ADD_MANUALDVPHOOK(CheckTransmit, globals::gameEntities, SH_MEMBER(this, &EntityManager::CheckTransmit), true);
check_transmit = globals::callbackManager.CreateCallback("CheckTransmit");
on_entity_spawned_callback = globals::callbackManager.CreateCallback("OnEntitySpawned");
@@ -106,8 +110,7 @@ void EntityManager::OnShutdown()
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);
SH_REMOVE_HOOK_ID(g_iCheckTransmit);
}
void CEntityListener::OnEntitySpawned(CEntityInstance* pEntity)
@@ -194,21 +197,22 @@ void EntityManager::UnhookEntityOutput(const char* szClassname, const char* szOu
}
}
void EntityManager::CheckTransmit(CCheckTransmitInfo** pInfoInfoList,
int nInfoCount,
CBitVec<16384>& unionTransmitEdicts,
void EntityManager::CheckTransmit(ISource2GameEntities* pThis,
CCheckTransmitInfoHack** ppInfoList,
uint32_t nInfoCount,
CBitVec<16384>& unionTransmitEdicts1,
CBitVec<16384>& unionTransmitEdicts2,
const Entity2Networkable_t** pNetworkables,
const uint16* pEntityIndicies,
int nEntityIndices,
bool bEnablePVSBits)
uint32_t nEntities)
{
VPROF_BUDGET(m_profile_name.c_str(), "CS# CheckTransmit");
// 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);
CCheckTransmitInfoList* infoList = new CCheckTransmitInfoList(ppInfoList, nInfoCount);
callback->ScriptContext().Reset();
callback->ScriptContext().Push(infoList);
@@ -218,8 +222,13 @@ void EntityManager::CheckTransmit(CCheckTransmitInfo** pInfoInfoList,
}
}
void DetourFireOutputInternal(
CEntityIOOutput* const pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, const CVariant* const value, float flDelay)
void DetourFireOutputInternal(CEntityIOOutput* const pThis,
CEntityInstance* pActivator,
CEntityInstance* pCaller,
const CVariant* const value,
float flDelay,
void* unk1,
char* unk2)
{
std::vector vecSearchKeys{ OutputKey_t("*", pThis->m_pDesc->m_pName), OutputKey_t("*", "*") };
@@ -288,7 +297,7 @@ void DetourFireOutputInternal(
return;
}
m_pFireOutputInternal(pThis, pActivator, pCaller, value, flDelay);
m_pFireOutputInternal(pThis, pActivator, pCaller, value, flDelay, unk1, unk2);
for (auto pCallbackPair : vecCallbackPairs)
{
@@ -308,7 +317,7 @@ void DetourFireOutputInternal(
SndOpEventGuid_t EntityEmitSoundFilter(IRecipientFilter& filter, uint32 ent, const char* pszSound, float flVolume, float flPitch)
{
if (!CBaseEntity_EmitSoundFilter)
if (true)
{
CSSHARP_CORE_ERROR("[EntityManager][EmitSoundFilter] - Failed to emit a sound. Signature for \'CBaseEntity_EmitSoundFilter\' is "
"not found. The latest update may have broken it.");

View File

@@ -29,6 +29,19 @@
#include "vprof.h"
class CCheckTransmitInfoHack
{
public:
CBitVec<16384>* m_pTransmitEntity;
private:
[[maybe_unused]] int8_t m_pad8[568];
public:
int32_t m_nPlayerSlot;
bool m_bFullUpdate;
};
namespace counterstrikesharp {
class ScriptCallback;
@@ -45,10 +58,10 @@ class CEntityListener : public IEntityListener
class CCheckTransmitInfoList
{
public:
CCheckTransmitInfoList(CCheckTransmitInfo** pInfoInfoList, int nInfoCount);
CCheckTransmitInfoList(CCheckTransmitInfoHack** pInfoInfoList, int nInfoCount);
private:
CCheckTransmitInfo** infoList;
CCheckTransmitInfoHack** infoList;
int infoCount;
};
@@ -67,13 +80,14 @@ class EntityManager : public GlobalClass
std::map<OutputKey_t, CallbackPair*> m_pHookMap;
private:
void CheckTransmit(CCheckTransmitInfo** pInfoInfoList,
int nInfoCount,
CBitVec<16384>& unionTransmitEdicts,
void CheckTransmit(ISource2GameEntities* pThis,
CCheckTransmitInfoHack** ppInfoList,
uint32_t infoCount,
CBitVec<16384>& unionTransmitEdicts1,
CBitVec<16384>& unionTransmitEdicts2,
const Entity2Networkable_t** pNetworkables,
const uint16* pEntityIndicies,
int nEntityIndices,
bool bEnablePVSBits);
uint32_t nEntities);
ScriptCallback* on_entity_spawned_callback;
ScriptCallback* on_entity_created_callback;
@@ -129,16 +143,27 @@ class CEntityIOOutput
EntityIOOutputDesc_t* m_pDesc;
};
typedef void (*FireOutputInternal)(CEntityIOOutput* const, CEntityInstance*, CEntityInstance*, const CVariant* const, float);
typedef void (*FireOutputInternal)(
CEntityIOOutput* const, CEntityInstance*, CEntityInstance*, const CVariant* const, float flDelay, void* unk1, char* unk2);
static void DetourFireOutputInternal(
CEntityIOOutput* const pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, const CVariant* const value, float flDelay);
static void DetourFireOutputInternal(CEntityIOOutput* const pThis,
CEntityInstance* pActivator,
CEntityInstance* pCaller,
const CVariant* const value,
float flDelay,
void* unk1,
char* unk2);
static FireOutputInternal m_pFireOutputInternal = nullptr;
// Do it in here because i didn't found a good place to do this
inline void (*CEntityInstance_AcceptInput)(
CEntityInstance* pThis, const char* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value, int nOutputID);
inline void (*CEntityInstance_AcceptInput)(CEntityInstance* pThis,
const char* pInputName,
CEntityInstance* pActivator,
CEntityInstance* pCaller,
variant_t* value,
int nOutputID,
void*);
inline void (*CEntitySystem_AddEntityIOEvent)(CEntitySystem* pEntitySystem,
CEntityInstance* pTarget,
@@ -147,7 +172,8 @@ inline void (*CEntitySystem_AddEntityIOEvent)(CEntitySystem* pEntitySystem,
CEntityInstance* pCaller,
variant_t* value,
float delay,
int nOutputID);
int nOutputID,
void*);
typedef uint32 SoundEventGuid_t;

View File

@@ -212,7 +212,7 @@ bool EventManager::OnFireEvent(IGameEvent* pEvent, bool bDontBroadcast)
pCallback->ScriptContext().Push(pEvent);
pCallback->ScriptContext().Push(&override);
VPROF_BUDGET("CS#::OnFireEvent", "CS# Event Hooks");
// VPROF_BUDGET("CS#::OnFireEvent", "CS# Event Hooks");
for (auto fnMethodToCall : pCallback->GetFunctions())
{
if (!fnMethodToCall) continue;
@@ -259,7 +259,7 @@ bool EventManager::OnFireEventPost(IGameEvent* pEvent, bool bDontBroadcast)
if (pCallback)
{
VPROF_BUDGET("CS#::OnFireEventPost", "CS# Event Hooks");
// VPROF_BUDGET("CS#::OnFireEventPost", "CS# Event Hooks");
auto pEventCopy = m_EventCopies.top();
CSSHARP_CORE_TRACE("Pushing event `{}` pointer: {}, dont broadcast: {}, post: {}", pEventCopy->GetName(), (void*)pEventCopy,

View File

@@ -330,7 +330,7 @@ CPlayer* PlayerManager::GetPlayerBySlot(int client) const
void PlayerManager::RunThink() const
{
VPROF_BUDGET("CS#::PlayerManager::RunThink", "CS# On Frame");
// VPROF_BUDGET("CS#::PlayerManager::RunThink", "CS# On Frame");
for (int i = 0; i <= MaxClients(); i++)
{

View File

@@ -182,6 +182,27 @@ CModule::CModule(std::string_view path, std::uint64_t base)
}
}
// Load all sections for FindVirtualTable
section = IMAGE_FIRST_SECTION(nt_header);
for (auto i = 0; i < nt_header->FileHeader.NumberOfSections; i++, section++)
{
const auto is_readable = (section->Characteristics & IMAGE_SCN_MEM_READ) != 0;
if (is_readable)
{
const auto start = this->m_baseAddress + section->VirtualAddress;
const auto size = (std::min)(section->SizeOfRawData, section->Misc.VirtualSize);
std::string section_name(reinterpret_cast<const char*>(section->Name),
strnlen(reinterpret_cast<const char*>(section->Name), IMAGE_SIZEOF_SHORT_NAME));
auto& sec = m_sections.emplace_back();
sec.m_szName = section_name;
sec.m_pBase = reinterpret_cast<void*>(start);
sec.m_iSize = size;
}
}
DumpSymbols();
if (m_fnCreateInterface == nullptr) return;
@@ -189,12 +210,94 @@ CModule::CModule(std::string_view path, std::uint64_t base)
m_bInitialized = true;
}
#else
#ifdef __linux__
#include "dbg.h"
#include "sys/mman.h"
#include <dlfcn.h>
#include <elf.h>
#include <fcntl.h>
#include <libgen.h>
#include <link.h>
#include <locale>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "tier0/memdbgon.h"
#endif
// Credits:
// https://github.com/alliedmodders/sourcemod/blob/master/core/logic/MemoryUtils.cpp#L502-L587
// https://github.com/komashchenko/DynLibUtils/blob/5eb95475170becfcc64fd5d32d14ec2b76dcb6d4/module_linux.cpp#L95
// https://github.com/Source2ZE/CS2Fixes/blob/e1a7aebee8b846b9c6be514dba890646b04a7792/src/utils/plat_unix.cpp#L53
int GetModuleInformation(HINSTANCE hModule, void** base, size_t* length, std::vector<Section>& m_sections)
{
link_map* lmap;
if (dlinfo(hModule, RTLD_DI_LINKMAP, &lmap) != 0)
{
dlclose(hModule);
return 1;
}
int fd = open(lmap->l_name, O_RDONLY);
if (fd == -1)
{
dlclose(hModule);
return 2;
}
struct stat st;
if (fstat(fd, &st) == 0)
{
void* map = mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (map != MAP_FAILED)
{
ElfW(Ehdr)* ehdr = static_cast<ElfW(Ehdr)*>(map);
ElfW(Shdr)* shdrs = reinterpret_cast<ElfW(Shdr)*>(reinterpret_cast<uintptr_t>(ehdr) + ehdr->e_shoff);
const char* strTab = reinterpret_cast<const char*>(reinterpret_cast<uintptr_t>(ehdr) + shdrs[ehdr->e_shstrndx].sh_offset);
for (auto i = 0; i < ehdr->e_phnum; ++i)
{
ElfW(Phdr)* phdr = reinterpret_cast<ElfW(Phdr)*>(reinterpret_cast<uintptr_t>(ehdr) + ehdr->e_phoff + i * ehdr->e_phentsize);
if (phdr->p_type == PT_LOAD && phdr->p_flags & PF_X)
{
*base = reinterpret_cast<void*>(lmap->l_addr + phdr->p_vaddr);
*length = phdr->p_filesz;
break;
}
}
for (auto i = 0; i < ehdr->e_shnum; ++i)
{
ElfW(Shdr)* shdr = reinterpret_cast<ElfW(Shdr)*>(reinterpret_cast<uintptr_t>(shdrs) + i * ehdr->e_shentsize);
if (*(strTab + shdr->sh_name) == '\0') continue;
Section section;
section.m_szName = strTab + shdr->sh_name;
section.m_pBase = reinterpret_cast<void*>(lmap->l_addr + shdr->sh_addr);
section.m_iSize = shdr->sh_size;
m_sections.push_back(section);
}
munmap(map, st.st_size);
}
}
close(fd);
return 0;
}
CModule::CModule(std::string_view path, dl_phdr_info* info)
{
m_pszModule = path.substr(path.find_last_of('/') + 1);
m_pszPath = path.data();
m_baseAddress = info->dlpi_addr;
auto module = dlmount(m_pszModule.c_str());
GetModuleInformation(module, &m_base, &m_size, m_sections);
const bool should_read_from_disk = std::any_of(modules_to_read_from_disk.begin(), modules_to_read_from_disk.end(), [&](const auto& i) {
return m_pszModule == i;
});
@@ -370,6 +473,10 @@ void CModule::DumpSymbols(ElfW(Dyn) * dyn)
else if (dyn->d_tag == DT_SYMTAB)
{
symbols = reinterpret_cast<ElfW(Sym)*>(dyn->d_un.d_ptr);
}
dyn++;
}
for (auto i = 0; i < symbol_count; i++)
{
@@ -394,10 +501,6 @@ void CModule::DumpSymbols(ElfW(Dyn) * dyn)
_symbols.insert({ name.data(), address });
}
}
dyn++;
}
}
#endif
std::optional<std::vector<std::uint8_t>>
@@ -456,7 +559,22 @@ void* CModule::FindSignature(const char* signature)
return nullptr;
}
return this->FindSignature(pData);
auto pOld = this->FindSignature(pData);
auto pNew = this->FindSignatureAlternative(pData);
if (pOld != pNew)
{
CSSHARP_CORE_DEBUG(
"Signature {} found different pointers using different signature scanning methods. Found old address: {}, new address: {}",
signature, (void*)pOld, (void*)pNew);
}
if (pNew)
{
return pNew;
}
return pOld;
}
void* CModule::FindSignature(const std::vector<int16_t>& sigBytes)
@@ -490,6 +608,39 @@ void* CModule::FindSignature(const std::vector<int16_t>& sigBytes)
return nullptr;
}
void* CModule::FindSignatureAlternative(const std::vector<int16_t>& sigBytes)
{
if (m_base == 0 || m_size == 0)
{
return nullptr;
}
auto* data = reinterpret_cast<std::uint8_t*>(m_base);
const auto size = m_size;
auto first_byte = sigBytes[0];
std::uint8_t* end = data + size - sigBytes.size();
for (std::uint8_t* current = data; current <= end; ++current)
{
if (first_byte != -1) current = std::find(current, end, first_byte);
if (current == end)
{
break;
}
if (std::equal(sigBytes.begin() + 1, sigBytes.end(), current + 1, [](auto opt, auto byte) {
return opt == -1 || opt == byte;
}))
{
return reinterpret_cast<void*>(current - data + (std::uintptr_t)m_base);
}
}
return nullptr;
}
void* CModule::FindInterface(std::string_view name)
{
if (_interfaces.empty())
@@ -560,4 +711,114 @@ void* CModule::FindSymbol(const std::string& name)
CSSHARP_CORE_ERROR("Cannot find symbol {}", name);
return nullptr;
}
#ifdef _WIN32
void* CModule::FindVirtualTable(const std::string& name)
{
auto runTimeData = GetSection(".data");
auto readOnlyData = GetSection(".rdata");
if (!runTimeData || !readOnlyData)
{
Warning("Failed to find .data or .rdata section\n");
return nullptr;
}
std::string decoratedTableName = ".?AV" + name + "@@";
SignatureIterator sigIt(runTimeData->m_pBase, runTimeData->m_iSize, (const byte*)decoratedTableName.c_str(),
decoratedTableName.size() + 1);
void* typeDescriptor = sigIt.FindNext(false);
if (!typeDescriptor)
{
Warning("Failed to find type descriptor for %s\n", name.c_str());
return nullptr;
}
typeDescriptor = (void*)((uintptr_t)typeDescriptor - 0x10);
const uint32_t rttiTDRva = (uintptr_t)typeDescriptor - (uintptr_t)m_base;
ConMsg("RTTI Type Descriptor RVA: 0x%p\n", rttiTDRva);
SignatureIterator sigIt2(readOnlyData->m_pBase, readOnlyData->m_iSize, (const byte*)&rttiTDRva, sizeof(uint32_t));
while (void* completeObjectLocator = sigIt2.FindNext(false))
{
auto completeObjectLocatorHeader = (uintptr_t)completeObjectLocator - 0xC;
// check RTTI Complete Object Locator header, always 0x1
if (*(int32_t*)(completeObjectLocatorHeader) != 1) continue;
// check RTTI Complete Object Locator vtable offset
if (*(int32_t*)((uintptr_t)completeObjectLocator - 0x8) != 0) continue;
SignatureIterator sigIt3(readOnlyData->m_pBase, readOnlyData->m_iSize, (const byte*)&completeObjectLocatorHeader, sizeof(void*));
void* vtable = sigIt3.FindNext(false);
if (!vtable)
{
Warning("Failed to find vtable for %s\n", name.c_str());
return nullptr;
}
return (void*)((uintptr_t)vtable + 0x8);
}
Warning("Failed to find RTTI Complete Object Locator for %s\n", name.c_str());
return nullptr;
}
#endif
#ifndef _WIN32
void* CModule::FindVirtualTable(const std::string& name)
{
auto readOnlyData = GetSection(".rodata");
auto readOnlyRelocations = GetSection(".data.rel.ro");
if (!readOnlyData || !readOnlyRelocations)
{
Warning("Failed to find .rodata or .data.rel.ro section\n");
return nullptr;
}
std::string decoratedTableName = std::to_string(name.length()) + name;
SignatureIterator sigIt(readOnlyData->m_pBase, readOnlyData->m_iSize, (const byte*)decoratedTableName.c_str(),
decoratedTableName.size() + 1);
void* classNameString = sigIt.FindNext(false);
if (!classNameString)
{
Warning("Failed to find type descriptor for %s\n", name.c_str());
return nullptr;
}
SignatureIterator sigIt2(readOnlyRelocations->m_pBase, readOnlyRelocations->m_iSize, (const byte*)&classNameString, sizeof(void*));
void* typeName = sigIt2.FindNext(false);
if (!typeName)
{
Warning("Failed to find type name for %s\n", name.c_str());
return nullptr;
}
void* typeInfo = (void*)((uintptr_t)typeName - 0x8);
for (const auto& sectionName : { std::string_view(".data.rel.ro"), std::string_view(".data.rel.ro.local") })
{
auto section = GetSection(sectionName);
if (!section) continue;
SignatureIterator sigIt3(section->m_pBase, section->m_iSize, (const byte*)&typeInfo, sizeof(void*));
while (void* vtable = sigIt3.FindNext(false))
if (*(int64_t*)((uintptr_t)vtable - 0x8) == 0) return (void*)((uintptr_t)vtable + 0x8);
}
Warning("Failed to find vtable for %s\n", name.c_str());
return nullptr;
}
#endif
} // namespace counterstrikesharp::modules

View File

@@ -36,6 +36,49 @@
namespace counterstrikesharp::modules {
struct Section
{
std::string m_szName;
void* m_pBase;
size_t m_iSize;
};
class SignatureIterator
{
public:
SignatureIterator(void* pBase, size_t iSize, const byte* pSignature, size_t iSigLength)
: m_pBase((byte*)pBase), m_iSize(iSize), m_pSignature(pSignature), m_iSigLength(iSigLength)
{
m_pCurrent = m_pBase;
}
void* FindNext(bool allowWildcard)
{
for (size_t i = 0; i < m_iSize; i++)
{
size_t Matches = 0;
while (*(m_pCurrent + i + Matches) == m_pSignature[Matches] || (allowWildcard && m_pSignature[Matches] == '\x2A'))
{
Matches++;
if (Matches == m_iSigLength)
{
m_pCurrent += i + 1;
return m_pCurrent - 1;
}
}
}
return nullptr;
}
private:
byte* m_pBase;
size_t m_iSize;
const byte* m_pSignature;
size_t m_iSigLength;
byte* m_pCurrent;
};
struct Segments
{
Segments() = default;
@@ -64,6 +107,16 @@ class CModule
void* FindSymbol(const std::string& name);
void* FindVirtualTable(const std::string& name);
Section* GetSection(const std::string_view name)
{
for (auto& section : m_sections)
if (section.m_szName == name) return &section;
return nullptr;
}
[[nodiscard]] bool IsInitialized() const { return m_bInitialized; }
std::string m_pszModule{};
@@ -74,6 +127,7 @@ class CModule
private:
bool m_bInitialized{};
std::vector<Segments> m_vecSegments{};
std::vector<Section> m_sections{};
std::uintptr_t m_baseAddress{};
std::unordered_map<std::string, std::uintptr_t> _symbols{};
std::unordered_map<std::string, std::uintptr_t> _interfaces{};
@@ -90,6 +144,7 @@ class CModule
GetOriginalBytes(const std::vector<std::uint8_t>& disk_data, std::uintptr_t rva, std::size_t size);
void* FindSignature(const std::vector<int16_t>& sigBytes);
void* FindSignatureAlternative(const std::vector<int16_t>& sigBytes);
};
} // namespace counterstrikesharp::modules

View File

@@ -38,6 +38,8 @@
#define VERSION_STRING "v" SEMVER " @ " GITHUB_SHA
#define BUILD_TIMESTAMP __DATE__ " " __TIME__
int g_iLoadEventsFromFileId = -1;
counterstrikesharp::GlobalClass* counterstrikesharp::GlobalClass::head = nullptr;
CGameEntitySystem* GameEntitySystem() { return counterstrikesharp::globals::entitySystem; }
@@ -76,6 +78,7 @@ SH_DECL_HOOK3_void(
INetworkServerService, StartupServer, SH_NOATTRIB, 0, const GameSessionConfiguration_t&, ISource2WorldSession*, const char*);
SH_DECL_HOOK3_void(IEngineServiceMgr, RegisterLoopMode, SH_NOATTRIB, 0, const char*, ILoopModeFactory*, void**);
SH_DECL_HOOK1(IEngineServiceMgr, FindService, SH_NOATTRIB, 0, IEngineService*, const char*);
SH_DECL_HOOK2(IGameEventManager2, LoadEventsFromFile, SH_NOATTRIB, 0, int, const char*, bool);
CounterStrikeSharpMMPlugin gPlugin;
@@ -97,6 +100,7 @@ bool CounterStrikeSharpMMPlugin::Load(PluginId id, ISmmAPI* ismm, char* error, s
GET_V_IFACE_CURRENT(GetEngineFactory, globals::engineServer2, IVEngineServer2, SOURCE2ENGINETOSERVER_INTERFACE_VERSION);
GET_V_IFACE_CURRENT(GetEngineFactory, globals::engine, IVEngineServer, INTERFACEVERSION_VENGINESERVER);
GET_V_IFACE_CURRENT(GetEngineFactory, globals::cvars, ICvar, CVAR_INTERFACE_VERSION);
GET_V_IFACE_CURRENT(GetEngineFactory, g_pGameResourceServiceServer, IGameResourceService, GAMERESOURCESERVICESERVER_INTERFACE_VERSION);
GET_V_IFACE_ANY(GetServerFactory, globals::server, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL);
GET_V_IFACE_ANY(GetServerFactory, globals::serverGameClients, IServerGameClients, INTERFACEVERSION_SERVERGAMECLIENTS);
GET_V_IFACE_ANY(GetEngineFactory, globals::networkServerService, INetworkServerService, NETWORKSERVERSERVICE_INTERFACE_VERSION);
@@ -105,6 +109,9 @@ bool CounterStrikeSharpMMPlugin::Load(PluginId id, ISmmAPI* ismm, char* error, s
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);
g_pCVar = globals::cvars;
g_pSource2GameEntities = globals::gameEntities;
interfaces::pGameResourceServiceServer = (CGameResourceService*)g_pGameResourceServiceServer;
auto coreconfig_path = std::string(utils::ConfigsDirectory() + "/core");
globals::coreConfig = new CCoreConfig(coreconfig_path);
@@ -120,10 +127,14 @@ bool CounterStrikeSharpMMPlugin::Load(PluginId id, ISmmAPI* ismm, char* error, s
if (globals::coreConfig->AutoUpdateEnabled)
{
#ifdef _WIN32
if (!update::TryUpdateGameConfig())
{
CSSHARP_CORE_ERROR("Failed to update game config.");
}
#else
CSSHARP_CORE_WARN("Auto-update is not currently supported on this platform.");
#endif
}
auto gamedata_path = std::string(utils::GamedataDirectory() + "/gamedata.json");
@@ -154,18 +165,24 @@ bool CounterStrikeSharpMMPlugin::Load(PluginId id, ISmmAPI* ismm, char* error, s
SH_ADD_HOOK_MEMFUNC(IEngineServiceMgr, FindService, globals::engineServiceManager, this, &CounterStrikeSharpMMPlugin::Hook_FindService,
true);
if (!globals::dotnetManager.Initialize())
{
CSSHARP_CORE_ERROR("Failed to initialize .NET runtime");
}
auto pCGameEventManagerVTable = (IGameEventManager2*)modules::server->FindVirtualTable("CGameEventManager");
g_iLoadEventsFromFileId = SH_ADD_DVPHOOK(IGameEventManager2, LoadEventsFromFile, pCGameEventManagerVTable,
SH_MEMBER(this, &CounterStrikeSharpMMPlugin::Hook_LoadEventsFromFile), false);
if (!InitGameSystems())
{
CSSHARP_CORE_ERROR("Failed to initialize GameSystem!");
return false;
}
CSSHARP_CORE_INFO("Initialized GameSystem.");
if (!globals::dotnetManager.Initialize())
{
CSSHARP_CORE_ERROR("Failed to initialize .NET runtime");
}
CSSHARP_CORE_INFO("Hooks added.");
// Used by Metamod Console Commands
@@ -178,19 +195,30 @@ bool CounterStrikeSharpMMPlugin::Load(PluginId id, ISmmAPI* ismm, char* error, s
void CounterStrikeSharpMMPlugin::Hook_StartupServer(const GameSessionConfiguration_t& config, ISource2WorldSession*, const char*)
{
globals::entitySystem = interfaces::pGameResourceServiceServer->GetGameEntitySystem();
globals::entitySystem->AddListenerEntity(&globals::entityManager.entityListener);
// Temporary hack until CGameEntitySystem is updated in the sdk
#ifdef PLATFORM_LINUX
int offset = 8512;
#else
int offset = 8480;
#endif
auto pListeners = (CUtlVector<IEntityListener*>*)((byte*)globals::entitySystem + offset);
if (pListeners->Find(&globals::entityManager.entityListener) == -1) pListeners->AddToTail(&globals::entityManager.entityListener);
globals::timerSystem.OnStartupServer();
on_activate_callback->ScriptContext().Reset();
on_activate_callback->ScriptContext().Push(globals::getGlobalVars()->mapname.ToCStr());
on_activate_callback->Execute();
}
bool CounterStrikeSharpMMPlugin::Unload(char* error, size_t maxlen)
{
SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, GameFrame, globals::server, this, &CounterStrikeSharpMMPlugin::Hook_GameFrame, true);
SH_REMOVE_HOOK_MEMFUNC(INetworkServerService, StartupServer, globals::networkServerService, this,
&CounterStrikeSharpMMPlugin::Hook_StartupServer, true);
SH_REMOVE_HOOK_ID(g_iLoadEventsFromFileId);
globals::callbackManager.ReleaseCallback(on_activate_callback);
globals::callbackManager.ReleaseCallback(on_metamod_all_plugins_loaded_callback);
@@ -220,7 +248,7 @@ void CounterStrikeSharpMMPlugin::Hook_GameFrame(bool simulating, bool bFirstTick
* true | game is ticking
* false | game is not ticking
*/
VPROF_BUDGET("CS#::Hook_GameFrame", "CS# On Frame");
// VPROF_BUDGET("CS#::Hook_GameFrame", "CS# On Frame");
globals::timerSystem.OnGameFrame(simulating);
std::vector<std::function<void()>> out_list(1024);
@@ -276,6 +304,13 @@ IEngineService* CounterStrikeSharpMMPlugin::Hook_FindService(const char* service
return pService;
}
int CounterStrikeSharpMMPlugin::Hook_LoadEventsFromFile(const char* filename, bool bSearchAll)
{
ExecuteOnce(globals::gameEventManager = META_IFACEPTR(IGameEventManager2));
RETURN_META_VALUE(MRES_IGNORED, 0);
}
void CounterStrikeSharpMMPlugin::OnLevelShutdown() {}
bool CounterStrikeSharpMMPlugin::Pause(char* error, size_t maxlen) { return true; }

View File

@@ -52,6 +52,7 @@ class CounterStrikeSharpMMPlugin : public ISmmPlugin, public IMetamodListener
void AddTaskForNextFrame(std::function<void()>&& task);
void Hook_RegisterLoopMode(const char* pszLoopModeName, ILoopModeFactory* pLoopModeFactory, void** ppGlobalPointer);
int Hook_LoadEventsFromFile(const char* filename, bool bSearchAll);
IEngineService* Hook_FindService(const char* serviceName);
public:

View File

@@ -65,7 +65,7 @@ void ScriptCallback::Execute(bool bResetContext)
return;
}
VPROF_BUDGET(m_profile_name.c_str(), "CS# Script Callbacks");
// VPROF_BUDGET(m_profile_name.c_str(), "CS# Script Callbacks");
for (size_t nI = 0; nI < m_functions.size(); ++nI)
{

View File

@@ -217,8 +217,9 @@ void ReplicateConVar(ScriptContext& script_context)
cvarMsg->set_name(name);
cvarMsg->set_value(value);
CSingleRecipientFilter filter(slot);
globals::gameEventSystem->PostEventAbstract(-1, false, &filter, pNetMsg, msg, 0);
uint64 recipientMask = 0;
recipientMask |= (uint64)1 << slot;
globals::gameEventSystem->PostEventAbstract(-1, false, 1, &recipientMask, pNetMsg, msg, 0, NetChannelBufType_t::BUF_RELIABLE);
delete msg;
}

View File

@@ -0,0 +1,542 @@
/*
* 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/>. *
*/
#define private public
#include "core/log.h"
#include "scripting/autonative.h"
#include "scripting/script_engine.h"
#include <eiface.h>
#include <convar.h>
#undef private
namespace counterstrikesharp {
static void SetConvarFlags(ScriptContext& script_context)
{
auto convarAccessIndex = script_context.GetArgument<uint16>(0);
auto ref = ConVarRefAbstract(convarAccessIndex);
if (!ref.IsValidRef())
{
script_context.ThrowNativeError("Invalid convar access index.");
return;
}
auto flags = script_context.GetArgument<uint64_t>(1);
ref.GetConVarData()->m_nFlags = flags;
}
static void GetConvarFlags(ScriptContext& script_context)
{
auto convarAccessIndex = script_context.GetArgument<uint16>(0);
auto ref = ConVarRefAbstract(convarAccessIndex);
if (!ref.IsValidRef())
{
script_context.ThrowNativeError("Invalid convar access index.");
return;
}
script_context.SetResult(ref.GetConVarData()->m_nFlags);
}
static void GetConvarType(ScriptContext& script_context)
{
auto convarAccessIndex = script_context.GetArgument<uint16>(0);
auto ref = ConVarRefAbstract(convarAccessIndex);
if (!ref.IsValidRef())
{
script_context.ThrowNativeError("Invalid convar access index.");
return;
}
script_context.SetResult(ref.GetConVarData()->GetType());
}
static void GetConvarName(ScriptContext& script_context)
{
auto convarAccessIndex = script_context.GetArgument<uint16>(0);
auto ref = ConVarRefAbstract(convarAccessIndex);
if (!ref.IsValidRef())
{
script_context.ThrowNativeError("Invalid convar access index.");
return;
}
script_context.SetResult(ref.GetConVarData()->GetName());
}
static void GetConvarHelpText(ScriptContext& script_context)
{
auto convarAccessIndex = script_context.GetArgument<uint16>(0);
auto ref = ConVarRefAbstract(convarAccessIndex);
if (!ref.IsValidRef())
{
script_context.ThrowNativeError("Invalid convar access index.");
return;
}
if (!ref.GetConVarData()->HasHelpText())
{
return;
}
script_context.SetResult(ref.GetConVarData()->GetHelpText());
}
static void GetConvarAccessIndexByName(ScriptContext& script_context)
{
auto convarName = script_context.GetArgument<const char*>(0);
ConVarRef ref(convarName);
if (!ref.IsValidRef())
{
script_context.SetResult(0);
return;
}
script_context.SetResult(ref.GetAccessIndex());
}
static void GetConvarValueAsString(ScriptContext& script_context)
{
auto convarAccessIndex = script_context.GetArgument<uint16>(0);
auto ref = ConVarRefAbstract(convarAccessIndex);
CSplitScreenSlot server(0);
if (!ref.IsValidRef())
{
script_context.ThrowNativeError("Invalid convar access index.");
return;
}
if (!ref.IsConVarDataValid())
{
script_context.ThrowNativeError("Convar data is not valid for access index %d.", convarAccessIndex);
return;
}
CBufferString buf;
ref.GetValueAsString(buf, server);
std::string result = buf.Get();
script_context.SetResult(result.c_str());
}
static void SetConvarValueAsString(ScriptContext& script_context)
{
auto convarAccessIndex = script_context.GetArgument<uint16>(0);
auto ref = ConVarRefAbstract(convarAccessIndex);
CSplitScreenSlot server(0);
if (!ref.IsValidRef())
{
script_context.ThrowNativeError("Invalid convar access index.");
return;
}
if (!ref.IsConVarDataValid())
{
script_context.ThrowNativeError("Convar data is not valid for access index %d.", convarAccessIndex);
return;
}
auto value = script_context.GetArgument<const char*>(1);
if (!ref.SetString(value, server))
{
script_context.ThrowNativeError("Failed to set value for convar %s.", ref.GetName());
return;
}
}
static void GetConvarValue(ScriptContext& script_context)
{
auto convarAccessIndex = script_context.GetArgument<uint16>(0);
auto cvar = ConVarRefAbstract(convarAccessIndex);
CSplitScreenSlot server(0);
if (!cvar.IsValidRef())
{
script_context.ThrowNativeError("Invalid convar access index.");
return;
}
if (!cvar.IsConVarDataValid())
{
script_context.ThrowNativeError("Convar data is not valid for access index %d.", convarAccessIndex);
return;
}
switch (cvar.GetType())
{
case EConVarType_Int16:
{
script_context.SetResult(cvar.GetAs<int16>(server));
break;
}
case EConVarType_UInt16:
{
script_context.SetResult(cvar.GetAs<uint16>(server));
break;
}
case EConVarType_UInt32:
{
script_context.SetResult(cvar.GetAs<uint32>(server));
break;
}
case EConVarType_Int32:
{
script_context.SetResult(cvar.GetAs<int32>(server));
break;
}
case EConVarType_UInt64:
{
script_context.SetResult(cvar.GetAs<uint64>(server));
break;
}
case EConVarType_Int64:
{
script_context.SetResult(cvar.GetAs<int64>(server));
break;
}
case EConVarType_Bool:
{
script_context.SetResult(cvar.GetAs<bool>(server));
break;
}
case EConVarType_Float32:
{
script_context.SetResult((float)cvar.GetAs<float32>(server));
break;
}
case EConVarType_Float64:
{
script_context.SetResult((double)cvar.GetAs<float64>(server));
break;
}
case EConVarType_String:
{
script_context.SetResult(cvar.GetString(server).String());
break;
}
case EConVarType_Color:
{
script_context.SetResult(&(cvar.GetConVarData()->ValueOrDefault(server)->m_clrValue));
break;
}
case EConVarType_Vector2:
{
script_context.SetResult(&(cvar.GetConVarData()->ValueOrDefault(server)->m_vec2Value));
break;
}
case EConVarType_Vector3:
{
script_context.SetResult(&(cvar.GetConVarData()->ValueOrDefault(server)->m_vec3Value));
break;
}
case EConVarType_Vector4:
{
script_context.SetResult(&(cvar.GetConVarData()->ValueOrDefault(server)->m_vec4Value));
break;
}
case EConVarType_Qangle:
{
script_context.SetResult(&(cvar.GetConVarData()->ValueOrDefault(server)->m_angValue));
break;
}
default:
{
script_context.SetResult(nullptr);
}
}
}
static void GetConvarValueAddress(ScriptContext& script_context)
{
auto convarAccessIndex = script_context.GetArgument<uint16>(0);
auto cvar = ConVarRefAbstract(convarAccessIndex);
CSplitScreenSlot server(0);
if (!cvar.IsValidRef())
{
script_context.ThrowNativeError("Invalid convar access index.");
return;
}
if (!cvar.IsConVarDataValid())
{
script_context.ThrowNativeError("Convar data is not valid for access index %d.", convarAccessIndex);
return;
}
script_context.SetResult(cvar.GetConVarData()->ValueOrDefault(server));
}
static void SetConvarValue(ScriptContext& script_context)
{
auto convarAccessIndex = script_context.GetArgument<uint16>(0);
auto cvar = ConVarRefAbstract(convarAccessIndex);
CSplitScreenSlot server(0);
if (!cvar.IsValidRef())
{
script_context.ThrowNativeError("Invalid convar access index.");
return;
}
if (!cvar.IsConVarDataValid())
{
script_context.ThrowNativeError("Convar data is not valid for access index %d.", convarAccessIndex);
return;
}
switch (cvar.GetType())
{
case EConVarType_Int16:
{
cvar.SetAs<int16>(script_context.GetArgument<int16>(1), server);
break;
}
case EConVarType_UInt16:
{
cvar.SetAs<uint16>(script_context.GetArgument<uint16>(1), server);
break;
}
case EConVarType_UInt32:
{
cvar.SetAs<uint32>(script_context.GetArgument<uint32>(1), server);
break;
}
case EConVarType_Int32:
{
cvar.SetAs<int32>(script_context.GetArgument<int32>(1), server);
break;
}
case EConVarType_UInt64:
{
cvar.SetAs<uint64>(script_context.GetArgument<uint64>(1), server);
break;
}
case EConVarType_Int64:
{
cvar.SetAs<int64>(script_context.GetArgument<int64>(1), server);
break;
}
case EConVarType_Bool:
{
cvar.SetAs<bool>(script_context.GetArgument<bool>(1), server);
break;
}
case EConVarType_Float32:
{
cvar.SetAs<float32>(script_context.GetArgument<float32>(1), server);
break;
}
case EConVarType_Float64:
{
cvar.SetAs<float64>(script_context.GetArgument<float64>(1), server);
break;
}
case EConVarType_String:
{
cvar.SetString(script_context.GetArgument<const char*>(1), server);
break;
}
case EConVarType_Vector2:
{
cvar.SetAs<Vector2D>(*script_context.GetArgument<Vector2D*>(1), server);
break;
}
case EConVarType_Vector3:
{
cvar.SetAs<Vector>(*script_context.GetArgument<Vector*>(1), server);
break;
}
case EConVarType_Vector4:
{
cvar.SetAs<Vector4D>(*script_context.GetArgument<Vector4D*>(1), server);
break;
}
case EConVarType_Qangle:
{
cvar.SetAs<QAngle>(*script_context.GetArgument<QAngle*>(1), server);
break;
}
default:
{
script_context.ThrowNativeError("Unsupported convar type: %d", cvar.GetType());
}
}
}
#define CREATE_CVAR(type) \
auto createdConVar = new CConVar<type>(name, flags, helpText, script_context.GetArgument<type>(6), hasMin, \
script_context.GetArgument<type>(7), hasMax, script_context.GetArgument<type>(8)); \
createdConVarPtr = (void*)createdConVar; \
createdConVarAccessIndex = createdConVar->GetAccessIndex();
#define CREATE_CVAR_PTR(type) \
auto createdConVar = new CConVar<type>(name, flags, helpText, *script_context.GetArgument<type*>(6), hasMin, \
*script_context.GetArgument<type*>(7), hasMax, *script_context.GetArgument<type*>(8)); \
createdConVarPtr = (void*)createdConVar; \
createdConVarAccessIndex = createdConVar->GetAccessIndex();
static void CreateConVar(ScriptContext& script_context)
{
auto name = script_context.GetArgument<const char*>(0);
auto type = script_context.GetArgument<EConVarType>(1);
auto helpText = script_context.GetArgument<const char*>(2);
auto flags = script_context.GetArgument<uint64_t>(3);
auto hasMin = script_context.GetArgument<bool>(4);
auto hasMax = script_context.GetArgument<bool>(5);
// default, min, max is 6,7,8
ConVarRefAbstract cvar(name);
if (cvar.IsValidRef())
{
script_context.ThrowNativeError("Convar with name '%s' already exists.", name);
return;
}
uint16 createdConVarAccessIndex = 0;
void* createdConVarPtr = nullptr;
switch (type)
{
case EConVarType_Int16:
{
CREATE_CVAR(int16);
break;
}
case EConVarType_UInt16:
{
CREATE_CVAR(uint16);
break;
}
case EConVarType_UInt32:
{
CREATE_CVAR(uint32);
break;
}
case EConVarType_Int32:
{
CREATE_CVAR(int32);
break;
}
case EConVarType_UInt64:
{
CREATE_CVAR(uint64);
break;
}
case EConVarType_Int64:
{
CREATE_CVAR(int64);
break;
}
case EConVarType_Bool:
{
CREATE_CVAR(bool);
break;
}
case EConVarType_Float32:
{
CREATE_CVAR(float32);
break;
}
case EConVarType_Float64:
{
CREATE_CVAR(float64);
break;
}
case EConVarType_String:
{
auto createdConVar =
new CConVar<CUtlString>(name, flags, helpText, script_context.GetArgument<const char*>(6), hasMin,
script_context.GetArgument<const char*>(7), hasMax, script_context.GetArgument<const char*>(8));
createdConVarAccessIndex = createdConVar->GetAccessIndex();
break;
}
case EConVarType_Vector2:
{
CREATE_CVAR_PTR(Vector2D);
break;
}
case EConVarType_Vector3:
{
CREATE_CVAR_PTR(Vector);
break;
}
case EConVarType_Vector4:
{
CREATE_CVAR_PTR(Vector4D);
break;
}
case EConVarType_Qangle:
{
CREATE_CVAR_PTR(QAngle);
break;
}
default:
{
script_context.ThrowNativeError("Unsupported convar type: %d", type);
return;
}
}
script_context.SetResult(createdConVarAccessIndex);
}
static void DeleteConVar(ScriptContext& script_context)
{
auto convarAccessIndex = script_context.GetArgument<uint16>(0);
auto ref = ConVarRefAbstract(convarAccessIndex);
if (!ref.IsValidRef())
{
script_context.ThrowNativeError("Invalid convar access index.");
return;
}
if (ref.GetConVarData() == nullptr)
{
script_context.ThrowNativeError("Convar data is null.");
return;
}
ref.GetConVarData()->Invalidate();
}
REGISTER_NATIVES(convars, {
ScriptEngine::RegisterNativeHandler("SET_CONVAR_FLAGS", SetConvarFlags);
ScriptEngine::RegisterNativeHandler("GET_CONVAR_FLAGS", GetConvarFlags);
ScriptEngine::RegisterNativeHandler("GET_CONVAR_TYPE", GetConvarType);
ScriptEngine::RegisterNativeHandler("GET_CONVAR_NAME", GetConvarName);
ScriptEngine::RegisterNativeHandler("GET_CONVAR_HELP_TEXT", GetConvarHelpText);
ScriptEngine::RegisterNativeHandler("GET_CONVAR_ACCESS_INDEX_BY_NAME", GetConvarAccessIndexByName);
ScriptEngine::RegisterNativeHandler("GET_CONVAR_VALUE", GetConvarValue);
ScriptEngine::RegisterNativeHandler("GET_CONVAR_VALUE_ADDRESS", GetConvarValueAddress);
ScriptEngine::RegisterNativeHandler("GET_CONVAR_VALUE_AS_STRING", GetConvarValueAsString);
ScriptEngine::RegisterNativeHandler("SET_CONVAR_VALUE_AS_STRING", SetConvarValueAsString);
ScriptEngine::RegisterNativeHandler("SET_CONVAR_VALUE", SetConvarValue);
ScriptEngine::RegisterNativeHandler("CREATE_CONVAR", CreateConVar);
ScriptEngine::RegisterNativeHandler("DELETE_CONVAR", DeleteConVar);
})
} // namespace counterstrikesharp

View File

@@ -0,0 +1,13 @@
SET_CONVAR_FLAGS: convar:uint16,flags:uint64 -> void
GET_CONVAR_FLAGS: convar:uint16 -> uint64
GET_CONVAR_TYPE: convar:uint16 -> int16
GET_CONVAR_NAME: convar:uint16 -> string
GET_CONVAR_HELP_TEXT: convar:uint16 -> string
GET_CONVAR_ACCESS_INDEX_BY_NAME: name:string -> uint16
GET_CONVAR_VALUE: convar:uint16 -> any
GET_CONVAR_VALUE_AS_STRING: convar:uint16 -> string
GET_CONVAR_VALUE_ADDRESS: convar:uint16 -> pointer
SET_CONVAR_VALUE_AS_STRING: convar:uint16, value:string -> void
SET_CONVAR_VALUE: convar:uint16, value:any -> void
CREATE_CONVAR: name:string, type:int16, helpText:string, flags:uint64, hasMin:bool, hasMax:bool, defaultValue:any, minValue:any, maxValue:any -> uint16
DELETE_CONVAR: convar:uint16 -> void

View File

@@ -14,6 +14,8 @@
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
*/
#include "igameeventsystem.h"
#include <IEngineSound.h>
#include <edict.h>
#include <eiface.h>
@@ -28,9 +30,12 @@
#include "core/memory.h"
#include "core/log.h"
#include "core/function.h"
#include "core/recipientfilters.h"
#include "core/managers/player_manager.h"
#include "core/managers/server_manager.h"
#include "core/tick_scheduler.h"
#include "networksystem/inetworkmessages.h"
#include "usermessages.pb.h"
#if _WIN32
#undef GetCurrentTime
@@ -59,7 +64,7 @@ float GetCurrentTime(ScriptContext& script_context) { return globals::getGlobalV
int GetTickCount(ScriptContext& script_context) { return globals::getGlobalVars()->tickcount; }
float GetGameFrameTime(ScriptContext& script_context) { return globals::getGlobalVars()->frametime; }
float GetGameFrameTime(ScriptContext& script_context) { return 0; }
double GetEngineTime(ScriptContext& script_context) { return Plat_FloatTime(); }
@@ -236,6 +241,28 @@ void DisconnectClient(ScriptContext& scriptContext)
globals::engineServer2->DisconnectClient(slot, disconnectReason);
}
void ClientPrint(ScriptContext& scriptContext)
{
auto slot = scriptContext.GetArgument<int>(0);
auto hudDestination = scriptContext.GetArgument<int>(1);
auto message = scriptContext.GetArgument<const char*>(2);
INetworkMessageInternal* pNetMsg = globals::networkMessages->FindNetworkMessagePartial("TextMsg");
auto data = pNetMsg->AllocateMessage()->ToPB<CUserMessageTextMsg>();
data->set_dest(hudDestination);
data->add_param(message);
CPlayerBitVec recipients;
recipients.Set(slot);
globals::gameEventSystem->PostEventAbstract(CSplitScreenSlot(-1), false, ABSOLUTE_PLAYER_LIMIT,
reinterpret_cast<const uint64*>(recipients.Base()), pNetMsg, data, 0,
NetChannelBufType_t::BUF_RELIABLE);
delete data;
}
REGISTER_NATIVES(engine, {
ScriptEngine::RegisterNativeHandler("GET_GAME_DIRECTORY", GetGameDirectory);
ScriptEngine::RegisterNativeHandler("GET_MAP_NAME", GetMapName);
@@ -261,5 +288,6 @@ REGISTER_NATIVES(engine, {
ScriptEngine::RegisterNativeHandler("GET_COMMAND_PARAM_VALUE", GetCommandParamValue);
ScriptEngine::RegisterNativeHandler("PRINT_TO_SERVER_CONSOLE", PrintToServerConsole);
ScriptEngine::RegisterNativeHandler("DISCONNECT_CLIENT", DisconnectClient);
ScriptEngine::RegisterNativeHandler("CLIENT_PRINT", ClientPrint);
})
} // namespace counterstrikesharp

View File

@@ -28,3 +28,4 @@ GET_VALVE_INTERFACE: interfaceType:int, interfaceName:string -> pointer
GET_COMMAND_PARAM_VALUE: param:string, dataType:DataType_t, defaultValue:any -> any
PRINT_TO_SERVER_CONSOLE: msg:string -> void
DISCONNECT_CLIENT: slot:int, reason:int -> void
CLIENT_PRINT: slot:int, hudDestination:int, msg:string -> void

View File

@@ -234,7 +234,7 @@ void AcceptInput(ScriptContext& script_context)
int outputID = script_context.GetArgument<int>(5);
variant_t _value = variant_t(value);
CEntityInstance_AcceptInput(pThis, pInputName, pActivator, pCaller, &_value, outputID);
CEntityInstance_AcceptInput(pThis, pInputName, pActivator, pCaller, &_value, outputID, nullptr);
}
void AddEntityIOEvent(ScriptContext& script_context)
@@ -254,7 +254,7 @@ void AddEntityIOEvent(ScriptContext& script_context)
int outputID = script_context.GetArgument<int>(6);
variant_t _value = variant_t(value);
CEntitySystem_AddEntityIOEvent(GameEntitySystem(), pTarget, pInputName, pActivator, pCaller, &_value, delay, outputID);
CEntitySystem_AddEntityIOEvent(GameEntitySystem(), pTarget, pInputName, pActivator, pCaller, &_value, delay, outputID, nullptr);
}
SoundEventGuid_t EmitSoundFilter(ScriptContext& script_context)

View File

@@ -213,11 +213,34 @@ void SetSchemaValueByName(ScriptContext& script_context)
}
}
void SchemaSetStateChanged(ScriptContext& script_context)
{
auto instancePointer = script_context.GetArgument<void*>(0);
auto offset = script_context.GetArgument<uint32_t>(1);
auto nArrayIndex = script_context.GetArgument<uint32_t>(2);
auto nPathIndex = script_context.GetArgument<uint32_t>(3);
SetStateChanged(reinterpret_cast<uintptr_t>(instancePointer), offset, nArrayIndex, nPathIndex);
}
void SchemaNetworkStateChanged(ScriptContext& script_context)
{
auto instancePointer = script_context.GetArgument<void*>(0);
auto offset = script_context.GetArgument<uint32_t>(1);
auto nArrayIndex = script_context.GetArgument<uint32_t>(2);
auto nPathIndex = script_context.GetArgument<uint32_t>(3);
NetworkStateChanged(reinterpret_cast<uintptr_t>(instancePointer), offset, nArrayIndex, nPathIndex);
} // namespace counterstrikesharp
REGISTER_NATIVES(schema, {
ScriptEngine::RegisterNativeHandler("GET_SCHEMA_OFFSET", GetSchemaOffset);
ScriptEngine::RegisterNativeHandler("IS_SCHEMA_FIELD_NETWORKED", IsSchemaFieldNetworked);
ScriptEngine::RegisterNativeHandler("GET_SCHEMA_VALUE_BY_NAME", GetSchemaValueByName);
ScriptEngine::RegisterNativeHandler("SET_SCHEMA_VALUE_BY_NAME", SetSchemaValueByName);
ScriptEngine::RegisterNativeHandler("GET_SCHEMA_CLASS_SIZE", GetSchemaClassSize);
ScriptEngine::RegisterNativeHandler("SCHEMA_SET_STATE_CHANGED", SchemaSetStateChanged);
ScriptEngine::RegisterNativeHandler("SCHEMA_NETWORK_STATE_CHANGED", SchemaNetworkStateChanged);
})
} // namespace counterstrikesharp

View File

@@ -3,3 +3,5 @@ IS_SCHEMA_FIELD_NETWORKED: className:string, propName:string -> bool
GET_SCHEMA_VALUE_BY_NAME: instance:pointer, returnType:int, className:string, propName:string -> any
SET_SCHEMA_VALUE_BY_NAME: instance:pointer, returnType:int, className:string, propName:string, value:any -> void
GET_SCHEMA_CLASS_SIZE: className:string -> int
SCHEMA_SET_STATE_CHANGED: instance:pointer, offset:uint, arrayIndex:uint, pathIndex:uint -> void
SCHEMA_NETWORK_STATE_CHANGED: instance:pointer, offset:uint, arrayIndex:uint, pathIndex:uint -> void

View File

@@ -734,19 +734,27 @@ static void UserMessageSend(ScriptContext& scriptContext)
{
auto message = scriptContext.GetArgument<UserMessage*>(0);
CRecipientFilter filter{};
filter.AddRecipientsFromMask(message->GetRecipientMask() ? *message->GetRecipientMask() : 0);
// This is for calling send in a UM hook, if calling normal send using the UM instance from the UM hook, it will cause an inifinite
// loop, then crashing the server
static void (IGameEventSystem::*PostEventAbstract)(CSplitScreenSlot, bool, IRecipientFilter*, INetworkMessageInternal*,
const CNetMessage*, unsigned long) = &IGameEventSystem::PostEventAbstract;
static void (IGameEventSystem::*PostEventAbstract)(CSplitScreenSlot, bool, int, const uint64*, INetworkMessageInternal*,
const CNetMessage*, unsigned long, NetChannelBufType_t) =
&IGameEventSystem::PostEventAbstract;
// @hack Swap this back to CRecipientFilter when that is properly reversed so we don't have to do this crappy loop hack
std::vector<CPlayerSlot> recipients;
uint64 recipientMask = message->GetRecipientMask() ? *message->GetRecipientMask() : 0;
for (int i = 0; i < 64; i++)
{
if (recipientMask & ((uint64)1 << i))
{
if (message->IsManuallyAllocated())
globals::gameEventSystem->PostEventAbstract(0, false, &filter, message->GetSerializableMessage(), message->GetProtobufMessage(), 0);
globals::gameEventSystem->PostEventAbstract(i, false, 1, &recipientMask, message->GetSerializableMessage(),
message->GetProtobufMessage(), 0, NetChannelBufType_t::BUF_RELIABLE);
else
SH_CALL(globals::gameEventSystem, PostEventAbstract)(0, false, &filter, message->GetSerializableMessage(),
message->GetProtobufMessage(), 0);
SH_CALL(globals::gameEventSystem, PostEventAbstract)(i, false, 1, &recipientMask, message->GetSerializableMessage(),
message->GetProtobufMessage(), 0, NetChannelBufType_t::BUF_RELIABLE);
}
}
}
static void UserMessageDelete(ScriptContext& scriptContext)

View File

@@ -40,6 +40,10 @@ public class Mapping
return "int";
case "uint":
return "uint";
case "int16":
return "short";
case "uint16":
return "ushort";
case "bool":
return "bool";
case "pointer":