diff --git a/managed/src/SwiftlyS2.Core/Modules/Events/EventParams/OnPlayerPawnPostThinkHookEvent.cs b/managed/src/SwiftlyS2.Core/Modules/Events/EventParams/OnPlayerPawnPostThinkHookEvent.cs new file mode 100644 index 000000000..38c89d531 --- /dev/null +++ b/managed/src/SwiftlyS2.Core/Modules/Events/EventParams/OnPlayerPawnPostThinkHookEvent.cs @@ -0,0 +1,9 @@ +using SwiftlyS2.Shared.Events; +using SwiftlyS2.Shared.SchemaDefinitions; + +namespace SwiftlyS2.Core.Events; + +internal class OnPlayerPawnPostThinkHookEvent : IOnPlayerPawnPostThinkHookEvent +{ + public required CCSPlayerPawn PlayerPawn { get; init; } +} \ No newline at end of file diff --git a/managed/src/SwiftlyS2.Core/Modules/Events/EventPublisher.cs b/managed/src/SwiftlyS2.Core/Modules/Events/EventPublisher.cs index a8a5bb390..ff64638df 100644 --- a/managed/src/SwiftlyS2.Core/Modules/Events/EventPublisher.cs +++ b/managed/src/SwiftlyS2.Core/Modules/Events/EventPublisher.cs @@ -724,4 +724,21 @@ public static void InvokeOnMovementServicesRunCommandHook( OnMovementServicesRun return; } } + + public static void InvokeOnPlayerPawnPostThinkHook( OnPlayerPawnPostThinkHookEvent @event ) + { + if (_subscribers.Count == 0) return; + try + { + foreach (var subscriber in _subscribers) + { + subscriber.InvokeOnPlayerPawnPostThinkHook(@event); + } + } + catch (Exception e) + { + if (!GlobalExceptionHandler.Handle(e)) return; + AnsiConsole.WriteException(e); + } + } } \ No newline at end of file diff --git a/managed/src/SwiftlyS2.Core/Modules/Events/EventSubscriber.cs b/managed/src/SwiftlyS2.Core/Modules/Events/EventSubscriber.cs index bc292452a..12d3e6013 100644 --- a/managed/src/SwiftlyS2.Core/Modules/Events/EventSubscriber.cs +++ b/managed/src/SwiftlyS2.Core/Modules/Events/EventSubscriber.cs @@ -13,591 +13,610 @@ namespace SwiftlyS2.Core.Events; internal class EventSubscriber : IEventSubscriber, IDisposable { - private CoreContext _Id { get; init; } - private IContextedProfilerService _Profiler { get; init; } - private ILogger _Logger { get; init; } - - public EventSubscriber( CoreContext id, IContextedProfilerService profiler, ILogger logger ) - { - _Id = id; - _Profiler = profiler; - _Logger = logger; - EventPublisher.Subscribe(this); - } - - public event EventDelegates.OnTick? OnTick; - public event EventDelegates.OnWorldUpdate? OnWorldUpdate; - - public event EventDelegates.OnClientConnected? OnClientConnected; - - public event EventDelegates.OnClientDisconnected? OnClientDisconnected; - public event EventDelegates.OnClientKeyStateChanged? OnClientKeyStateChanged; - public event EventDelegates.OnClientPutInServer? OnClientPutInServer; - public event EventDelegates.OnClientSteamAuthorize? OnClientSteamAuthorize; - public event EventDelegates.OnClientSteamAuthorizeFail? OnClientSteamAuthorizeFail; - public event EventDelegates.OnEntityCreated? OnEntityCreated; - public event EventDelegates.OnEntityDeleted? OnEntityDeleted; - public event EventDelegates.OnEntityParentChanged? OnEntityParentChanged; - public event EventDelegates.OnEntitySpawned? OnEntitySpawned; - public event EventDelegates.OnMapLoad? OnMapLoad; - public event EventDelegates.OnMapUnload? OnMapUnload; - public event EventDelegates.OnClientProcessUsercmds? OnClientProcessUsercmds; - public event EventDelegates.OnConVarValueChanged? OnConVarValueChanged; - public event EventDelegates.OnConCommandCreated? OnConCommandCreated; - public event EventDelegates.OnConVarCreated? OnConVarCreated; - public event EventDelegates.OnEntityTakeDamage? OnEntityTakeDamage; - public event EventDelegates.OnPrecacheResource? OnPrecacheResource; - public event EventDelegates.OnEntityTouchHook? OnEntityTouchHook; - public event EventDelegates.OnEntityStartTouch? OnEntityStartTouch; - public event EventDelegates.OnEntityTouch? OnEntityTouch; - public event EventDelegates.OnEntityEndTouch? OnEntityEndTouch; - public event EventDelegates.OnItemServicesCanAcquireHook? OnItemServicesCanAcquireHook; - public event EventDelegates.OnWeaponServicesCanUseHook? OnWeaponServicesCanUseHook; - public event EventDelegates.OnConsoleOutput? OnConsoleOutput; - public event EventDelegates.OnCommandExecuteHook? OnCommandExecuteHook; - public event EventDelegates.OnSteamAPIActivated? OnSteamAPIActivated; - public event EventDelegates.OnMovementServicesRunCommandHook? OnMovementServicesRunCommandHook; - - public void Dispose() - { - EventPublisher.Unsubscribe(this); - } - - public void InvokeOnTick() - { - try - { - _Profiler.StartRecording("Event::OnTick"); - OnTick?.Invoke(); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnTick."); - } - finally - { - _Profiler.StopRecording("Event::OnTick"); - } - } - - public void InvokeOnWorldUpdate() - { - try - { - _Profiler.StartRecording("Event::OnWorldUpdate"); - OnWorldUpdate?.Invoke(); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnWorldUpdate."); - } - finally - { - _Profiler.StopRecording("Event::OnWorldUpdate"); - } - } - - public void InvokeOnClientConnected( OnClientConnectedEvent @event ) - { - try - { - if (OnClientConnected == null) return; - _Profiler.StartRecording("Event::OnClientConnected"); - OnClientConnected?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnClientConnected."); - } - finally - { - _Profiler.StopRecording("Event::OnClientConnected"); - } - } - - public void InvokeOnClientDisconnected( OnClientDisconnectedEvent @event ) - { - try - { - if (OnClientDisconnected == null) return; - _Profiler.StartRecording("Event::OnClientDisconnected"); - OnClientDisconnected?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnClientDisconnected."); - } - finally - { - _Profiler.StopRecording("Event::OnClientDisconnected"); - } - } - - public void InvokeOnClientKeyStateChanged( OnClientKeyStateChangedEvent @event ) - { - try - { - if (OnClientKeyStateChanged == null) return; - _Profiler.StartRecording("Event::OnClientKeyStateChanged"); - OnClientKeyStateChanged?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnClientKeyStateChanged."); - } - finally - { - _Profiler.StopRecording("Event::OnClientKeyStateChanged"); - } - } - - public void InvokeOnClientPutInServer( OnClientPutInServerEvent @event ) - { - try - { - if (OnClientPutInServer == null) return; - _Profiler.StartRecording("Event::OnClientPutInServer"); - OnClientPutInServer?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnClientPutInServer."); - } - finally - { - _Profiler.StopRecording("Event::OnClientPutInServer"); - } - } - - public void InvokeOnClientSteamAuthorize( OnClientSteamAuthorizeEvent @event ) - { - try - { - if (OnClientSteamAuthorize == null) return; - _Profiler.StartRecording("Event::OnClientSteamAuthorize"); - OnClientSteamAuthorize?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnClientSteamAuthorize."); - } - finally - { - _Profiler.StopRecording("Event::OnClientSteamAuthorize"); - } - } - - public void InvokeOnClientSteamAuthorizeFail( OnClientSteamAuthorizeFailEvent @event ) - { - try - { - if (OnClientSteamAuthorizeFail == null) return; - _Profiler.StartRecording("Event::OnClientSteamAuthorizeFail"); - OnClientSteamAuthorizeFail?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnClientSteamAuthorizeFail."); - } - finally - { - _Profiler.StopRecording("Event::OnClientSteamAuthorizeFail"); - } - } - - public void InvokeOnEntityCreated( OnEntityCreatedEvent @event ) - { - try - { - if (OnEntityCreated == null) return; - _Profiler.StartRecording("Event::OnEntityCreated"); - OnEntityCreated?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnEntityCreated."); - } - finally - { - _Profiler.StopRecording("Event::OnEntityCreated"); - } - } - - public void InvokeOnEntityDeleted( OnEntityDeletedEvent @event ) - { - try - { - if (OnEntityDeleted == null) return; - _Profiler.StartRecording("Event::OnEntityDeleted"); - OnEntityDeleted?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnEntityDeleted."); - } - finally - { - _Profiler.StopRecording("Event::OnEntityDeleted"); - } - } - - public void InvokeOnEntityParentChanged( OnEntityParentChangedEvent @event ) - { - try - { - if (OnEntityParentChanged == null) return; - _Profiler.StartRecording("Event::OnEntityParentChanged"); - OnEntityParentChanged?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnEntityParentChanged."); - } - finally - { - _Profiler.StopRecording("Event::OnEntityParentChanged"); - } - } - - public void InvokeOnEntitySpawned( OnEntitySpawnedEvent @event ) - { - try - { - if (OnEntitySpawned == null) return; - _Profiler.StartRecording("Event::OnEntitySpawned"); - OnEntitySpawned?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnEntitySpawned."); - } - finally - { - _Profiler.StopRecording("Event::OnEntitySpawned"); - } - } - - public void InvokeOnMapLoad( OnMapLoadEvent @event ) - { - try - { - if (OnMapLoad == null) return; - _Profiler.StartRecording("Event::OnMapLoad"); - OnMapLoad?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnMapLoad."); - } - finally - { - _Profiler.StopRecording("Event::OnMapLoad"); - } - } - - public void InvokeOnMapUnload( OnMapUnloadEvent @event ) - { - try - { - if (OnMapUnload == null) return; - _Profiler.StartRecording("Event::OnMapUnload"); - OnMapUnload?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnMapUnload."); - } - finally - { - _Profiler.StopRecording("Event::OnMapUnload"); - } - } - - public void InvokeOnClientProcessUsercmds( OnClientProcessUsercmdsEvent @event ) - { - try - { - if (OnClientProcessUsercmds == null) return; - _Profiler.StartRecording("Event::OnClientProcessUsercmds"); - OnClientProcessUsercmds?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnClientProcessUsercmds."); - } - finally - { - _Profiler.StopRecording("Event::OnClientProcessUsercmds"); - } - } - - public void InvokeOnEntityTakeDamage( OnEntityTakeDamageEvent @event ) - { - try - { - if (OnEntityTakeDamage == null) return; - _Profiler.StartRecording("Event::OnEntityTakeDamage"); - OnEntityTakeDamage?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnEntityTakeDamage."); - } - finally - { - _Profiler.StopRecording("Event::OnEntityTakeDamage"); - } - } - - public void InvokeOnPrecacheResource( OnPrecacheResourceEvent @event ) - { - try - { - if (OnPrecacheResource == null) return; - _Profiler.StartRecording("Event::OnPrecacheResource"); - OnPrecacheResource?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnPrecacheResource."); - } - finally - { - _Profiler.StopRecording("Event::OnPrecacheResource"); - } - } - - [Obsolete("InvokeOnEntityTouchHook is deprecated. Use InvokeOnEntityStartTouch, InvokeOnEntityTouch, or InvokeOnEntityEndTouch instead.")] - public void InvokeOnEntityTouchHook( OnEntityTouchHookEvent @event ) - { - try - { - if (OnEntityTouchHook == null) return; - _Profiler.StartRecording("Event::OnEntityTouchHook"); - OnEntityTouchHook?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnEntityTouchHook."); - } - finally - { - _Profiler.StopRecording("Event::OnEntityTouchHook"); - } - } - - public void InvokeOnEntityStartTouch( OnEntityStartTouchEvent @event ) - { - try - { - if (OnEntityStartTouch == null) return; - _Profiler.StartRecording("Event::OnEntityStartTouch"); - OnEntityStartTouch?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnEntityStartTouch."); - } - finally - { - _Profiler.StopRecording("Event::OnEntityStartTouch"); - } - } - - public void InvokeOnEntityTouch( OnEntityTouchEvent @event ) - { - try - { - if (OnEntityTouch == null) return; - _Profiler.StartRecording("Event::OnEntityTouch"); - OnEntityTouch?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnEntityTouch."); - } - finally - { - _Profiler.StopRecording("Event::OnEntityTouch"); - } - } - - public void InvokeOnEntityEndTouch( OnEntityEndTouchEvent @event ) - { - try - { - if (OnEntityEndTouch == null) return; - _Profiler.StartRecording("Event::OnEntityEndTouch"); - OnEntityEndTouch?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnEntityEndTouch."); - } - finally - { - _Profiler.StopRecording("Event::OnEntityEndTouch"); - } - } - - public void InvokeOnSteamAPIActivatedHook() - { - try - { - _Profiler.StartRecording("Event::OnSteamAPIActivatedHook"); - OnSteamAPIActivated?.Invoke(); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnSteamAPIActivatedHook."); - } - finally - { - _Profiler.StopRecording("Event::OnSteamAPIActivatedHook"); - } - } - - public void InvokeOnItemServicesCanAcquireHook( OnItemServicesCanAcquireHookEvent @event ) - { - try - { - if (OnItemServicesCanAcquireHook == null) return; - _Profiler.StartRecording("Event::OnItemServicesCanAcquireHook"); - OnItemServicesCanAcquireHook?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnItemServicesCanAcquireHook."); - } - finally - { - _Profiler.StopRecording("Event::OnItemServicesCanAcquireHook"); - } - } - - public void InvokeOnWeaponServicesCanUseHook( OnWeaponServicesCanUseHookEvent @event ) - { - try - { - if (OnWeaponServicesCanUseHook == null) return; - _Profiler.StartRecording("Event::OnWeaponServicesCanUseHook"); - OnWeaponServicesCanUseHook?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnWeaponServicesCanUseHook."); - } - finally - { - _Profiler.StopRecording("Event::OnWeaponServicesCanUseHook"); - } - } - - public void InvokeOnConsoleOutput( OnConsoleOutputEvent @event ) - { - try - { - if (OnConsoleOutput == null) return; - _Profiler.StartRecording("Event::OnConsoleOutput"); - OnConsoleOutput?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnConsoleOutput."); - } - finally - { - _Profiler.StopRecording("Event::OnConsoleOutput"); - } - } - - public void InvokeOnConVarValueChanged( OnConVarValueChanged @event ) - { - try - { - if (OnConVarValueChanged == null) return; - _Profiler.StartRecording("Event::OnConVarValueChanged"); - OnConVarValueChanged?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnConVarValueChanged."); - } - finally - { - _Profiler.StopRecording("Event::OnConVarValueChanged"); - } - } - - public void InvokeOnConCommandCreated( OnConCommandCreated @event ) - { - try - { - if (OnConCommandCreated == null) return; - _Profiler.StartRecording("Event::OnConCommandCreated"); - OnConCommandCreated?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnConCommandCreated."); - } - finally - { - _Profiler.StopRecording("Event::OnConCommandCreated"); - } - } - - public void InvokeOnConVarCreated( OnConVarCreated @event ) - { - try - { - if (OnConVarCreated == null) return; - _Profiler.StartRecording("Event::OnConVarCreated"); - OnConVarCreated?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnConVarCreated."); - } - finally - { - _Profiler.StopRecording("Event::OnConVarCreated"); - } - } - - public void InvokeOnCommandExecuteHook( OnCommandExecuteHookEvent @event ) - { - try - { - if (OnCommandExecuteHook == null) return; - _Profiler.StartRecording("Event::OnCommandExecuteHook"); - OnCommandExecuteHook?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnCommandExecuteHook."); - } - finally - { - _Profiler.StopRecording("Event::OnCommandExecuteHook"); - } - } - - public void InvokeOnMovementServicesRunCommandHook( OnMovementServicesRunCommandHookEvent @event ) - { - try - { - if (OnMovementServicesRunCommandHook == null) return; - _Profiler.StartRecording("Event::OnMovementServicesRunCommandHook"); - OnMovementServicesRunCommandHook?.Invoke(@event); - } - catch (Exception e) - { - if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnMovementServicesRunCommandHook."); - } - finally - { - _Profiler.StopRecording("Event::OnMovementServicesRunCommandHook"); + private CoreContext _Id { get; init; } + private IContextedProfilerService _Profiler { get; init; } + private ILogger _Logger { get; init; } + + public EventSubscriber( CoreContext id, IContextedProfilerService profiler, ILogger logger ) + { + _Id = id; + _Profiler = profiler; + _Logger = logger; + EventPublisher.Subscribe(this); + } + + public event EventDelegates.OnTick? OnTick; + public event EventDelegates.OnWorldUpdate? OnWorldUpdate; + + public event EventDelegates.OnClientConnected? OnClientConnected; + + public event EventDelegates.OnClientDisconnected? OnClientDisconnected; + public event EventDelegates.OnClientKeyStateChanged? OnClientKeyStateChanged; + public event EventDelegates.OnClientPutInServer? OnClientPutInServer; + public event EventDelegates.OnClientSteamAuthorize? OnClientSteamAuthorize; + public event EventDelegates.OnClientSteamAuthorizeFail? OnClientSteamAuthorizeFail; + public event EventDelegates.OnEntityCreated? OnEntityCreated; + public event EventDelegates.OnEntityDeleted? OnEntityDeleted; + public event EventDelegates.OnEntityParentChanged? OnEntityParentChanged; + public event EventDelegates.OnEntitySpawned? OnEntitySpawned; + public event EventDelegates.OnMapLoad? OnMapLoad; + public event EventDelegates.OnMapUnload? OnMapUnload; + public event EventDelegates.OnClientProcessUsercmds? OnClientProcessUsercmds; + public event EventDelegates.OnConVarValueChanged? OnConVarValueChanged; + public event EventDelegates.OnConCommandCreated? OnConCommandCreated; + public event EventDelegates.OnConVarCreated? OnConVarCreated; + public event EventDelegates.OnEntityTakeDamage? OnEntityTakeDamage; + public event EventDelegates.OnPrecacheResource? OnPrecacheResource; + public event EventDelegates.OnEntityTouchHook? OnEntityTouchHook; + public event EventDelegates.OnEntityStartTouch? OnEntityStartTouch; + public event EventDelegates.OnEntityTouch? OnEntityTouch; + public event EventDelegates.OnEntityEndTouch? OnEntityEndTouch; + public event EventDelegates.OnItemServicesCanAcquireHook? OnItemServicesCanAcquireHook; + public event EventDelegates.OnWeaponServicesCanUseHook? OnWeaponServicesCanUseHook; + public event EventDelegates.OnConsoleOutput? OnConsoleOutput; + public event EventDelegates.OnCommandExecuteHook? OnCommandExecuteHook; + public event EventDelegates.OnSteamAPIActivated? OnSteamAPIActivated; + public event EventDelegates.OnMovementServicesRunCommandHook? OnMovementServicesRunCommandHook; + public event EventDelegates.OnPlayerPawnPostThink? OnPlayerPawnPostThink; + + public void Dispose() + { + EventPublisher.Unsubscribe(this); + } + + public void InvokeOnTick() + { + try + { + _Profiler.StartRecording("Event::OnTick"); + OnTick?.Invoke(); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnTick."); + } + finally + { + _Profiler.StopRecording("Event::OnTick"); + } + } + + public void InvokeOnWorldUpdate() + { + try + { + _Profiler.StartRecording("Event::OnWorldUpdate"); + OnWorldUpdate?.Invoke(); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnWorldUpdate."); + } + finally + { + _Profiler.StopRecording("Event::OnWorldUpdate"); + } + } + + public void InvokeOnClientConnected( OnClientConnectedEvent @event ) + { + try + { + if (OnClientConnected == null) return; + _Profiler.StartRecording("Event::OnClientConnected"); + OnClientConnected?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnClientConnected."); + } + finally + { + _Profiler.StopRecording("Event::OnClientConnected"); + } + } + + public void InvokeOnClientDisconnected( OnClientDisconnectedEvent @event ) + { + try + { + if (OnClientDisconnected == null) return; + _Profiler.StartRecording("Event::OnClientDisconnected"); + OnClientDisconnected?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnClientDisconnected."); + } + finally + { + _Profiler.StopRecording("Event::OnClientDisconnected"); + } + } + + public void InvokeOnClientKeyStateChanged( OnClientKeyStateChangedEvent @event ) + { + try + { + if (OnClientKeyStateChanged == null) return; + _Profiler.StartRecording("Event::OnClientKeyStateChanged"); + OnClientKeyStateChanged?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnClientKeyStateChanged."); + } + finally + { + _Profiler.StopRecording("Event::OnClientKeyStateChanged"); + } + } + + public void InvokeOnClientPutInServer( OnClientPutInServerEvent @event ) + { + try + { + if (OnClientPutInServer == null) return; + _Profiler.StartRecording("Event::OnClientPutInServer"); + OnClientPutInServer?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnClientPutInServer."); + } + finally + { + _Profiler.StopRecording("Event::OnClientPutInServer"); + } + } + + public void InvokeOnClientSteamAuthorize( OnClientSteamAuthorizeEvent @event ) + { + try + { + if (OnClientSteamAuthorize == null) return; + _Profiler.StartRecording("Event::OnClientSteamAuthorize"); + OnClientSteamAuthorize?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnClientSteamAuthorize."); + } + finally + { + _Profiler.StopRecording("Event::OnClientSteamAuthorize"); + } + } + + public void InvokeOnClientSteamAuthorizeFail( OnClientSteamAuthorizeFailEvent @event ) + { + try + { + if (OnClientSteamAuthorizeFail == null) return; + _Profiler.StartRecording("Event::OnClientSteamAuthorizeFail"); + OnClientSteamAuthorizeFail?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnClientSteamAuthorizeFail."); + } + finally + { + _Profiler.StopRecording("Event::OnClientSteamAuthorizeFail"); + } + } + + public void InvokeOnEntityCreated( OnEntityCreatedEvent @event ) + { + try + { + if (OnEntityCreated == null) return; + _Profiler.StartRecording("Event::OnEntityCreated"); + OnEntityCreated?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnEntityCreated."); + } + finally + { + _Profiler.StopRecording("Event::OnEntityCreated"); + } + } + + public void InvokeOnEntityDeleted( OnEntityDeletedEvent @event ) + { + try + { + if (OnEntityDeleted == null) return; + _Profiler.StartRecording("Event::OnEntityDeleted"); + OnEntityDeleted?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnEntityDeleted."); + } + finally + { + _Profiler.StopRecording("Event::OnEntityDeleted"); + } + } + + public void InvokeOnEntityParentChanged( OnEntityParentChangedEvent @event ) + { + try + { + if (OnEntityParentChanged == null) return; + _Profiler.StartRecording("Event::OnEntityParentChanged"); + OnEntityParentChanged?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnEntityParentChanged."); + } + finally + { + _Profiler.StopRecording("Event::OnEntityParentChanged"); + } + } + + public void InvokeOnEntitySpawned( OnEntitySpawnedEvent @event ) + { + try + { + if (OnEntitySpawned == null) return; + _Profiler.StartRecording("Event::OnEntitySpawned"); + OnEntitySpawned?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnEntitySpawned."); + } + finally + { + _Profiler.StopRecording("Event::OnEntitySpawned"); + } + } + + public void InvokeOnMapLoad( OnMapLoadEvent @event ) + { + try + { + if (OnMapLoad == null) return; + _Profiler.StartRecording("Event::OnMapLoad"); + OnMapLoad?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnMapLoad."); + } + finally + { + _Profiler.StopRecording("Event::OnMapLoad"); + } + } + + public void InvokeOnMapUnload( OnMapUnloadEvent @event ) + { + try + { + if (OnMapUnload == null) return; + _Profiler.StartRecording("Event::OnMapUnload"); + OnMapUnload?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnMapUnload."); + } + finally + { + _Profiler.StopRecording("Event::OnMapUnload"); + } + } + + public void InvokeOnClientProcessUsercmds( OnClientProcessUsercmdsEvent @event ) + { + try + { + if (OnClientProcessUsercmds == null) return; + _Profiler.StartRecording("Event::OnClientProcessUsercmds"); + OnClientProcessUsercmds?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnClientProcessUsercmds."); + } + finally + { + _Profiler.StopRecording("Event::OnClientProcessUsercmds"); + } + } + + public void InvokeOnEntityTakeDamage( OnEntityTakeDamageEvent @event ) + { + try + { + if (OnEntityTakeDamage == null) return; + _Profiler.StartRecording("Event::OnEntityTakeDamage"); + OnEntityTakeDamage?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnEntityTakeDamage."); + } + finally + { + _Profiler.StopRecording("Event::OnEntityTakeDamage"); + } + } + + public void InvokeOnPrecacheResource( OnPrecacheResourceEvent @event ) + { + try + { + if (OnPrecacheResource == null) return; + _Profiler.StartRecording("Event::OnPrecacheResource"); + OnPrecacheResource?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnPrecacheResource."); + } + finally + { + _Profiler.StopRecording("Event::OnPrecacheResource"); + } + } + + [Obsolete("InvokeOnEntityTouchHook is deprecated. Use InvokeOnEntityStartTouch, InvokeOnEntityTouch, or InvokeOnEntityEndTouch instead.")] + public void InvokeOnEntityTouchHook( OnEntityTouchHookEvent @event ) + { + try + { + if (OnEntityTouchHook == null) return; + _Profiler.StartRecording("Event::OnEntityTouchHook"); + OnEntityTouchHook?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnEntityTouchHook."); + } + finally + { + _Profiler.StopRecording("Event::OnEntityTouchHook"); + } + } + + public void InvokeOnEntityStartTouch( OnEntityStartTouchEvent @event ) + { + try + { + if (OnEntityStartTouch == null) return; + _Profiler.StartRecording("Event::OnEntityStartTouch"); + OnEntityStartTouch?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnEntityStartTouch."); + } + finally + { + _Profiler.StopRecording("Event::OnEntityStartTouch"); + } + } + + public void InvokeOnEntityTouch( OnEntityTouchEvent @event ) + { + try + { + if (OnEntityTouch == null) return; + _Profiler.StartRecording("Event::OnEntityTouch"); + OnEntityTouch?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnEntityTouch."); + } + finally + { + _Profiler.StopRecording("Event::OnEntityTouch"); + } + } + + public void InvokeOnEntityEndTouch( OnEntityEndTouchEvent @event ) + { + try + { + if (OnEntityEndTouch == null) return; + _Profiler.StartRecording("Event::OnEntityEndTouch"); + OnEntityEndTouch?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnEntityEndTouch."); + } + finally + { + _Profiler.StopRecording("Event::OnEntityEndTouch"); + } + } + + public void InvokeOnSteamAPIActivatedHook() + { + try + { + _Profiler.StartRecording("Event::OnSteamAPIActivatedHook"); + OnSteamAPIActivated?.Invoke(); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnSteamAPIActivatedHook."); + } + finally + { + _Profiler.StopRecording("Event::OnSteamAPIActivatedHook"); + } + } + + public void InvokeOnItemServicesCanAcquireHook( OnItemServicesCanAcquireHookEvent @event ) + { + try + { + if (OnItemServicesCanAcquireHook == null) return; + _Profiler.StartRecording("Event::OnItemServicesCanAcquireHook"); + OnItemServicesCanAcquireHook?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnItemServicesCanAcquireHook."); + } + finally + { + _Profiler.StopRecording("Event::OnItemServicesCanAcquireHook"); + } + } + + public void InvokeOnWeaponServicesCanUseHook( OnWeaponServicesCanUseHookEvent @event ) + { + try + { + if (OnWeaponServicesCanUseHook == null) return; + _Profiler.StartRecording("Event::OnWeaponServicesCanUseHook"); + OnWeaponServicesCanUseHook?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnWeaponServicesCanUseHook."); + } + finally + { + _Profiler.StopRecording("Event::OnWeaponServicesCanUseHook"); + } + } + + public void InvokeOnConsoleOutput( OnConsoleOutputEvent @event ) + { + try + { + if (OnConsoleOutput == null) return; + _Profiler.StartRecording("Event::OnConsoleOutput"); + OnConsoleOutput?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnConsoleOutput."); + } + finally + { + _Profiler.StopRecording("Event::OnConsoleOutput"); + } + } + + public void InvokeOnConVarValueChanged( OnConVarValueChanged @event ) + { + try + { + if (OnConVarValueChanged == null) return; + _Profiler.StartRecording("Event::OnConVarValueChanged"); + OnConVarValueChanged?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnConVarValueChanged."); + } + finally + { + _Profiler.StopRecording("Event::OnConVarValueChanged"); + } + } + + public void InvokeOnConCommandCreated( OnConCommandCreated @event ) + { + try + { + if (OnConCommandCreated == null) return; + _Profiler.StartRecording("Event::OnConCommandCreated"); + OnConCommandCreated?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnConCommandCreated."); + } + finally + { + _Profiler.StopRecording("Event::OnConCommandCreated"); + } + } + + public void InvokeOnConVarCreated( OnConVarCreated @event ) + { + try + { + if (OnConVarCreated == null) return; + _Profiler.StartRecording("Event::OnConVarCreated"); + OnConVarCreated?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnConVarCreated."); + } + finally + { + _Profiler.StopRecording("Event::OnConVarCreated"); + } + } + + public void InvokeOnCommandExecuteHook( OnCommandExecuteHookEvent @event ) + { + try + { + if (OnCommandExecuteHook == null) return; + _Profiler.StartRecording("Event::OnCommandExecuteHook"); + OnCommandExecuteHook?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnCommandExecuteHook."); + } + finally + { + _Profiler.StopRecording("Event::OnCommandExecuteHook"); + } + } + + public void InvokeOnMovementServicesRunCommandHook( OnMovementServicesRunCommandHookEvent @event ) + { + try + { + if (OnMovementServicesRunCommandHook == null) return; + _Profiler.StartRecording("Event::OnMovementServicesRunCommandHook"); + OnMovementServicesRunCommandHook?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnMovementServicesRunCommandHook."); + } + finally + { + _Profiler.StopRecording("Event::OnMovementServicesRunCommandHook"); + } + } + + public void InvokeOnPlayerPawnPostThinkHook( OnPlayerPawnPostThinkHookEvent @event ) + { + try + { + if (OnPlayerPawnPostThink == null) return; + _Profiler.StartRecording("Event::OnPlayerPawnPostThink"); + OnPlayerPawnPostThink?.Invoke(@event); + } + catch (Exception e) + { + if (GlobalExceptionHandler.Handle(e)) _Logger.LogError(e, "Error invoking OnPlayerPawnPostThink."); + } + finally + { + _Profiler.StopRecording("Event::OnPlayerPawnPostThink"); + } } - } } \ No newline at end of file diff --git a/managed/src/SwiftlyS2.Core/Services/CoreHookService.cs b/managed/src/SwiftlyS2.Core/Services/CoreHookService.cs index 8e68ed426..d7a65ce83 100644 --- a/managed/src/SwiftlyS2.Core/Services/CoreHookService.cs +++ b/managed/src/SwiftlyS2.Core/Services/CoreHookService.cs @@ -1,400 +1,411 @@ -using System.Runtime.CompilerServices; using System.Text; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Microsoft.Extensions.Logging; -using SwiftlyS2.Core.Events; -using SwiftlyS2.Core.Extensions; -using SwiftlyS2.Core.Natives; using SwiftlyS2.Shared; -using SwiftlyS2.Shared.Memory; using SwiftlyS2.Shared.Misc; -using SwiftlyS2.Shared.SchemaDefinitions; +using SwiftlyS2.Core.Events; +using SwiftlyS2.Shared.Memory; using SwiftlyS2.Shared.Natives; -using SwiftlyS2.Core.SchemaDefinitions; +using SwiftlyS2.Core.Extensions; using SwiftlyS2.Shared.SteamAPI; +using SwiftlyS2.Core.SchemaDefinitions; using SwiftlyS2.Core.ProtobufDefinitions; +using SwiftlyS2.Shared.SchemaDefinitions; namespace SwiftlyS2.Core.Services; internal class CoreHookService : IDisposable { - private ILogger _Logger { get; init; } - private ISwiftlyCore _Core { get; init; } - - public CoreHookService( ILogger logger, ISwiftlyCore core ) - { - _Logger = logger; - _Core = core; - - HookTouch(); - HookCanAcquire(); - HookCommandExecute(); - HookICVarFindConCommand(); - HookCCSPlayer_WeaponServices_CanUse(); - HookSteamServerAPIActivated(); - HookCPlayer_MovementServices_RunCommand(); - } - - private delegate int CanAcquireDelegate( nint pItemServices, nint pEconItemView, nint acquireMethod, nint unk1 ); - /* - Original function in engine2.dll: __int64 sub_1C0CD0(__int64 a1, int a2, unsigned int a3, ...) - This is a variadic function, but we only need the first two variable arguments (v55, v57) - - __int64 sub_1C0CD0(__int64 a1, int a2, unsigned int a3, ...) + private readonly ILogger logger; + private readonly ISwiftlyCore core; + + public CoreHookService( ILogger logger, ISwiftlyCore core ) { - ... - - va_list va; // [rsp+D28h] [rbp+D28h] - __int64 v55; // [rsp+E28h] [rbp+D28h] BYREF - va_list va1; // [rsp+E28h] [rbp+D28h] + this.logger = logger; + this.core = core; + + HookExecuteCommand(); + HookFindConCommandTemplate(); + HookCCSPlayerItemServicesCanAcquire(); + HookCCSPlayerWeaponServicesCanUse(); + HookCBaseEntityTouchTemplate(); + HookSteamServerAPIActivated(); + HookCPlayerMovementServicesRunCommand(); + HookCCSPlayerPawnPostThink(); + } - ... + /* + Original function in engine2.dll: __int64 sub_1C0CD0(__int64 a1, int a2, unsigned int a3, ...) + This is a variadic function, but we only need the first two variable arguments (v55, v57) - va_start(va1, a3); - va_start(va, a3); - v55 = va_arg(va1, _QWORD); - v57 = va_arg(va1, _QWORD); + __int64 sub_1C0CD0(__int64 a1, int a2, unsigned int a3, ...) + { + ... + + va_list va; // [rsp+D28h] [rbp+D28h] + __int64 v55; // [rsp+E28h] [rbp+D28h] BYREF + va_list va1; // [rsp+E28h] [rbp+D28h] + + ... + + va_start(va1, a3); + va_start(va, a3); + v55 = va_arg(va1, _QWORD); + v57 = va_arg(va1, _QWORD); + + ... + } + + So we model it as a fixed 5-parameter function for interop purposes + */ + private delegate nint ExecuteCommand( nint a1, int a2, uint a3, nint a4, nint a5 ); + private delegate nint FindConCommandWindows( nint pICvar, nint pRet, nint pConCommandName, int unk1 ); + private delegate nint FindConCommandLinux( nint pICvar, nint pConCommandName, int unk1 ); + private delegate int CCSPlayerItemServicesCanAcquire( nint pItemServices, nint pEconItemView, nint acquireMethod, nint unk1 ); + private delegate byte CCSPlayerWeaponServicesCanUse( nint pWeaponServices, nint pBasePlayerWeapon ); + private delegate nint CBaseEntityTouchTemplate( nint pBaseEntity, nint pOtherEntity ); + private delegate void SteamServerAPIActivated( nint pServer ); + private delegate nint CPlayerMovementServicesRunCommand( nint pMovementServices, nint pUserCmd ); + private delegate void CCSPlayerPawnPostThink( nint pPlayerPawn ); + + private IUnmanagedFunction? executeCommand; + private Guid executeCommandGuid; + private IUnmanagedFunction? findConCommandWindows; + private IUnmanagedFunction? findConCommandLinux; + private Guid findConCommandGuid; + private IUnmanagedFunction? itemServicesCanAcquire; + private Guid itemServicesCanAcquireGuid; + private IUnmanagedFunction? weaponServicesCanUse; + private Guid weaponServicesCanUseGuid; + private IUnmanagedFunction? entityStartTouch; + private Guid entityStartTouchGuid; + private IUnmanagedFunction? entityTouch; + private Guid entityTouchGuid; + private IUnmanagedFunction? entityEndTouch; + private Guid entityEndTouchGuid; + private IUnmanagedFunction? steamServerAPIActivated; + private Guid steamServerAPIActivatedGuid; + private IUnmanagedFunction? movementServiceRunCommand; + private Guid movementServiceRunCommandGuid; + private IUnmanagedFunction? playerPawnPostThink; + private Guid playerPawnPostThinkGuid; + + private void HookExecuteCommand() + { + var address = core.GameData.GetSignature("Cmd_ExecuteCommand"); - ... - } + logger.LogInformation("Hooking Cmd_ExecuteCommand at {Address}", address); - So we model it as a fixed 5-parameter function for interop purposes - */ - private delegate nint ExecuteCommandDelegate( nint a1, int a2, uint a3, nint a4, nint a5 ); - - private delegate byte CCSPlayer_WeaponServices_CanUse( nint pWeaponServices, nint pBasePlayerWeapon ); - private delegate nint CBaseEntity_Touch_Template( nint pBaseEntity, nint pOtherEntity ); - private delegate void SteamServerAPIActivated( nint pServer ); - private delegate nint CPlayer_MovementServices_RunCommandDelegate( nint pMovementServices, nint pUserCmd ); - - private IUnmanagedFunction? _ExecuteCommand; - private Guid _ExecuteCommandGuid; - private IUnmanagedFunction? _CanAcquire; - private Guid _CanAcquireGuid; - private IUnmanagedFunction? _CCSPlayer_WeaponServices_CanUse; - private Guid _CCSPlayer_WeaponServices_CanUseGuid; - private IUnmanagedFunction? _CBaseEntity_StartTouch; - private Guid _CBaseEntity_StartTouchGuid; - private IUnmanagedFunction? _CBaseEntity_Touch; - private Guid _CBaseEntity_TouchGuid; - private IUnmanagedFunction? _CBaseEntity_EndTouch; - private Guid _CBaseEntity_EndTouchGuid; - private IUnmanagedFunction? _SteamServerAPIActivated; - private Guid _SteamServerAPIActivatedGuid; - private IUnmanagedFunction? _CPlayer_MovementServices_RunCommand; - private Guid _CPlayer_MovementServices_RunCommandGuid; - - private void HookSteamServerAPIActivated() - { - var offset = _Core.GameData.GetOffset("IServerGameDLL::GameServerSteamAPIActivated"); - var pVtable = _Core.Memory.GetVTableAddress(Library.Server, "CSource2Server"); - - if (pVtable == null) - { - _Logger.LogError("Failed to get CSource2Server vtable."); - return; + executeCommand = core.Memory.GetUnmanagedFunctionByAddress(address); + executeCommandGuid = executeCommand.AddHook(( next ) => + { + return ( a1, a2, a3, a4, a5 ) => + { + unsafe + { + if (a5 != nint.Zero) + { + ref var command = ref Unsafe.AsRef((void*)a5); + var @eventPre = new OnCommandExecuteHookEvent(ref command, HookMode.Pre); + EventPublisher.InvokeOnCommandExecuteHook(@eventPre); + + var result = next()(a1, a2, a3, a4, a5); + + var @eventPost = new OnCommandExecuteHookEvent(ref command, HookMode.Post); + EventPublisher.InvokeOnCommandExecuteHook(@eventPost); + return result; + } + return next()(a1, a2, a3, a4, a5); + } + }; + }); } - _SteamServerAPIActivated = _Core.Memory.GetUnmanagedFunctionByVTable(pVtable!.Value, offset); - _Logger.LogInformation("Hooking IServerGameDLL::GameServerSteamAPIActivated at {Address}", _SteamServerAPIActivated.Address); - _SteamServerAPIActivatedGuid = _SteamServerAPIActivated.AddHook(next => + private void HookFindConCommandTemplate() { - return ( pServer ) => - { - if (!CSteamGameServerAPIContext.Init()) + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - _Logger.LogError("Failed to initialize Steamworks GameServer API context."); - return; - } + 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); - - EventPublisher.InvokeOnSteamAPIActivatedHook(); - next()(pServer); - }; - }); - } + 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, pConCommandName, unk1); + }; + }); + } + else + { + var offset = core.GameData.GetOffset("ICvar::FindConCommand"); + findConCommandWindows = core.Memory.GetUnmanagedFunctionByVTable(core.Memory.GetVTableAddress("tier0", "CCvar")!.Value, offset); - private void HookCCSPlayer_WeaponServices_CanUse() - { - var offset = _Core.GameData.GetOffset("CCSPlayer_WeaponServices::CanUse"); - var pVtable = _Core.Memory.GetVTableAddress(Library.Server, "CCSPlayer_WeaponServices"); + logger.LogInformation("Hooking ICvar::FindConCommand at {Address}", findConCommandWindows.Address); - if (pVtable == null) - { - _Logger.LogError("Failed to get CCSPlayer_WeaponServices vtable."); - return; + 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); + }; + }); + } } - _CCSPlayer_WeaponServices_CanUse = _Core.Memory.GetUnmanagedFunctionByVTable(pVtable!.Value, offset); - _Logger.LogInformation("Hooking CCSPlayer_WeaponServices::CanUse at {Address}", _CCSPlayer_WeaponServices_CanUse.Address); - _CCSPlayer_WeaponServices_CanUseGuid = _CCSPlayer_WeaponServices_CanUse.AddHook(next => + + private void HookCCSPlayerItemServicesCanAcquire() { - return ( pWeaponServices, pBasePlayerWeapon ) => - { + var address = core.GameData.GetSignature("CCSPlayer_ItemServices::CanAcquire"); - var result = next()(pWeaponServices, pBasePlayerWeapon); + logger.LogInformation("Hooking CCSPlayer_ItemServices::CanAcquire at {Address}", address); - var weaponServices = new CCSPlayer_WeaponServicesImpl(pWeaponServices); - var basePlayerWeapon = new CCSWeaponBaseImpl(pBasePlayerWeapon); + itemServicesCanAcquire = core.Memory.GetUnmanagedFunctionByAddress(address); + itemServicesCanAcquireGuid = itemServicesCanAcquire.AddHook(next => + { + return ( pItemServices, pEconItemView, acquireMethod, unk1 ) => + { + var result = next()(pItemServices, pEconItemView, acquireMethod, unk1); + + var itemServices = core.Memory.ToSchemaClass(pItemServices); + var econItemView = core.Memory.ToSchemaClass(pEconItemView); + + var @event = new OnItemServicesCanAcquireHookEvent { + ItemServices = itemServices, + EconItemView = econItemView, + WeaponVData = core.Helpers.GetWeaponCSDataFromKey(econItemView.ItemDefinitionIndex), + AcquireMethod = (AcquireMethod)acquireMethod, + OriginalResult = (AcquireResult)result + }; + + EventPublisher.InvokeOnCanAcquireHook(@event); + + if (@event.Intercepted) + { + // original result is modified here. + return (int)@event.OriginalResult; + } + + return result; + }; + }); + } - var @event = new OnWeaponServicesCanUseHookEvent { - WeaponServices = weaponServices, - Weapon = basePlayerWeapon, - OriginalResult = result != 0 - }; - EventPublisher.InvokeOnWeaponServicesCanUseHook(@event); + private void HookCCSPlayerWeaponServicesCanUse() + { + var offset = core.GameData.GetOffset("CCSPlayer_WeaponServices::CanUse"); + var pVtable = core.Memory.GetVTableAddress(Library.Server, "CCSPlayer_WeaponServices"); - if (@event.Intercepted) + if (pVtable == null) { - return @event.OriginalResult ? (byte)1 : (byte)0; + logger.LogError("Failed to get CCSPlayer_WeaponServices vtable."); + return; } + weaponServicesCanUse = core.Memory.GetUnmanagedFunctionByVTable(pVtable!.Value, offset); + logger.LogInformation("Hooking CCSPlayer_WeaponServices::CanUse at {Address}", weaponServicesCanUse.Address); + weaponServicesCanUseGuid = weaponServicesCanUse.AddHook(next => + { + return ( pWeaponServices, pBasePlayerWeapon ) => + { + var result = next()(pWeaponServices, pBasePlayerWeapon); - return result; - }; - }); - } + var weaponServices = new CCSPlayer_WeaponServicesImpl(pWeaponServices); + var basePlayerWeapon = new CCSWeaponBaseImpl(pBasePlayerWeapon); - private void HookTouch() - { - var touchOffset = _Core.GameData.GetOffset("CBaseEntity::Touch"); - var startTouchOffset = _Core.GameData.GetOffset("CBaseEntity::StartTouch"); - var endTouchOffset = _Core.GameData.GetOffset("CBaseEntity::EndTouch"); - var pVtable = _Core.Memory.GetVTableAddress(Library.Server, "CBaseEntity"); + var @event = new OnWeaponServicesCanUseHookEvent { + WeaponServices = weaponServices, + Weapon = basePlayerWeapon, + OriginalResult = result != 0 + }; + EventPublisher.InvokeOnWeaponServicesCanUseHook(@event); - if (pVtable == null) - { - _Logger.LogError("Failed to get CBaseEntity vtable."); - return; + return @event.Intercepted ? @event.OriginalResult ? (byte)1 : (byte)0 : result; + }; + }); } - _CBaseEntity_StartTouch = _Core.Memory.GetUnmanagedFunctionByVTable(pVtable!.Value, startTouchOffset); - _CBaseEntity_Touch = _Core.Memory.GetUnmanagedFunctionByVTable(pVtable!.Value, touchOffset); - _CBaseEntity_EndTouch = _Core.Memory.GetUnmanagedFunctionByVTable(pVtable!.Value, endTouchOffset); - _Logger.LogInformation("Hooking CBaseEntity::StartTouch at {Address}", _CBaseEntity_StartTouch.Address); - _Logger.LogInformation("Hooking CBaseEntity::Touch at {Address}", _CBaseEntity_Touch.Address); - _Logger.LogInformation("Hooking CBaseEntity::EndTouch at {Address}", _CBaseEntity_EndTouch.Address); - - _CBaseEntity_StartTouchGuid = _CBaseEntity_StartTouch.AddHook(next => - { - return ( pBaseEntity, pOtherEntity ) => - { - var entity = new CBaseEntityImpl(pBaseEntity); - var otherEntity = new CBaseEntityImpl(pOtherEntity); - EventPublisher.InvokeOnEntityStartTouch(new OnEntityStartTouchEvent { Entity = entity, OtherEntity = otherEntity }); - EventPublisher.InvokeOnEntityTouchHook(new OnEntityTouchHookEvent { Entity = entity, OtherEntity = otherEntity, TouchType = EntityTouchType.StartTouch }); - return next()(pBaseEntity, pOtherEntity); - }; - }); - - _CBaseEntity_TouchGuid = _CBaseEntity_Touch.AddHook(next => - { - return ( pBaseEntity, pOtherEntity ) => - { - var entity = new CBaseEntityImpl(pBaseEntity); - var otherEntity = new CBaseEntityImpl(pOtherEntity); - EventPublisher.InvokeOnEntityTouch(new OnEntityTouchEvent { Entity = entity, OtherEntity = otherEntity }); - EventPublisher.InvokeOnEntityTouchHook(new OnEntityTouchHookEvent { Entity = entity, OtherEntity = otherEntity, TouchType = EntityTouchType.Touch }); - return next()(pBaseEntity, pOtherEntity); - }; - }); - - _CBaseEntity_EndTouchGuid = _CBaseEntity_EndTouch.AddHook(next => - { - return ( pBaseEntity, pOtherEntity ) => - { - var entity = new CBaseEntityImpl(pBaseEntity); - var otherEntity = new CBaseEntityImpl(pOtherEntity); - EventPublisher.InvokeOnEntityEndTouch(new OnEntityEndTouchEvent { Entity = entity, OtherEntity = otherEntity }); - EventPublisher.InvokeOnEntityTouchHook(new OnEntityTouchHookEvent { Entity = entity, OtherEntity = otherEntity, TouchType = EntityTouchType.EndTouch }); - return next()(pBaseEntity, pOtherEntity); - }; - }); - } - 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 => - { - return ( pItemServices, pEconItemView, acquireMethod, unk1 ) => - { - var result = next()(pItemServices, pEconItemView, acquireMethod, unk1); - - var itemServices = _Core.Memory.ToSchemaClass(pItemServices); - var econItemView = _Core.Memory.ToSchemaClass(pEconItemView); - - var @event = new OnItemServicesCanAcquireHookEvent { - ItemServices = itemServices, - EconItemView = econItemView, - WeaponVData = _Core.Helpers.GetWeaponCSDataFromKey(econItemView.ItemDefinitionIndex), - AcquireMethod = (AcquireMethod)acquireMethod, - OriginalResult = (AcquireResult)result - }; - - EventPublisher.InvokeOnCanAcquireHook(@event); + private void HookCBaseEntityTouchTemplate() + { + var touchOffset = core.GameData.GetOffset("CBaseEntity::Touch"); + var startTouchOffset = core.GameData.GetOffset("CBaseEntity::StartTouch"); + var endTouchOffset = core.GameData.GetOffset("CBaseEntity::EndTouch"); + var pVtable = core.Memory.GetVTableAddress(Library.Server, "CBaseEntity"); - if (@event.Intercepted) + if (pVtable == null) { - // original result is modified here. - return (int)@event.OriginalResult; + logger.LogError("Failed to get CBaseEntity vtable."); + return; } + entityStartTouch = core.Memory.GetUnmanagedFunctionByVTable(pVtable!.Value, startTouchOffset); + entityTouch = core.Memory.GetUnmanagedFunctionByVTable(pVtable!.Value, touchOffset); + entityEndTouch = core.Memory.GetUnmanagedFunctionByVTable(pVtable!.Value, endTouchOffset); + logger.LogInformation("Hooking CBaseEntity::StartTouch at {Address}", entityStartTouch.Address); + logger.LogInformation("Hooking CBaseEntity::Touch at {Address}", entityTouch.Address); + logger.LogInformation("Hooking CBaseEntity::EndTouch at {Address}", entityEndTouch.Address); + + entityStartTouchGuid = entityStartTouch.AddHook(next => + { + return ( pBaseEntity, pOtherEntity ) => + { + var entity = new CBaseEntityImpl(pBaseEntity); + var otherEntity = new CBaseEntityImpl(pOtherEntity); + EventPublisher.InvokeOnEntityStartTouch(new OnEntityStartTouchEvent { Entity = entity, OtherEntity = otherEntity }); + EventPublisher.InvokeOnEntityTouchHook(new OnEntityTouchHookEvent { Entity = entity, OtherEntity = otherEntity, TouchType = EntityTouchType.StartTouch }); + return next()(pBaseEntity, pOtherEntity); + }; + }); + + entityTouchGuid = entityTouch.AddHook(next => + { + return ( pBaseEntity, pOtherEntity ) => + { + var entity = new CBaseEntityImpl(pBaseEntity); + var otherEntity = new CBaseEntityImpl(pOtherEntity); + EventPublisher.InvokeOnEntityTouch(new OnEntityTouchEvent { Entity = entity, OtherEntity = otherEntity }); + EventPublisher.InvokeOnEntityTouchHook(new OnEntityTouchHookEvent { Entity = entity, OtherEntity = otherEntity, TouchType = EntityTouchType.Touch }); + return next()(pBaseEntity, pOtherEntity); + }; + }); + + entityEndTouchGuid = entityEndTouch.AddHook(next => + { + return ( pBaseEntity, pOtherEntity ) => + { + var entity = new CBaseEntityImpl(pBaseEntity); + var otherEntity = new CBaseEntityImpl(pOtherEntity); + EventPublisher.InvokeOnEntityEndTouch(new OnEntityEndTouchEvent { Entity = entity, OtherEntity = otherEntity }); + EventPublisher.InvokeOnEntityTouchHook(new OnEntityTouchHookEvent { Entity = entity, OtherEntity = otherEntity, TouchType = EntityTouchType.EndTouch }); + return next()(pBaseEntity, pOtherEntity); + }; + }); + } - return result; - }; - }); - } - - private void HookCommandExecute() - { - - var address = _Core.GameData.GetSignature("Cmd_ExecuteCommand"); - - _Logger.LogInformation("Hooking Cmd_ExecuteCommand at {Address}", address); - - _ExecuteCommand = _Core.Memory.GetUnmanagedFunctionByAddress(address); - _ExecuteCommandGuid = _ExecuteCommand.AddHook(( next ) => + private void HookSteamServerAPIActivated() { - return ( a1, a2, a3, a4, a5 ) => - { - unsafe + var offset = core.GameData.GetOffset("IServerGameDLL::GameServerSteamAPIActivated"); + var pVtable = core.Memory.GetVTableAddress(Library.Server, "CSource2Server"); + + if (pVtable == null) { - if (a5 != nint.Zero) - { - ref var command = ref Unsafe.AsRef((void*)a5); - var @eventPre = new OnCommandExecuteHookEvent(ref command, HookMode.Pre); - EventPublisher.InvokeOnCommandExecuteHook(@eventPre); - - var result = next()(a1, a2, a3, a4, a5); - - var @eventPost = new OnCommandExecuteHookEvent(ref command, HookMode.Post); - EventPublisher.InvokeOnCommandExecuteHook(@eventPost); - return result; - } - return next()(a1, a2, a3, a4, a5); + logger.LogError("Failed to get CSource2Server vtable."); + return; } - }; - }); - } - private delegate nint FindConCommandDelegate( nint pICvar, nint pRet, nint pConCommandName, int unk1 ); - private delegate nint FindConCommandDelegateLinux( nint pICvar, nint pConCommandName, int unk1 ); - - private IUnmanagedFunction? _FindConCommandWindows; - private IUnmanagedFunction? _FindConCommandLinux; - private Guid _FindConCommandGuid; + steamServerAPIActivated = core.Memory.GetUnmanagedFunctionByVTable(pVtable!.Value, offset); + logger.LogInformation("Hooking IServerGameDLL::GameServerSteamAPIActivated at {Address}", steamServerAPIActivated.Address); + steamServerAPIActivatedGuid = steamServerAPIActivated.AddHook(next => + { + return ( pServer ) => + { + if (!CSteamGameServerAPIContext.Init()) + { + logger.LogError("Failed to initialize Steamworks GameServer API context."); + return; + } + + EventPublisher.InvokeOnSteamAPIActivatedHook(); + next()(pServer); + }; + }); + } - private void HookICVarFindConCommand() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + private void HookCPlayerMovementServicesRunCommand() { - 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 offset = core.GameData.GetOffset("CPlayer_MovementServices::RunCommand"); + var pVtable = core.Memory.GetVTableAddress(Library.Server, "CPlayer_MovementServices"); + if (pVtable == null) { - var commandName = Marshal.PtrToStringAnsi(pConCommandName)!; - if (commandName.StartsWith("^wb^")) - { - commandName = commandName.Substring(4); - var bytes = Encoding.UTF8.GetBytes(commandName); - unsafe + logger.LogError("Failed to get CPlayer_MovementServices vtable."); + return; + } + movementServiceRunCommand = core.Memory.GetUnmanagedFunctionByVTable(pVtable!.Value, offset); + logger.LogInformation("Hooking CPlayer_MovementServices::RunCommand at {Address}", movementServiceRunCommand.Address); + movementServiceRunCommandGuid = movementServiceRunCommand.AddHook(( next ) => + { + return ( pMovementServices, pUserCmd ) => { - 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, pConCommandName, unk1); - }; - }); + var movementService = new CCSPlayer_MovementServicesImpl(pMovementServices); + var userCmdPb = new CSGOUserCmdPBImpl(pUserCmd + 0x10, false); + var buttonState = new CInButtonStateImpl(pUserCmd + 0x58); + + var @event = new OnMovementServicesRunCommandHookEvent { + MovementServices = movementService, + ButtonState = buttonState, + UserCmdPB = userCmdPb + }; + EventPublisher.InvokeOnMovementServicesRunCommandHook(@event); + + var result = next()(pMovementServices, pUserCmd); + return result; + }; + }); } - else + + private void HookCCSPlayerPawnPostThink() { - var offset = _Core.GameData.GetOffset("ICvar::FindConCommand"); - _FindConCommandWindows = _Core.Memory.GetUnmanagedFunctionByVTable(_Core.Memory.GetVTableAddress("tier0", "CCvar")!.Value, offset); + var address = core.GameData.GetSignature("CCSPlayerPawn::PostThink"); - _Logger.LogInformation("Hooking ICvar::FindConCommand at {Address}", _FindConCommandWindows.Address); + logger.LogInformation("Hooking CCSPlayerPawn::PostThink at {Address}", address); - _FindConCommandGuid = _FindConCommandWindows.AddHook(( next ) => - { - return ( pICvar, pRet, pConCommandName, unk1 ) => + playerPawnPostThink = core.Memory.GetUnmanagedFunctionByAddress(address); + playerPawnPostThinkGuid = playerPawnPostThink.AddHook(( next ) => { - var commandName = Marshal.PtrToStringAnsi(pConCommandName)!; - if (commandName.StartsWith("^wb^")) - { - commandName = commandName.Substring(4); - var bytes = Encoding.UTF8.GetBytes(commandName); - unsafe + return ( pPlayerPawn ) => { - 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); - }; - }); - } - } + var playerPawn = new CCSPlayerPawnImpl(pPlayerPawn); + var @event = new OnPlayerPawnPostThinkHookEvent { + PlayerPawn = playerPawn + }; + EventPublisher.InvokeOnPlayerPawnPostThinkHook(@event); - private void HookCPlayer_MovementServices_RunCommand() - { - var offset = _Core.GameData.GetOffset("CPlayer_MovementServices::RunCommand"); - var pVtable = _Core.Memory.GetVTableAddress(Library.Server, "CPlayer_MovementServices"); - if (pVtable == null) - { - _Logger.LogError("Failed to get CPlayer_MovementServices vtable."); - return; + next()(pPlayerPawn); + }; + }); } - _CPlayer_MovementServices_RunCommand = _Core.Memory.GetUnmanagedFunctionByVTable(pVtable!.Value, offset); - _Logger.LogInformation("Hooking CPlayer_MovementServices::RunCommand at {Address}", _CPlayer_MovementServices_RunCommand.Address); - _CPlayer_MovementServices_RunCommandGuid = _CPlayer_MovementServices_RunCommand.AddHook(( next ) => - { - return ( pMovementServices, pUserCmd ) => - { - var movementService = new CCSPlayer_MovementServicesImpl(pMovementServices); - - var userCmdPb = new CSGOUserCmdPBImpl(pUserCmd + 0x10, false); - - var buttonState = new CInButtonStateImpl(pUserCmd + 0x58); - - var @event = new OnMovementServicesRunCommandHookEvent { - MovementServices = movementService, - ButtonState = buttonState, - UserCmdPB = userCmdPb - }; - EventPublisher.InvokeOnMovementServicesRunCommandHook(@event); - - var result = next()(pMovementServices, pUserCmd); - return result; - }; - }); - } - - public void Dispose() - { - _CanAcquire!.RemoveHook(_CanAcquireGuid); - _ExecuteCommand!.RemoveHook(_ExecuteCommandGuid); - _FindConCommandWindows?.RemoveHook(_FindConCommandGuid); - _FindConCommandLinux?.RemoveHook(_FindConCommandGuid); - _CCSPlayer_WeaponServices_CanUse!.RemoveHook(_CCSPlayer_WeaponServices_CanUseGuid); - _CBaseEntity_StartTouch!.RemoveHook(_CBaseEntity_StartTouchGuid); - _CBaseEntity_Touch!.RemoveHook(_CBaseEntity_TouchGuid); - _CBaseEntity_EndTouch!.RemoveHook(_CBaseEntity_EndTouchGuid); - _SteamServerAPIActivated?.RemoveHook(_SteamServerAPIActivatedGuid); - _CPlayer_MovementServices_RunCommand?.RemoveHook(_CPlayer_MovementServices_RunCommandGuid); - } + public void Dispose() + { + executeCommand?.RemoveHook(executeCommandGuid); + findConCommandWindows?.RemoveHook(findConCommandGuid); + findConCommandLinux?.RemoveHook(findConCommandGuid); + itemServicesCanAcquire?.RemoveHook(itemServicesCanAcquireGuid); + weaponServicesCanUse?.RemoveHook(weaponServicesCanUseGuid); + entityStartTouch?.RemoveHook(entityStartTouchGuid); + entityTouch?.RemoveHook(entityTouchGuid); + entityEndTouch?.RemoveHook(entityEndTouchGuid); + steamServerAPIActivated?.RemoveHook(steamServerAPIActivatedGuid); + movementServiceRunCommand?.RemoveHook(movementServiceRunCommandGuid); + playerPawnPostThink?.RemoveHook(playerPawnPostThinkGuid); + } } \ No newline at end of file diff --git a/managed/src/SwiftlyS2.Shared/Modules/Events/EventDelegates.cs b/managed/src/SwiftlyS2.Shared/Modules/Events/EventDelegates.cs index 5670d6ab7..090a447dc 100644 --- a/managed/src/SwiftlyS2.Shared/Modules/Events/EventDelegates.cs +++ b/managed/src/SwiftlyS2.Shared/Modules/Events/EventDelegates.cs @@ -6,154 +6,159 @@ namespace SwiftlyS2.Shared.Events; public class EventDelegates { - /// - /// Called when game has processed a tick. Won't be called if the server is in hibernation. - /// This callback is a hot path, be careful with it and don't do anything expensive. - /// - public delegate void OnTick(); - - /// - /// Called when the world is updated. This happens even in hibernation. - /// This callback is a hot path, be careful with it and don't do anything expensive. - /// - public delegate void OnWorldUpdate(); - - /// - /// Called when Steam API is activated. - /// - public delegate void OnSteamAPIActivated(); - - /// - /// Called when a client connects to the server. - /// - public delegate void OnClientConnected( IOnClientConnectedEvent @event ); - - /// - /// Called when a client disconnects from the server. - /// - public delegate void OnClientDisconnected( IOnClientDisconnectedEvent @event ); - - /// - /// Called when a client's key state changes. - /// - public delegate void OnClientKeyStateChanged( IOnClientKeyStateChangedEvent @event ); - - /// - /// Called when a client is fully put in server. - /// - public delegate void OnClientPutInServer( IOnClientPutInServerEvent @event ); - - /// - /// Called when a client is authorized by Steam. - /// - public delegate void OnClientSteamAuthorize( IOnClientSteamAuthorizeEvent @event ); - - /// - /// Called when a client's Steam authorization fails. - /// - public delegate void OnClientSteamAuthorizeFail( IOnClientSteamAuthorizeFailEvent @event ); - - /// - /// Called when an entity is created. - /// - public delegate void OnEntityCreated( IOnEntityCreatedEvent @event ); - - /// - /// Called when an entity is deleted. - /// - public delegate void OnEntityDeleted( IOnEntityDeletedEvent @event ); - - /// - /// Called when an entity's parent changes. - /// - public delegate void OnEntityParentChanged( IOnEntityParentChangedEvent @event ); - - /// - /// Called when an entity is spawned. - /// - public delegate void OnEntitySpawned( IOnEntitySpawnedEvent @event ); - - /// - /// Called when a map is loaded. - /// - public delegate void OnMapLoad( IOnMapLoadEvent @event ); - - /// - /// Called when a map is unloaded. - /// - public delegate void OnMapUnload( IOnMapUnloadEvent @event ); - - /// - /// Called when a client processes user commands. - /// This callback is a hot path, be careful with it and don't do anything expensive. - /// - public delegate void OnClientProcessUsercmds( IOnClientProcessUsercmdsEvent @event ); - - /// - /// Called when a ConVar value is changed. - /// - public delegate void OnConVarValueChanged( IOnConVarValueChanged @event ); - - /// - /// Called when a ConCommand is created. - /// - public delegate void OnConCommandCreated( IOnConCommandCreated @event ); - - /// - /// Called when a ConVar is created. - /// - public delegate void OnConVarCreated( IOnConVarCreated @event ); - - /// - /// Called when an entity takes damage. - /// - public delegate void OnEntityTakeDamage( IOnEntityTakeDamageEvent @event ); - - /// - /// Called when the game is precaching resources. - /// - public delegate void OnPrecacheResource( IOnPrecacheResourceEvent @event ); - - [Obsolete("OnEntityTouchHook is deprecated. Use OnEntityStartTouch, OnEntityTouch, or OnEntityEndTouch instead.")] - public delegate void OnEntityTouchHook( IOnEntityTouchHookEvent @event ); - - /// - /// Called when an entity starts touching another entity. - /// - public delegate void OnEntityStartTouch( IOnEntityStartTouchEvent @event ); - - /// - /// Called when an entity is touching another entity. - /// - public delegate void OnEntityTouch( IOnEntityTouchEvent @event ); - - /// - /// Called when an entity ends touching another entity. - /// - public delegate void OnEntityEndTouch( IOnEntityEndTouchEvent @event ); - - /// - /// Called when an item services can acquire hook is triggered. - /// - public delegate void OnItemServicesCanAcquireHook( IOnItemServicesCanAcquireHookEvent @event ); - - /// - /// Called when a weapon services can use hook is triggered. - /// - public delegate void OnWeaponServicesCanUseHook( IOnWeaponServicesCanUseHookEvent @event ); - - /// - /// Called when a console output is received. - /// - public delegate void OnConsoleOutput( IOnConsoleOutputEvent @event ); - - /// - /// Called when a command is executed. - /// - public delegate void OnCommandExecuteHook( IOnCommandExecuteHookEvent @event ); - - /// - /// Called when the movement services run command hook is triggered. - /// - public delegate void OnMovementServicesRunCommandHook( IOnMovementServicesRunCommandHookEvent @event ); + /// + /// Called when game has processed a tick. Won't be called if the server is in hibernation. + /// This callback is a hot path, be careful with it and don't do anything expensive. + /// + public delegate void OnTick(); + + /// + /// Called when the world is updated. This happens even in hibernation. + /// This callback is a hot path, be careful with it and don't do anything expensive. + /// + public delegate void OnWorldUpdate(); + + /// + /// Called when Steam API is activated. + /// + public delegate void OnSteamAPIActivated(); + + /// + /// Called when a client connects to the server. + /// + public delegate void OnClientConnected( IOnClientConnectedEvent @event ); + + /// + /// Called when a client disconnects from the server. + /// + public delegate void OnClientDisconnected( IOnClientDisconnectedEvent @event ); + + /// + /// Called when a client's key state changes. + /// + public delegate void OnClientKeyStateChanged( IOnClientKeyStateChangedEvent @event ); + + /// + /// Called when a client is fully put in server. + /// + public delegate void OnClientPutInServer( IOnClientPutInServerEvent @event ); + + /// + /// Called when a client is authorized by Steam. + /// + public delegate void OnClientSteamAuthorize( IOnClientSteamAuthorizeEvent @event ); + + /// + /// Called when a client's Steam authorization fails. + /// + public delegate void OnClientSteamAuthorizeFail( IOnClientSteamAuthorizeFailEvent @event ); + + /// + /// Called when an entity is created. + /// + public delegate void OnEntityCreated( IOnEntityCreatedEvent @event ); + + /// + /// Called when an entity is deleted. + /// + public delegate void OnEntityDeleted( IOnEntityDeletedEvent @event ); + + /// + /// Called when an entity's parent changes. + /// + public delegate void OnEntityParentChanged( IOnEntityParentChangedEvent @event ); + + /// + /// Called when an entity is spawned. + /// + public delegate void OnEntitySpawned( IOnEntitySpawnedEvent @event ); + + /// + /// Called when a map is loaded. + /// + public delegate void OnMapLoad( IOnMapLoadEvent @event ); + + /// + /// Called when a map is unloaded. + /// + public delegate void OnMapUnload( IOnMapUnloadEvent @event ); + + /// + /// Called when a client processes user commands. + /// This callback is a hot path, be careful with it and don't do anything expensive. + /// + public delegate void OnClientProcessUsercmds( IOnClientProcessUsercmdsEvent @event ); + + /// + /// Called when a ConVar value is changed. + /// + public delegate void OnConVarValueChanged( IOnConVarValueChanged @event ); + + /// + /// Called when a ConCommand is created. + /// + public delegate void OnConCommandCreated( IOnConCommandCreated @event ); + + /// + /// Called when a ConVar is created. + /// + public delegate void OnConVarCreated( IOnConVarCreated @event ); + + /// + /// Called when an entity takes damage. + /// + public delegate void OnEntityTakeDamage( IOnEntityTakeDamageEvent @event ); + + /// + /// Called when the game is precaching resources. + /// + public delegate void OnPrecacheResource( IOnPrecacheResourceEvent @event ); + + [Obsolete("OnEntityTouchHook is deprecated. Use OnEntityStartTouch, OnEntityTouch, or OnEntityEndTouch instead.")] + public delegate void OnEntityTouchHook( IOnEntityTouchHookEvent @event ); + + /// + /// Called when an entity starts touching another entity. + /// + public delegate void OnEntityStartTouch( IOnEntityStartTouchEvent @event ); + + /// + /// Called when an entity is touching another entity. + /// + public delegate void OnEntityTouch( IOnEntityTouchEvent @event ); + + /// + /// Called when an entity ends touching another entity. + /// + public delegate void OnEntityEndTouch( IOnEntityEndTouchEvent @event ); + + /// + /// Called when an item services can acquire hook is triggered. + /// + public delegate void OnItemServicesCanAcquireHook( IOnItemServicesCanAcquireHookEvent @event ); + + /// + /// Called when a weapon services can use hook is triggered. + /// + public delegate void OnWeaponServicesCanUseHook( IOnWeaponServicesCanUseHookEvent @event ); + + /// + /// Called when a console output is received. + /// + public delegate void OnConsoleOutput( IOnConsoleOutputEvent @event ); + + /// + /// Called when a command is executed. + /// + public delegate void OnCommandExecuteHook( IOnCommandExecuteHookEvent @event ); + + /// + /// Called when the movement services run command hook is triggered. + /// + public delegate void OnMovementServicesRunCommandHook( IOnMovementServicesRunCommandHookEvent @event ); + + /// + /// Called when the player pawn post think hook is triggered. + /// + public delegate void OnPlayerPawnPostThink( IOnPlayerPawnPostThinkHookEvent @event ); } \ No newline at end of file diff --git a/managed/src/SwiftlyS2.Shared/Modules/Events/EventParams/IOnPlayerPawnPostThinkHookEvent.cs b/managed/src/SwiftlyS2.Shared/Modules/Events/EventParams/IOnPlayerPawnPostThinkHookEvent.cs new file mode 100644 index 000000000..66e2279d6 --- /dev/null +++ b/managed/src/SwiftlyS2.Shared/Modules/Events/EventParams/IOnPlayerPawnPostThinkHookEvent.cs @@ -0,0 +1,14 @@ +using SwiftlyS2.Shared.SchemaDefinitions; + +namespace SwiftlyS2.Shared.Events; + +/// +/// Called when the player pawn post think hook is triggered. +/// +public interface IOnPlayerPawnPostThinkHookEvent +{ + /// + /// The player pawn. + /// + public CCSPlayerPawn PlayerPawn { get; } +} \ No newline at end of file diff --git a/managed/src/SwiftlyS2.Shared/Modules/Events/IEventSubscriber.cs b/managed/src/SwiftlyS2.Shared/Modules/Events/IEventSubscriber.cs index d260af3b6..86dd49d34 100644 --- a/managed/src/SwiftlyS2.Shared/Modules/Events/IEventSubscriber.cs +++ b/managed/src/SwiftlyS2.Shared/Modules/Events/IEventSubscriber.cs @@ -6,154 +6,159 @@ namespace SwiftlyS2.Shared.Events; public interface IEventSubscriber { - /// - /// Called when game has processed a tick. Won't be called if the server is in hibernation. - /// This callback is a hot path, be careful with it and don't do anything expensive. - /// - public event EventDelegates.OnTick? OnTick; - - /// - /// Called when the world is updated. This happens even in hibernation. - /// This callback is a hot path, be careful with it and don't do anything expensive. - /// - public event EventDelegates.OnWorldUpdate? OnWorldUpdate; - - /// - /// Called when Steam API is activated. - /// - public event EventDelegates.OnSteamAPIActivated? OnSteamAPIActivated; - - /// - /// Called when a client connects to the server. - /// - public event EventDelegates.OnClientConnected? OnClientConnected; - - /// - /// Called when a client disconnects from the server. - /// - public event EventDelegates.OnClientDisconnected? OnClientDisconnected; - - /// - /// Called when a client's key state changes. - /// - public event EventDelegates.OnClientKeyStateChanged? OnClientKeyStateChanged; - - /// - /// Called when a client is fully put in server. - /// - public event EventDelegates.OnClientPutInServer? OnClientPutInServer; - - /// - /// Called when a client is authorized by Steam. - /// - public event EventDelegates.OnClientSteamAuthorize? OnClientSteamAuthorize; - - /// - /// Called when a client's Steam authorization fails. - /// - public event EventDelegates.OnClientSteamAuthorizeFail? OnClientSteamAuthorizeFail; - - /// - /// Called when an entity is created. - /// - public event EventDelegates.OnEntityCreated? OnEntityCreated; - - /// - /// Called when an entity is deleted. - /// - public event EventDelegates.OnEntityDeleted? OnEntityDeleted; - - /// - /// Called when an entity's parent changes. - /// - public event EventDelegates.OnEntityParentChanged? OnEntityParentChanged; - - /// - /// Called when an entity is spawned. - /// - public event EventDelegates.OnEntitySpawned? OnEntitySpawned; - - /// - /// Called when a map is loaded. - /// - public event EventDelegates.OnMapLoad? OnMapLoad; - - /// - /// Called when a map is unloaded. - /// - public event EventDelegates.OnMapUnload? OnMapUnload; - - /// - /// Called when the game process user's input. - /// This callback is a hot path, be careful with it and don't do anything expensive. - /// - public event EventDelegates.OnClientProcessUsercmds? OnClientProcessUsercmds; - - /// - /// Called when a ConVar value is changed. - /// - public event EventDelegates.OnConVarValueChanged? OnConVarValueChanged; - - /// - /// Called when a ConCommand is created. - /// - public event EventDelegates.OnConCommandCreated? OnConCommandCreated; - - /// - /// Called when a ConVar is created. - /// - public event EventDelegates.OnConVarCreated? OnConVarCreated; - - /// - /// Called when an entity takes damage. - /// - public event EventDelegates.OnEntityTakeDamage? OnEntityTakeDamage; - - /// - /// Called when the game is precaching resources. - /// - public event EventDelegates.OnPrecacheResource? OnPrecacheResource; - - /// - /// Called when an item services can acquire hook is triggered. - /// - public event EventDelegates.OnItemServicesCanAcquireHook? OnItemServicesCanAcquireHook; - - /// - /// Called when a weapon services can use hook is triggered. - /// - public event EventDelegates.OnWeaponServicesCanUseHook? OnWeaponServicesCanUseHook; - - /// - /// Called when the game outputs a console message. - /// - public event EventDelegates.OnConsoleOutput? OnConsoleOutput; - - /// - /// Called when a command is executed. - /// - public event EventDelegates.OnCommandExecuteHook? OnCommandExecuteHook; - - [Obsolete("OnEntityTouchHook is deprecated. Use OnEntityStartTouch, OnEntityTouch, or OnEntityEndTouch instead.")] - public event EventDelegates.OnEntityTouchHook? OnEntityTouchHook; - - /// - /// Called when an entity starts touching another entity. - /// - public event EventDelegates.OnEntityStartTouch? OnEntityStartTouch; - - /// - /// Called when an entity is touching another entity. - /// - public event EventDelegates.OnEntityTouch? OnEntityTouch; - - /// - /// Called when an entity ends touching another entity. - /// - public event EventDelegates.OnEntityEndTouch? OnEntityEndTouch; - - /// - /// Called when the movement services run command hook is triggered. - /// - public event EventDelegates.OnMovementServicesRunCommandHook? OnMovementServicesRunCommandHook; + /// + /// Called when game has processed a tick. Won't be called if the server is in hibernation. + /// This callback is a hot path, be careful with it and don't do anything expensive. + /// + public event EventDelegates.OnTick? OnTick; + + /// + /// Called when the world is updated. This happens even in hibernation. + /// This callback is a hot path, be careful with it and don't do anything expensive. + /// + public event EventDelegates.OnWorldUpdate? OnWorldUpdate; + + /// + /// Called when Steam API is activated. + /// + public event EventDelegates.OnSteamAPIActivated? OnSteamAPIActivated; + + /// + /// Called when a client connects to the server. + /// + public event EventDelegates.OnClientConnected? OnClientConnected; + + /// + /// Called when a client disconnects from the server. + /// + public event EventDelegates.OnClientDisconnected? OnClientDisconnected; + + /// + /// Called when a client's key state changes. + /// + public event EventDelegates.OnClientKeyStateChanged? OnClientKeyStateChanged; + + /// + /// Called when a client is fully put in server. + /// + public event EventDelegates.OnClientPutInServer? OnClientPutInServer; + + /// + /// Called when a client is authorized by Steam. + /// + public event EventDelegates.OnClientSteamAuthorize? OnClientSteamAuthorize; + + /// + /// Called when a client's Steam authorization fails. + /// + public event EventDelegates.OnClientSteamAuthorizeFail? OnClientSteamAuthorizeFail; + + /// + /// Called when an entity is created. + /// + public event EventDelegates.OnEntityCreated? OnEntityCreated; + + /// + /// Called when an entity is deleted. + /// + public event EventDelegates.OnEntityDeleted? OnEntityDeleted; + + /// + /// Called when an entity's parent changes. + /// + public event EventDelegates.OnEntityParentChanged? OnEntityParentChanged; + + /// + /// Called when an entity is spawned. + /// + public event EventDelegates.OnEntitySpawned? OnEntitySpawned; + + /// + /// Called when a map is loaded. + /// + public event EventDelegates.OnMapLoad? OnMapLoad; + + /// + /// Called when a map is unloaded. + /// + public event EventDelegates.OnMapUnload? OnMapUnload; + + /// + /// Called when the game process user's input. + /// This callback is a hot path, be careful with it and don't do anything expensive. + /// + public event EventDelegates.OnClientProcessUsercmds? OnClientProcessUsercmds; + + /// + /// Called when a ConVar value is changed. + /// + public event EventDelegates.OnConVarValueChanged? OnConVarValueChanged; + + /// + /// Called when a ConCommand is created. + /// + public event EventDelegates.OnConCommandCreated? OnConCommandCreated; + + /// + /// Called when a ConVar is created. + /// + public event EventDelegates.OnConVarCreated? OnConVarCreated; + + /// + /// Called when an entity takes damage. + /// + public event EventDelegates.OnEntityTakeDamage? OnEntityTakeDamage; + + /// + /// Called when the game is precaching resources. + /// + public event EventDelegates.OnPrecacheResource? OnPrecacheResource; + + /// + /// Called when an item services can acquire hook is triggered. + /// + public event EventDelegates.OnItemServicesCanAcquireHook? OnItemServicesCanAcquireHook; + + /// + /// Called when a weapon services can use hook is triggered. + /// + public event EventDelegates.OnWeaponServicesCanUseHook? OnWeaponServicesCanUseHook; + + /// + /// Called when the game outputs a console message. + /// + public event EventDelegates.OnConsoleOutput? OnConsoleOutput; + + /// + /// Called when a command is executed. + /// + public event EventDelegates.OnCommandExecuteHook? OnCommandExecuteHook; + + [Obsolete("OnEntityTouchHook is deprecated. Use OnEntityStartTouch, OnEntityTouch, or OnEntityEndTouch instead.")] + public event EventDelegates.OnEntityTouchHook? OnEntityTouchHook; + + /// + /// Called when an entity starts touching another entity. + /// + public event EventDelegates.OnEntityStartTouch? OnEntityStartTouch; + + /// + /// Called when an entity is touching another entity. + /// + public event EventDelegates.OnEntityTouch? OnEntityTouch; + + /// + /// Called when an entity ends touching another entity. + /// + public event EventDelegates.OnEntityEndTouch? OnEntityEndTouch; + + /// + /// Called when the movement services run command hook is triggered. + /// + public event EventDelegates.OnMovementServicesRunCommandHook? OnMovementServicesRunCommandHook; + + /// + /// Called when the player pawn post think hook is triggered. + /// + public event EventDelegates.OnPlayerPawnPostThink? OnPlayerPawnPostThink; } \ No newline at end of file diff --git a/managed/src/TestPlugin/TestPlugin.cs b/managed/src/TestPlugin/TestPlugin.cs index 2e55aaaf0..9342ee300 100644 --- a/managed/src/TestPlugin/TestPlugin.cs +++ b/managed/src/TestPlugin/TestPlugin.cs @@ -140,6 +140,11 @@ public override void Load( bool hotReload ) // } // }; + // Core.Event.OnPlayerPawnPostThink += ( @event ) => + // { + // Console.WriteLine($"PostThink -> {@event.PlayerPawn.OriginalController.Value?.PlayerName}"); + // }; + Core.Engine.ExecuteCommandWithBuffer("@ping", ( buffer ) => { Console.WriteLine($"pong: {buffer}"); diff --git a/plugin_files/gamedata/cs2/core/signatures.jsonc b/plugin_files/gamedata/cs2/core/signatures.jsonc index ff103e4fe..37625f9c6 100644 --- a/plugin_files/gamedata/cs2/core/signatures.jsonc +++ b/plugin_files/gamedata/cs2/core/signatures.jsonc @@ -77,7 +77,7 @@ "linux": "55 48 8D 87 ? ? ? ? 48 89 E5 41 57 41 56 41 89 CE 41 55 45 89 CD" }, // The function contains "enter_buyzone", "exit_buyzone", "userid", "canbuy", "exit_rescue_zone" - "CCSPlayerPawnBase::PostThink": { + "CCSPlayerPawn::PostThink": { "lib": "server", "windows": "48 ? ? 55 53 56 57 41 ? 48 ? ? ? 48 ? ? ? ? ? ? 4C 89 68", "linux": "55 48 89 E5 41 56 41 55 41 54 53 48 89 FB 48 83 EC 40 E8 ? ? ? ? F3 0F 10 83"