Game Event Broadcast Manipulation & Cancellation (#10)

This commit is contained in:
Michael Wilson
2023-10-23 19:56:42 +10:00
committed by GitHub
parent 37b085a9f5
commit 65bdd0b5ff
17 changed files with 620 additions and 523 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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`.

View File

@@ -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")]

View File

@@ -30,5 +30,5 @@ SET(
dynload_s
dyncall_s
distorm
funchook-shared
funchook-static
)

View File

@@ -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 });

View 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

View File

@@ -0,0 +1,9 @@
namespace CounterStrikeSharp.API.Core;
public enum HookResult
{
Continue = 0,
Changed = 1,
Handled = 3,
Stop = 4,
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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)}