mirror of
https://github.com/roflmuffin/CounterStrikeSharp.git
synced 2025-12-05 23:58:24 -08:00
fix: Update CS# for Patch 19388062 (#958)
This commit is contained in:
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
Submodule libraries/hl2sdk-cs2 updated: bc59586979...f6ca58ae0f
Submodule libraries/metamod-source updated: b17dc63de7...5a282eb8fc
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 --project ../../tooling/CodeGen.Natives" />-->
|
||||
<!-- </Target>-->
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
94
managed/CounterStrikeSharp.Tests.Native/ConVarTests.cs
Normal file
94
managed/CounterStrikeSharp.Tests.Native/ConVarTests.cs
Normal 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";
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) \
|
||||
{ \
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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.");
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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++)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 §ion;
|
||||
|
||||
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
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
542
src/scripting/natives/natives_convars.cpp
Normal file
542
src/scripting/natives/natives_convars.cpp
Normal 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
|
||||
13
src/scripting/natives/natives_convars.yaml
Normal file
13
src/scripting/natives/natives_convars.yaml
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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":
|
||||
|
||||
Reference in New Issue
Block a user