From 7fffc96412018a5740effcaa2c3a4ad54a163870 Mon Sep 17 00:00:00 2001 From: Michael Wilson Date: Sun, 15 Oct 2023 18:28:54 +1000 Subject: [PATCH] Entity Manipulation/Schema System (#6) --- CMakeLists.txt | 30 ++- makefiles/shared.cmake | 1 + managed/CounterStrikeSharp.API/Core/API.cs | 32 ++- .../Core/ScriptContext.cs | 10 +- .../Modules/Memory/VirtualFunction.cs | 9 +- managed/TestPlugin/TestPlugin.cs | 20 +- src/core/cs2_sdk/README.md | 1 + src/core/cs2_sdk/interfaces/centitysystem.cpp | 61 ++++++ .../interfaces/cgameresourceserviceserver.h | 34 +++ .../cs2_sdk/interfaces/cs2_interfaces.cpp | 37 ++++ src/core/cs2_sdk/interfaces/cs2_interfaces.h | 32 +++ src/core/cs2_sdk/interfaces/cschemasystem.h | 127 +++++++++++ src/core/cs2_sdk/schema.cpp | 130 +++++++++++ src/core/cs2_sdk/schema.h | 69 ++++++ src/core/globals.cpp | 24 +- src/core/globals.h | 13 +- src/core/managers/player_manager.cpp | 1 + src/core/memory.h | 12 + src/core/memory_module.h | 97 +++++++++ src/mm_plugin.cpp | 5 + src/scripting/natives/natives_memory.yaml | 2 +- src/scripting/natives/natives_schema.cpp | 206 ++++++++++++++++++ src/scripting/natives/natives_schema.yaml | 2 + src/utils/virtual.h | 5 +- tooling/CodeGen.Natives/Mapping.cs | 2 + .../Scripts/GenerateNatives.cs | 3 +- 26 files changed, 928 insertions(+), 37 deletions(-) create mode 100644 src/core/cs2_sdk/README.md create mode 100644 src/core/cs2_sdk/interfaces/centitysystem.cpp create mode 100644 src/core/cs2_sdk/interfaces/cgameresourceserviceserver.h create mode 100644 src/core/cs2_sdk/interfaces/cs2_interfaces.cpp create mode 100644 src/core/cs2_sdk/interfaces/cs2_interfaces.h create mode 100644 src/core/cs2_sdk/interfaces/cschemasystem.h create mode 100644 src/core/cs2_sdk/schema.cpp create mode 100644 src/core/cs2_sdk/schema.h create mode 100644 src/core/memory_module.h create mode 100644 src/scripting/natives/natives_schema.cpp create mode 100644 src/scripting/natives/natives_schema.yaml diff --git a/CMakeLists.txt b/CMakeLists.txt index e032bceb5..950833499 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,25 +53,34 @@ SET(SOURCE_FILES src/core/managers/con_command_manager.cpp src/core/managers/con_command_manager.h src/scripting/natives/natives_commands.cpp + src/core/memory_module.h + src/core/cs2_sdk/interfaces/cgameresourceserviceserver.h + src/core/cs2_sdk/interfaces/cschemasystem.h + src/core/cs2_sdk/interfaces/centitysystem.cpp + src/core/cs2_sdk/interfaces/cs2_interfaces.h + src/core/cs2_sdk/interfaces/cs2_interfaces.cpp + src/core/cs2_sdk/schema.h + src/core/cs2_sdk/schema.cpp src/core/function.cpp src/core/function.h src/scripting/natives/natives_memory.cpp + src/scripting/natives/natives_schema.cpp ) set(PROTO_DIRS -I${CMAKE_CURRENT_SOURCE_DIR}/libraries/GameTracking-CS2/Protobufs) file(GLOB PROTOS "${CMAKE_CURRENT_SOURCE_DIR}/libraries/GameTracking-CS2/Protobufs/*.proto") ## Generate protobuf source & headers -add_custom_command( - OUTPUT protobuf_output_stamp - COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/compile.sh - COMMENT "Generating protobuf files using compile.sh script" - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf - DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/compile.sh - VERBATIM -) - -SET(SOURCE_FILES ${SOURCE_FILES} protobuf_output_stamp) +#add_custom_command( +# OUTPUT protobuf_output_stamp +# COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/compile.sh +# COMMENT "Generating protobuf files using compile.sh script" +# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf +# DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/compile.sh +# VERBATIM +#) +# +#SET(SOURCE_FILES ${SOURCE_FILES} protobuf_output_stamp) # Sources add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${NATIVES_SOURCES} ${CONVERSIONS_SOURCES} ${CONVERSIONS_HEADERS}) @@ -80,6 +89,7 @@ target_include_directories( ${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/src/core/cs2_sdk ) include("makefiles/linux.base.cmake") diff --git a/makefiles/shared.cmake b/makefiles/shared.cmake index 3edc2c16e..c330fff29 100644 --- a/makefiles/shared.cmake +++ b/makefiles/shared.cmake @@ -24,6 +24,7 @@ include_directories( ${SOURCESDK}/public/mathlib ${SOURCESDK}/public/tier0 ${SOURCESDK}/public/tier1 + ${SOURCESDK}/public/entity2 ${SOURCESDK}/public/game/server ${SOURCESDK}/public/entity2 ${METAMOD_DIR}/core diff --git a/managed/CounterStrikeSharp.API/Core/API.cs b/managed/CounterStrikeSharp.API/Core/API.cs index 9add9502d..c72639cbd 100644 --- a/managed/CounterStrikeSharp.API/Core/API.cs +++ b/managed/CounterStrikeSharp.API/Core/API.cs @@ -634,7 +634,7 @@ public static IntPtr CreateVirtualFunctionBySignature(IntPtr pointer, string bin } } - public static object ExecuteVirtualFunction(IntPtr function, object[] arguments){ + public static T ExecuteVirtualFunction(IntPtr function, object[] arguments){ lock (ScriptContext.GlobalScriptContext.Lock) { ScriptContext.GlobalScriptContext.Reset(); ScriptContext.GlobalScriptContext.Push(function); @@ -645,7 +645,7 @@ public static object ExecuteVirtualFunction(IntPtr function, object[] arguments) ScriptContext.GlobalScriptContext.SetIdentifier(0x376A0359); ScriptContext.GlobalScriptContext.Invoke(); ScriptContext.GlobalScriptContext.CheckErrors(); - return (object)ScriptContext.GlobalScriptContext.GetResult(typeof(object)); + return (T)ScriptContext.GlobalScriptContext.GetResult(typeof(T)); } } @@ -661,6 +661,34 @@ public static IntPtr FindSignature(string modulepath, string signature){ } } + public static T GetSchemaValueByName(IntPtr instance, int returntype, string classname, string propname){ + lock (ScriptContext.GlobalScriptContext.Lock) { + ScriptContext.GlobalScriptContext.Reset(); + ScriptContext.GlobalScriptContext.Push(instance); + ScriptContext.GlobalScriptContext.Push(returntype); + ScriptContext.GlobalScriptContext.Push(classname); + ScriptContext.GlobalScriptContext.Push(propname); + ScriptContext.GlobalScriptContext.SetIdentifier(0xD01E4EB5); + ScriptContext.GlobalScriptContext.Invoke(); + ScriptContext.GlobalScriptContext.CheckErrors(); + return (T)ScriptContext.GlobalScriptContext.GetResult(typeof(T)); + } + } + + public static void SetSchemaValueByName(IntPtr instance, int returntype, string classname, string propname, T value){ + lock (ScriptContext.GlobalScriptContext.Lock) { + ScriptContext.GlobalScriptContext.Reset(); + ScriptContext.GlobalScriptContext.Push(instance); + ScriptContext.GlobalScriptContext.Push(returntype); + ScriptContext.GlobalScriptContext.Push(classname); + ScriptContext.GlobalScriptContext.Push(propname); + ScriptContext.GlobalScriptContext.Push(value); + ScriptContext.GlobalScriptContext.SetIdentifier(0xAB9AA921); + ScriptContext.GlobalScriptContext.Invoke(); + ScriptContext.GlobalScriptContext.CheckErrors(); + } + } + public static IntPtr CreateTimer(float interval, InputArgument callback, int flags){ lock (ScriptContext.GlobalScriptContext.Lock) { ScriptContext.GlobalScriptContext.Reset(); diff --git a/managed/CounterStrikeSharp.API/Core/ScriptContext.cs b/managed/CounterStrikeSharp.API/Core/ScriptContext.cs index 29335e639..9deb67a45 100644 --- a/managed/CounterStrikeSharp.API/Core/ScriptContext.cs +++ b/managed/CounterStrikeSharp.API/Core/ScriptContext.cs @@ -422,11 +422,11 @@ internal unsafe object GetResult(Type type, byte* ptr) if (type == typeof(object)) { - var dataPtr = *(IntPtr*)&ptr[0]; - var dataLength = *(long*)&ptr[8]; - - byte[] data = new byte[dataLength]; - Marshal.Copy(dataPtr, data, 0, (int)dataLength); + // var dataPtr = *(IntPtr*)&ptr[0]; + // var dataLength = *(long*)&ptr[8]; + // + // byte[] data = new byte[dataLength]; + // Marshal.Copy(dataPtr, data, 0, (int)dataLength); return null; //return MsgPackDeserializer.Deserialize(data); diff --git a/managed/CounterStrikeSharp.API/Modules/Memory/VirtualFunction.cs b/managed/CounterStrikeSharp.API/Modules/Memory/VirtualFunction.cs index f267a5941..ab15ba9cb 100644 --- a/managed/CounterStrikeSharp.API/Modules/Memory/VirtualFunction.cs +++ b/managed/CounterStrikeSharp.API/Modules/Memory/VirtualFunction.cs @@ -137,13 +137,12 @@ public VirtualFunction(IntPtr objPtr, string signature, DataType?[] arguments, D public T InvokeInternal(object[] arguments) { - NativeAPI.ExecuteVirtualFunction(Handle, arguments); - return ScriptContext.GlobalScriptContext.GetResult(); + return NativeAPI.ExecuteVirtualFunction(Handle, arguments); } protected void InvokeInternal(object[] arguments) { - NativeAPI.ExecuteVirtualFunction(Handle, arguments); + NativeAPI.ExecuteVirtualFunction(Handle, arguments); } private static void ExecuteFunction(IntPtr objPtr, int offset, DataType?[] argumentTypes, DataType returnType, @@ -159,7 +158,7 @@ private static void ExecuteFunction(IntPtr objPtr, int offset, DataType?[] argum var ptr = NativeAPI.CreateVirtualFunction(objPtr, offset, convertedArguments.Length, (int)returnType, convertedArguments); - NativeAPI.ExecuteVirtualFunction(ptr, arguments); + NativeAPI.ExecuteVirtualFunction(ptr, arguments); } private static void ExecuteFunction(IntPtr objPtr, string signature, DataType?[] argumentTypes, @@ -175,7 +174,7 @@ private static void ExecuteFunction(IntPtr objPtr, string signature, DataType?[] var ptr = NativeAPI.CreateVirtualFunctionBySignature(objPtr, Addresses.ServerPath, signature, convertedArguments.Length, (int)returnType, convertedArguments); - NativeAPI.ExecuteVirtualFunction(ptr, arguments); + NativeAPI.ExecuteVirtualFunction(ptr, arguments); } public static VirtualFunctionVoid CreateObject(IntPtr objPtr, diff --git a/managed/TestPlugin/TestPlugin.cs b/managed/TestPlugin/TestPlugin.cs index 02beea25b..486a4662c 100644 --- a/managed/TestPlugin/TestPlugin.cs +++ b/managed/TestPlugin/TestPlugin.cs @@ -43,14 +43,22 @@ public override void Load(bool hotReload) RegisterEventHandler(GenericEventHandler); RegisterEventHandler(@event => { + var steamId = NativeAPI.GetSchemaValueByName(@event.Userid.Handle, + (int)DataType.DATA_TYPE_ULONG_LONG, + "CBasePlayerController", "m_steamID"); + + var playerName = NativeAPI.GetSchemaValueByName(@event.Userid.Handle, + (int)DataType.DATA_TYPE_STRING, "CBasePlayerController", "m_iszPlayerName"); + var playerHealth = NativeAPI.GetSchemaValueByName(@event.Userid.PawnHandle, + (int)DataType.DATA_TYPE_INT, "CBaseEntity", "m_iHealth"); + NativeAPI.SetSchemaValueByName(@event.Userid.PawnHandle, (int)DataType.DATA_TYPE_INT, + "CBaseEntity", "m_iHealth", playerHealth + 5); + Log($"Found steamID {new SteamID(steamId)} for player {playerName}:{playerHealth}"); Log($"{@event.Userid}, {@event.X},{@event.Y},{@event.Z}"); }); - + // Hook global listeners defined by CounterStrikeSharp - RegisterListener(mapName => - { - Log($"Map {mapName} has started!"); - }); + RegisterListener(mapName => { Log($"Map {mapName} has started!"); }); RegisterListener((index, name, ip) => { Log($"Client {name} from {ip} has connected!"); @@ -59,7 +67,7 @@ public override void Load(bool hotReload) { Log($"Client {index} with address {id}"); }); - + // You can use `ModuleDirectory` to get the directory of the plugin (for storing config files, saving database files etc.) File.WriteAllText(Path.Join(ModuleDirectory, "example.txt"), $"Test file created by TestPlugin at {DateTime.Now}"); diff --git a/src/core/cs2_sdk/README.md b/src/core/cs2_sdk/README.md new file mode 100644 index 000000000..e6d1f8c93 --- /dev/null +++ b/src/core/cs2_sdk/README.md @@ -0,0 +1 @@ +Credit for these files goes to [CS2Fixes](https://github.com/Source2ZE/CS2Fixes), licensed under GPLv3 and modified for use in CounterStrikeSharp \ No newline at end of file diff --git a/src/core/cs2_sdk/interfaces/centitysystem.cpp b/src/core/cs2_sdk/interfaces/centitysystem.cpp new file mode 100644 index 000000000..9816ebe5f --- /dev/null +++ b/src/core/cs2_sdk/interfaces/centitysystem.cpp @@ -0,0 +1,61 @@ +/** + * ============================================================================= + * CS2Fixes + * Copyright (C) 2023 Source2ZE + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program 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 + * this program. If not, see . + */ + +#include "utlstring.h" +#include "entity2/entitysystem.h" + +#include "tier0/memdbgon.h" + +CBaseEntity* CEntitySystem::GetBaseEntity(CEntityIndex entnum) +{ + if (entnum.Get() <= -1 || entnum.Get() >= (MAX_TOTAL_ENTITIES - 1)) + return nullptr; + + CEntityIdentity* pChunkToUse = m_EntityList.m_pIdentityChunks[entnum.Get() / MAX_ENTITIES_IN_LIST]; + if (!pChunkToUse) + return nullptr; + + CEntityIdentity* pIdentity = &pChunkToUse[entnum.Get() % MAX_ENTITIES_IN_LIST]; + if (!pIdentity) + return nullptr; + + if (pIdentity->m_EHandle.GetEntryIndex() != entnum.Get()) + return nullptr; + + return dynamic_cast(pIdentity->m_pInstance); +} + +CBaseEntity* CEntitySystem::GetBaseEntity(const CEntityHandle& hEnt) +{ + if (!hEnt.IsValid()) + return nullptr; + + CEntityIdentity* pChunkToUse = m_EntityList.m_pIdentityChunks[hEnt.GetEntryIndex() / MAX_ENTITIES_IN_LIST]; + if (!pChunkToUse) + return nullptr; + + CEntityIdentity* pIdentity = &pChunkToUse[hEnt.GetEntryIndex() % MAX_ENTITIES_IN_LIST]; + if (!pIdentity) + return nullptr; + + if (pIdentity->m_EHandle != hEnt) + return nullptr; + + return dynamic_cast(pIdentity->m_pInstance); +} \ No newline at end of file diff --git a/src/core/cs2_sdk/interfaces/cgameresourceserviceserver.h b/src/core/cs2_sdk/interfaces/cgameresourceserviceserver.h new file mode 100644 index 000000000..028e6b684 --- /dev/null +++ b/src/core/cs2_sdk/interfaces/cgameresourceserviceserver.h @@ -0,0 +1,34 @@ +/** + * ============================================================================= + * CS2Fixes + * Copyright (C) 2023 Source2ZE + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program 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 + * this program. If not, see . + */ + +#pragma once +#include +#include "interfaces/interfaces.h" +#include + +class CGameEntitySystem; + +class CGameResourceService +{ +public: + CGameEntitySystem *GetGameEntitySystem() + { + return *reinterpret_cast((uintptr_t)(this) + 0x50); + } +}; \ No newline at end of file diff --git a/src/core/cs2_sdk/interfaces/cs2_interfaces.cpp b/src/core/cs2_sdk/interfaces/cs2_interfaces.cpp new file mode 100644 index 000000000..4bfb17b13 --- /dev/null +++ b/src/core/cs2_sdk/interfaces/cs2_interfaces.cpp @@ -0,0 +1,37 @@ +/** + * ============================================================================= + * CS2Fixes + * Copyright (C) 2023 Source2ZE + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program 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 + * this program. If not, see . + */ + +#include "cs2_interfaces.h" +#include "interfaces/interfaces.h" + +#include "tier0/memdbgon.h" +#include "core/memory_module.h" +#include "core/globals.h" + +namespace counterstrikesharp { +void interfaces::Initialize() { + pGameResourceServiceServer = (CGameResourceService*)modules::engine->FindInterface( + GAMERESOURCESERVICESERVER_INTERFACE_VERSION); + g_pCVar = (ICvar*)modules::tier0->FindInterface(CVAR_INTERFACE_VERSION); + g_pSource2GameEntities = (ISource2GameEntities*)modules::server->FindInterface( + SOURCE2GAMEENTITIES_INTERFACE_VERSION); + pSchemaSystem = + (CSchemaSystem*)modules::schemasystem->FindInterface(SCHEMASYSTEM_INTERFACE_VERSION); +} +} // namespace counterstrikesharp diff --git a/src/core/cs2_sdk/interfaces/cs2_interfaces.h b/src/core/cs2_sdk/interfaces/cs2_interfaces.h new file mode 100644 index 000000000..a284cdb86 --- /dev/null +++ b/src/core/cs2_sdk/interfaces/cs2_interfaces.h @@ -0,0 +1,32 @@ +/** + * ============================================================================= + * CS2Fixes + * Copyright (C) 2023 Source2ZE + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program 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 + * this program. If not, see . + */ + +#pragma once + +#include "cgameresourceserviceserver.h" +#include "cschemasystem.h" + +namespace counterstrikesharp { +namespace interfaces { +void Initialize(); + +inline CGameResourceService *pGameResourceServiceServer = nullptr; +inline CSchemaSystem *pSchemaSystem = nullptr; +} // namespace interfaces +} // namespace counterstrikesharp \ No newline at end of file diff --git a/src/core/cs2_sdk/interfaces/cschemasystem.h b/src/core/cs2_sdk/interfaces/cschemasystem.h new file mode 100644 index 000000000..8632f5164 --- /dev/null +++ b/src/core/cs2_sdk/interfaces/cschemasystem.h @@ -0,0 +1,127 @@ +/** + * ============================================================================= + * CS2Fixes + * Copyright (C) 2023 Source2ZE + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program 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 + * this program. If not, see . + */ + +#pragma once +#include "../../utils/virtual.h" +#include + +struct CSchemaNetworkValue { + union { + const char* m_sz_value; + int m_n_value; + float m_f_value; + uintptr_t m_p_value; + }; +}; + +struct SchemaMetadataEntryData_t { + const char *m_name; + CSchemaNetworkValue* m_value; +}; + + +struct SchemaClassFieldData_t +{ + const char *m_name; + char pad0[0x8]; + short m_offset; + int32_t m_metadata_size; + SchemaMetadataEntryData_t* m_metadata; +}; + +class SchemaClassInfoData_t; + +struct SchemaBaseClassInfoData_t +{ + unsigned int m_offset; + SchemaClassInfoData_t *m_class; +}; + +class SchemaClassInfoData_t +{ +public: + auto GetName() + { + return m_name; + } + + auto GetFieldsSize() + { + return m_align; + } + + auto GetFields() + { + return m_fields; + } + + SchemaClassInfoData_t* GetParent() + { + if (!m_schema_parent) + return nullptr; + + return m_schema_parent->m_class; + } + +private: + char pad_0x0000[0x8]; // 0x0000 + + const char *m_name; // 0x0008 + char *m_module; // 0x0010 + + int m_size; // 0x0018 + int16_t m_align; // 0x001C + + int16_t m_static_size; // 0x001E + int16_t m_metadata_size; // 0x0020 + int16_t m_i_unk1; // 0x0022 + int16_t m_i_unk2; // 0x0024 + int16_t m_i_unk3; // 0x0026 + + SchemaClassFieldData_t *m_fields; // 0x0028 + + char pad_0x0030[0x8]; // 0x0030 + SchemaBaseClassInfoData_t *m_schema_parent; // 0x0038 + + char pad_0x0038[0x10]; // 0x0038 +}; + +class CSchemaSystemTypeScope +{ +public: + SchemaClassInfoData_t* FindDeclaredClass(const char *pClass) + { +#ifdef _WIN32 + SchemaClassInfoData_t *rv = nullptr; + CALL_VIRTUAL(void, 2, this, &rv, pClass); + return rv; +#else + return CALL_VIRTUAL(SchemaClassInfoData_t*, 2, this, pClass); +#endif + } +}; + +class CSchemaSystem +{ +public: + auto FindTypeScopeForModule(const char *module) + { + return CALL_VIRTUAL(CSchemaSystemTypeScope *, 13, this, module, nullptr); + } +}; \ No newline at end of file diff --git a/src/core/cs2_sdk/schema.cpp b/src/core/cs2_sdk/schema.cpp new file mode 100644 index 000000000..fe0657e58 --- /dev/null +++ b/src/core/cs2_sdk/schema.cpp @@ -0,0 +1,130 @@ +/** + * ============================================================================= + * CS2Fixes + * Copyright (C) 2023 Source2ZE + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program 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 + * this program. If not, see . + */ + +#include "schema.h" + +#include "interfaces/cs2_interfaces.h" +#include "../globals.h" +// #include +#include "tier1/utlmap.h" +#include "tier0/memdbgon.h" +#include "../memory.h" +#include "core/log.h" + +using SchemaKeyValueMap_t = CUtlMap; +using SchemaTableMap_t = CUtlMap; + +bool IsFieldNetworked(SchemaClassFieldData_t& field) { + for (int i = 0; i < field.m_metadata_size; i++) { + static auto networkEnabled = hash_32_fnv1a_const("MNetworkEnable"); + if (networkEnabled == hash_32_fnv1a_const(field.m_metadata[i].m_name)) return true; + } + + return false; +} + +static bool InitSchemaFieldsForClass(SchemaTableMap_t* tableMap, + const char* className, + uint32_t classKey) { + CSchemaSystemTypeScope* pType = + counterstrikesharp::interfaces::pSchemaSystem->FindTypeScopeForModule(MODULE_PREFIX "server" MODULE_EXT); + + if (!pType) return false; + + SchemaClassInfoData_t* pClassInfo = pType->FindDeclaredClass(className); + + if (!pClassInfo) { + SchemaKeyValueMap_t* map = new SchemaKeyValueMap_t(0, 0, DefLessFunc(uint32_t)); + tableMap->Insert(classKey, map); + + Warning("InitSchemaFieldsForClass(): '%s' was not found!\n", className); + return false; + } + + short fieldsSize = pClassInfo->GetFieldsSize(); + SchemaClassFieldData_t* pFields = pClassInfo->GetFields(); + + SchemaKeyValueMap_t* keyValueMap = new SchemaKeyValueMap_t(0, 0, DefLessFunc(uint32_t)); + keyValueMap->EnsureCapacity(fieldsSize); + tableMap->Insert(classKey, keyValueMap); + + for (int i = 0; i < fieldsSize; ++i) { + SchemaClassFieldData_t& field = pFields[i]; + + keyValueMap->Insert(hash_32_fnv1a_const(field.m_name), + {field.m_offset, IsFieldNetworked(field)}); + } + + return true; +} + +int16_t schema::FindChainOffset(const char* className) { + CSchemaSystemTypeScope* pType = + counterstrikesharp::interfaces::pSchemaSystem->FindTypeScopeForModule(MODULE_PREFIX "server" MODULE_EXT); + + if (!pType) return false; + + SchemaClassInfoData_t* pClassInfo = pType->FindDeclaredClass(className); + + do { + SchemaClassFieldData_t* pFields = pClassInfo->GetFields(); + short fieldsSize = pClassInfo->GetFieldsSize(); + for (int i = 0; i < fieldsSize; ++i) { + SchemaClassFieldData_t& field = pFields[i]; + + if (V_strcmp(field.m_name, "__m_pChainEntity") == 0) { + return field.m_offset; + } + } + } while ((pClassInfo = pClassInfo->GetParent()) != nullptr); + + return 0; +} + +SchemaKey schema::GetOffset(const char* className, + uint32_t classKey, + const char* memberName, + uint32_t memberKey) { + static SchemaTableMap_t schemaTableMap(0, 0, DefLessFunc(uint32_t)); + int16_t tableMapIndex = schemaTableMap.Find(classKey); + if (!schemaTableMap.IsValidIndex(tableMapIndex)) { + if (InitSchemaFieldsForClass(&schemaTableMap, className, classKey)) + return GetOffset(className, classKey, memberName, memberKey); + + return {0, 0}; + } + + SchemaKeyValueMap_t* tableMap = schemaTableMap[tableMapIndex]; + int16_t memberIndex = tableMap->Find(memberKey); + if (!tableMap->IsValidIndex(memberIndex)) { + Warning("schema::GetOffset(): '%s' was not found in '%s'!\n", memberName, className); + return {0, 0}; + } + + return tableMap->Element(memberIndex); +} + +void SetStateChanged(Z_CBaseEntity* pEntity, int offset) { + // addresses::StateChanged(pEntity->m_NetworkTransmitComponent(), pEntity, offset, -1, -1); + auto vars = counterstrikesharp::globals::getGlobalVars(); + + // if (vars) pEntity->m_lastNetworkChange = vars->curtime; + + // pEntity->m_isSteadyState().ClearAll(); +}; diff --git a/src/core/cs2_sdk/schema.h b/src/core/cs2_sdk/schema.h new file mode 100644 index 000000000..aa4dbdca3 --- /dev/null +++ b/src/core/cs2_sdk/schema.h @@ -0,0 +1,69 @@ +/** + * ============================================================================= + * CS2Fixes + * Copyright (C) 2023 Source2ZE + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program 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 + * this program. If not, see . + */ + +#pragma once + +#include "stdint.h" + +#ifdef _WIN32 + #pragma warning(push) + #pragma warning(disable : 4005) +#endif + +#include + +#ifdef _WIN32 + #pragma warning(pop) +#endif + +#include "tier0/dbg.h" +#include "const.h" +#include "../../utils/virtual.h" +#include "stdint.h" +#undef schema + +struct SchemaKey { + int16_t offset; + bool networked; +}; + +class Z_CBaseEntity; +void SetStateChanged(Z_CBaseEntity *pEntity, int offset); + +inline uint32_t val_32_const = 0x811c9dc5; +inline uint32_t prime_32_const = 0x1000193; +inline uint64_t val_64_const = 0xcbf29ce484222325; +inline uint64_t prime_64_const = 0x100000001b3; + +inline uint32_t hash_32_fnv1a_const(const char *str, const uint32_t value = val_32_const) noexcept { + return (str[0] == '\0') + ? value + : hash_32_fnv1a_const(&str[1], (value ^ uint32_t(str[0])) * prime_32_const); +} + +inline uint64_t hash_64_fnv1a_const(const char *str, const uint64_t value = val_64_const) noexcept { + return (str[0] == '\0') + ? value + : hash_64_fnv1a_const(&str[1], (value ^ uint64_t(str[0])) * prime_64_const); +} + +namespace schema { +int16_t FindChainOffset(const char *className); +SchemaKey GetOffset(const char *className, uint32_t classKey, const char *memberName, uint32_t memberKey); +} // namespace schema diff --git a/src/core/globals.cpp b/src/core/globals.cpp index 5e3611940..d9394307f 100644 --- a/src/core/globals.cpp +++ b/src/core/globals.cpp @@ -13,10 +13,21 @@ #include "log.h" #include "utils/virtual.h" #include "core/managers/con_command_manager.h" +#include "memory_module.h" +#include "interfaces/cs2_interfaces.h" #include +#include namespace counterstrikesharp { +namespace modules { +CModule *engine = nullptr; +CModule *tier0 = nullptr; +CModule *server = nullptr; +CModule *schemasystem = nullptr; +CModule *vscript = nullptr; +} // namespace modules + namespace globals { IVEngineServer *engine = nullptr; IGameEventManager2 *gameEventManager = nullptr; @@ -47,6 +58,7 @@ CounterStrikeSharpMMPlugin *mmPlugin = nullptr; SourceHook::Impl::CSourceHookImpl source_hook_impl; SourceHook::ISourceHook *source_hook = &source_hook_impl; ISmmAPI *ismm = nullptr; +CEntitySystem* entitySystem = nullptr; // Custom Managers CallbackManager callbackManager; @@ -56,9 +68,17 @@ TimerSystem timerSystem; ConCommandManager conCommandManager; void Initialize() { - gameEventManager = (IGameEventManager2 *)(CALL_VIRTUAL(uintptr_t, 91, server) - 8); + modules::engine = new modules::CModule(ROOTBIN, "engine2"); + modules::tier0 = new modules::CModule(ROOTBIN, "tier0"); + modules::server = new modules::CModule(GAMEBIN, "server"); + modules::schemasystem = new modules::CModule(ROOTBIN, "schemasystem"); + modules::vscript = new modules::CModule(ROOTBIN, "vscript"); + + interfaces::Initialize(); - CSSHARP_CORE_TRACE("[GLOBALS] globals::gameEventManager: {0}", (void *)gameEventManager); + globals::entitySystem = interfaces::pGameResourceServiceServer->GetGameEntitySystem(); + + gameEventManager = (IGameEventManager2 *)(CALL_VIRTUAL(uintptr_t, 91, server) - 8); } int source_hook_pluginid = 0; diff --git a/src/core/globals.h b/src/core/globals.h index 660d8af97..da354ff71 100644 --- a/src/core/globals.h +++ b/src/core/globals.h @@ -26,6 +26,7 @@ class CDotNetManager; class ICvar; class IGameEventSystem; class CounterStrikeSharpMMPlugin; +class CEntitySystem; namespace counterstrikesharp { class EntityListener; @@ -67,6 +68,7 @@ extern ICvar *cvars; extern ISource2Server *server; extern CGlobalEntityList *globalEntityList; extern EntityListener entityListener; +extern CEntitySystem *entitySystem; extern EventManager eventManager; extern UserMessageManager userMessageManager; extern ConCommandManager conCommandManager; @@ -87,9 +89,18 @@ void Initialize(); // Should only be called within the active game loop (i e map should be loaded // and active) otherwise that'll be nullptr! CGlobalVars *getGlobalVars(); - } // namespace globals +namespace modules { +class CModule; + +extern CModule *engine; +extern CModule *tier0; +extern CModule *server; +extern CModule *schemasystem; +extern CModule *vscript; +} // namespace modules + } // namespace counterstrikesharp #undef SH_GLOB_SHPTR diff --git a/src/core/managers/player_manager.cpp b/src/core/managers/player_manager.cpp index 850be69ae..4d0ea7874 100644 --- a/src/core/managers/player_manager.cpp +++ b/src/core/managers/player_manager.cpp @@ -39,6 +39,7 @@ #include "core/log.h" #include "scripting/callback_manager.h" #include +// extern CEntitySystem *g_pEntitySystem; SH_DECL_HOOK4_void( IServerGameClients, ClientActive, SH_NOATTRIB, 0, CPlayerSlot, bool, const char *, uint64); diff --git a/src/core/memory.h b/src/core/memory.h index 867092c85..50c90aa7a 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -1,3 +1,15 @@ #include + +#ifdef _WIN32 + #define ROOTBIN "/bin/win64/" + #define GAMEBIN "/csgo/bin/win64/" +#else + #define ROOTBIN "/bin/linuxsteamrt64/" + #define GAMEBIN "/csgo/bin/linuxsteamrt64/" +#endif + +#define MODULE_PREFIX "lib" +#define MODULE_EXT ".so" + int GetModuleInformation(void *hModule, void **base, size_t *length); \ No newline at end of file diff --git a/src/core/memory_module.h b/src/core/memory_module.h new file mode 100644 index 000000000..a83e755f9 --- /dev/null +++ b/src/core/memory_module.h @@ -0,0 +1,97 @@ +/** + * ============================================================================= + * CS2Fixes + * Copyright (C) 2023 Source2ZE + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program 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 + * this program. If not, see . + */ + +#pragma once +#include "dbg.h" +#include "interface.h" +#include "strtools.h" +#include "metamod_oslink.h" +#include "memory.h" + +#ifdef _WIN32 + #include +#endif +namespace counterstrikesharp { +namespace modules { + +class CModule { +public: + CModule(const char *path, const char *module) + : m_pszModule(module), + m_pszPath(path) { + char szModule[MAX_PATH]; + + V_snprintf(szModule, MAX_PATH, "%s%s%s%s%s", Plat_GetGameDirectory(), path, MODULE_PREFIX, + m_pszModule, MODULE_EXT); + + m_hModule = dlmount(szModule); + + if (!m_hModule) Error("Could not find %s\n", szModule); + +#ifdef _WIN32 + MODULEINFO m_hModuleInfo; + GetModuleInformation(GetCurrentProcess(), m_hModule, &m_hModuleInfo, sizeof(m_hModuleInfo)); + + m_base = (void *)m_hModuleInfo.lpBaseOfDll; + m_size = m_hModuleInfo.SizeOfImage; +#else + if (int e = GetModuleInformation(m_hModule, &m_base, &m_size)) + Error("Failed to get module info for %s, error %d\n", szModule, e); +#endif + } + + void *FindSignature(const byte *pData) { + unsigned char *pMemory; + void *return_addr = nullptr; + + size_t iSigLength = V_strlen((const char *)pData); + + pMemory = (byte *)m_base; + + for (size_t i = 0; i < m_size; i++) { + size_t Matches = 0; + while (*(pMemory + i + Matches) == pData[Matches] || pData[Matches] == '\x2A') { + Matches++; + if (Matches == iSigLength) return_addr = (void *)(pMemory + i); + } + } + + return return_addr; + } + + void *FindInterface(const char *name) { + CreateInterfaceFn fn = (CreateInterfaceFn)dlsym(m_hModule, "CreateInterface"); + + if (!fn) Error("Could not find CreateInterface in %s\n", m_pszModule); + + void *pInterface = fn(name, nullptr); + + if (!pInterface) Error("Could not find %s in %s\n", name, m_pszModule); + + return pInterface; + } + + const char *m_pszModule; + const char *m_pszPath; + HINSTANCE m_hModule; + void *m_base; + size_t m_size; +}; +} // namespace modules +} // namespace counterstrikesharp \ No newline at end of file diff --git a/src/mm_plugin.cpp b/src/mm_plugin.cpp index 398653200..f13f80090 100644 --- a/src/mm_plugin.cpp +++ b/src/mm_plugin.cpp @@ -26,6 +26,8 @@ #include "scripting/callback_manager.h" #include "scripting/dotnet_host.h" #include "scripting/script_engine.h" +#include "entity2/entitysystem.h" +#include "interfaces/cs2_interfaces.h" counterstrikesharp::GlobalClass *counterstrikesharp::GlobalClass::head = nullptr; @@ -114,6 +116,8 @@ void CounterStrikeSharpMMPlugin::Hook_StartupServer(const GameSessionConfigurati on_activate_callback->ScriptContext().Reset(); on_activate_callback->ScriptContext().Push(globals::getGlobalVars()->mapname); on_activate_callback->Execute(); + + globals::entitySystem = interfaces::pGameResourceServiceServer->GetGameEntitySystem(); } bool CounterStrikeSharpMMPlugin::Unload(char *error, size_t maxlen) { @@ -137,6 +141,7 @@ void CounterStrikeSharpMMPlugin::AddTaskForNextFrame(std::function &&tas m_nextTasks.push_back(std::forward(task)); } + void CounterStrikeSharpMMPlugin::Hook_GameFrame(bool simulating, bool bFirstTick, bool bLastTick) { /** * simulating: diff --git a/src/scripting/natives/natives_memory.yaml b/src/scripting/natives/natives_memory.yaml index 565f4a852..f654b30ec 100644 --- a/src/scripting/natives/natives_memory.yaml +++ b/src/scripting/natives/natives_memory.yaml @@ -1,4 +1,4 @@ CREATE_VIRTUAL_FUNCTION: pointer:pointer,vtableOffset:int,numArguments:int,returnType:int,arguments:object[] -> pointer CREATE_VIRTUAL_FUNCTION_BY_SIGNATURE: pointer:pointer,binaryName:string,signature:string,numArguments:int,returnType:int,arguments:object[] -> pointer -EXECUTE_VIRTUAL_FUNCTION: function:pointer,arguments:object[] -> object +EXECUTE_VIRTUAL_FUNCTION: function:pointer,arguments:object[] -> any FIND_SIGNATURE: modulePath:string, signature:string -> pointer \ No newline at end of file diff --git a/src/scripting/natives/natives_schema.cpp b/src/scripting/natives/natives_schema.cpp new file mode 100644 index 000000000..4299334a2 --- /dev/null +++ b/src/scripting/natives/natives_schema.cpp @@ -0,0 +1,206 @@ +/* + * 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 . * + */ + +#include +#include +#include + +#include "scripting/autonative.h" +#include "scripting/script_engine.h" +#include "core/memory.h" +#include "core/log.h" +#include "schema.h" +#include "core/function.h" + +namespace counterstrikesharp { + +void GetSchemaValueByName(ScriptContext &script_context) { + auto instancePointer = script_context.GetArgument(0); + auto returnType = script_context.GetArgument(1); + auto className = script_context.GetArgument(2); + auto memberName = script_context.GetArgument(3); + auto classKey = hash_32_fnv1a_const(className); + auto memberKey = hash_32_fnv1a_const(memberName); + + const auto m_key = schema::GetOffset(className, classKey, memberName, memberKey); + + switch (returnType) { + case DATA_TYPE_BOOL: + script_context.SetResult(*reinterpret_cast>( + (uintptr_t)(instancePointer) + m_key.offset)); + break; + case DATA_TYPE_CHAR: + script_context.SetResult(*reinterpret_cast>( + (uintptr_t)(instancePointer) + m_key.offset)); + break; + case DATA_TYPE_UCHAR: + script_context.SetResult(*reinterpret_cast>( + (uintptr_t)(instancePointer) + m_key.offset)); + break; + case DATA_TYPE_SHORT: + script_context.SetResult(*reinterpret_cast>( + (uintptr_t)(instancePointer) + m_key.offset)); + break; + case DATA_TYPE_USHORT: + script_context.SetResult(*reinterpret_cast>( + (uintptr_t)(instancePointer) + m_key.offset)); + break; + case DATA_TYPE_INT: + script_context.SetResult(*reinterpret_cast>( + (uintptr_t)(instancePointer) + m_key.offset)); + break; + case DATA_TYPE_UINT: + script_context.SetResult(*reinterpret_cast>( + (uintptr_t)(instancePointer) + m_key.offset)); + break; + case DATA_TYPE_LONG: + script_context.SetResult(*reinterpret_cast>( + (uintptr_t)(instancePointer) + m_key.offset)); + break; + case DATA_TYPE_ULONG: + script_context.SetResult(*reinterpret_cast>( + (uintptr_t)(instancePointer) + m_key.offset)); + break; + case DATA_TYPE_LONG_LONG: + script_context.SetResult(*reinterpret_cast>( + (uintptr_t)(instancePointer) + m_key.offset)); + break; + case DATA_TYPE_ULONG_LONG: + script_context.SetResult(*reinterpret_cast>( + (uintptr_t)(instancePointer) + m_key.offset)); + break; + case DATA_TYPE_FLOAT: + script_context.SetResult(*reinterpret_cast>( + (uintptr_t)(instancePointer) + m_key.offset)); + break; + case DATA_TYPE_DOUBLE: + script_context.SetResult(*reinterpret_cast>( + (uintptr_t)(instancePointer) + m_key.offset)); + break; + case DATA_TYPE_POINTER: + script_context.SetResult(reinterpret_cast>( + (uintptr_t)(instancePointer) + m_key.offset)); + break; + case DATA_TYPE_STRING: + script_context.SetResult(reinterpret_cast>( + (uintptr_t)(instancePointer) + m_key.offset)); + break; + default: + assert(!"Unknown function return type!"); + break; + } +} + +void SetSchemaValueByName(ScriptContext &script_context) { + auto instancePointer = script_context.GetArgument(0); + auto dataType = script_context.GetArgument(1); + auto className = script_context.GetArgument(2); + auto memberName = script_context.GetArgument(3); + auto classKey = hash_32_fnv1a_const(className); + auto memberKey = hash_32_fnv1a_const(memberName); + + const auto m_key = schema::GetOffset(className, classKey, memberName, memberKey); + const auto m_chain = schema::FindChainOffset(className); + + // todo network updates + // if (m_chain != 0 && m_key.networked) { + // addresses::NetworkStateChanged((uintptr_t)(instancePointer) + m_chain, m_key.offset, + // 0xFFFFFFFF); + // } else if (m_key.networked) { /* WIP: Works fine for most props, but inlined classes in + // the + // middle of a class will need to have their this pointer + // corrected by the offset .*/ + // CALL_VIRTUAL(void, 1, instancePointer, m_key.offset, 0xFFFFFFFF, 0xFFFF); + // } + + switch (dataType) { + case DATA_TYPE_BOOL: + *reinterpret_cast>( + (uintptr_t)(instancePointer) + m_key.offset) = script_context.GetArgument(4); + break; + case DATA_TYPE_CHAR: + *reinterpret_cast>( + (uintptr_t)(instancePointer) + m_key.offset) = script_context.GetArgument(4); + break; + case DATA_TYPE_UCHAR: + *reinterpret_cast>((uintptr_t)(instancePointer) + + m_key.offset) = + script_context.GetArgument(4); + break; + case DATA_TYPE_SHORT: + *reinterpret_cast>( + (uintptr_t)(instancePointer) + m_key.offset) = script_context.GetArgument(4); + break; + case DATA_TYPE_USHORT: + *reinterpret_cast>((uintptr_t)(instancePointer) + + m_key.offset) = + script_context.GetArgument(4); + break; + case DATA_TYPE_INT: + *reinterpret_cast>( + (uintptr_t)(instancePointer) + m_key.offset) = script_context.GetArgument(4); + break; + case DATA_TYPE_UINT: + *reinterpret_cast>((uintptr_t)(instancePointer) + + m_key.offset) = + script_context.GetArgument(4); + break; + case DATA_TYPE_LONG: + *reinterpret_cast>( + (uintptr_t)(instancePointer) + m_key.offset) = script_context.GetArgument(4); + break; + case DATA_TYPE_ULONG: + *reinterpret_cast>((uintptr_t)(instancePointer) + + m_key.offset) = + script_context.GetArgument(4); + break; + case DATA_TYPE_LONG_LONG: + *reinterpret_cast>((uintptr_t)(instancePointer) + + m_key.offset) = + script_context.GetArgument(4); + break; + case DATA_TYPE_ULONG_LONG: + *reinterpret_cast>((uintptr_t)(instancePointer) + + m_key.offset) = + script_context.GetArgument(4); + break; + case DATA_TYPE_FLOAT: + *reinterpret_cast>( + (uintptr_t)(instancePointer) + m_key.offset) = script_context.GetArgument(4); + break; + case DATA_TYPE_DOUBLE: + *reinterpret_cast>((uintptr_t)(instancePointer) + + m_key.offset) = + script_context.GetArgument(4); + break; + // Do not support pointer setting at this stage + // case DATA_TYPE_POINTER: + // reinterpret_cast>((uintptr_t)(instancePointer) + + // m_key.offset) = script_context.GetArgument(4); break; + // case DATA_TYPE_STRING: + // reinterpret_cast>((uintptr_t)(instancePointer) + + // m_key.offset) = script_context.GetArgument(4); break; + default: + assert(!"Unknown function data type!"); + break; + } +} + +REGISTER_NATIVES(schema, { + ScriptEngine::RegisterNativeHandler("GET_SCHEMA_VALUE_BY_NAME", GetSchemaValueByName); + ScriptEngine::RegisterNativeHandler("SET_SCHEMA_VALUE_BY_NAME", SetSchemaValueByName); +}) +} // namespace counterstrikesharp \ No newline at end of file diff --git a/src/scripting/natives/natives_schema.yaml b/src/scripting/natives/natives_schema.yaml new file mode 100644 index 000000000..7e6d99d75 --- /dev/null +++ b/src/scripting/natives/natives_schema.yaml @@ -0,0 +1,2 @@ +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 \ No newline at end of file diff --git a/src/utils/virtual.h b/src/utils/virtual.h index 62d8c2b7e..5f52a2e18 100644 --- a/src/utils/virtual.h +++ b/src/utils/virtual.h @@ -26,13 +26,11 @@ namespace vmt { template inline T GetVMethod(uint32 uIndex, void *pClass) { if (!pClass) { - Warning("Tried getting virtual function from a null class.\n"); return T(); } void **pVTable = *static_cast(pClass); if (!pVTable) { - Warning("Tried getting virtual function from a null vtable.\n"); return T(); } @@ -44,10 +42,9 @@ inline T CallVirtual(uint32 uIndex, void *pClass, Args... args) { #ifdef _WIN32 auto pFunc = GetVMethod(uIndex, pClass); #else - auto pFunc = GetVMethod(uIndex, pClass); + auto pFunc = GetVMethod(uIndex, pClass); #endif if (!pFunc) { - Warning("Tried calling a null virtual function.\n"); return T(); } diff --git a/tooling/CodeGen.Natives/Mapping.cs b/tooling/CodeGen.Natives/Mapping.cs index b74b56d80..1c3b5b013 100644 --- a/tooling/CodeGen.Natives/Mapping.cs +++ b/tooling/CodeGen.Natives/Mapping.cs @@ -58,6 +58,8 @@ public static string GetCSharpType(string type) return "object[]"; case "SteamID": return "[CastFrom(typeof(ulong))]SteamID"; + case "any": + return "T"; } return "object"; diff --git a/tooling/CodeGen.Natives/Scripts/GenerateNatives.cs b/tooling/CodeGen.Natives/Scripts/GenerateNatives.cs index e7ca57c66..619de8db2 100644 --- a/tooling/CodeGen.Natives/Scripts/GenerateNatives.cs +++ b/tooling/CodeGen.Natives/Scripts/GenerateNatives.cs @@ -48,8 +48,9 @@ public static void GenerateNatives() var arguments = string.Join(", ", native.Arguments.Select(pair => $"{Mapping.GetCSharpType(pair.Value)} {pair.Key}")); + var hasGenerics = native.ReturnType == "any" || native.Arguments.Any(pair => pair.Value == "any"); var returnStr = new StringBuilder($@" - public static {Mapping.GetCSharpType(native.ReturnType)} {native.NameCamelCase}({arguments}){{{Environment.NewLine}"); + public static {Mapping.GetCSharpType(native.ReturnType)} {native.NameCamelCase}{(hasGenerics ? "" : "")}({arguments}){{{Environment.NewLine}"); returnStr.Append("\t\t\tlock (ScriptContext.GlobalScriptContext.Lock) {\n"); returnStr.Append("\t\t\tScriptContext.GlobalScriptContext.Reset();\n");