mirror of
https://github.com/roflmuffin/CounterStrikeSharp.git
synced 2025-12-05 15:40:24 -08:00
Game Event Broadcast Manipulation & Cancellation (#10)
This commit is contained in:
@@ -1,19 +1,21 @@
|
||||
---
|
||||
Language: Cpp
|
||||
BasedOnStyle: Google
|
||||
AccessModifierOffset: -4
|
||||
Standard: c++11
|
||||
BasedOnStyle: LLVM
|
||||
IndentWidth: 4
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
||||
ColumnLimit: 100
|
||||
DerivePointerAlignment: false
|
||||
PointerAlignment: Left
|
||||
AlignAfterOpenBracket: Align
|
||||
BinPackParameters: false
|
||||
AlignEscapedNewlines: Left
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
PackConstructorInitializers: Never
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
IndentPPDirectives: BeforeHash
|
||||
SortIncludes: Never
|
||||
...
|
||||
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
SortIncludes: false
|
||||
SpaceBeforeParens: ControlStatements
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
IndentCaseLabels: false
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterClass: true
|
||||
AfterStruct: true
|
||||
AfterEnum: true
|
||||
AfterUnion: true
|
||||
AfterNamespace: false
|
||||
AfterFunction: true
|
||||
IndentBraces: false
|
||||
|
||||
45
.clang-tidy
45
.clang-tidy
@@ -83,6 +83,7 @@ Checks: >
|
||||
-readability-redundant-declaration,
|
||||
-readability-function-cognitive-complexity,
|
||||
-readability-convert-member-functions-to-static,
|
||||
-readability-implicit-bool-conversion,
|
||||
-bugprone-narrowing-conversions,
|
||||
-bugprone-easily-swappable-parameters,
|
||||
-bugprone-implicit-widening-of-multiplication-result,
|
||||
@@ -92,25 +93,25 @@ Checks: >
|
||||
WarningsAsErrors: "*"
|
||||
|
||||
CheckOptions:
|
||||
- key: google-readability-braces-around-statements.ShortStatementLines
|
||||
value: '1'
|
||||
- key: google-readability-function-size.StatementThreshold
|
||||
value: '800'
|
||||
- key: google-readability-namespace-comments.ShortNamespaceLines
|
||||
value: '10'
|
||||
- key: google-readability-namespace-comments.SpacesBeforeComments
|
||||
value: '2'
|
||||
- key: readability-identifier-naming.ClassCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.MemberCase
|
||||
value: camelBack
|
||||
- key: readability-identifier-naming.EnumCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.FunctionCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.ParameterCase
|
||||
value: camelBack
|
||||
- key: readability-identifier-naming.UnionCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.VariableCase
|
||||
value: camelBack
|
||||
google-readability-braces-around-statements.ShortStatementLines: '1'
|
||||
google-readability-function-size.StatementThreshold: '800'
|
||||
google-readability-namespace-comments.ShortNamespaceLines: '10'
|
||||
google-readability-namespace-comments.SpacesBeforeComments: '2'
|
||||
readability-identifier-naming.PrivateMemberPrefix: 'm_'
|
||||
readability-identifier-naming.ProtectedMemberPrefix: 'm_'
|
||||
readability-identifier-naming.MemberPrefix: 'm_'
|
||||
readability-identifier-naming.ClassCase: CamelCase
|
||||
readability-identifier-naming.MemberCase: CamelCase
|
||||
readability-identifier-naming.EnumCase: CamelCase
|
||||
readability-identifier-naming.FunctionCase: CamelCase
|
||||
readability-identifier-naming.ParameterCase: CamelCase
|
||||
readability-identifier-naming.UnionCase: CamelCase
|
||||
readability-identifier-naming.VariableCase: CamelCase
|
||||
readability-identifier-naming.LocalConstantPointerPrefix: 'p'
|
||||
readability-identifier-naming.VariableHungarianPrefix: On
|
||||
readability-identifier-naming.ParameterHungarianPrefix: On
|
||||
readability-identifier-naming.MemberHungarianPrefix: On
|
||||
readability-identifier-naming.PointerParameterHungarianPrefix: On
|
||||
readability-identifier-naming.PointerParameterCase: CamelCase
|
||||
readability-identifier-naming.HungarianNotation.UserDefinedType.std::string: s
|
||||
|
||||
|
||||
@@ -15,10 +15,12 @@ The first parameter type must be a subclass of the `GameEvent` class. The names
|
||||
|
||||
```csharp
|
||||
[GameEventHandler]
|
||||
public void OnPlayerConnect(EventPlayerConnect @event)
|
||||
public HookResult OnPlayerConnect(EventPlayerConnect @event, GameEventInfo info)
|
||||
{
|
||||
// Userid will give you a reference to a CCSPlayerController class
|
||||
Log($"Player {@event.Userid.PlayerName} has connected!");
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -29,9 +31,11 @@ It is also possible to bind event listeners in the `OnLoad` (or anywhere you hav
|
||||
```csharp
|
||||
public override void Load(bool hotReload)
|
||||
{
|
||||
RegisterEventHandler<EventRoundStart>(@event =>
|
||||
RegisterEventHandler<EventRoundStart>((@event, info) =>
|
||||
{
|
||||
Console.WriteLine($"Round has started with time limit of {@event.Timelimit}");
|
||||
|
||||
return HookResult.Continue;
|
||||
});
|
||||
}
|
||||
```
|
||||
@@ -41,3 +45,11 @@ public override void Load(bool hotReload)
|
||||
The specific subclass of `GameEvent` will provide strongly typed parameters from the event definition. e.g. `event.Timelimit` will be a `long` value, `event.UserId` will be a `CCSPlayerController` and so-on.
|
||||
|
||||
These event properties are mutable so you can update them as normal and they will update in the event instance.
|
||||
|
||||
## Preventing Broadcast
|
||||
|
||||
You can modify a game event so that it does not get broadcast to clients by modifying the `bool info.DontBroadcast` property. e.g.
|
||||
|
||||
## Cancelling an Event
|
||||
|
||||
In a pre-event hook, you can prevent the event from continuing to other plugins by returning `HookResult.Handled` or `HookResult.Stop`.
|
||||
|
||||
@@ -31,10 +31,12 @@ public class HelloWorldPlugin : BasePlugin
|
||||
}
|
||||
|
||||
[GameEventHandler]
|
||||
public void OnPlayerConnect(EventPlayerConnect @event)
|
||||
public HookResult OnPlayerConnect(EventPlayerConnect @event, GameEventInfo info)
|
||||
{
|
||||
// Userid will give you a reference to a CCSPlayerController class
|
||||
Log($"Player {@event.Userid.PlayerName} has connected!");
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
[ConsoleCommand("issue_warning", "Issue warning to player")]
|
||||
|
||||
@@ -30,5 +30,5 @@ SET(
|
||||
dynload_s
|
||||
dyncall_s
|
||||
distorm
|
||||
funchook-shared
|
||||
funchook-static
|
||||
)
|
||||
@@ -103,26 +103,19 @@ namespace CounterStrikeSharp.API.Core
|
||||
|
||||
public readonly List<Timer> Timers = new List<Timer>();
|
||||
|
||||
private void RegisterEventHandlerInternal<T>(string name, Action<T> handler, bool post = false)
|
||||
where T : GameEvent, new()
|
||||
{
|
||||
var wrappedHandler = new Action<IntPtr>(pointer =>
|
||||
{
|
||||
var @event = new T
|
||||
{
|
||||
Handle = pointer
|
||||
};
|
||||
handler.Invoke(@event);
|
||||
});
|
||||
public delegate HookResult GameEventHandler<T>(T @event, GameEventInfo info) where T : GameEvent;
|
||||
|
||||
var subscriber = new CallbackSubscriber(handler, wrappedHandler,
|
||||
private void RegisterEventHandlerInternal<T>(string name, GameEventHandler<T> handler, bool post = false)
|
||||
where T : GameEvent
|
||||
{
|
||||
var subscriber = new CallbackSubscriber(handler, handler,
|
||||
() => DeregisterEventHandler(name, handler, post));
|
||||
|
||||
NativeAPI.HookEvent(name, subscriber.GetInputArgument(), post);
|
||||
Handlers[handler] = subscriber;
|
||||
}
|
||||
|
||||
public void RegisterEventHandler<T>(Action<T> handler, bool post = false) where T : GameEvent, new()
|
||||
public void RegisterEventHandler<T>(GameEventHandler<T> handler, bool post = false) where T : GameEvent
|
||||
{
|
||||
var name = typeof(T).GetCustomAttribute<EventNameAttribute>()?.Name;
|
||||
RegisterEventHandlerInternal(name, handler, post);
|
||||
@@ -276,8 +269,8 @@ namespace CounterStrikeSharp.API.Core
|
||||
var parameterType = eventHandler.GetParameters().First().ParameterType;
|
||||
var eventName = parameterType.GetCustomAttribute<EventNameAttribute>()?.Name;
|
||||
|
||||
var actionType = typeof(Action<>).MakeGenericType(parameterType);
|
||||
var action = eventHandler.CreateDelegate(actionType, instance);
|
||||
var actionType = typeof(GameEventHandler<>).MakeGenericType(parameterType);
|
||||
var action = Delegate.CreateDelegate(actionType, instance, eventHandler);
|
||||
|
||||
var generic = method.MakeGenericMethod(parameterType);
|
||||
generic.Invoke(this, new object[] { eventName, action, false });
|
||||
|
||||
13
managed/CounterStrikeSharp.API/Core/GameEventInfo.cs
Normal file
13
managed/CounterStrikeSharp.API/Core/GameEventInfo.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public class GameEventInfo : NativeObject
|
||||
{
|
||||
public GameEventInfo(IntPtr pointer) : base(pointer)
|
||||
{
|
||||
}
|
||||
|
||||
public unsafe ref bool DontBroadcast => ref Unsafe.AsRef<bool>((void*)Handle);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
9
managed/CounterStrikeSharp.API/Core/HookResult.cs
Normal file
9
managed/CounterStrikeSharp.API/Core/HookResult.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public enum HookResult
|
||||
{
|
||||
Continue = 0,
|
||||
Changed = 1,
|
||||
Handled = 3,
|
||||
Stop = 4,
|
||||
}
|
||||
@@ -30,11 +30,9 @@ namespace CounterStrikeSharp.API.Modules.Events
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
public class GameEvent
|
||||
public class GameEvent : NativeObject
|
||||
{
|
||||
public IntPtr Handle { get; internal set; }
|
||||
|
||||
protected GameEvent()
|
||||
public GameEvent(IntPtr pointer) : base(pointer)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -42,11 +40,6 @@ namespace CounterStrikeSharp.API.Modules.Events
|
||||
{
|
||||
}
|
||||
|
||||
internal GameEvent(IntPtr pointer)
|
||||
{
|
||||
Handle = pointer;
|
||||
}
|
||||
|
||||
public string EventName => NativeAPI.GetEventName(Handle);
|
||||
|
||||
public T Get<T>(string name)
|
||||
|
||||
@@ -54,19 +54,34 @@ namespace TestPlugin
|
||||
VirtualFunction.CreateVoid<IntPtr, int>(GameData.GetSignature("CCSPlayerController_SwitchTeam"));
|
||||
// Register Game Event Handlers
|
||||
RegisterEventHandler<EventPlayerConnect>(GenericEventHandler);
|
||||
RegisterEventHandler<EventPlayerJump>(@event =>
|
||||
RegisterEventHandler<EventPlayerDeath>((@event, info) =>
|
||||
{
|
||||
// You can use `info.DontBroadcast` to set the dont broadcast flag on the event.
|
||||
if (new Random().NextSingle() > 0.5f)
|
||||
{
|
||||
@event.Attacker.PrintToChat($"Skipping player_death broadcast at {Server.CurrentTime}");
|
||||
info.DontBroadcast = true;
|
||||
}
|
||||
|
||||
return HookResult.Continue;
|
||||
});
|
||||
RegisterEventHandler<EventPlayerJump>((@event, info) =>
|
||||
{
|
||||
sigVirtualFunc(@event.Userid.Handle, 2, "Test", IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
|
||||
|
||||
return HookResult.Continue;
|
||||
});
|
||||
RegisterEventHandler<EventPlayerSpawn>(@event =>
|
||||
RegisterEventHandler<EventPlayerSpawn>((@event, info) =>
|
||||
{
|
||||
if (!@event.Userid.IsValid) return;
|
||||
if (!@event.Userid.PlayerPawn.IsValid) return;
|
||||
if (!@event.Userid.IsValid) return 0;
|
||||
if (!@event.Userid.PlayerPawn.IsValid) return 0;
|
||||
|
||||
Log($"Player spawned with entity index: {@event.Userid.EntityIndex} & User ID: {@event.Userid.UserId}");
|
||||
|
||||
return HookResult.Continue;
|
||||
});
|
||||
RegisterEventHandler<EventPlayerBlind>(GenericEventHandler);
|
||||
RegisterEventHandler<EventBulletImpact>(@event =>
|
||||
RegisterEventHandler<EventBulletImpact>((@event, info) =>
|
||||
{
|
||||
var player = @event.Userid;
|
||||
var pawn = player.PlayerPawn.Value;
|
||||
@@ -83,21 +98,26 @@ namespace TestPlugin
|
||||
|
||||
VirtualFunctions.GiveNamedItem(pawn.ItemServices.Handle, "weapon_ak47", 0, 0, 0, 0);
|
||||
|
||||
Log($"Pawn Position: {pawn.CBodyComponent?.SceneNode?.AbsOrigin} @{pawn.CBodyComponent?.SceneNode.Rotation}");
|
||||
Log(
|
||||
$"Pawn Position: {pawn.CBodyComponent?.SceneNode?.AbsOrigin} @{pawn.CBodyComponent?.SceneNode.Rotation}");
|
||||
|
||||
char randomColourChar = (char)new Random().Next(0, 16);
|
||||
printAllFunc(3, $"Random String with Random Colour: {randomColourChar}{new Random().Next()}", IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
|
||||
printAllFunc(3, $"Random String with Random Colour: {randomColourChar}{new Random().Next()}",
|
||||
IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
|
||||
|
||||
pawn.Health += 5;
|
||||
|
||||
Log(
|
||||
$"Found steamID {new SteamID(player.SteamID)} for player {player.PlayerName}:{pawn.Health}|{pawn.InBuyZone}");
|
||||
Log($"{@event.Userid}, {@event.X},{@event.Y},{@event.Z}");
|
||||
|
||||
return HookResult.Continue;
|
||||
});
|
||||
RegisterEventHandler<EventRoundStart>(@event =>
|
||||
RegisterEventHandler<EventRoundStart>((@event, info) =>
|
||||
{
|
||||
// Grab all cs_player_controller entities and set their cash value to $1337.
|
||||
var playerEntities = Utilities.FindAllEntitiesByDesignerName<CCSPlayerController>("cs_player_controller");
|
||||
var playerEntities =
|
||||
Utilities.FindAllEntitiesByDesignerName<CCSPlayerController>("cs_player_controller");
|
||||
Log($"cs_player_controller count: {playerEntities.Count<CCSPlayerController>()}");
|
||||
|
||||
foreach (var player in playerEntities)
|
||||
@@ -116,6 +136,8 @@ namespace TestPlugin
|
||||
var gamerulesEnt = new CCSGameRules(entity.Handle);
|
||||
gamerulesEnt.CTTimeOutActive = true;
|
||||
}
|
||||
|
||||
return HookResult.Continue;
|
||||
});
|
||||
|
||||
// Hook global listeners defined by CounterStrikeSharp
|
||||
@@ -131,7 +153,6 @@ namespace TestPlugin
|
||||
|
||||
RegisterListener<Listeners.OnEntitySpawned>(entity =>
|
||||
{
|
||||
|
||||
var designerName = entity.DesignerName;
|
||||
if (designerName != "smokegrenade_projectile") return;
|
||||
|
||||
@@ -151,7 +172,6 @@ namespace TestPlugin
|
||||
$"Test file created by TestPlugin at {DateTime.Now}");
|
||||
|
||||
|
||||
|
||||
// Execute a server command as if typed into the server console.
|
||||
Server.ExecuteCommand("find \"cssharp\"");
|
||||
|
||||
@@ -160,7 +180,8 @@ namespace TestPlugin
|
||||
(player, info) =>
|
||||
{
|
||||
if (player == null) return;
|
||||
Log($"CounterStrikeSharp - a test command was called by {new SteamID(player.SteamID).SteamId2} with {info.ArgString}");
|
||||
Log(
|
||||
$"CounterStrikeSharp - a test command was called by {new SteamID(player.SteamID).SteamId2} with {info.ArgString}");
|
||||
});
|
||||
|
||||
// Example vfunc call that usually gets the game event manager pointer
|
||||
@@ -169,14 +190,14 @@ namespace TestPlugin
|
||||
var virtualFunc = VirtualFunction.Create<IntPtr>(server.Pointer, 91);
|
||||
var result = virtualFunc() - 8;
|
||||
Log($"Result of virtual func call is {result:X}");
|
||||
|
||||
|
||||
}
|
||||
|
||||
[GameEventHandler]
|
||||
public void OnPlayerConnect(EventPlayerConnect @event)
|
||||
public HookResult OnPlayerConnect(EventPlayerConnect @event, GameEventInfo info)
|
||||
{
|
||||
Log($"Player {@event.Name} has connected!");
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
[ConsoleCommand("cssharp_attribute", "This is a custom attribute event")]
|
||||
@@ -185,9 +206,11 @@ namespace TestPlugin
|
||||
Log("cssharp_attribute called!");
|
||||
}
|
||||
|
||||
private void GenericEventHandler<T>(T @event) where T : GameEvent
|
||||
private HookResult GenericEventHandler<T>(T @event, GameEventInfo info) where T : GameEvent
|
||||
{
|
||||
Log($"Event found {@event.Handle:X}, event name: {@event.EventName}");
|
||||
Log($"Event found {@event.Handle:X}, event name: {@event.EventName} dont broadcast: {info.DontBroadcast}");
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
private void Log(string message)
|
||||
|
||||
@@ -34,147 +34,173 @@
|
||||
#include "core/log.h"
|
||||
#include "scripting/callback_manager.h"
|
||||
|
||||
SH_DECL_HOOK2(IGameEventManager2, FireEvent, SH_NOATTRIB, 0, bool, IGameEvent *, bool);
|
||||
SH_DECL_HOOK2(IGameEventManager2, FireEvent, SH_NOATTRIB, 0, bool, IGameEvent*, bool);
|
||||
|
||||
namespace counterstrikesharp {
|
||||
|
||||
EventManager::EventManager() {}
|
||||
EventManager::EventManager() = default;
|
||||
|
||||
EventManager::~EventManager() {}
|
||||
EventManager::~EventManager() = default;
|
||||
|
||||
void EventManager::OnStartup() {}
|
||||
|
||||
void EventManager::OnAllInitialized() {
|
||||
void EventManager::OnAllInitialized()
|
||||
{
|
||||
SH_ADD_HOOK(IGameEventManager2, FireEvent, globals::gameEventManager,
|
||||
SH_MEMBER(this, &EventManager::OnFireEvent), false);
|
||||
SH_ADD_HOOK(IGameEventManager2, FireEvent, globals::gameEventManager,
|
||||
SH_MEMBER(this, &EventManager::OnFireEvent_Post), true);
|
||||
SH_MEMBER(this, &EventManager::OnFireEventPost), true);
|
||||
}
|
||||
|
||||
void EventManager::OnShutdown() {
|
||||
void EventManager::OnShutdown()
|
||||
{
|
||||
SH_REMOVE_HOOK(IGameEventManager2, FireEvent, globals::gameEventManager,
|
||||
SH_MEMBER(this, &EventManager::OnFireEvent), false);
|
||||
SH_REMOVE_HOOK(IGameEventManager2, FireEvent, globals::gameEventManager,
|
||||
SH_MEMBER(this, &EventManager::OnFireEvent_Post), true);
|
||||
SH_MEMBER(this, &EventManager::OnFireEventPost), true);
|
||||
|
||||
globals::gameEventManager->RemoveListener(this);
|
||||
}
|
||||
|
||||
void EventManager::FireGameEvent(IGameEvent *event) {}
|
||||
void EventManager::FireGameEvent(IGameEvent* pEvent) {}
|
||||
|
||||
bool EventManager::HookEvent(const char *name, CallbackT callback, bool post) {
|
||||
EventHook *p_hook;
|
||||
bool EventManager::HookEvent(const char* szName, CallbackT fnCallback, bool bPost)
|
||||
{
|
||||
EventHook* pHook;
|
||||
|
||||
if (!globals::gameEventManager->FindListener(this, name)) {
|
||||
globals::gameEventManager->AddListener(this, name, true);
|
||||
if (!globals::gameEventManager->FindListener(this, szName)) {
|
||||
globals::gameEventManager->AddListener(this, szName, true);
|
||||
}
|
||||
|
||||
CSSHARP_CORE_INFO("Hooking event: {0} with callback pointer: {1}", name, (void *)callback);
|
||||
CSSHARP_CORE_INFO("Hooking event: {0} with callback pointer: {1}", szName, (void*)fnCallback);
|
||||
|
||||
auto search = m_hooks.find(name);
|
||||
auto search = m_hooksMap.find(szName);
|
||||
// If hook struct is not found
|
||||
if (search == m_hooks.end()) {
|
||||
p_hook = new EventHook();
|
||||
if (search == m_hooksMap.end()) {
|
||||
pHook = new EventHook();
|
||||
|
||||
if (post) {
|
||||
p_hook->PostHook = globals::callbackManager.CreateCallback(name);
|
||||
p_hook->PostHook->AddListener(callback);
|
||||
if (bPost) {
|
||||
pHook->m_pPostHook = globals::callbackManager.CreateCallback(szName);
|
||||
pHook->m_pPostHook->AddListener(fnCallback);
|
||||
} else {
|
||||
p_hook->PreHook = globals::callbackManager.CreateCallback(name);
|
||||
p_hook->PreHook->AddListener(callback);
|
||||
pHook->m_pPreHook = globals::callbackManager.CreateCallback(szName);
|
||||
pHook->m_pPreHook->AddListener(fnCallback);
|
||||
}
|
||||
|
||||
p_hook->name = std::string(name);
|
||||
pHook->m_Name = std::string(szName);
|
||||
|
||||
m_hooks[name] = p_hook;
|
||||
m_hooksMap[szName] = pHook;
|
||||
|
||||
return true;
|
||||
} else {
|
||||
p_hook = search->second;
|
||||
pHook = search->second;
|
||||
}
|
||||
|
||||
if (post) {
|
||||
if (!p_hook->PostHook) {
|
||||
p_hook->PostHook = globals::callbackManager.CreateCallback("");
|
||||
if (bPost) {
|
||||
if (!pHook->m_pPostHook) {
|
||||
pHook->m_pPostHook = globals::callbackManager.CreateCallback("");
|
||||
}
|
||||
|
||||
p_hook->PostHook->AddListener(callback);
|
||||
pHook->m_pPostHook->AddListener(fnCallback);
|
||||
} else {
|
||||
if (!p_hook->PreHook) {
|
||||
p_hook->PreHook = globals::callbackManager.CreateCallback("");
|
||||
if (!pHook->m_pPreHook) {
|
||||
pHook->m_pPreHook = globals::callbackManager.CreateCallback("");
|
||||
;
|
||||
}
|
||||
|
||||
p_hook->PreHook->AddListener(callback);
|
||||
pHook->m_pPreHook->AddListener(fnCallback);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EventManager::UnhookEvent(const char *name, CallbackT callback, bool post) {
|
||||
EventHook *p_hook;
|
||||
ScriptCallback *p_callback;
|
||||
bool EventManager::UnhookEvent(const char* szName, CallbackT fnCallback, bool bPost)
|
||||
{
|
||||
EventHook* pHook;
|
||||
ScriptCallback* pCallback;
|
||||
|
||||
auto search = m_hooks.find(name);
|
||||
if (search == m_hooks.end()) {
|
||||
auto search = m_hooksMap.find(szName);
|
||||
if (search == m_hooksMap.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p_hook = search->second;
|
||||
pHook = search->second;
|
||||
|
||||
if (post) {
|
||||
p_callback = p_hook->PostHook;
|
||||
if (bPost) {
|
||||
pCallback = pHook->m_pPostHook;
|
||||
} else {
|
||||
p_callback = p_hook->PreHook;
|
||||
pCallback = pHook->m_pPreHook;
|
||||
}
|
||||
|
||||
// Remove from function list
|
||||
if (p_callback == nullptr) {
|
||||
if (pCallback == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p_callback = nullptr;
|
||||
if (post) {
|
||||
p_hook->PostHook = nullptr;
|
||||
if (bPost) {
|
||||
pHook->m_pPostHook = nullptr;
|
||||
} else {
|
||||
p_hook->PreHook = nullptr;
|
||||
pHook->m_pPreHook = nullptr;
|
||||
}
|
||||
|
||||
// TODO: Clean up callback if theres noone left attached.
|
||||
|
||||
CSSHARP_CORE_INFO("Unhooking event: {0} with callback pointer: {1}", name, (void *)callback);
|
||||
CSSHARP_CORE_INFO("Unhooking event: {0} with callback pointer: {1}", szName, (void*)fnCallback);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EventManager::OnFireEvent(IGameEvent *pEvent, bool bDontBroadcast) {
|
||||
EventHook *p_hook;
|
||||
const char *name;
|
||||
bool EventManager::OnFireEvent(IGameEvent* pEvent, bool bDontBroadcast)
|
||||
{
|
||||
const char* szName;
|
||||
bool bLocalDontBroadcast = bDontBroadcast;
|
||||
|
||||
if (!pEvent) {
|
||||
RETURN_META_VALUE(MRES_IGNORED, false);
|
||||
}
|
||||
|
||||
name = pEvent->GetName();
|
||||
szName = pEvent->GetName();
|
||||
|
||||
auto search = m_hooks.find(name);
|
||||
if (search != m_hooks.end()) {
|
||||
auto p_callback = search->second->PreHook;
|
||||
CSSHARP_CORE_TRACE("OnFireEvent {}", szName);
|
||||
|
||||
if (p_callback) {
|
||||
CSSHARP_CORE_INFO("Pushing event `{0}` pointer: {1}", name, (void *)pEvent);
|
||||
p_callback->ScriptContext().Reset();
|
||||
p_callback->ScriptContext().SetArgument(0, pEvent);
|
||||
p_callback->Execute();
|
||||
auto I = m_hooksMap.find(szName);
|
||||
if (I != m_hooksMap.end()) {
|
||||
auto* pCallback = I->second->m_pPreHook;
|
||||
|
||||
RETURN_META_VALUE(MRES_IGNORED, false);
|
||||
if (pCallback) {
|
||||
CSSHARP_CORE_INFO("Pushing event `{0}` pointer: {1}, dont broadcast: {2}", szName,
|
||||
(void*)pEvent, bDontBroadcast);
|
||||
EventOverride override = {bDontBroadcast};
|
||||
pCallback->Reset();
|
||||
pCallback->ScriptContext().Push(pEvent);
|
||||
pCallback->ScriptContext().Push(&override);
|
||||
|
||||
for (auto fnMethodToCall : pCallback->GetFunctions()) {
|
||||
if (!fnMethodToCall)
|
||||
continue;
|
||||
|
||||
fnMethodToCall(&pCallback->ScriptContextStruct());
|
||||
auto result = pCallback->ScriptContext().GetResult<HookResult>();
|
||||
|
||||
bLocalDontBroadcast = override.m_bDontBroadcast;
|
||||
|
||||
if (result >= HookResult::Handled) {
|
||||
globals::gameEventManager->FreeEvent(pEvent);
|
||||
RETURN_META_VALUE(MRES_SUPERCEDE, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bLocalDontBroadcast != bDontBroadcast)
|
||||
RETURN_META_VALUE_NEWPARAMS(MRES_IGNORED, true, &IGameEventManager2::FireEvent,
|
||||
(pEvent, bLocalDontBroadcast));
|
||||
|
||||
RETURN_META_VALUE(MRES_IGNORED, true);
|
||||
}
|
||||
|
||||
bool EventManager::OnFireEvent_Post(IGameEvent *pEvent, bool bDontBroadcast) {
|
||||
bool EventManager::OnFireEventPost(IGameEvent* pEvent, bool bDontBroadcast)
|
||||
{
|
||||
RETURN_META_VALUE(MRES_IGNORED, true);
|
||||
}
|
||||
} // namespace counterstrikesharp
|
||||
} // namespace counterstrikesharp
|
||||
@@ -46,43 +46,48 @@ class CUtlString;
|
||||
namespace counterstrikesharp {
|
||||
class ScriptCallback;
|
||||
class PluginFunction;
|
||||
} // namespace counterstrikesharp
|
||||
} // namespace counterstrikesharp
|
||||
|
||||
struct EventHook {
|
||||
EventHook() {
|
||||
PreHook = nullptr;
|
||||
PostHook = nullptr;
|
||||
struct EventHook
|
||||
{
|
||||
EventHook()
|
||||
{
|
||||
m_pPreHook = nullptr;
|
||||
m_pPostHook = nullptr;
|
||||
}
|
||||
counterstrikesharp::ScriptCallback *PreHook;
|
||||
counterstrikesharp::ScriptCallback *PostHook;
|
||||
std::string name;
|
||||
counterstrikesharp::ScriptCallback* m_pPreHook;
|
||||
counterstrikesharp::ScriptCallback* m_pPostHook;
|
||||
std::string m_Name;
|
||||
};
|
||||
|
||||
struct EventOverride {
|
||||
bool m_bDontBroadcast;
|
||||
};
|
||||
|
||||
namespace counterstrikesharp {
|
||||
|
||||
class EventManager : public IGameEventListener2, public GlobalClass {
|
||||
public:
|
||||
class EventManager : public IGameEventListener2, public GlobalClass
|
||||
{
|
||||
public:
|
||||
EventManager();
|
||||
~EventManager();
|
||||
~EventManager() override;
|
||||
|
||||
public: // GlobalClass
|
||||
// GlobalClass
|
||||
void OnShutdown() override;
|
||||
void OnAllInitialized() override;
|
||||
void OnStartup() override;
|
||||
|
||||
public: // IGameEventListener2
|
||||
void FireGameEvent(IGameEvent *event) override;
|
||||
// IGameEventListener2
|
||||
void FireGameEvent(IGameEvent* pEvent) override;
|
||||
|
||||
public:
|
||||
bool UnhookEvent(const char *name, CallbackT callback, bool post);
|
||||
bool HookEvent(const char *name, CallbackT callback, bool post);
|
||||
bool UnhookEvent(const char* szName, CallbackT fnCallback, bool bPost);
|
||||
bool HookEvent(const char* szName, CallbackT fnCallback, bool bPost);
|
||||
|
||||
private:
|
||||
bool OnFireEvent(IGameEvent *pEvent, bool bDontBroadcast);
|
||||
bool OnFireEvent_Post(IGameEvent *pEvent, bool bDontBroadcast);
|
||||
private:
|
||||
bool OnFireEvent(IGameEvent* pEvent, bool bDontBroadcast);
|
||||
bool OnFireEventPost(IGameEvent* pEvent, bool bDontBroadcast);
|
||||
|
||||
private:
|
||||
std::map<std::string, EventHook *> m_hooks;
|
||||
std::map<std::string, EventHook*> m_hooksMap;
|
||||
};
|
||||
|
||||
} // namespace counterstrikesharp
|
||||
} // namespace counterstrikesharp
|
||||
|
||||
@@ -20,95 +20,102 @@
|
||||
|
||||
namespace counterstrikesharp {
|
||||
|
||||
ScriptCallback::ScriptCallback(const char *name)
|
||||
: m_root_context(fxNativeContext{}) {
|
||||
ScriptCallback::ScriptCallback(const char* szName) : m_root_context(fxNativeContext{})
|
||||
{
|
||||
m_script_context_raw = ScriptContextRaw(m_root_context);
|
||||
m_name = std::string(name);
|
||||
m_name = std::string(szName);
|
||||
}
|
||||
|
||||
ScriptCallback::~ScriptCallback() { m_functions.clear(); }
|
||||
|
||||
void ScriptCallback::AddListener(CallbackT plugin_function) {
|
||||
m_functions.push_back(plugin_function);
|
||||
void ScriptCallback::AddListener(CallbackT fnPluginFunction)
|
||||
{
|
||||
m_functions.push_back(fnPluginFunction);
|
||||
}
|
||||
|
||||
bool ScriptCallback::RemoveListener(CallbackT pluginFunction) {
|
||||
bool success;
|
||||
bool ScriptCallback::RemoveListener(CallbackT fnPluginFunction)
|
||||
{
|
||||
bool bSuccess;
|
||||
|
||||
m_functions.erase(std::remove(m_functions.begin(), m_functions.end(), pluginFunction),
|
||||
m_functions.erase(std::remove(m_functions.begin(), m_functions.end(), fnPluginFunction),
|
||||
m_functions.end());
|
||||
|
||||
return success;
|
||||
return bSuccess;
|
||||
}
|
||||
|
||||
void ScriptCallback::Execute(bool resetContext) {
|
||||
for (auto method_to_call : m_functions) {
|
||||
if (method_to_call) {
|
||||
method_to_call(&ScriptContextStruct());
|
||||
void ScriptCallback::Execute(bool bResetContext)
|
||||
{
|
||||
for (auto fnMethodToCall : m_functions) {
|
||||
if (fnMethodToCall) {
|
||||
fnMethodToCall(&ScriptContextStruct());
|
||||
}
|
||||
}
|
||||
|
||||
if (resetContext) {
|
||||
ResetContext();
|
||||
if (bResetContext) {
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptCallback::ResetContext() { ScriptContext().Reset(); }
|
||||
void ScriptCallback::Reset() { ScriptContext().Reset(); }
|
||||
|
||||
CallbackManager::CallbackManager() {}
|
||||
CallbackManager::CallbackManager() = default;
|
||||
|
||||
ScriptCallback *CallbackManager::CreateCallback(const char *name) {
|
||||
CSSHARP_CORE_TRACE("Creating callback {0}", name);
|
||||
auto *callback = new ScriptCallback(name);
|
||||
m_managed.push_back(callback);
|
||||
ScriptCallback* CallbackManager::CreateCallback(const char* szName)
|
||||
{
|
||||
CSSHARP_CORE_TRACE("Creating callback {0}", szName);
|
||||
auto* pCallback = new ScriptCallback(szName);
|
||||
m_managed.push_back(pCallback);
|
||||
|
||||
return callback;
|
||||
return pCallback;
|
||||
}
|
||||
|
||||
ScriptCallback *CallbackManager::FindCallback(const char *name) {
|
||||
for (auto it = m_managed.begin(); it != m_managed.end(); ++it) {
|
||||
ScriptCallback *marshal = *it;
|
||||
if (strcmp(marshal->GetName().c_str(), name) == 0) {
|
||||
return marshal;
|
||||
ScriptCallback* CallbackManager::FindCallback(const char* szName)
|
||||
{
|
||||
for (auto* pMarshal : m_managed) {
|
||||
if (strcmp(pMarshal->GetName().c_str(), szName) == 0) {
|
||||
return pMarshal;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CallbackManager::ReleaseCallback(ScriptCallback *callback) {
|
||||
bool success;
|
||||
auto it = std::remove_if(m_managed.begin(), m_managed.end(),
|
||||
[callback](ScriptCallback *i) { return callback == i; });
|
||||
void CallbackManager::ReleaseCallback(ScriptCallback* pCallback)
|
||||
{
|
||||
auto I = std::remove_if(m_managed.begin(), m_managed.end(),
|
||||
[pCallback](ScriptCallback* pI) { return pCallback == pI; });
|
||||
|
||||
if ((success = it != m_managed.end())) m_managed.erase(it, m_managed.end());
|
||||
delete callback;
|
||||
if (I != m_managed.end())
|
||||
m_managed.erase(I, m_managed.end());
|
||||
delete pCallback;
|
||||
}
|
||||
|
||||
bool CallbackManager::TryAddFunction(const char *name, CallbackT pCallable) {
|
||||
auto *fwd = FindCallback(name);
|
||||
if (fwd) {
|
||||
fwd->AddListener(pCallable);
|
||||
bool CallbackManager::TryAddFunction(const char* szName, CallbackT fnCallable)
|
||||
{
|
||||
auto* pCallback = FindCallback(szName);
|
||||
if (pCallback) {
|
||||
pCallback->AddListener(fnCallable);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CallbackManager::TryRemoveFunction(const char *name, CallbackT pCallable) {
|
||||
auto *fwd = FindCallback(name);
|
||||
if (fwd) {
|
||||
bool success = fwd->RemoveListener(pCallable);
|
||||
return success;
|
||||
bool CallbackManager::TryRemoveFunction(const char* szName, CallbackT fnCallable)
|
||||
{
|
||||
auto* pCallback = FindCallback(szName);
|
||||
if (pCallback) {
|
||||
return pCallback->RemoveListener(fnCallable);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CallbackManager::PrintCallbackDebug() {
|
||||
void CallbackManager::PrintCallbackDebug()
|
||||
{
|
||||
CSSHARP_CORE_INFO("----CALLBACKS----");
|
||||
for (auto it : m_managed) {
|
||||
CSSHARP_CORE_INFO("{0} ({0})\n", it->GetName().c_str(), 1);
|
||||
for (auto* pCallback : m_managed) {
|
||||
CSSHARP_CORE_INFO("{0} ({0})\n", pCallback->GetName().c_str(), 1);
|
||||
}
|
||||
}
|
||||
} // namespace counterstrikesharp
|
||||
} // namespace counterstrikesharp
|
||||
@@ -24,40 +24,44 @@
|
||||
|
||||
namespace counterstrikesharp {
|
||||
|
||||
class ScriptCallback {
|
||||
public:
|
||||
ScriptCallback(const char *name);
|
||||
class ScriptCallback
|
||||
{
|
||||
public:
|
||||
ScriptCallback(const char* szName);
|
||||
~ScriptCallback();
|
||||
void AddListener(CallbackT plugin_function);
|
||||
bool RemoveListener(CallbackT plugin_function);
|
||||
void AddListener(CallbackT fnPluginFunction);
|
||||
bool RemoveListener(CallbackT fnPluginFunction);
|
||||
std::string GetName() { return m_name; }
|
||||
unsigned int GetFunctionCount() { return m_functions.size(); }
|
||||
void Execute(bool resetContext = true);
|
||||
void ResetContext();
|
||||
ScriptContextRaw &ScriptContext() { return m_script_context_raw; }
|
||||
fxNativeContext &ScriptContextStruct() { return m_root_context; }
|
||||
std::vector<CallbackT> GetFunctions() { return m_functions; }
|
||||
|
||||
private:
|
||||
|
||||
void Execute(bool bResetContext = true);
|
||||
void Reset();
|
||||
ScriptContextRaw& ScriptContext() { return m_script_context_raw; }
|
||||
fxNativeContext& ScriptContextStruct() { return m_root_context; }
|
||||
|
||||
private:
|
||||
std::vector<CallbackT> m_functions;
|
||||
std::string m_name;
|
||||
ScriptContextRaw m_script_context_raw;
|
||||
fxNativeContext m_root_context;
|
||||
};
|
||||
|
||||
class CallbackManager : public GlobalClass {
|
||||
public:
|
||||
class CallbackManager : public GlobalClass
|
||||
{
|
||||
public:
|
||||
CallbackManager();
|
||||
|
||||
public:
|
||||
ScriptCallback *CreateCallback(const char *name);
|
||||
ScriptCallback *FindCallback(const char *name);
|
||||
void ReleaseCallback(ScriptCallback *callback);
|
||||
bool TryAddFunction(const char *name, CallbackT pCallable);
|
||||
bool TryRemoveFunction(const char *name, CallbackT pCallable);
|
||||
ScriptCallback* CreateCallback(const char* szName);
|
||||
ScriptCallback* FindCallback(const char* szName);
|
||||
void ReleaseCallback(ScriptCallback* pCallback);
|
||||
bool TryAddFunction(const char* szName, CallbackT fnCallable);
|
||||
bool TryRemoveFunction(const char* szName, CallbackT fnCallable);
|
||||
void PrintCallbackDebug();
|
||||
|
||||
private:
|
||||
std::vector<ScriptCallback *> m_managed;
|
||||
private:
|
||||
std::vector<ScriptCallback*> m_managed;
|
||||
};
|
||||
|
||||
} // namespace counterstrikesharp
|
||||
} // namespace counterstrikesharp
|
||||
@@ -33,6 +33,13 @@
|
||||
|
||||
namespace counterstrikesharp {
|
||||
|
||||
enum HookResult {
|
||||
Continue = 0,
|
||||
Changed = 1,
|
||||
Handled = 3,
|
||||
Stop = 4,
|
||||
};
|
||||
|
||||
inline uint32_t hash_string(const char *string) {
|
||||
unsigned long result = 5381;
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ public partial class Generators
|
||||
[EventName(""{gameEvent.Name}"")]
|
||||
public class Event{gameEvent.NamePascalCase} : GameEvent
|
||||
{{
|
||||
public Event{gameEvent.NamePascalCase}() : base(){{}}
|
||||
public Event{gameEvent.NamePascalCase}(IntPtr pointer) : base(pointer){{}}
|
||||
public Event{gameEvent.NamePascalCase}(bool force) : base(""{gameEvent.Name}"", force){{}}
|
||||
|
||||
{string.Join("\n", propertyDefinition)}
|
||||
|
||||
Reference in New Issue
Block a user