Compare commits

...

3 Commits

Author SHA1 Message Date
Yarukon
e270bdfd44 AcceptInput changes (#433) 2024-05-01 01:12:25 +10:00
Michael Wilson
f591fe58d0 fix: add memoverride on windows, use correct MT flag (#431) 2024-04-30 17:40:25 +10:00
Roflmuffin
052cb4e14e chore: improve ccsplayercontroller helpers validity checks 2024-04-30 11:39:49 +10:00
12 changed files with 185 additions and 72 deletions

View File

@@ -5,6 +5,8 @@ project(counterstrikesharp C CXX ASM)
include("makefiles/shared.cmake")
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
add_subdirectory(libraries/spdlog)
add_subdirectory(libraries/dyncall)
add_subdirectory(libraries/funchook)
@@ -13,7 +15,8 @@ add_subdirectory(libraries/DynoHook)
set_property(TARGET dynohook PROPERTY DYNO_ARCH_X86 64)
set_property(TARGET funchook-static PROPERTY POSITION_INDEPENDENT_CODE ON)
SET(SOURCE_FILES
set(SOURCE_FILES
libraries/hl2sdk-cs2/public/tier0/memoverride.cpp
src/mm_plugin.cpp
src/mm_plugin.h
libraries/hl2sdk-cs2/tier1/convar.cpp
@@ -95,66 +98,55 @@ SET(SOURCE_FILES
src/core/game_system.cpp
)
if (LINUX)
# memoverride.cpp is not usable on CMake Windows, cuz CMake default link libraries (seems) always link ucrt.lib
set(SOURCE_FILES
${SOURCE_FILES}
libraries/hl2sdk-cs2/public/tier0/memoverride.cpp
)
endif()
set(PROTO_DIRS -I${CMAKE_CURRENT_SOURCE_DIR}/libraries/Protobufs/csgo)
file(GLOB PROTOS "${CMAKE_CURRENT_SOURCE_DIR}/libraries/Protobufs/csgo/*.proto")
## Generate protobuf source & headers
if (LINUX)
if(LINUX)
set(PROTOC_EXECUTABLE ${CMAKE_CURRENT_SOURCE_DIR}/libraries/hl2sdk-cs2/devtools/bin/linux/protoc)
elseif(WIN32)
set(PROTOC_EXECUTABLE ${CMAKE_CURRENT_SOURCE_DIR}/libraries/hl2sdk-cs2/devtools/bin/protoc.exe)
endif()
add_custom_command(
OUTPUT protobuf_output_stamp
COMMAND ${PROTOC_EXECUTABLE} --proto_path=thirdparty/protobuf-3.21.8/src --proto_path=common --cpp_out=common common/network_connection.proto
COMMENT "Generating protobuf file"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/libraries/hl2sdk-cs2
VERBATIM
OUTPUT protobuf_output_stamp
COMMAND ${PROTOC_EXECUTABLE} --proto_path=thirdparty/protobuf-3.21.8/src --proto_path=common --cpp_out=common common/network_connection.proto
COMMENT "Generating protobuf file"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/libraries/hl2sdk-cs2
VERBATIM
)
SET(SOURCE_FILES ${SOURCE_FILES} protobuf_output_stamp)
set(SOURCE_FILES ${SOURCE_FILES} protobuf_output_stamp)
# Sources
add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${NATIVES_SOURCES} ${CONVERSIONS_SOURCES} ${CONVERSIONS_HEADERS})
target_include_directories(
${PROJECT_NAME}
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/src/core/cs2_sdk
${PROJECT_NAME}
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/src/core/cs2_sdk
)
if (LINUX)
if(LINUX)
include("makefiles/linux.base.cmake")
set_target_properties(${PROJECT_NAME} PROPERTIES
PREFIX ""
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/addons/counterstrikesharp/bin/linuxsteamrt64"
PREFIX ""
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/addons/counterstrikesharp/bin/linuxsteamrt64"
)
elseif(WIN32)
include("makefiles/windows.base.cmake")
set_target_properties(${PROJECT_NAME} PROPERTIES
PREFIX ""
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/addons/counterstrikesharp/bin/win64"
PREFIX ""
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/addons/counterstrikesharp/bin/win64"
)
endif()
# Libraries
target_link_libraries(${PROJECT_NAME} ${COUNTER_STRIKE_SHARP_LINK_LIBRARIES})
add_custom_command(
TARGET ${PROJECT_NAME} PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/configs ${CMAKE_BINARY_DIR}
)
TARGET ${PROJECT_NAME} PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/configs ${CMAKE_BINARY_DIR}
)

View File

@@ -133,6 +133,13 @@
"linux": "55 48 89 E5 41 57 49 89 FF 41 56 48 8D 7D C0"
}
},
"CEntitySystem_AddEntityIOEvent": {
"signatures": {
"library": "server",
"windows": "48 89 5C 24 ? 48 89 74 24 ? 57 48 ? ? ? 49 ? ? 48 ? ? 48 ? ? 74",
"linux": "55 41 BA FF FF FF FF"
}
},
"LegacyGameEventListener": {
"signatures": {
"library": "server",

View File

@@ -6,6 +6,7 @@ add_definitions(
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4819 /wd4828 /wd5033 /permissive- /utf-8 /wd4005 /MP")
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /OPT:REF /OPT:ICF")
set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} /NODEFAULTLIB:libcmt")
set(COUNTER_STRIKE_SHARP_LINK_LIBRARIES
${SOURCESDK_LIB}/public/win64/tier0.lib
@@ -18,4 +19,4 @@ set(COUNTER_STRIKE_SHARP_LINK_LIBRARIES
distorm
funchook-static
dynohook
)
)

View File

@@ -714,6 +714,37 @@ namespace CounterStrikeSharp.API.Core
}
}
public static void AcceptInput(IntPtr pthis, string inputname, IntPtr activator, IntPtr caller, string value, int outputid){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(pthis);
ScriptContext.GlobalScriptContext.Push(inputname);
ScriptContext.GlobalScriptContext.Push(activator);
ScriptContext.GlobalScriptContext.Push(caller);
ScriptContext.GlobalScriptContext.Push(value);
ScriptContext.GlobalScriptContext.Push(outputid);
ScriptContext.GlobalScriptContext.SetIdentifier(0x259E084C);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static void AddEntityIoEvent(IntPtr ptarget, string inputname, IntPtr activator, IntPtr caller, string value, float delay, int outputid){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(ptarget);
ScriptContext.GlobalScriptContext.Push(inputname);
ScriptContext.GlobalScriptContext.Push(activator);
ScriptContext.GlobalScriptContext.Push(caller);
ScriptContext.GlobalScriptContext.Push(value);
ScriptContext.GlobalScriptContext.Push(delay);
ScriptContext.GlobalScriptContext.Push(outputid);
ScriptContext.GlobalScriptContext.SetIdentifier(0x4CFDE98A);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static void HookEvent(string name, InputArgument callback, bool ispost){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();

View File

@@ -23,12 +23,7 @@ public partial class CCSPlayerController
{
Guard.IsValidEntity(this);
if (!PlayerPawn.IsValid) return null;
if (PlayerPawn.Value == null) return null; ;
if (!PlayerPawn.Value.IsValid) return null;
if (PlayerPawn.Value.ItemServices == null) return null;
return PlayerPawn.Value.ItemServices.As<CCSPlayer_ItemServices>().GiveNamedItem<T>(item);
return PlayerPawn.Value?.ItemServices?.As<CCSPlayer_ItemServices>().GiveNamedItem<T>(item);
}
public IntPtr GiveNamedItem(CsItem item)
@@ -98,17 +93,13 @@ public partial class CCSPlayerController
public void DropActiveWeapon()
{
Guard.IsValidEntity(this);
if (!PlayerPawn.IsValid) return;
if (PlayerPawn.Value == null) return;
if (!PlayerPawn.Value.IsValid) return;
if (PlayerPawn.Value.ItemServices == null) return;
if (PlayerPawn.Value.WeaponServices == null) return;
if (!PlayerPawn.Value.WeaponServices.ActiveWeapon.IsValid) return;
CCSPlayer_ItemServices itemServices = new CCSPlayer_ItemServices(PlayerPawn.Value.ItemServices.Handle);
CCSPlayer_WeaponServices weaponServices = new CCSPlayer_WeaponServices(PlayerPawn.Value.WeaponServices.Handle);
var itemServices = PlayerPawn.Value?.ItemServices?.As<CCSPlayer_ItemServices>();
var activeWeapon = PlayerPawn.Value?.WeaponServices?.ActiveWeapon.Value;
itemServices.DropActivePlayerWeapon(weaponServices.ActiveWeapon.Value);
if (activeWeapon == null || itemServices == null) return;
itemServices.DropActivePlayerWeapon(activeWeapon);
}
/// <summary>
@@ -118,13 +109,8 @@ public partial class CCSPlayerController
public void RemoveWeapons()
{
Guard.IsValidEntity(this);
if (!PlayerPawn.IsValid) return;
if (PlayerPawn.Value == null) return;
if (!PlayerPawn.Value.IsValid) return;
if (PlayerPawn.Value.ItemServices == null) return;
CCSPlayer_ItemServices itemServices = new CCSPlayer_ItemServices(PlayerPawn.Value.ItemServices.Handle);
itemServices.RemoveWeapons();
PlayerPawn.Value?.ItemServices?.As<CCSPlayer_ItemServices>().RemoveWeapons();
}
/// <summary>
@@ -136,11 +122,8 @@ public partial class CCSPlayerController
public void CommitSuicide(bool explode, bool force)
{
Guard.IsValidEntity(this);
if (!PlayerPawn.IsValid) return;
if (PlayerPawn.Value == null) return;
if (!PlayerPawn.Value.IsValid) return;
PlayerPawn.Value.CommitSuicide(explode, force);
PlayerPawn.Value?.CommitSuicide(explode, force);
}
/// <summary>
@@ -150,9 +133,7 @@ public partial class CCSPlayerController
public void Respawn()
{
Guard.IsValidEntity(this);
if (!PlayerPawn.IsValid) return;
if (PlayerPawn.Value == null) return;
if (!PlayerPawn.Value.IsValid) return;
// The Call To Arms update appears to have invalidated the need for CCSPlayerPawn_Respawn.
SetPawn(PlayerPawn.Value);

View File

@@ -65,7 +65,7 @@ public partial class CEntityInstance : IEquatable<CEntityInstance>
}
/// <summary>
/// Calls a named input method on an entity.
/// Calls a named input method on an entity, this will bypass the map IO event queue system.
/// <example>
/// <code>
/// entity.AcceptInput("Break");
@@ -82,7 +82,30 @@ public partial class CEntityInstance : IEquatable<CEntityInstance>
{
Guard.IsValidEntity(this);
VirtualFunctions.AcceptInput(Handle, inputName, activator?.Handle ?? IntPtr.Zero, caller?.Handle ?? IntPtr.Zero, value, 0);
NativeAPI.AcceptInput(Handle, inputName, activator?.Handle ?? IntPtr.Zero, caller?.Handle ?? IntPtr.Zero, value, outputId);
}
/// <summary>
/// Calls a named input method on an entity, conforming to the map IO event queue system.
/// <example>
/// <code>
/// entity.AddEntityIOEvent("Break");
/// </code>
/// </example>
/// </summary>
/// <param name="inputName">Input action name</param>
/// <param name="activator">Entity which initiated the action, <see langword="null"/> for no entity</param>
/// <param name="caller">Entity that is sending the event, <see langword="null"/> for no entity</param>
/// <param name="value">String variant value to send with the event</param>
/// <param name="delay">Delay in seconds before calling the input</param>
/// <param name="outputId">Unknown, defaults to 0</param>
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
public void AddEntityIOEvent(string inputName, CEntityInstance? activator = null, CEntityInstance? caller = null, string value = "", float delay = 0, int outputId = 0)
{
Guard.IsValidEntity(this);
NativeAPI.AddEntityIoEvent(Handle, inputName, activator?.Handle ?? IntPtr.Zero, caller?.Handle ?? IntPtr.Zero, value, delay, outputId);
}
}
@@ -90,4 +113,4 @@ public partial class CEntityIdentity
{
public unsafe CEntityInstance EntityInstance => new(Unsafe.Read<IntPtr>((void*)Handle));
public unsafe CHandle<CEntityInstance> EntityHandle => new(this.Handle + 0x10);
}
}

View File

@@ -81,9 +81,6 @@ public static class VirtualFunctions
public static MemoryFunctionVoid<IntPtr, IntPtr> RemovePlayerItemFunc =
new(GameData.GetSignature("CBasePlayerPawn_RemovePlayerItem"));
public static Action<IntPtr, IntPtr> RemovePlayerItemVirtual = RemovePlayerItemFunc.Invoke;
public static MemoryFunctionVoid<IntPtr, string, IntPtr, IntPtr, string, int> AcceptInputFunc = new(GameData.GetSignature("CEntityInstance_AcceptInput"));
public static Action<IntPtr, string, IntPtr, IntPtr, string, int> AcceptInput = AcceptInputFunc.Invoke;
public static MemoryFunctionVoid<IntPtr, IntPtr, int, short, short> StateChangedFunc =
new(GameData.GetSignature("StateChanged"));
@@ -92,4 +89,4 @@ public static class VirtualFunctions
public static MemoryFunctionVoid<IntPtr, int, long> NetworkStateChangedFunc = new(GameData.GetSignature("NetworkStateChanged"));
public static Action<IntPtr, int, long> NetworkStateChanged = NetworkStateChangedFunc.Invoke;
}
}

View File

@@ -122,7 +122,7 @@ namespace TestPlugin
{
RegisterListener<Listeners.OnMapStart>(name =>
{
ConVar.FindRequired("sv_cheats").SetValue(true);
ConVar.Find("sv_cheats")?.SetValue(true);
var numericCvar = ConVar.Find("mp_warmuptime");
Logger.LogInformation("mp_warmuptime = {Value}", numericCvar?.GetPrimitiveValue<float>());
@@ -357,6 +357,17 @@ namespace TestPlugin
return HookResult.Continue;
}
[ConsoleCommand("css_testinput", "Test AcceptInput and AddEntityIOEvent")]
public void OnTestInput(CCSPlayerController? player, CommandInfo command)
{
if (player == null) return;
var pawn = player.PlayerPawn.Get();
if (pawn == null) return;
pawn!.AcceptInput("SetHealth", null, null, "50");
pawn!.AddEntityIOEvent("SetHealth", null, null, "75", 5);
}
[ConsoleCommand("css_killmeplease", "Kills the player")]
[ConsoleCommand("css_killme", "Kills the player")]
public void OnKillme(CCSPlayerController? player, CommandInfo command)

View File

@@ -46,6 +46,22 @@ void EntityManager::OnAllInitialized()
return;
}
CEntityInstance_AcceptInput = decltype(CEntityInstance_AcceptInput)(
modules::server->FindSignature(globals::gameConfig->GetSignature("CEntityInstance_AcceptInput")));
if (!CEntityInstance_AcceptInput)
{
CSSHARP_CORE_CRITICAL("Failed to find signature for \'CEntityInstance_AcceptInput\'");
}
CEntitySystem_AddEntityIOEvent = decltype(CEntitySystem_AddEntityIOEvent)(
modules::server->FindSignature(globals::gameConfig->GetSignature("CEntitySystem_AddEntityIOEvent")));
if (!CEntitySystem_AddEntityIOEvent)
{
CSSHARP_CORE_CRITICAL("Failed to find signature for \'CEntitySystem_AddEntityIOEvent\'");
}
auto m_hook = funchook_create();
funchook_prepare(m_hook, (void**)&m_pFireOutputInternal, (void*)&DetourFireOutputInternal);
funchook_install(m_hook, 0);
@@ -219,4 +235,4 @@ void DetourFireOutputInternal(CEntityIOOutput* const pThis, CEntityInstance* pAc
}
}
} // namespace counterstrikesharp
} // namespace counterstrikesharp

View File

@@ -111,4 +111,15 @@ static void DetourFireOutputInternal(CEntityIOOutput* const pThis, CEntityInstan
static FireOutputInternal m_pFireOutputInternal = nullptr;
} // namespace counterstrikesharp
// 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 (*CEntitySystem_AddEntityIOEvent)(CEntitySystem* pEntitySystem,
CEntityInstance* pTarget,
const char* pInputName,
CEntityInstance* pActivator,
CEntityInstance* pCaller,
variant_t* value,
float delay,
int nOutputID);
} // namespace counterstrikesharp

View File

@@ -193,6 +193,45 @@ void UnhookEntityOutput(ScriptContext& script_context)
globals::entityManager.UnhookEntityOutput(szClassname, szOutput, callback, mode);
}
void AcceptInput(ScriptContext& script_context)
{
if (!CEntityInstance_AcceptInput)
{
script_context.ThrowNativeError("Failed to find signature for \'CEntityInstance_AcceptInput\'");
return;
}
CEntityInstance* pThis = script_context.GetArgument<CEntityInstance*>(0);
const char* pInputName = script_context.GetArgument<const char*>(1);
CEntityInstance* pActivator = script_context.GetArgument<CEntityInstance*>(2);
CEntityInstance* pCaller = script_context.GetArgument<CEntityInstance*>(3);
const char* value = script_context.GetArgument<const char*>(4);
int outputID = script_context.GetArgument<int>(5);
variant_t _value = variant_t(value);
CEntityInstance_AcceptInput(pThis, pInputName, pActivator, pCaller, &_value, outputID);
}
void AddEntityIOEvent(ScriptContext& script_context)
{
if (!CEntitySystem_AddEntityIOEvent)
{
script_context.ThrowNativeError("Failed to find signature for \'CEntitySystem_AddEntityIOEvent\'");
return;
}
CEntityInstance* pTarget = script_context.GetArgument<CEntityInstance*>(0);
const char* pInputName = script_context.GetArgument<const char*>(1);
CEntityInstance* pActivator = script_context.GetArgument<CEntityInstance*>(2);
CEntityInstance* pCaller = script_context.GetArgument<CEntityInstance*>(3);
const char* value = script_context.GetArgument<const char*>(4);
float delay = script_context.GetArgument<float>(5);
int outputID = script_context.GetArgument<int>(6);
variant_t _value = variant_t(value);
CEntitySystem_AddEntityIOEvent(GameEntitySystem(), pTarget, pInputName, pActivator, pCaller, &_value, delay, outputID);
}
REGISTER_NATIVES(entities, {
ScriptEngine::RegisterNativeHandler("GET_ENTITY_FROM_INDEX", GetEntityFromIndex);
ScriptEngine::RegisterNativeHandler("GET_USERID_FROM_INDEX", GetUserIdFromIndex);
@@ -209,5 +248,7 @@ REGISTER_NATIVES(entities, {
ScriptEngine::RegisterNativeHandler("GET_PLAYER_IP_ADDRESS", GetPlayerIpAddress);
ScriptEngine::RegisterNativeHandler("HOOK_ENTITY_OUTPUT", HookEntityOutput);
ScriptEngine::RegisterNativeHandler("UNHOOK_ENTITY_OUTPUT", UnhookEntityOutput);
ScriptEngine::RegisterNativeHandler("ACCEPT_INPUT", AcceptInput);
ScriptEngine::RegisterNativeHandler("ADD_ENTITY_IO_EVENT", AddEntityIOEvent);
})
} // namespace counterstrikesharp
} // namespace counterstrikesharp

View File

@@ -11,4 +11,6 @@ GET_FIRST_ACTIVE_ENTITY: -> pointer
GET_PLAYER_AUTHORIZED_STEAMID: slot:int -> uint64
GET_PLAYER_IP_ADDRESS: slot:int -> string
HOOK_ENTITY_OUTPUT: classname:string, outputName:string, callback:func, mode:HookMode -> void
UNHOOK_ENTITY_OUTPUT: classname:string, outputName:string, callback:func, mode:HookMode -> void
UNHOOK_ENTITY_OUTPUT: classname:string, outputName:string, callback:func, mode:HookMode -> void
ACCEPT_INPUT: pThis:pointer, inputName:string, activator:pointer, caller:pointer, value:string, outputID:int -> void
ADD_ENTITY_IO_EVENT: pTarget:pointer, inputName:string, activator:pointer, caller:pointer, value:string, delay:float, outputID:int -> void