diff --git a/managed/src/SwiftlyS2.Core/Bootstrap.cs b/managed/src/SwiftlyS2.Core/Bootstrap.cs index 1dd62747..e35de1f4 100644 --- a/managed/src/SwiftlyS2.Core/Bootstrap.cs +++ b/managed/src/SwiftlyS2.Core/Bootstrap.cs @@ -62,6 +62,7 @@ public static void Start(IntPtr nativeTable, int nativeTableSize, string basePat .AddTraceManagerService() .AddPermissionManager() .AddCoreHookService() + .AddCoreCommandService() .AddMenuService() .AddCommandTrackerManager() .AddCommandTrackerService() diff --git a/managed/src/SwiftlyS2.Core/Services/CoreHookService.cs b/managed/src/SwiftlyS2.Core/Services/CoreHookService.cs index f4a6592e..926ce0ef 100644 --- a/managed/src/SwiftlyS2.Core/Services/CoreHookService.cs +++ b/managed/src/SwiftlyS2.Core/Services/CoreHookService.cs @@ -12,14 +12,16 @@ namespace SwiftlyS2.Core.Services; -internal class CoreHookService : IDisposable { +internal class CoreHookService : IDisposable +{ private ILogger _Logger { get; init; } private ISwiftlyCore _Core { get; init; } - public CoreHookService(ILogger logger, ISwiftlyCore core) { + public CoreHookService(ILogger logger, ISwiftlyCore core) + { _Logger = logger; _Core = core; - + HookCanAcquire(); HookCommandExecute(); HookICVarFindConCommand(); @@ -56,14 +58,16 @@ __int64 sub_1C0CD0(__int64 a1, int a2, unsigned int a3, ...) private IUnmanagedFunction? _CanAcquire; private Guid _CanAcquireGuid; - private void HookCanAcquire() { + private void HookCanAcquire() + { var address = _Core.GameData.GetSignature("CCSPlayer_ItemServices::CanAcquire"); _Logger.LogInformation("Hooking CCSPlayer_ItemServices::CanAcquire at {Address}", address); _CanAcquire = _Core.Memory.GetUnmanagedFunctionByAddress(address); - _CanAcquireGuid = _CanAcquire.AddHook(next => { + _CanAcquireGuid = _CanAcquire.AddHook(next => + { return (pItemServices, pEconItemView, acquireMethod, unk1) => { @@ -72,7 +76,8 @@ private void HookCanAcquire() { var itemServices = _Core.Memory.ToSchemaClass(pItemServices); var econItemView = _Core.Memory.ToSchemaClass(pEconItemView); - var @event = new OnItemServicesCanAcquireHookEvent { + var @event = new OnItemServicesCanAcquireHookEvent + { ItemServices = itemServices, EconItemView = econItemView, AcquireMethod = (AcquireMethod)acquireMethod, @@ -81,7 +86,8 @@ private void HookCanAcquire() { EventPublisher.InvokeOnCanAcquireHook(@event); - if (@event.Intercepted) { + if (@event.Intercepted) + { // original result is modified here. return (int)@event.OriginalResult; } @@ -91,7 +97,8 @@ private void HookCanAcquire() { }); } - private void HookCommandExecute() { + private void HookCommandExecute() + { var address = _Core.GameData.GetSignature("Cmd_ExecuteCommand"); @@ -103,13 +110,15 @@ private void HookCommandExecute() { { return (a1, a2, a3, a4, a5) => { - var (commandName, commandPtr) = (a5 != nint.Zero && a5 < nint.MaxValue && commandNameOffset != 0) switch { + var (commandName, commandPtr) = (a5 != nint.Zero && a5 < nint.MaxValue && commandNameOffset != 0) switch + { true when Marshal.ReadIntPtr(new nint(a5 + commandNameOffset)) is var basePtr && basePtr != nint.Zero && basePtr < nint.MaxValue => (Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(basePtr)) ?? string.Empty, Marshal.ReadIntPtr(basePtr)), _ => (string.Empty, nint.Zero) }; - var preEvent = new OnCommandExecuteHookEvent { + var preEvent = new OnCommandExecuteHookEvent + { OriginalName = commandName, HookMode = HookMode.Pre }; @@ -117,10 +126,11 @@ true when Marshal.ReadIntPtr(new nint(a5 + commandNameOffset)) is var basePtr && nint newCommandNamePtr = nint.Zero; - if (preEvent.Intercepted && preEvent.CommandName.Length < commandName.Length) { + if (preEvent.Intercepted && preEvent.CommandName.Length < commandName.Length) + { var newCommandName = Encoding.UTF8.GetBytes(preEvent.CommandName); - newCommandNamePtr = Marshal.AllocHGlobal(newCommandName.Length + 1); + newCommandNamePtr = NativeAllocator.Alloc((ulong)(newCommandName.Length + 1)); newCommandNamePtr.Write(newCommandName.Length, 0); newCommandNamePtr.CopyFrom(newCommandName); @@ -128,55 +138,95 @@ true when Marshal.ReadIntPtr(new nint(a5 + commandNameOffset)) is var basePtr && } var result = next()(a1, a2, a3, a4, a5); - - var postEvent = new OnCommandExecuteHookEvent { + + var postEvent = new OnCommandExecuteHookEvent + { OriginalName = commandName, HookMode = HookMode.Post }; EventPublisher.InvokeOnCommandExecuteHook(postEvent); - if (newCommandNamePtr != nint.Zero) { - Marshal.FreeHGlobal(newCommandNamePtr); + if (newCommandNamePtr != nint.Zero) + { + NativeAllocator.Free(newCommandNamePtr); } - + return result; }; }); } private delegate nint FindConCommandDelegate(nint pICvar, nint pRet, nint pConCommandName, int unk1); - private IUnmanagedFunction? _FindConCommand; + private delegate nint FindConCommandDelegateLinux(nint pICvar, nint pConCommandName, int unk1); + + private IUnmanagedFunction? _FindConCommandWindows; + private IUnmanagedFunction? _FindConCommandLinux; private Guid _FindConCommandGuid; - private void HookICVarFindConCommand() { - - var icvar = _Core.Memory.GetInterfaceByName("VEngineCvar007")!; - var offset = _Core.GameData.GetOffset("ICvar::FindConCommand"); - _FindConCommand = _Core.Memory.GetUnmanagedFunctionByVTable(icvar.Value.Read(), offset); - - _Logger.LogInformation("Hooking ICvar::FindConCommand at {Address}", _FindConCommand.Address); - - _FindConCommandGuid = _FindConCommand.AddHook((next) => { - return (pICvar, pRet, pConCommandName, unk1) => { - var commandName = Marshal.PtrToStringAnsi(pConCommandName)!; - if (commandName.StartsWith("^wb^")) { - commandName = commandName.Substring(4); - var bytes = Encoding.UTF8.GetBytes(commandName); - unsafe { - var pStr = (nint)NativeMemory.AllocZeroed((nuint)bytes.Length); - pStr.CopyFrom(bytes); - var result = next()(pICvar, pRet, pStr, unk1); - NativeMemory.Free((void*)pStr); - return result; + private void HookICVarFindConCommand() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + var offset = _Core.GameData.GetOffset("ICvar::FindConCommand"); + _FindConCommandLinux = _Core.Memory.GetUnmanagedFunctionByVTable(_Core.Memory.GetVTableAddress("tier0", "CCvar")!.Value, offset); + + _Logger.LogInformation("Hooking ICvar::FindConCommand at {Address}", _FindConCommandLinux.Address); + + _FindConCommandGuid = _FindConCommandLinux.AddHook((next) => + { + return (pICvar, pConCommandName, unk1) => + { + var commandName = Marshal.PtrToStringAnsi(pConCommandName)!; + if (commandName.StartsWith("^wb^")) + { + commandName = commandName.Substring(4); + var bytes = Encoding.UTF8.GetBytes(commandName); + unsafe + { + var pStr = (nint)NativeMemory.AllocZeroed((nuint)bytes.Length); + pStr.CopyFrom(bytes); + var result = next()(pICvar, pStr, unk1); + NativeMemory.Free((void*)pStr); + return result; + } } - } - return next()(pICvar, pRet, pConCommandName, unk1); - }; - }); + return next()(pICvar, pConCommandName, unk1); + }; + }); + } + else + { + var offset = _Core.GameData.GetOffset("ICvar::FindConCommand"); + _FindConCommandWindows = _Core.Memory.GetUnmanagedFunctionByVTable(_Core.Memory.GetVTableAddress("tier0", "CCvar")!.Value, offset); + _Logger.LogInformation("Hooking ICvar::FindConCommand at {Address}", _FindConCommandWindows.Address); + + _FindConCommandGuid = _FindConCommandWindows.AddHook((next) => + { + return (pICvar, pRet, pConCommandName, unk1) => + { + var commandName = Marshal.PtrToStringAnsi(pConCommandName)!; + if (commandName.StartsWith("^wb^")) + { + commandName = commandName.Substring(4); + var bytes = Encoding.UTF8.GetBytes(commandName); + unsafe + { + var pStr = (nint)NativeMemory.AllocZeroed((nuint)bytes.Length); + pStr.CopyFrom(bytes); + var result = next()(pICvar, pRet, pStr, unk1); + NativeMemory.Free((void*)pStr); + return result; + } + } + return next()(pICvar, pRet, pConCommandName, unk1); + }; + }); + } } - public void Dispose() { + public void Dispose() + { _CanAcquire!.RemoveHook(_CanAcquireGuid); _ExecuteCommand!.RemoveHook(_ExecuteCommandGuid); } diff --git a/managed/src/SwiftlyS2.Core/Services/StartupService.cs b/managed/src/SwiftlyS2.Core/Services/StartupService.cs index 3bdc0b4d..f150bf4e 100644 --- a/managed/src/SwiftlyS2.Core/Services/StartupService.cs +++ b/managed/src/SwiftlyS2.Core/Services/StartupService.cs @@ -12,6 +12,7 @@ internal class StartupService : IHostedService public StartupService(IServiceProvider provider) { _provider = provider; + provider.UseCoreCommandService(); provider.UseCoreHookService(); provider.UsePermissionManager(); provider.UsePluginManager(); diff --git a/managed/src/SwiftlyS2.Shared/Natives/Structs/CTakeDamageInfo.cs b/managed/src/SwiftlyS2.Shared/Natives/Structs/CTakeDamageInfo.cs index 9d7d134b..0583bc08 100644 --- a/managed/src/SwiftlyS2.Shared/Natives/Structs/CTakeDamageInfo.cs +++ b/managed/src/SwiftlyS2.Shared/Natives/Structs/CTakeDamageInfo.cs @@ -85,11 +85,12 @@ public CTakeDamageInfo(CBaseEntity inflictor, CBaseEntity attacker, CBaseEntity public HitGroup_t ActualHitGroup => Trace->HitBox->m_nGroupId; } -[StructLayout(LayoutKind.Sequential)] +[StructLayout(LayoutKind.Sequential,Pack=8,Size=40)] public unsafe struct CTakeDamageResult { public CTakeDamageInfo* OriginatingInfo; public int HealthLost; + public int HealthBefore; public int DamageDealt; public float PreModifiedDamage; public int TotalledHealthLost; diff --git a/plugin_files/gamedata/cs2/core/offsets.jsonc b/plugin_files/gamedata/cs2/core/offsets.jsonc index ab655bcc..f08950be 100644 --- a/plugin_files/gamedata/cs2/core/offsets.jsonc +++ b/plugin_files/gamedata/cs2/core/offsets.jsonc @@ -121,7 +121,7 @@ }, "IGameEventSystem::PostEventAbstract": { "windows": 16, - "linux": 16 + "linux": 15 }, "INetworkServerService::StartupServer": { "windows": 26, diff --git a/plugin_files/gamedata/cs2/core/signatures.jsonc b/plugin_files/gamedata/cs2/core/signatures.jsonc index b7f834e9..1578a346 100644 --- a/plugin_files/gamedata/cs2/core/signatures.jsonc +++ b/plugin_files/gamedata/cs2/core/signatures.jsonc @@ -99,7 +99,7 @@ "CBaseEntity::TakeDamage": { "lib": "server", "windows": "40 55 41 54 41 55 41 56 41 57 48 81 EC ? ? ? ? 48 8D 6C 24 ? 48 89 9D ? ? ? ? 45 33 ED", - "linux": "55 48 89 E5 41 57 41 56 49 89 F6 41 55 41 54 49 89 D4 53 48 89 FB 48 83 EC ? 48 85 D2" + "linux": "55 48 89 E5 41 57 41 56 49 89 F6 41 55 41 54 49 89 FC 53 48 89 D3 48 83 EC ? 48 85 D2" }, // "Error - cannot add bots after game is over." "BotNavIgnore1": { diff --git a/src/core/bridge/metamod.cpp b/src/core/bridge/metamod.cpp index 61f768c5..22163e5c 100644 --- a/src/core/bridge/metamod.cpp +++ b/src/core/bridge/metamod.cpp @@ -35,18 +35,12 @@ class GameSessionConfiguration_t { }; -ICvar* g_pcVar = nullptr; - PLUGIN_EXPOSE(SwiftlyMMBridge, g_MMPluginBridge); bool SwiftlyMMBridge::Load(PluginId id, ISmmAPI* ismm, char* error, size_t maxlen, bool late) { PLUGIN_SAVEVARS(); g_SMAPI->AddListener(this, this); - GET_V_IFACE_CURRENT(GetEngineFactory, g_pCVar, ICvar, CVAR_INTERFACE_VERSION); - - META_CONVAR_REGISTER(FCVAR_RELEASE | FCVAR_SERVER_CAN_EXECUTE | FCVAR_CLIENT_CAN_EXECUTE | FCVAR_GAMEDLL); - bool result = g_SwiftlyCore.Load(BridgeKind_t::Metamod); if (late) diff --git a/src/core/entrypoint.cpp b/src/core/entrypoint.cpp index 9adf0ba0..f03a9671 100644 --- a/src/core/entrypoint.cpp +++ b/src/core/entrypoint.cpp @@ -40,12 +40,17 @@ #include #include +#include #include #include #include +#include + +#include + SwiftlyCore g_SwiftlyCore; InterfacesManager g_ifaceService; CSteamGameServerAPIContext g_SteamAPI; @@ -74,6 +79,8 @@ void DestroyLoopModeHook(void* _this, void* loopmode); bool LoopInitHook(void* _this, KeyValues* pKeyValues, void* pRegistry); void LoopShutdownHook(void* _this); +extern ICvar* g_pCVar; + bool SwiftlyCore::Load(BridgeKind_t kind) { m_iKind = kind; @@ -83,6 +90,10 @@ bool SwiftlyCore::Load(BridgeKind_t kind) s2binlib_set_module_base_from_pointer("server", g_ifaceService.FetchInterface(INTERFACEVERSION_SERVERGAMEDLL)); s2binlib_set_module_base_from_pointer("engine2", g_ifaceService.FetchInterface(INTERFACEVERSION_VENGINESERVER)); + auto cvars = g_ifaceService.FetchInterface(CVAR_INTERFACE_VERSION); + g_pCVar = cvars; + ConVar_Register(FCVAR_RELEASE | FCVAR_SERVER_CAN_EXECUTE | FCVAR_CLIENT_CAN_EXECUTE | FCVAR_GAMEDLL, nullptr, nullptr); + auto logger = g_ifaceService.FetchInterface(LOGGER_INTERFACE_VERSION); if (GetCurrentGame() == "unknown") { @@ -414,6 +425,16 @@ void* SwiftlyCore::GetInterface(const std::string& interface_name) ifaceCreate = get_export(lib, "CreateInterface"); unload_library(lib); } + else if (CVAR_INTERFACE_VERSION == interface_name) { + void* lib = load_library( + (const char_t*)WIN_LINUX( + StringWide(Plat_GetGameDirectory() + std::string("\\bin\\win64\\tier0.dll")).c_str(), + (Plat_GetGameDirectory() + std::string("/bin/linuxsteamrt64/libtier0.so")).c_str() + ) + ); + ifaceCreate = get_export(lib, "CreateInterface"); + unload_library(lib); + } else if (NETWORKMESSAGES_INTERFACE_VERSION == interface_name) { void* lib = load_library( (const char_t*)WIN_LINUX(