[
  {
    "path": ".gitignore",
    "content": "# Visual Studio 2015 user specific files\n.vs/\n\n# Visual Studio 2015 database file\n*.VC.db\n\n# Compiled Object files\n*.slo\n*.lo\n*.o\n*.obj\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Compiled Dynamic libraries\n*.so\n*.dylib\n*.dll\n\n# Fortran module files\n*.mod\n\n# Compiled Static libraries\n*.lai\n*.la\n*.a\n*.lib\n\n# Executables\n*.exe\n*.out\n*.app\n*.ipa\n\n# These project files can be generated by the engine\n*.xcodeproj\n*.xcworkspace\n*.sln\n*.suo\n*.opensdf\n*.sdf\n*.VC.db\n*.VC.opendb\n\n# Precompiled Assets\nSourceArt/**/*.png\nSourceArt/**/*.tga\n\n# Binary Files\nBinaries/*\nPlugins/*/Binaries/*\n\n# Builds\nBuild/*\n\n# Whitelist PakBlacklist-<BuildConfiguration>.txt files\n!Build/*/\nBuild/*/**\n!Build/*/PakBlacklist*.txt\n\n# Don't ignore icon files in Build\n!Build/**/*.ico\n\n# Built data for maps\n*_BuiltData.uasset\n\n# Configuration files generated by the Editor\nSaved/*\n\n# Compiled source files for the engine to use\nIntermediate/*\nPlugins/*/Intermediate/*\n\n# Cache files for the editor to use\nDerivedDataCache/*\n"
  },
  {
    "path": "Content/Scripts/ges/gesWrapper.js",
    "content": "const uclass = require('uclass')().bind(this, global);\r\n\r\n/** \r\nWrapper class to enable some passthrough ges binding.\r\nATM only supports string params and a maximum of 10 bind events\r\nbefore it overwrites older binds. This is due to the limitation that\r\nUFUNCTIONS have to be defined at design time. Todo: add support for ~100?\r\n\r\nSuper experimental atm\r\n*/\r\nclass GESJsReceiver extends JsOwner.ClassMap['GESJsReceiverBpActor']{\r\n\tctor(){\r\n\t\tthis.callbacks = {};\r\n\t}\r\n\tconstructor(){\r\n\t}\r\n\r\n\tOnJsReceive(uniqueId, property){\r\n\t\tthis.callbacks[uniqueId](property);\r\n\t}\r\n\r\n\tOnJsReceiveObj(uniqueId, property){\r\n\t\tthis.callbacks[uniqueId](property);\r\n\t}\r\n\r\n\t//returns a unique id for potential unbinding\r\n\tbind(domain='global.default', event, callback){\r\n\t\t//const uniqueKey = domain + '.' + event;\r\n\t\tconst uniqueFunctionId = this.NextUniqueReceiver()['NextUniqueFunction'];\r\n\r\n\t\tif(uniqueFunctionId == 'OnJsReceiveOneParam_9'){\r\n\t\t\tconsole.warn('GESJsReceiver: Maximum receivers reached!');\r\n\t\t}\r\n\r\n\t\tthis.callbacks[uniqueFunctionId] = callback;\r\n\r\n\t\tthis.JsGESBindEvent(domain,\r\n\t\t\tevent,\r\n\t\t\tuniqueFunctionId);\r\n\t\treturn uniqueFunctionId;\r\n\t}\r\n\r\n\tbindToObjCallback(domain='global.default', event, callback, uniqueReceiver = false){\r\n\t\t//const uniqueKey = domain + '.' + event;\r\n\t\tconst uniqueFunctionId = this.NextUniqueReceiverObj()['NextUniqueFunction'];\r\n\r\n\t\tif(uniqueReceiver && uniqueFunctionId == 'OnJsReceiveOneParamObj_1'){\r\n\t\t\tconsole.warn('GESJsReceiver: Maximum obj receivers (1) reached (uniqueReceiver: true)!');\r\n\t\t\treturn '';\r\n\t\t}\r\n\t\t\r\n\t\tif(uniqueFunctionId == 'OnJsReceiveOneParamObj_4'){\r\n\t\t\tconsole.warn('GESJsReceiver: Maximum obj receivers reached!');\r\n\t\t}\r\n\r\n\t\tthis.callbacks[uniqueFunctionId] = callback;\r\n\r\n\t\tconsole.log(`UniqueId is <${uniqueFunctionId}>`)\r\n\r\n\t\tthis.JsGESBindEvent(domain,\r\n\t\t\tevent,\r\n\t\t\tuniqueFunctionId);\r\n\t\treturn uniqueFunctionId;\r\n\t}\r\n\r\n\temit(domain='global.default', event, data='', pinned=false){\r\n\t\tif(typeof data === 'string'){\r\n\t\t\tthis.JsGESEmitEventOneParamString(domain, event, data, pinned);\r\n\t\t}\r\n\t\telse{\r\n\t\t\tthis.JsGESEmitEventOneParamObject(domain, event, data, pinned);\r\n\t\t}\r\n\t}\r\n\r\n\t//NB: need to store the unique function id you get from bind\r\n\tunbind(domain='global.default', event, uniqueFunctionId){\r\n\t\tthis.UnbindEvent(domain, event, uniqueFunctionId);\r\n\t}\r\n\r\n\twlog(text){\r\n\t\tif(typeof text !== 'string'){\r\n\t\t\ttext = JSON.stringify(text);\r\n\t\t}\r\n\t\tthis.emit('global.console', 'log', text);\r\n\t}\r\n\tunbindAll(){\r\n\t\tthis.UnbindAllEvents();\r\n\t\t//GlobalEventSystemBPLibrary.GESUnbindAllEventsForContext(this);\r\n\t}\r\n}\r\n\r\nconst GESJsReceiver_C = uclass(GESJsReceiver);\r\n\r\nexports.ges = new GESJsReceiver_C(GWorld, {Z:0});"
  },
  {
    "path": "GlobalEventSystem.uplugin",
    "content": "{\n\t\"FileVersion\": 3,\n\t\"Version\": 1,\n\t\"VersionName\": \"0.15.1\",\n\t\"FriendlyName\": \"GlobalEventSystem\",\n\t\"Description\": \"Loosely coupled internal event system.\",\n\t\"Category\": \"Utility\",\n\t\"CreatedBy\": \"getnamo\",\n\t\"CreatedByURL\": \"getnamo.com\",\n\t\"DocsURL\": \"https://github.com/getnamo/global-event-system-ue4\",\n\t\"MarketplaceURL\": \"\",\n\t\"SupportURL\": \"https://github.com/getnamo/global-event-system-ue4/issues\",\n\t\"CanContainContent\": true,\n\t\"IsBetaVersion\": false,\n\t\"Installed\": false,\n\t\"Modules\": [\n\t\t{\n\t\t\t\"Name\": \"GlobalEventSystem\",\n\t\t\t\"Type\": \"Runtime\",\n\t\t\t\"LoadingPhase\": \"Default\"\n\t\t}\n\t]\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019 Jan Kaniewski\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# GlobalEventSystem-Unreal\nA loosely coupled internal global event system (GES) plugin for the Unreal Engine. Aims to solve cross-map and cross-blueprint communication for reliable and inferable event flow. Should enable a publisher-observer pattern.\n\n[![GitHub release](https://img.shields.io/github/release/getnamo/GlobalEventSystem-Unreal.svg)](https://github.com/getnamo/GlobalEventSystem-Unreal/releases)\n[![Github All Releases](https://img.shields.io/github/downloads/getnamo/GlobalEventSystem-Unreal/total.svg)](https://github.com/getnamo/GlobalEventSystem-Unreal/releases)\n\nBecause the events are emitted to a dynamic map of listeners you can loosely link parts of your project without needing to redo boilerplate when you change parts of the code, dynamically change environments, or e.g. load a different submap. Fire something away, and if something is interested in that information, they can do something with it; optional.\n\nQuestions? See https://github.com/getnamo/GlobalEventSystem-Unreal/issues\n\nDiscussions? See [Unreal Thread](https://forums.unrealengine.com/t/plugin-global-event-system/134063)\n\n[Discord Server](https://discord.gg/qfJUyxaW4s)\n\n### Current Important Issue\n\nEmitting a struct from C++ to blueprint receiver will currently not fill properly. All other emit/receive pairs work. Use object wrapper as workaround until fix is found. Issue: https://github.com/getnamo/GlobalEventSystem-Unreal/issues/15\n\n## Quick Install & Setup ##\n 1. [Download Latest Release](https://github.com/getnamo/GlobalEventSystem-Unreal/releases)\n 2. Create new or choose project.\n 3. Browse to your project folder (typically found at Documents/Unreal Project/{Your Project Root})\n 4. Copy *Plugins* folder into your Project root.\n 5. Plugin should be now ready to use.\n\n## How to use - Basics and API\n\nThere are globally available functions that you can use to emit and bind events. At this time there are two variants for emitting (no parameters and one wildcard parameter) and one for binding events to your local functions. There are also GameplayTag variants of these emitters and receivers for easy dropdown linking.\n\nEach emit is a multi-cast to all valid bound receivers. If the parameters don't match you'll be warned in the log with fairly verbose messages while emitting to all other valid targets. \n\nConsider optionally using Gameplay tagged based emitters/receivers, or extending GESReceiver components to keep messaging organized.\n\n### Emit Event\n\n#### ```GESEmitEvent```\n\n##### Param: Pinned\nWhether the event should trigger for listeners added after the event has fired. Useful to communicate state.\n\n##### Param: Domain\nA string type similar to a namespace with reverse-DNS like structure encouraged, but not enforced. By default there is a ```global.default``` prefilled which can be changed to any valid utf8 string.\n\n##### Param: Event\nThis is an abstract name and is considered unique for that domain. You'll need the same domain and event name to match in your binding function to receive the event.\n\n#### ```GESEmitEventOneParam```\n\n##### Additional Param: Parameter Data\nWildcard Property, will accept any single property type e.g. *int, float, byte, string, name, bool, struct,* and *object*. Wrap arrays and maps in a custom struct to emit more complex data types. \n\nBreak pin to set a new type of value. \n\nKeep in mind that the receiving listeners need to match the property type to receive the data.\n\n![emit](https://i.imgur.com/8nXb5ya.png)\n\n#### ```GESEmitTagEvent```\n\nGameplayTag variant of GESEmitEvent. Instead of ```Domain``` and ```Event``` string you pick an event from a GameplayTag via dropdown\n\n##### Param: Domained Event Tag\n\nA GameplayTag similar to a namespace with reverse-DNS like structure. Select or make one from the drop down list. Any depth tag should be supported and will automatically translate to domain and event under the hood.\n\n##### Param: Pinned\nWhether the event should trigger for listeners added after the event has fired. Useful to communicate state.\n\n![image](https://user-images.githubusercontent.com/542365/113543315-f605a780-959a-11eb-9b70-83208ad2f002.png)\n\n\n#### ```GESEmitTagEventOneParam```\n\n##### Additional Param: Parameter Data\nWildcard Property, will accept any single property type e.g. *int, float, byte, string, name, bool, struct,* and *object*. Wrap arrays and maps in a custom struct to emit more complex data types. \n\nBreak pin to set a new type of value. \n\nKeep in mind that the receiving listeners need to match the property type to receive the data.\n\n![image](https://user-images.githubusercontent.com/542365/113543142-a030ff80-959a-11eb-93bd-7e9d5ec55a79.png)\n\n\n### Bind Event\n\n```GESBindEvent```\n##### Param: Domain\nA string type similar to a namespace with reverse-DNS like structure encouraged, but not enforced. By default there is a ```global.default``` prefilled which can be changed to any valid utf8 string.\n\n##### Param: Event\nThis is an abstract name and is considered unique for that domain. You'll need the same domain and event name to match in your emitting function to receive the event.\n\n##### Param: Receiving Function\nThe final parameter is your local function name. This will be called on the graph context object (owner of graph e.g. the calling actor).\n\n![bind](https://i.imgur.com/WzHhEeG.png)\n\nThen make your custom event or blueprint function with a matching name and matching parameters.\n\n### Bind Event to Wildcard Delegate\n\nInstead of linking via function name, you can connect or make a wildcard property delegate (c++ type _FGESOnePropertySignature_).\n\n![wildcard delegate](https://i.imgur.com/bOX2lve.png)\n\nYou can then convert your received wildcard property to a fixed type with a boolean indicator if the conversion was successful. Below are the available conversion types.\n\n![other conversions](https://i.imgur.com/iOtaJTq.png)\n\nNB: The struct property in the conversion node will appear gray until linked with a local/member variable via e.g. a Set call.\n\n\n### Bind Event via GameplayTag\n\nSimilar to the emit ```GESEmitTagEvent```, you can use the GameplayTag based variants to bind to a delegate or function by name\n\n![image](https://user-images.githubusercontent.com/542365/113543759-e6d32980-959b-11eb-8839-97138b49c1de.png)\n\n\n## Unbinding\n\nEvents automatically unbind on world end, but if you expect your receiver to last shorter than the world, consider unbinding all events attached to receiver on its _EndPlay_ call\n\n![unbind all](https://i.imgur.com/ePryxZ4.png)\n\nor optionally unbind individual events\n\n![unbind](https://i.imgur.com/Qw3znMg.png)\n\n## Examples\n\nKeep in mind that you can start using GES incrementally for specific tasks or parts of large projects instead of replacing too many parts at once. Below are some very basic examples where GES could be useful.\n\n### Cross-map reference pinning\nLet's say you had two actors in two different sub-maps and you wanted one actor to know that it has spawned from e.g. some dynamic process. Delay nodes shown below are only used to show example event delays due to e.g. async processing or waiting on something else to happen; not needed for function.\n\n![actor ready](https://i.imgur.com/BLUFoFs.png)\n\nIn the spawned actor you could emit a reference to itself.\n\n![listen actor](https://i.imgur.com/IP0XTtC.png)\n\nand in the other actor you could bind to that event to do something with that information. Normally even without pinning this event should be received because you bind before you emit. But what if you couldn't control the delay?\n\n![delayed bind](https://i.imgur.com/UfQYsJa.png)\n\nThis is the case where pinning the event would help as now when the receiving actor binds to the event, it will automatically receive the last emit even though it was called after the event was emitted. From a developer perspective you can now just handle the receiving logic and not worry about whether you need to add delays or loop through all actors in the map. By arranging your events to signal selectively and muxing those states you can ensure that the order of your events remains predictable; only start x when part y and z in the map have happened.\n\n### Flow muxing and loose coupling\n\nYou can add a simple actor to the map which listens to various GES events. When for example two of those events have fired you can fire off another event which is a composite logic of the source events e.g. ANDGate or much more complex logic if we decide to use variable state.\n\n![](https://i.imgur.com/ickckJe.png)\n\nBlueprints which would listen to the SAReady event, don't even have to care where the source came from and you could easily swap out this logic actor for maybe another type without changing any other code; an example of the loose coupling enabled by GES. The actor is replaceable, there is no additional boilerplate that needs to be changed if replaced.\n\n## Component Receivers - Optional way of organizing events\n\nIf your receiver is an actor, you can organize your events via _GESBaseReceiverComponent_ sub-classed _ActorComponent_ receivers. These receivers automatically store the last received value and auto-unbind on EndPlay.\n\nThere are a few built-in types available e.g. a float receiver\n\n![float receiver](https://i.imgur.com/eVCxucx.png)\n\nJust add the component to your actor and add the OnFloatReceived Event. Change the BindSettings to match your expected _Domain_ and _Event_ names. Leave receiving function unless you want to specialize this receiver.\n\nBelow are the available built-in receivers.\n\n![convenience receivers](https://i.imgur.com/wcECuDo.png)\n\n#### Customizing your own receiver\n\nStart with adding a new blueprint with _GESBaseReceiverComponent_ base class\n\n![subclass](https://i.imgur.com/W2qvQR8.png)\n\nThen modify your blueprint to store your own data type and forward the GES event to your own Event Dispatcher.\n\n![example customization](https://i.imgur.com/x04WW5c.png)\n\ne.g. a custom struct specialized receiver\n\nYou can then just add this component to all the actors that are interested in this type of event.\n\n## Options\n\nThere are some simple options to toggle some log messages and detailed struct type checking.\n\n![options](https://i.imgur.com/22tC4lI.png)\n\n## C++\n\nTo use GES in C++, add ```\"GlobalEventSystem\"``` to your project Build.cs e.g.\n\n```PublicDependencyModuleNames.AddRange(new string[] { \"Core\", \"CoreUObject\", \"Engine\", \"InputCore\", \"GlobalEventSystem\" });```\n\nthen in your implementation file of choice add the ```#include \"GESHandler.h\"``` header.\n\n### Emit an event\n\nUse _FGESHandler_ class to get static access to a default handler.\n\n```FGESHandler::DefaultHandler()```\n\nCall functions on this handler to both emit and bind events.\n\n#### No param\nTo emit a no-param event you specify an _FGESEmitContext_ struct as the first function parameter\n\n```c++\n//define emit contexts\nFGESEmitContext Context;\nContext.Domain = TEXT(\"global.default\");\nContext.Event = TEXT(\"MyEvent\");\nContext.bPinned = true;      //whether the event state should be available after emit\nContext.WorldContext = this; //all GES events require a WorldContext object, typically this will be an actor or anything with a world.\n\nFGESHandler::DefaultHandler()->EmitEvent(Context);\n```\n\n#### One param\n\nFor any other emit type with one parameter, you pass the parameter value of choice as the second function parameter.\nMost common types are overloaded in _EmitEvent_. For multi-param and complex data types wrap or use a UStruct or UObject sub-class.\n\n##### FString\n\n```c++\n...\n\nFString MyString = TEXT(\"MyStringData\");\nFGESHandler::DefaultHandler()->EmitEvent(Context, MyString);\n```\n\nor you can emit string literals via\n\n```c++\n...\nFGESHandler::DefaultHandler()->EmitEvent(Context, TEXT(\"MyStringData\"));\n```\n\n##### int32\n```c++\n...\n\nFGESHandler::DefaultHandler()->EmitEvent(Context, 5);\n```\n\n##### float\n```c++\n...\n\nFGESHandler::DefaultHandler()->EmitEvent(Context, 1.3);\n```\n\n##### bool\n\n```c++\n...\n\nFGESHandler::DefaultHandler()->EmitEvent(Context, true);\n```\n\n##### FName\n\n```c++\n...\n\nFName MyName = TEXT(\"my name\");\nFGESHandler::DefaultHandler()->EmitEvent(Context, MyName);\n```\n\n##### UObject*\n```c++\n...\n\nUObject* SomeObject;\n\nFGESHandler::DefaultHandler()->EmitEvent(Context, SomeObject);\n```\n\n##### Struct\n\n```c++\n\n//Assuming e.g. this custom struct definition\n//NB : blueprint type declaration is optional, but will expose it to bp for easier receiving in that context\nUSTRUCT(BlueprintType)\nstruct FCustomTestData\n{\n    GENERATED_BODY()\n\n    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category= Test)\n    FString Name;\n\n    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Test)\n    int32 Index;\n\n    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Test)\n    TArray<float> Data;\n};\n\n...\n\nFCustomTestData EmitStruct;\nEmitStruct.Name = TEXT(\"Testy\");\nEmitStruct.Index = 5;\nEmitStruct.Data = {1.2, 2.3};\n\n\nFGESHandler::DefaultHandler()->EmitEvent(FCustomTestData::StaticStruct(), &EmitStruct);\n```\n\nNB: v0.7.0 has a bug where c++ struct emits to blueprint receivers do not properly fill. Use object wrappers until a fix is found.\n\n### Receive an event\n\nThe recommended method is using lambda receivers. Define an _FGESEventContext_ struct as the first param, then pass your overloaded lambda as the second type. NB: you can also alternatively organize your receivers with e.g. subclassing a _GESBaseReceiverComponent_, but these are only applicable for actor owners and thus not recommended over lambda receivers in general. \n\n#### No param event\n\nOnly the event context is required. Use 'this' capture context in the lambda to enable calling e.g. member functions (optional).\n\n```c++\nFGESEventContext Context;\nContext.Domain = TEXT(\"global.default\");\nContext.Event = TEXT(\"MyEvent\");\nContext.WorldContext = this;\n \nFGESHandler::DefaultHandler()->AddLambdaListener(Context, [this]\n{\n    //handle receive\n});\n```\n\n#### FString param event\n\n```c++\n...\n\nFGESHandler::DefaultHandler()->AddLambdaListener(Context, [this](const FString& StringData)\n{\n    //handle receive, e.g. log result\n    UE_LOG(LogTemp, Log, TEXT(\"Received %s\"), *StringData);\n});\n```\n\n#### float param event\n\n```c++\n...\n\nFGESHandler::DefaultHandler()->AddLambdaListener(Context, [this](float FloatData)\n{\n    //handle receive, e.g. log result\n    UE_LOG(LogTemp, Log, TEXT(\"Received %1.3f\"), FloatData);\n});\n```\n\n#### int32 param event\n\nNB: name specialization of this bind due to lambda bind ambiguity with float callback\n\n```c++\n...\n\nFGESHandler::DefaultHandler()->AddLambdaListenerInt(Context, [this](int32 IntData)\n{\n    //handle receive, e.g. log result\n    UE_LOG(LogTemp, Log, TEXT(\"Received %d\"), IntData);\n});\n```\n\n#### bool param event\n\nNB: name specialization of this bind due to lambda bind ambiguity with float callback\n\n```c++\n...\n\nFGESHandler::DefaultHandler()->AddLambdaListenerBool(Context, [this](bool BoolData)\n{\n    //handle receive, e.g. log result\n    UE_LOG(LogTemp, Log, TEXT(\"Received %d\"), BoolData);\n});\n```\n\n#### FName param event\n\n```c++\n...\n\nFGESHandler::DefaultHandler()->AddLambdaListener(Context, [this](const FName& NameData)\n{\n    //handle receive, e.g. log result\n    UE_LOG(LogTemp, Log, TEXT(\"Received %s\"), *NameData.ToString());\n});\n```\n\n#### UObject* param event\n\n```c++\n...\n\nFGESHandler::DefaultHandler()->AddLambdaListener(Context, [this](UObject* ObjectData)\n{\n    //handle receive, e.g. log result\n    UE_LOG(LogTemp, Log, TEXT(\"Received %s\"), *ObjectData.GetName());\n});\n```\n\n#### Struct param event\n\nStructs need a deep copy to be readable.\n\n```c++\n\n//Assuming this custom struct\nUSTRUCT(BlueprintType)\nstruct FCustomTestData\n{\n\tGENERATED_BODY()\n\n\tUPROPERTY(BlueprintReadWrite, EditAnywhere, Category=Test)\n\tFString Name;\n\n\tUPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Test)\n\tint32 Index;\n\n\tUPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Test)\n\tTArray<float> Data;\n};\n\n...\n\nFGESHandler::DefaultHandler()->AddLambdaListener(Context, [this](UStruct* Struct, void* StructPtr)\n{\n    //Confirm matching struct\n    if (Struct == FCustomTestData::StaticStruct())\n    {\n        //Deep copy your struct to local ref\n\tFCustomTestData TestData;\n\tTestData = *(FCustomTestData*)StructPtr;\n        \n\t//Test data is now usable\n\tUE_LOG(LogTemp, Log, TEXT(\"Struct data: %s %d\"), *TestData.Name, TestData.Data.Num());\n    }\n});\n```\n\n#### Wildcard\nIf you're not sure of the type of data you can receive, try a wildcard lambda and cast to test validity of data types. You'll need to add ```\"#include \"GlobalEventSystemBPLibrary.h\"``` to use the wildcard property conversion functions.\n\n```c++\n...\nFGESHandler::DefaultHandler()->AddLambdaListener(Context, [this](const FGESWildcardProperty& WildcardProperty)\n{\n    //Let's try to decode a float\n    float MaybeFloat;\n    bool bDidGetFloat = UGlobalEventSystemBPLibrary::Conv_PropToFloat(WildcardProperty, MaybeFloat);\n    if(bDidGetFloat)\n    {\n        //good to go\n    }\n});\n```\n\n#### Unbinding Events\nEach bound event function should unbind automatically when the world gets removed, but it is recommended to remove your listener if your receiver has a shorter lifetime e.g. on its _EndPlay_ call.\n\nRemove all listeners attached to this owner (where _this_ == world context object).\n\n```c++\nFGESHandler::DefaultHandler()->RemoveAllListenersForReceiver(this);\n```\n\nOr you unbind each listener via the returned lambda function name you get when you bind the listener to the event.\n\n```c++\n...\n\n//Store a reference to your lambda via string name\nFString LambdaFunctionName = FGESHandler::DefaultHandler()->AddLambdaListener(Context, [this]\n{\n    //handle receive\n});\n\n...\n\n//let's say we're done listening now\nFGESHandler::DefaultHandler()->RemoveLambdaListener(Context, LambdaFunctionName);\n\n```\n\nOptionally you can also store the function and pass it instead of the lambda name to unbind it. Name method is preferred due to developers often defining anonymous functions inline when binding.\n\n## When not to use GES\n- There are some performance considerations to keep in mind. While the overall architecture is fairly optimized, it can be more expensive than a simple function call due to function and type checking. Consider it appropriate for signaling more than a hammer to use everywhere.\n\n- If your objects have a tight coupling or it's easily accessible in a tree hierarchy pattern I would use standard methods instead of GES.\n\n- Background threads. Current version is not thread safe and should be called only in your game thread.\n\n## Possible Improvements\nSee https://github.com/getnamo/GlobalEventSystem-Unreal/issues for latest.\nGeneral enhancements:\n- Event with callback (get information from a listener)\n- Add optional logging utility to record event flow with possibly replay (attach middleware function)\n- Trigger limits, e.g. can only trigger n times\n- Add receiver limits (target requires interface/etc)\n- Bind to Interface (binds all events in an interface map to functions in interface)\n"
  },
  {
    "path": "Source/GlobalEventSystem/GlobalEventSystem.Build.cs",
    "content": "// Some copyright should be here...\n\nusing UnrealBuildTool;\n\npublic class GlobalEventSystem : ModuleRules\n{\n\tpublic GlobalEventSystem(ReadOnlyTargetRules Target) : base(Target)\n\t{\n\t\tPCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;\n\t\t\n\t\tPublicIncludePaths.AddRange(\n\t\t\tnew string[] {\n\t\t\t\t// ... add public include paths required here ...\n\t\t\t}\n\t\t\t);\n\t\t\t\t\n\t\t\n\t\tPrivateIncludePaths.AddRange(\n\t\t\tnew string[] {\n\t\t\t\t// ... add other private include paths required here ...\n\t\t\t}\n\t\t\t);\n\t\t\t\n\t\t\n\t\tPublicDependencyModuleNames.AddRange(\n\t\t\tnew string[]\n\t\t\t{\n\t\t\t\t\"Core\",\n\t\t\t\t// ... add other public dependencies that you statically link with here ...\n\t\t\t}\n\t\t\t);\n\t\t\t\n\t\t\n\t\tPrivateDependencyModuleNames.AddRange(\n\t\t\tnew string[]\n\t\t\t{\n\t\t\t\t\"CoreUObject\",\n\t\t\t\t\"Engine\",\n\t\t\t\t\"Slate\",\n\t\t\t\t\"SlateCore\",\n\t\t\t\t\"GameplayTags\"\n\t\t\t\t// ... add private dependencies that you statically link with here ...\t\n\t\t\t}\n\t\t\t);\n\t\t\n\t\t\n\t\tDynamicallyLoadedModuleNames.AddRange(\n\t\t\tnew string[]\n\t\t\t{\n\t\t\t\t// ... add any modules that your module loads dynamically here ...\n\t\t\t}\n\t\t\t);\n\t\t\n\t\tif (Target.Type == TargetRules.TargetType.Editor)\n\t\t{\n\t\t\tPublicDependencyModuleNames.Add(\"UnrealEd\");\n\t\t} \n\t}\n}\n"
  },
  {
    "path": "Source/GlobalEventSystem/Private/GESBaseReceiverComponent.cpp",
    "content": "#include \"GESBaseReceiverComponent.h\"\n#include \"GlobalEventSystemBPLibrary.h\"\n\nUGESBaseReceiverComponent::UGESBaseReceiverComponent(const FObjectInitializer& init) : UActorComponent(init)\n{\n\tbBindOnBeginPlay = true;\n\tbUnbindOnEndPlay = true;\n\tbPinInternalDataForPolling = true;\n\tbDidReceiveEventAtLeastOnce = false;\n\n\tBindSettings.ReceivingFunction = TEXT(\"OnEvent(component)\");\n}\n\nvoid UGESBaseReceiverComponent::BeginPlay()\n{\n\tSuper::BeginPlay();\n\tif (bBindOnBeginPlay)\n\t{\n\t\t//special case\n\t\tif (BindSettings.ReceivingFunction == TEXT(\"OnEvent(component)\"))\n\t\t{\n\t\t\tInternalListener.BindDynamic(this, &UGESBaseReceiverComponent::HandleInternalEvent);\n\t\t\tUGlobalEventSystemBPLibrary::GESBindEventToDelegate(this, InternalListener, BindSettings.Domain, BindSettings.Event);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tUGlobalEventSystemBPLibrary::GESBindEvent(this, BindSettings.Domain, BindSettings.Event, BindSettings.ReceivingFunction);\n\t\t}\n\t}\n}\n\nvoid UGESBaseReceiverComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)\n{\n\tif (bUnbindOnEndPlay)\n\t{\n\t\tif (BindSettings.ReceivingFunction == TEXT(\"OnEvent(component)\"))\n\t\t{\n\t\t\tUGlobalEventSystemBPLibrary::GESUnbindDelegate(this, InternalListener, BindSettings.Domain, BindSettings.Event);\n\t\t\tPinnedData.CleanupPinnedData();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tUGlobalEventSystemBPLibrary::GESUnbindEvent(this, BindSettings.Domain, BindSettings.Event, BindSettings.ReceivingFunction);\n\t\t}\n\t}\n\tSuper::EndPlay(EndPlayReason);\n}\n\nvoid UGESBaseReceiverComponent::HandleInternalEvent(const FGESWildcardProperty& WildcardProperty)\n{\n\tLastReceivedProperty = WildcardProperty;\n\n\tif (bPinInternalDataForPolling)\n\t{\n\t\tPinnedData.Property = WildcardProperty.Property.Get();\n\t\tPinnedData.PropertyPtr = WildcardProperty.PropertyPtr;\n\n\t\t//We need to use pinning to catch non-pinned data emitted\n\t\tPinnedData.CopyPropertyToPinnedBuffer();\n\n\t\tLastReceivedProperty.Property = PinnedData.Property;\n\t\tLastReceivedProperty.PropertyPtr = PinnedData.PropertyPtr;\n\t}\n\n\tbDidReceiveEventAtLeastOnce = true;\n\n\tOnEvent.Broadcast(WildcardProperty);\n}\n"
  },
  {
    "path": "Source/GlobalEventSystem/Private/GESDataTypes.cpp",
    "content": "#include \"GESDataTypes.h\"\n"
  },
  {
    "path": "Source/GlobalEventSystem/Private/GESHandler.cpp",
    "content": "#include \"GESHandler.h\"\n#include \"GlobalEventSystemBPLibrary.h\"\n#include \"Engine/World.h\"\n\nTSharedPtr<FGESHandler> FGESHandler::PrivateDefaultHandler = MakeShareable(new FGESHandler());\n\nvoid FGESHandler::Clear()\n{\n\tPrivateDefaultHandler = MakeShareable(new FGESHandler());\n}\n\nbool FGESHandler::FirstParamIsCppType(UFunction* Function, const FString& TypeString)\n{\n\tTArray<FProperty*> Properties;\n\tFunctionParameters(Function, Properties);\n\tif (Properties.Num() == 0)\n\t{\n\t\treturn false;\n\t}\n\n\tconst FString& FirstParam = Properties[0]->GetCPPType();\n\treturn (FirstParam == TypeString);\n}\n\nbool FGESHandler::FirstParamIsSubclassOf(UFunction* Function, FFieldClass* ClassType)\n{\n\tTArray<FProperty*> Properties;\n\tFunctionParameters(Function, Properties);\n\tif (Properties.Num() == 0)\n\t{\n\t\treturn false;\n\t}\n\treturn Properties[0]->GetClass()->IsChildOf(ClassType);\n}\n\nFString FGESHandler::ListenerLogString(const FGESEventListener& Listener)\n{\n\treturn Listener.ReceiverWCO.Get()->GetName() + TEXT(\":\") + Listener.FunctionName;\n}\n\nFString FGESHandler::EventLogString(const FGESEvent& Event)\n{\n\treturn Event.Domain + TEXT(\".\") + Event.Event;\n}\n\nFString FGESHandler::EmitEventLogString(const FGESEmitContext& EmitData)\n{\n\treturn EmitData.Domain + TEXT(\".\") + EmitData.Event;\n}\n\nvoid FGESHandler::FunctionParameters(UFunction* Function, TArray<FProperty*>& OutParamProperties)\n{\n\tTFieldIterator<FProperty> Iterator(Function);\n\n\twhile (Iterator && (Iterator->PropertyFlags & CPF_Parm))\n\t{\n\t\tFProperty* Prop = *Iterator;\n\t\tOutParamProperties.Add(Prop);\n\t\t++Iterator;\n\t}\n}\n\nbool FGESHandler::FunctionHasValidParams(UFunction* Function, FFieldClass* ClassType, const FGESEmitContext& EmitData, const FGESEventListener& Listener)\n{\n\tif (FirstParamIsSubclassOf(Function, ClassType))\n\t{\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\tUE_LOG(LogTemp, Warning, TEXT(\"FGESHandler::EmitEvent %s skipped listener %s due to function not having a matching %s signature.\"),\n\t\t\t*EmitEventLogString(EmitData),\n\t\t\t*ListenerLogString(Listener),\n\t\t\t*ClassType->GetName());\n\t\treturn false;\n\t}\n}\n\nTSharedPtr<FGESHandler> FGESHandler::DefaultHandler()\n{\n\treturn FGESHandler::PrivateDefaultHandler;\n}\n\nvoid FGESHandler::CreateEvent(const FString& Domain, const FString& Event, bool bPinned /*= false*/)\n{\n\tFGESEvent CreatedFunction;\n\tCreatedFunction.Domain = Domain;\n\tCreatedFunction.Event = Event;\n\tCreatedFunction.bPinned = bPinned;\n\tEventMap.Add(Key(Domain, Event), CreatedFunction);\n}\n\nvoid FGESHandler::DeleteEvent(const FString& Domain, const FString& Event)\n{\n\tDeleteEvent(Key(Domain, Event));\n}\n\nvoid FGESHandler::DeleteEvent(const FString& DomainAndEvent)\n{\n\t//ensure any pinned data gets cleaned up on event deletion\n\tif (EventMap.Contains(DomainAndEvent))\n\t{\n\t\tFGESEvent& Event = EventMap[DomainAndEvent];\n\t\tif (Event.bPinned)\n\t\t{\n\t\t\tEvent.PinnedData.CleanupPinnedData();\n\t\t}\n\t}\n\n\t//remove the event\n\tEventMap.Remove(DomainAndEvent);\n}\n\nbool FGESHandler::HasEvent(const FString& Domain, const FString& Event)\n{\n\treturn EventMap.Contains(Key(Domain, Event));\n}\n\nvoid FGESHandler::UnpinEvent(const FString& Domain, const FString& EventName)\n{\n\tFString KeyString = Key(Domain, EventName);\n\tif (EventMap.Contains(KeyString))\n\t{\n\t\tFGESEvent& Event = EventMap[KeyString];\n\t\tEvent.bPinned = false;\n\t\t//Event.PinnedData.Property->RemoveFromRoot();\n\t\t//Event.PinnedData.PropertyData.Empty();  not sure if safe to delete instead of rebuilding on next pin\n\t}\n}\n\nvoid FGESHandler::AddListener(const FString& Domain, const FString& EventName, const FGESEventListener& Listener)\n{\n\tFString KeyString = Key(Domain, EventName);\n\n\t//Create event if not already created\n\tif (!EventMap.Contains(KeyString))\n\t{\n\t\tCreateEvent(Domain, EventName);\n\t}\n\n\t//Check passed listener validity\n\tif (Listener.IsValidListener())\n\t{\n\t\t//Actually add this valid listener to map\n\t\tFGESEvent& Event = EventMap[KeyString];\n\t\tEvent.Listeners.Add(Listener);\n\n\t\t//TODO: check receivermap logic\n\t\tFGESEventListenerWithContext ListenContext;\n\t\tListenContext.Domain = Domain;\n\t\tListenContext.Event = EventName;\n\t\tFGESMinimalEventListener Minimal;\n\t\tMinimal.FunctionName = Listener.FunctionName;\n\t\tMinimal.ReceiverWCO = Listener.ReceiverWCO;\n\t\tListenContext.Listener = Minimal;\n\n\t\tif (!ReceiverMap.Contains(Listener.ReceiverWCO.Get()))\n\t\t{\n\t\t\tTArray<FGESEventListenerWithContext> Array;\n\t\t\tReceiverMap.Add(Listener.ReceiverWCO.Get(), Array);\n\t\t}\n\t\tReceiverMap[Listener.ReceiverWCO.Get()].Add(ListenContext);\n\n\t\t//if it's pinned re-emit it immediately to this listener\n\t\tif (Event.bPinned) \n\t\t{\n\t\t\tFGESPropertyEmitContext EmitData;\n\t\t\t\n\t\t\tEmitData.Domain = Domain;\n\t\t\tEmitData.Event = EventName;\n\n\t\t\tEmitData.Property = Event.PinnedData.Property;\n\t\t\tEmitData.PropertyPtr = Event.PinnedData.PropertyPtr;\n\t\t\tEmitData.bPinned = Event.bPinned;\n\t\t\tEmitData.SpecificTarget = (FGESEventListener*)&Listener;\t//this immediate call should only be calling our listener\n\t\t\tEmitData.WorldContext = Event.WorldContext;\n\t\t\t\n\t\t\t//did we fail to emit?\n\t\t\tif (!EmitPropertyEvent(EmitData))\n\t\t\t{\n\t\t\t\t//did the event get removed due to being stale? The listener may still be valid so re-run this add listener loop\n\t\t\t\tif (!HasEvent(Domain, EventName))\n\t\t\t\t{\n\t\t\t\t\tAddListener(Domain, EventName, Listener);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\t//NB: validity can be violated due to delegate and lambda too\n\t\t//TODO: add warnings in case of invalid delegate/lambda function binds\n\n\t\t//Not valid, emit warnings\n\t\tif (Listener.ReceiverWCO->IsValidLowLevelFast())\n\t\t{\n\t\t\tUE_LOG(LogTemp, Warning, TEXT(\"FGESHandler::AddListener Warning: \\n%s does not have the function '%s'. Attempted to bind to GESEvent %s.%s\"), *Listener.ReceiverWCO->GetFullName(), *Listener.FunctionName, *Domain, *EventName);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tUE_LOG(LogTemp, Warning, TEXT(\"FGESHandler::AddListener: (invalid object) does not have the function '%s'. Attempted to bind to GESEvent %s.%s\"), *Listener.FunctionName, *Domain, *EventName);\n\t\t}\n\t}\n}\n\nFString FGESHandler::AddLambdaListener(FGESEventContext Context, TFunction<void(const FGESWildcardProperty&)> ReceivingLambda)\n{\n\tif (Context.WorldContext == nullptr)\n\t{\n\t\tUE_LOG(LogTemp, Warning, TEXT(\"FGESHandler::AddLambdaListener No valid world context provided. Not added.\"));\n\t\treturn TEXT(\"Invalid\");\n\t}\n\tFGESEventListener Listener;\n\tListener.bIsBoundToLambda = true;\n\tListener.LambdaFunction = ReceivingLambda;\n\tListener.ReceiverWCO = Context.WorldContext;\n\n\t//name is derived from WCO + lambda pointer address\n\tFString FunctionPtr = FString::Printf(TEXT(\"%d\"), (void*)&ReceivingLambda);\n\tListener.FunctionName = Listener.ReceiverWCO->GetName() + TEXT(\".lambda.\") + FunctionPtr;\n\n\tAddListener(Context.Domain, Context.Event, Listener);\n\n\treturn Listener.FunctionName;\n}\n\nFString FGESHandler::AddLambdaListener(FGESEventContext BindInfo, TFunction<void(UStruct* Struct, void* StructPtr)> ReceivingLambda)\n{\n\treturn AddLambdaListener(BindInfo,\n\t\t[ReceivingLambda](const FGESWildcardProperty& Data)\n\t\t{\n\t\t\tFStructProperty* StructProperty = CastField<FStructProperty>(Data.Property.Get());\n\n\t\t\tif (!StructProperty)\n\t\t\t{\n\t\t\t\tUE_LOG(LogTemp, Warning, TEXT(\"FGESHandler::AddLambdaListener callback: Expected a property structure, received %s; Receive skipped.\"), *Data.Property->GetName());\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tReceivingLambda(StructProperty->Struct, Data.PropertyPtr);\n\t\t});\n}\n\nFString FGESHandler::AddLambdaListener(FGESEventContext BindInfo, TFunction<void(const FString&)> ReceivingLambda)\n{\n\treturn AddLambdaListener(BindInfo,\n\t\t[ReceivingLambda](const FGESWildcardProperty& Data)\n\t\t{\n\t\t\tFString Value;\n\t\t\tUGlobalEventSystemBPLibrary::Conv_PropToStringRef(Data, Value);\n\t\t\tReceivingLambda(Value);\n\t\t});\n}\n\nFString FGESHandler::AddLambdaListener(FGESEventContext BindInfo, TFunction<void(UObject*)> ReceivingLambda)\n{\n\treturn AddLambdaListener(BindInfo,\n\t\t[ReceivingLambda](const FGESWildcardProperty& Data)\n\t\t{\n\t\t\tUObject* Value = 0;\n\t\t\tUGlobalEventSystemBPLibrary::Conv_PropToObject(Data, Value);\n\t\t\tReceivingLambda(Value);\n\t\t});\n}\n\nFString FGESHandler::AddLambdaListener(FGESEventContext BindInfo, TFunction<void(float)> ReceivingLambda)\n{\n\treturn AddLambdaListener(BindInfo,\n\t\t[ReceivingLambda](const FGESWildcardProperty& Data)\n\t\t{\n\t\t\tfloat Value = 0;\n\t\t\tUGlobalEventSystemBPLibrary::Conv_PropToFloat(Data, Value);\n\t\t\tReceivingLambda(Value);\n\t\t});\n}\n\nFString FGESHandler::AddLambdaListener(FGESEventContext BindInfo, TFunction<void(const FName&)> ReceivingLambda)\n{\n\treturn AddLambdaListener(BindInfo,\n\t\t[ReceivingLambda](const FGESWildcardProperty& Data)\n\t\t{\n\t\t\tFName Value;\n\t\t\tUGlobalEventSystemBPLibrary::Conv_PropToName(Data, Value);\n\t\t\tReceivingLambda(Value);\n\t\t});\n}\n\nFString FGESHandler::AddLambdaListener(FGESEventContext BindInfo, TFunction<void(void)> ReceivingLambda)\n{\n\treturn AddLambdaListener(BindInfo,\n\t\t[ReceivingLambda](const FGESWildcardProperty& Data)\n\t\t{\n\t\t\tReceivingLambda();\n\t\t});\n}\n\nFString FGESHandler::AddLambdaListenerInt(FGESEventContext EventInfo, TFunction<void(int32)> ReceivingLambda)\n{\n\treturn AddLambdaListener(EventInfo,\n\t\t[ReceivingLambda](const FGESWildcardProperty& Data)\n\t\t{\n\t\t\tint32 Value = 0;\n\t\t\tUGlobalEventSystemBPLibrary::Conv_PropToInt(Data, Value);\n\n\t\t\tReceivingLambda(Value);\n\t\t});\n}\n\nFString FGESHandler::AddLambdaListenerBool(FGESEventContext EventInfo, TFunction<void(bool)> ReceivingLambda)\n{\n\treturn AddLambdaListener(EventInfo,\n\t\t[ReceivingLambda](const FGESWildcardProperty& Data)\n\t\t{\n\t\t\tbool Value = false;\n\t\t\tUGlobalEventSystemBPLibrary::Conv_PropToBool(Data, Value);\n\t\t\tReceivingLambda(Value);\n\t\t});\n}\n\nvoid FGESHandler::RemoveListener(const FString& Domain, const FString& Event, const FGESEventListener& Listener)\n{\n\tFString KeyString = Key(Domain, Event);\n\tif (!EventMap.Contains(KeyString))\n\t{\n\t\tif (Options.bLogStaleRemovals)\n\t\t{\n\t\t\tUE_LOG(LogTemp, Warning, TEXT(\"FGESHandler::RemoveListener, tried to remove a listener from an event that doesn't exist (%s.%s). Ignored.\"), *Domain, *Event);\n\t\t}\n\t\treturn;\n\t}\n\n\t//Remove from main listener map\n\tEventMap[KeyString].Listeners.Remove(Listener);\n\n\t//Remove matched entry in receiver map\n\tif (ReceiverMap.Contains(Listener.ReceiverWCO.Get()))\n\t{\n\t\tFGESEventListenerWithContext ContextListener;\n\t\tContextListener.Domain = Domain;\n\t\tContextListener.Event = Event;\n\t\tContextListener.Listener.FunctionName = Listener.FunctionName;\n\t\tContextListener.Listener.ReceiverWCO = Listener.ReceiverWCO;\n\t\tReceiverMap[Listener.ReceiverWCO.Get()].Remove(ContextListener);\n\t}\n}\n\nvoid FGESHandler::RemoveAllListenersForReceiver(UObject* ReceiverWCO)\n{\n\tif (!ReceiverMap.Contains(ReceiverWCO))\n\t{\n\t\tUE_LOG(LogTemp, Warning, TEXT(\"FGESHandler::RemoveAllListenersForReceiver, tried to remove listeners from an WCO that doesn't exist. Ignored.\"));\n\t\treturn;\n\t}\n\n\t//Copy array so we can loop over\n\tTArray<FGESEventListenerWithContext> ReceiverArray = ReceiverMap[ReceiverWCO];\n\t\n\tfor (FGESEventListenerWithContext& ListenContext: ReceiverArray)\n\t{\n\t\tRemoveListener(ListenContext.Domain, ListenContext.Event, FGESEventListener(ListenContext.Listener));\n\t}\n\n\tReceiverMap.Remove(ReceiverWCO);\n}\n\nvoid FGESHandler::RemoveLambdaListener(FGESEventContext BindInfo, TFunction<void(const FGESWildcardProperty&)> ReceivingLambda)\n{\n\tFGESEventListener Listener;\n\tListener.bIsBoundToLambda = true;\n\tListener.LambdaFunction = ReceivingLambda;\n\tListener.ReceiverWCO = BindInfo.WorldContext;\n\n\tFString FunctionPtr = FString::Printf(TEXT(\"%d\"), (void*)&ReceivingLambda);\n\tListener.FunctionName = Listener.ReceiverWCO->GetName() + TEXT(\".lambda.\") + FunctionPtr;\n\n\tRemoveListener(BindInfo.Domain, BindInfo.Event, Listener);\n}\n\nvoid FGESHandler::RemoveLambdaListener(FGESEventContext BindInfo, const FString& LambdaName)\n{\n\tFGESEventListener Listener;\n\tListener.bIsBoundToLambda = true;\n\tListener.ReceiverWCO = BindInfo.WorldContext;\n\tListener.FunctionName = LambdaName;\n\n\tRemoveListener(BindInfo.Domain, BindInfo.Event, Listener);\n}\n\nvoid FGESHandler::EmitToListenersWithData(const FGESPropertyEmitContext& EmitData, TFunction<void(const FGESEventListener&)> DataFillCallback)\n{\n\tFString KeyString = Key(EmitData.Domain, EmitData.Event);\n\tif (!EventMap.Contains(KeyString))\n\t{\n\t\tCreateEvent(EmitData.Domain, EmitData.Event, false);\n\t}\n\tFGESEvent& Event = EventMap[KeyString];\n\tEvent.WorldContext = EmitData.WorldContext;\n\n\tif (EmitData.WorldContext == nullptr)\n\t{\n\t\tUE_LOG(LogTemp, Error, TEXT(\"FGESHandler::EmitToListenersWithData: Emitted event has no world context!\"));\n\t\treturn;\n\t}\n\n\tUWorld* World = EmitData.WorldContext->GetWorld();\n\tif (!World->IsValidLowLevelFast())\n\t{\n\t\tUE_LOG(LogTemp, Error, TEXT(\"FGESHandler::EmitToListenersWithData: Emitted event has no world!\"));\n\t\treturn;\n\t}\n\n\t//Attach a world listener to each unique world\n\tif (!WorldMap.Contains(World))\n\t{\n\t\tAGESWorldListenerActor* WorldListener = World->SpawnActor<AGESWorldListenerActor>();\n\t\tWorldListener->OnEndPlay = [this, WorldListener, World]\n\t\t{\n\t\t\tfor (const FString& EventKey : WorldListener->WorldEvents)\n\t\t\t{\n\t\t\t\tDeleteEvent(EventKey);\n\t\t\t}\n\t\t\tWorldListener->WorldEvents.Empty();\n\n\t\t\t//For now always clear receiver map if any world ends\n\t\t\tReceiverMap.Empty();\n\n\t\t\tWorldMap.Remove(World);\n\t\t};\n\t\tWorldMap.Add(World, WorldListener);\n\t}\n\n\t//ensure this event is registered\n\tWorldMap[World]->WorldEvents.Add(KeyString);\n\n\t//is there a property to pin?\n\tif (EmitData.Property)\n\t{\n\t\t//Warn if we're trying to pin a new event without unpinning old one\n\t\tif (Event.bPinned && EmitData.bPinned)\n\t\t{\n\t\t\t//cleanup if different\n\t\t\tif (EmitData.Property != Event.PinnedData.Property ||\n\t\t\t\tEmitData.PropertyPtr != Event.PinnedData.PropertyPtr)\n\t\t\t{\n\t\t\t\tEvent.PinnedData.CleanupPinnedData();\n\t\t\t}\n\t\t\t\n\t\t\tEvent.PinnedData.bHandlePropertyDeletion = EmitData.bHandleAllocation;\n\t\t\tEvent.PinnedData.Property = EmitData.Property;\n\n\t\t\t//only copy if ptrs are different or nullptr\n\t\t\tif (EmitData.PropertyPtr != Event.PinnedData.PropertyPtr || \n\t\t\t\tEvent.PinnedData.PropertyPtr == nullptr)\n\t\t\t{\n\t\t\t\tEvent.PinnedData.PropertyPtr = EmitData.PropertyPtr;\n\t\t\t\tEvent.PinnedData.CopyPropertyToPinnedBuffer();\n\t\t\t}\n\t\t\n\t\t\t//UE_LOG(LogTemp, Warning, TEXT(\"FGESHandler::EmitToListenersWithData Emitted a pinned event to an already pinned event. Pinned data updated.\"));\n\t\t}\n\t\tif (!Event.bPinned && EmitData.bPinned)\n\t\t{\n\t\t\tEvent.PinnedData.CleanupPinnedData();\n\t\t\tEvent.PinnedData.bHandlePropertyDeletion = EmitData.bHandleAllocation;\n\t\t\tEvent.PinnedData.Property = EmitData.Property;\n\t\t\tEvent.PinnedData.PropertyPtr = EmitData.PropertyPtr;\n\t\t\tEvent.PinnedData.CopyPropertyToPinnedBuffer();\n\t\t}\n\t}\n\tEvent.bPinned = EmitData.bPinned;\n\n\n\t//only emit to this target\n\tif (EmitData.SpecificTarget)\n\t{\n\t\tFGESEventListener Listener = *EmitData.SpecificTarget;\n\n\t\t//stale listener, remove it\n\t\tif (!Listener.ReceiverWCO->IsValidLowLevelFast())\n\t\t{\n\t\t\tRemovalArray.Add(&Listener);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//potential issue: this opt bypasses specialization via datafillcallback\n\t\t\tEmitToListenerWithData(EmitData, Listener, DataFillCallback);\n\t\t}\n\t}\n\t//emit to all targets\n\telse\n\t{\n\t\tfor (FGESEventListener& Listener : Event.Listeners)\n\t\t{\n\t\t\t//stale listener, remove it\n\t\t\tif (!Listener.ReceiverWCO->IsValidLowLevelFast())\n\t\t\t{\n\t\t\t\tRemovalArray.Add(&Listener);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t//potential issue: this opt bypasses specialization via datafillcallback\n\t\t\t\tEmitToListenerWithData(EmitData, Listener, DataFillCallback);\n\t\t\t}\n\t\t}\n\t}\n\n\t//Go through stale listeners and remove them\n\tif (RemovalArray.Num() > 0)\n\t{\n\t\tfor (int i = 0; i < RemovalArray.Num(); i++)\n\t\t{\n\t\t\tFGESEventListener Listener = *RemovalArray[i];\n\t\t\tEvent.Listeners.Remove(Listener);\n\t\t}\n\t\tif (Options.bLogStaleRemovals)\n\t\t{\n\t\t\tUE_LOG(LogTemp, Log, TEXT(\"FGESHandler::EmitEvent: auto-removed %d stale listeners.\"), RemovalArray.Num());\n\t\t}\n\t\tRemovalArray.Empty();\n\t}\n}\n\nbool FGESHandler::EmitToListenerWithData(const FGESPropertyEmitContext& EmitData, const FGESEventListener& Listener, TFunction<void(const FGESEventListener&)>& DataFillCallback)\n{\n\tif (Listener.ReceiverWCO->IsValidLowLevelFast())\n\t{\n\t\tif (Listener.bIsBoundToLambda && Listener.LambdaFunction != nullptr)\n\t\t{\n\t\t\t//Opt1) this listener is handled by lambda\n\t\t\tFGESWildcardProperty Wrapper;\n\t\t\tWrapper.Property = EmitData.Property;\n\t\t\tWrapper.PropertyPtr = EmitData.PropertyPtr;\n\n\t\t\tListener.LambdaFunction(Wrapper);\n\t\t\treturn true;\n\t\t}\n\t\tif (Listener.bIsBoundToDelegate)\n\t\t{\n\t\t\t//Opt2) this listener is handled by wildcard event delegate\n\t\t\tFGESWildcardProperty Wrapper;\n\t\t\tWrapper.Property = EmitData.Property;\n\t\t\tWrapper.PropertyPtr = EmitData.PropertyPtr;\n\t\t\tListener.OnePropertyFunctionDelegate.ExecuteIfBound(Wrapper);\n\t\t\treturn true;\n\t\t}\n\n\t\tUFunction* BPFunction = Listener.ReceiverWCO->FindFunction(FName(*Listener.FunctionName));\n\t\tif (BPFunction != nullptr)\n\t\t{\n\t\t\t//Opt3) listener is handled by function bind by name\n\t\t\tDataFillCallback(Listener);\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tUE_LOG(LogTemp, Warning, TEXT(\"FGESHandler::EmitEvent: Function not found '%s'\"), *Listener.FunctionName);\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn false;\n}\n\nvoid FGESHandler::EmitEvent(const FGESEmitContext& EmitData, UStruct* Struct, void* StructPtr)\n{\n\tbool bValidateStructs = Options.bValidateStructTypes;\n\tFGESPropertyEmitContext PropData(EmitData);\n\tUClass* Class = EmitData.WorldContext->GetClass();\n\n\tFField* OldProperty = Class->ChildProperties;\n\n\tFStructProperty* StructProperty = new FStructProperty(FFieldVariant(Class), TEXT(\"StructProperty\"), RF_NoFlags);\n\tStructProperty->Struct = (UScriptStruct*)Struct;\n\tStructProperty->ElementSize = Struct->GetStructureSize();\n\n\t//undo what we just did so it won't be traversed because of init\n\tClass->ChildProperties = OldProperty;\n\n\t//Store our struct data in a buffer we can reference\n\tTArray<uint8> Buffer;\n\tint32 Size = Struct->GetStructureSize();\n\tBuffer.SetNum(Size);\n\n\t//StructProperty->CopyCompleteValue(Buffer.GetData(), StructPtr);\n\tFPlatformMemory::Memcpy(Buffer.GetData(), StructPtr, Size);\n\n\tPropData.Property = StructProperty;\n\tPropData.PropertyPtr = Buffer.GetData();\n\tif (PropData.bPinned)\n\t{\n\t\tPropData.bHandleAllocation = true;\n\t}\n\n\tEmitToListenersWithData(PropData, [&PropData, &Struct, &Buffer, bValidateStructs](const FGESEventListener& Listener)\n\t{\n\t\tUE_LOG(LogTemp, Warning, TEXT(\"FGESHandler::EmitEvent struct Emit called\"));\n\n\t\tif (FunctionHasValidParams(Listener.Function, FStructProperty::StaticClass(), PropData, Listener))\n\t\t{\n\t\t\tif (bValidateStructs)\n\t\t\t{\n\t\t\t\tUE_LOG(LogTemp, Warning, TEXT(\"Validation emit\"));\n\n\t\t\t\t//For structs we can have different mismatching structs at this point check class types\n\t\t\t\t//optimization note: unroll the above function for structs to avoid double param lookup\n\t\t\t\tTArray<FProperty*> Properties;\n\t\t\t\tFunctionParameters(Listener.Function, Properties);\n\t\t\t\tFStructProperty* SubStructProperty = CastField<FStructProperty>(Properties[0]);\n\t\t\t\tif (SubStructProperty->Struct == Struct)\n\t\t\t\t{\n\t\t\t\t\tListener.ReceiverWCO->ProcessEvent(Listener.Function, PropData.PropertyPtr);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tUE_LOG(LogTemp, Warning, TEXT(\"FGESHandler::EmitEvent %s skipped listener %s due to function not having a matching Struct type %s signature.\"),\n\t\t\t\t\t\t*EmitEventLogString(PropData),\n\t\t\t\t\t\t*ListenerLogString(Listener),\n\t\t\t\t\t\t*Struct->GetName());\n\t\t\t\t}\n\t\t\t}\n\t\t\t//No validation, e.g. vector-> rotator fill is accepted\n\t\t\telse\n\t\t\t{\n\t\t\t\tUE_LOG(LogTemp, Warning, TEXT(\"No validation emit\"));\n\t\t\t\tListener.ReceiverWCO->ProcessEvent(Listener.Function, (void*)Buffer.GetData()); //PropData.PropertyPtr); //\n\t\t\t}\n\t\t}\n\t});\n\n\n\tif (!EmitData.bPinned)\n\t{\n\t\tdelete StructProperty;\n\t}\n}\n\nvoid FGESHandler::EmitEvent(const FGESEmitContext& EmitData, const FString& ParamData)\n{\n\tFGESPropertyEmitContext PropData(EmitData);\n\n\t//We have no property context, make a new property\n\tFStrProperty* StrProperty = \n\t\tnew FStrProperty(FFieldVariant(EmitData.WorldContext->GetClass()),\n\t\t\tTEXT(\"StringValue\"),\n\t\t\tEObjectFlags::RF_Public | EObjectFlags::RF_LoadCompleted);\n\n\t//Wrap our FString into a buffer we can share\n\tTArray<uint8> Buffer;\n\tBuffer.SetNum(ParamData.GetAllocatedSize());\n\n\tStrProperty->SetPropertyValue_InContainer(Buffer.GetData(), ParamData);\n\n\tPropData.Property = StrProperty;\n\tPropData.PropertyPtr = Buffer.GetData();\n\tif (PropData.bPinned)\n\t{\n\t\tPropData.bHandleAllocation = true;\n\t}\n\n\tEmitToListenersWithData(PropData, [&PropData, ParamData](const FGESEventListener& Listener)\n\t{\n\t\tif (FunctionHasValidParams(Listener.Function, FStrProperty::StaticClass(), PropData, Listener))\n\t\t{\n\t\t\tListener.ReceiverWCO->ProcessEvent(Listener.Function, PropData.PropertyPtr);// (void*)*MutableString); // (void*)&ParamData);\n\t\t}\n\t});\n\n\tif (!EmitData.bPinned)\n\t{\n\t\tdelete StrProperty;\n\t}\n}\n\nvoid FGESHandler::EmitEvent(const FGESEmitContext& EmitData, UObject* ParamData)\n{\n\tFGESPropertyEmitContext PropData(EmitData);\n\n\tFObjectProperty* ObjectProperty =\n\t\tnew FObjectProperty(FFieldVariant(EmitData.WorldContext->GetClass()),\n\t\t\tTEXT(\"ObjectValue\"),\n\t\t\tEObjectFlags::RF_Public | EObjectFlags::RF_LoadCompleted);\n\n\t//wrapper required to avoid copied pointer to become the first function\n\tFGESDynamicArg ParamWrapper;\n\tParamWrapper.Arg01 = ParamData;\n\n\tPropData.Property = ObjectProperty;\n\tPropData.PropertyPtr = (void*)&ParamWrapper;\n\t\n\tif (PropData.bPinned)\n\t{\n\t\tPropData.bHandleAllocation = true;\n\t}\n\n\tEmitToListenersWithData(PropData, [&PropData, ParamWrapper](const FGESEventListener& Listener)\n\t{\n\t\tif (FunctionHasValidParams(Listener.Function, FObjectProperty::StaticClass(), PropData, Listener))\n\t\t{\n\t\t\tListener.ReceiverWCO->ProcessEvent(Listener.Function, (void*)&ParamWrapper);// PropData.PropertyPtr);\n\t\t}\n\t});\n\n\tif (!EmitData.bPinned)\n\t{\n\t\tdelete ObjectProperty;\n\t}\n}\n\nvoid FGESHandler::EmitEvent(const FGESEmitContext& EmitData, float ParamData)\n{\n\tFGESPropertyEmitContext PropData(EmitData);\n\n\tFGESWildcardProperty WrapperProperty;\n\n\tFFloatProperty* FloatProperty =\n\t\tnew FFloatProperty(FFieldVariant(EmitData.WorldContext->GetClass()),//WrapperProperty),\n\t\t\tTEXT(\"FloatValue\"),\n\t\t\tEObjectFlags::RF_Public | EObjectFlags::RF_LoadCompleted);\n\n\tPropData.Property = FloatProperty;\n\tPropData.PropertyPtr = &ParamData;// Buffer.GetData();\n\tif (PropData.bPinned)\n\t{\n\t\tPropData.bHandleAllocation = true;\n\t}\n\n\tEmitToListenersWithData(PropData, [&PropData, &ParamData](const FGESEventListener& Listener)\n\t{\n\t\tif (FunctionHasValidParams(Listener.Function, FNumericProperty::StaticClass(), PropData, Listener))\n\t\t{\n\t\t\tListener.ReceiverWCO->ProcessEvent(Listener.Function, PropData.PropertyPtr);// PropData.PropertyPtr);\n\t\t}\n\t});\n\n\tif (!EmitData.bPinned)\n\t{\n\t\tdelete FloatProperty;\n\t}\n}\n\nvoid FGESHandler::EmitEvent(const FGESEmitContext& EmitData, int32 ParamData)\n{\n\tFGESPropertyEmitContext PropData(EmitData);\n\n\tFIntProperty* IntProperty =\n\t\tnew FIntProperty(FFieldVariant(EmitData.WorldContext->GetClass()),\n\t\t\tTEXT(\"IntValue\"),\n\t\t\tEObjectFlags::RF_Public | EObjectFlags::RF_LoadCompleted);\n\n\tPropData.Property = IntProperty;\n\tPropData.PropertyPtr = &ParamData;\n\tif (PropData.bPinned)\n\t{\n\t\tPropData.bHandleAllocation = true;\n\t}\n\n\tEmitToListenersWithData(PropData, [&PropData](const FGESEventListener& Listener)\n\t{\n\t\tif (FunctionHasValidParams(Listener.Function, FNumericProperty::StaticClass(), PropData, Listener))\n\t\t{\n\t\t\tListener.ReceiverWCO->ProcessEvent(Listener.Function, PropData.PropertyPtr);\n\t\t}\n\t});\n\n\tif (!EmitData.bPinned)\n\t{\n\t\tdelete IntProperty;\n\t}\n}\n\nvoid FGESHandler::EmitEvent(const FGESEmitContext& EmitData, bool ParamData)\n{\n\tFGESPropertyEmitContext PropData(EmitData);\n\n\tFBoolProperty* BoolProperty =\n\t\tnew FBoolProperty(FFieldVariant(EmitData.WorldContext->GetClass()),\n\t\t\tTEXT(\"BoolValue\"),\n\t\t\tEObjectFlags::RF_Public | EObjectFlags::RF_LoadCompleted);\n\n\tPropData.Property = BoolProperty;\n\tPropData.PropertyPtr = &ParamData;\n\tif (PropData.bPinned)\n\t{\n\t\tPropData.bHandleAllocation = true;\n\t}\n\n\tEmitToListenersWithData(PropData, [&PropData](const FGESEventListener& Listener)\n\t{\n\t\tif (FunctionHasValidParams(Listener.Function, FBoolProperty::StaticClass(), PropData, Listener))\n\t\t{\n\t\t\tListener.ReceiverWCO->ProcessEvent(Listener.Function, PropData.PropertyPtr);\n\t\t}\n\t});\n\n\tif (!EmitData.bPinned)\n\t{\n\t\tdelete BoolProperty;\n\t}\n}\n\nvoid FGESHandler::EmitEvent(const FGESEmitContext& EmitData, const FName& ParamData)\n{\n\tFGESPropertyEmitContext PropData(EmitData);\n\n\t//We have no property context, make a new property\n\tFNameProperty* NameProperty =\n\t\tnew FNameProperty(FFieldVariant(EmitData.WorldContext->GetClass()),\n\t\t\tTEXT(\"NameValue\"),\n\t\t\tEObjectFlags::RF_Public | EObjectFlags::RF_LoadCompleted);\n\n\t//Wrap our FName into a buffer we can share\n\tTArray<uint8> Buffer;\n\tBuffer.SetNum(ParamData.StringBufferSize);\n\n\tNameProperty->SetPropertyValue_InContainer(Buffer.GetData(), ParamData);\n\n\tPropData.Property = NameProperty;\n\tPropData.PropertyPtr = Buffer.GetData();\n\tif (PropData.bPinned)\n\t{\n\t\tPropData.bHandleAllocation = true;\n\t}\n\n\tEmitToListenersWithData(PropData, [&PropData, ParamData](const FGESEventListener& Listener)\n\t\t{\n\t\t\tif (FunctionHasValidParams(Listener.Function, FStrProperty::StaticClass(), PropData, Listener))\n\t\t\t{\n\t\t\t\tListener.ReceiverWCO->ProcessEvent(Listener.Function, PropData.PropertyPtr);\n\t\t\t}\n\t\t});\n\n\tif (!EmitData.bPinned)\n\t{\n\t\tdelete NameProperty;\n\t}\n}\n\nbool FGESHandler::EmitEvent(const FGESEmitContext& EmitData)\n{\n\tFGESPropertyEmitContext FullEmitData(EmitData);\n\n\t//No param version\n\treturn EmitPropertyEvent(FullEmitData);\n}\n\nvoid FGESHandler::EmitEvent(const FGESEmitContext& EmitData, const GES_RAW_TEXT RawStringMessage)\n{\n\tEmitEvent(EmitData, FString(RawStringMessage));\n}\n\nbool FGESHandler::EmitPropertyEvent(const FGESPropertyEmitContext& EmitData)\n{\n\t//UE_LOG(LogTemp, Log, TEXT(\"World is: %s\"), *EmitData.WorldContext.Get()->GetName());\n\n\tif (!EmitData.WorldContext || !EmitData.WorldContext->IsValidLowLevel())\n\t{\n\t\t//Remove this event, it's emit context is invalid\n\t\tDeleteEvent(EmitData.Domain, EmitData.Event);\n\t\tif (Options.bLogStaleRemovals)\n\t\t{\n\t\t\tUE_LOG(LogTemp, Log, TEXT(\"FGESHandler::EmitEvent stale event removed due to invalid world context for <%s.%s>. (Usually due to pinned events that haven't been unpinned)\"),\n\t\t\t\t*EmitData.Domain, *EmitData.Event);\n\t\t}\n\t\treturn false;\n\t}\n\tFProperty* ParameterProp = EmitData.Property;\n\tvoid* PropPtr = EmitData.PropertyPtr;\n\n\t//no params specified\n\tif (ParameterProp == nullptr)\n\t{\n\t\tEmitToListenersWithData(EmitData, [&EmitData](const FGESEventListener& Listener)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\tNever gets called?\n\t\t\t\t//C++ lambda case\n\t\t\t\tif (Listener.bIsBoundToLambda && Listener.LambdaFunction != nullptr)\n\t\t\t\t{\n\t\t\t\t\tFGESWildcardProperty Wrapper;\n\t\t\t\t\tWrapper.Property = EmitData.Property;\n\t\t\t\t\tWrapper.PropertyPtr = EmitData.PropertyPtr;\n\n\t\t\t\t\tListener.LambdaFunction(Wrapper);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t//If the listener bound it to a wildcard event delegate, emit with nullptr\n\t\t\t\tif (Listener.bIsBoundToDelegate)\n\t\t\t\t{\n\t\t\t\t\tFGESWildcardProperty Wrapper;\n\t\t\t\t\tWrapper.Property = EmitData.Property;\n\t\t\t\t\tWrapper.PropertyPtr = EmitData.PropertyPtr;\n\t\t\t\t\tListener.OnePropertyFunctionDelegate.ExecuteIfBound(Wrapper);\n\t\t\t\t\treturn;\n\t\t\t\t}*/\n\n\t\t\t\t//Neither lambda nor wildcard delegate, process no param prop\n\t\t\t\tTFieldIterator<FProperty> Iterator(Listener.Function);\n\n\t\t\t\tTArray<FProperty*> Properties;\n\t\t\t\twhile (Iterator && (Iterator->PropertyFlags & CPF_Parm))\n\t\t\t\t{\n\t\t\t\t\tFProperty* Prop = *Iterator;\n\t\t\t\t\tProperties.Add(Prop);\n\t\t\t\t\t++Iterator;\n\t\t\t\t}\n\t\t\t\tif (Properties.Num() == 0)\n\t\t\t\t{\n\t\t\t\t\tListener.ReceiverWCO->ProcessEvent(Listener.Function, nullptr);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tUE_LOG(LogTemp, Warning, TEXT(\"FGESHandler::EmitEvent %s tried to emit an empty event to %s receiver expecting parameters.\"),\n\t\t\t\t\t\t*EmitData.Event,\n\t\t\t\t\t\t*Listener.ReceiverWCO->GetName());\n\t\t\t\t}\n\t\t\t});\n\t}\n\telse if (ParameterProp->IsA<FStructProperty>())\n\t{\n\t\tEmitSubPropertyEvent(EmitData);\n\t\treturn true;\n\t}\n\telse if (ParameterProp->IsA<FStrProperty>())\n\t{\n\t\tEmitSubPropertyEvent(EmitData);\n\t\treturn true;\n\t}\n\telse if (ParameterProp->IsA<FObjectProperty>())\n\t{\n\t\tEmitSubPropertyEvent(EmitData);\n\t\treturn true;\n\t}\n\telse if (ParameterProp->IsA<FNumericProperty>())\n\t{\n\t\t//todo warn numeric mismatch again (int/float)\n\t\tEmitSubPropertyEvent(EmitData);\n\t\treturn true;\n\t}\n\telse if (ParameterProp->IsA<FBoolProperty>())\n\t{\n\t\tEmitSubPropertyEvent(EmitData);\n\t\treturn true;\n\t}\n\telse if (ParameterProp->IsA<FNameProperty>())\n\t{\n\t\tEmitSubPropertyEvent(EmitData);\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\t//Maps, Array, Sets etc unsupported atm\n\t\tUE_LOG(LogTemp, Warning, TEXT(\"FGESHandler::EmitEvent Unsupported parameter\"));\n\t\treturn false;\n\t}\n\treturn false;\n}\n\nvoid FGESHandler::EmitSubPropertyEvent(const FGESPropertyEmitContext& EmitData)\n{\n\tEmitToListenersWithData(EmitData, [&EmitData](const FGESEventListener& Listener)\n\t{\n\t\tif (FunctionHasValidParams(Listener.Function, EmitData.Property->StaticClass(), EmitData, Listener))\n\t\t{\n\t\t\t/*\n\t\t\tNever gets called?\n\t\t\t//Lambda Bind\n\t\t\tif (Listener.bIsBoundToLambda && Listener.LambdaFunction != nullptr)\n\t\t\t{\n\t\t\t\tFGESWildcardProperty Wrapper;\n\t\t\t\tWrapper.Property = EmitData.Property;\n\t\t\t\tWrapper.PropertyPtr = EmitData.PropertyPtr;\n\n\t\t\t\tListener.LambdaFunction(Wrapper);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t//Delegate Bind\n\t\t\tif (Listener.bIsBoundToDelegate)\n\t\t\t{\n\t\t\t\tFGESWildcardProperty Wrapper;\n\t\t\t\tWrapper.Property = EmitData.Property;\n\t\t\t\tWrapper.PropertyPtr = EmitData.PropertyPtr;\n\t\t\t\tListener.OnePropertyFunctionDelegate.ExecuteIfBound(Wrapper);\n\t\t\t\treturn;\n\t\t\t}*/\n\n\t\t\t//Standard Function Name Bind\n\t\t\tListener.ReceiverWCO->ProcessEvent(Listener.Function, EmitData.PropertyPtr);\n\t\t}\n\t});\n}\n\nvoid FGESHandler::SetOptions(const FGESGlobalOptions& InOptions)\n{\n\tOptions = InOptions;\n}\n\nFString FGESHandler::Key(const FString& Domain, const FString& Event)\n{\n\treturn Domain + TEXT(\".\") + Event;\n}\n\nFGESHandler::FGESHandler()\n{\n\n}\n\nFGESHandler::~FGESHandler()\n{\n\t//Practically not needed due to shutdown happening on program exit\n\t/*for (TPair<FString, FGESEvent> Pair : FunctionMap)\n\t{\n\t\tif (Pair.Value.bPinned)\n\t\t{\n\t\t\tPair.Value.PinnedData.CleanupPinnedData();\n\t\t}\n\t}*/\n\tEventMap.Empty();\n}\n"
  },
  {
    "path": "Source/GlobalEventSystem/Private/GESHandlerDataTypes.cpp",
    "content": "#include \"GESHandlerDataTypes.h\"\n\nvoid FGESPinnedData::CopyPropertyToPinnedBuffer()\n{\n\t//Copy this property data to temp\n\t{\n\t\t//Workaround for our generated struct\n\t\tint32 Num = Property->GetSize();\n\t\t/*if (Property->IsA<FStructProperty>())\n\t\t{\n\t\t\tFStructProperty* StructProp = CastField<FStructProperty>(Property);\n\t\t\tif (StructProp->Struct)\n\t\t\t{\n\t\t\t\tNum = StructProp->Struct->PropertiesSize;\n\t\t\t}\n\t\t}*/\n\t\t\n\t\tPropertyData.SetNumUninitialized(Num);\n\t\tFMemory::Memcpy(PropertyData.GetData(), PropertyPtr, Num);\n\n\t\t//reset pointer to new copy\n\t\tPropertyPtr = PropertyData.GetData();\n\t}\n}\n\nvoid FGESPinnedData::CleanupPinnedData()\n{\n\tPropertyData.Empty();\n\n\t//Some properties are being allocated in C++, we need to clean them here\n\tif (bHandlePropertyDeletion)\n\t{\n\t\tif (Property != nullptr)\n\t\t{\n\t\t\tProperty->SetFlags(RF_BeginDestroyed);\n\t\t}\n\t\tdelete Property;\n\t}\n\tProperty = nullptr;\n\tPropertyPtr = nullptr;\n}\n\nFGESEvent::FGESEvent()\n{\n\tPinnedData = FGESPinnedData();\n}\n\nFGESPropertyEmitContext::FGESPropertyEmitContext()\n{\n\tProperty = nullptr;\n\tPropertyPtr = nullptr;\n\tSpecificTarget = nullptr;\n\tbHandleAllocation = false;\n}\n\nFGESPropertyEmitContext::FGESPropertyEmitContext(const FGESEmitContext& Other)\n{\n\tDomain = Other.Domain;\n\tEvent = Other.Event;\n\tWorldContext = Other.WorldContext;\n\tbPinned = Other.bPinned;\n\n\tProperty = nullptr;\n\tPropertyPtr = nullptr;\n\tSpecificTarget = nullptr;\n}\n\nFGESEvent::FGESEvent(const FGESEmitContext& Other)\n{\n\tDomain = Other.Domain;\n\tEvent = Other.Event;\n\tWorldContext = Other.WorldContext;\n\tbPinned = Other.bPinned;\n}\n\nFGESMinimalEventListener::FGESMinimalEventListener()\n{\n\tReceiverWCO = nullptr;\n\tFunctionName = TEXT(\"\");\n}\n\nFGESEventListener::FGESEventListener()\n{\n\tFGESMinimalEventListener();\n\tFunction = nullptr;\n\tbIsBoundToDelegate = false;\n\tbIsBoundToLambda = false;\n\tLambdaFunction = nullptr;\n}\n\nFGESEventListener::FGESEventListener(const FGESMinimalEventListener& Minimal)\n{\n\tReceiverWCO = Minimal.ReceiverWCO;\n\tFunctionName = Minimal.FunctionName;\n}\n\nbool FGESEventListener::LinkFunction()\n{\n\tFunction = ReceiverWCO->FindFunction(FName(*FunctionName));\n\treturn IsValidListener();\n}\n\nbool FGESEventListener::IsValidListener() const\n{\n\treturn (Function != nullptr || \n\t\tbIsBoundToDelegate ||\n\t\t(bIsBoundToLambda && LambdaFunction != nullptr));\n}\n\n\n"
  },
  {
    "path": "Source/GlobalEventSystem/Private/GESWorldListenerActor.cpp",
    "content": "// Copyright 2019-current Getnamo. All Rights Reserved\n\n\n#include \"GESWorldListenerActor.h\"\n\n// Sets default values\nAGESWorldListenerActor::AGESWorldListenerActor()\n{\n \t// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.\n\tPrimaryActorTick.bCanEverTick = false;\n\tOnEndPlay = nullptr;\n}\n\n// Called when the game starts or when spawned\nvoid AGESWorldListenerActor::BeginPlay()\n{\n\tSuper::BeginPlay();\n\t\n}\n\nvoid AGESWorldListenerActor::EndPlay(const EEndPlayReason::Type EndPlayReason)\n{\n\tOnEndPlay();\n\tSuper::EndPlay(EndPlayReason);\n}\n\n"
  },
  {
    "path": "Source/GlobalEventSystem/Private/GlobalEventSystem.cpp",
    "content": "// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.\n\n#include \"GlobalEventSystem.h\"\n\n#if WITH_EDITOR\n#include \"Editor.h\"\n#include \"GESHandler.h\"\n#endif\n\n#define LOCTEXT_NAMESPACE \"FGlobalEventSystemModule\"\n\nvoid FGlobalEventSystemModule::StartupModule()\n{\n#if WITH_EDITOR\n\tEndPieDelegate = FEditorDelegates::BeginPIE.AddLambda([](bool boolSent)\n\t{\n\t\tUE_LOG(LogTemp, Warning, TEXT(\"Clearing FGESHandler\"));\n\t\tFGESHandler::Clear();\t\t\n\t});\n#endif\n}\n\nvoid FGlobalEventSystemModule::ShutdownModule()\n{\n#if WITH_EDITOR\n\tFEditorDelegates::EndPIE.Remove(EndPieDelegate);\n#endif\n}\n\n#undef LOCTEXT_NAMESPACE\n\t\nIMPLEMENT_MODULE(FGlobalEventSystemModule, GlobalEventSystem)"
  },
  {
    "path": "Source/GlobalEventSystem/Private/GlobalEventSystemBPLibrary.cpp",
    "content": "// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.\n\n#include \"GlobalEventSystemBPLibrary.h\"\n#include \"GlobalEventSystem.h\"\n\nUGlobalEventSystemBPLibrary::UGlobalEventSystemBPLibrary(const FObjectInitializer& ObjectInitializer)\n: Super(ObjectInitializer)\n{\n}\n\nvoid UGlobalEventSystemBPLibrary::GESUnbindEvent(UObject* WorldContextObject, const FString& Domain /*= TEXT(\"global.default\")*/, const FString& Event /*= TEXT(\"\")*/, const FString& ReceivingFunction /*= TEXT(\"\")*/)\n{\n\tFGESEventListener Listener;\n\tListener.ReceiverWCO = WorldContextObject;\n\tListener.FunctionName = ReceivingFunction;\n\n\tFGESHandler::DefaultHandler()->RemoveListener(Domain, Event, Listener);\n}\n\nvoid UGlobalEventSystemBPLibrary::GESUnbindTagEvent(UObject* WorldContextObject, FGameplayTag Tag, const FString& ReceivingFunction /*= TEXT(\"\")*/)\n{\n\tFString Domain;\n\tFString Event;\n\tConv_TagToDomainAndEvent(Tag, Domain, Event);\n\tGESUnbindEvent(WorldContextObject, Domain, Event, ReceivingFunction);\n}\n\nvoid UGlobalEventSystemBPLibrary::GESUnbindAllEventsForContext(UObject* WorldContextObject, UObject* Context /*= nullptr*/)\n{\n\tif (Context == nullptr)\n\t{\n\t\tContext = WorldContextObject;\n\t}\n\tFGESHandler::DefaultHandler()->RemoveAllListenersForReceiver(Context);\n}\n\nvoid UGlobalEventSystemBPLibrary::GESUnbindDelegate(UObject* WorldContextObject, const FGESOnePropertySignature& ReceivingFunction, const FString& Domain /*= TEXT(\"global.default\")*/, const FString& Event /*= TEXT(\"\")*/)\n{\n\tFGESEventListener Listener;\n\tListener.ReceiverWCO = WorldContextObject;\n\n\tif (ReceivingFunction.GetUObject()->IsValidLowLevelFast())\n\t{\n\t\tListener.FunctionName = WorldContextObject->GetName() + ReceivingFunction.GetUObject()->GetName();\n\t}\n\telse\n\t{\n\t\tListener.FunctionName = WorldContextObject->GetName() + TEXT(\".UnboundDelegate\");\n\t}\n\tListener.OnePropertyFunctionDelegate = ReceivingFunction;\n\tListener.bIsBoundToDelegate = true;\n\n\tFGESHandler::DefaultHandler()->RemoveListener(Domain, Event, Listener);\n}\n\nvoid UGlobalEventSystemBPLibrary::GESUnbindTagDelegate(UObject* WorldContextObject, FGameplayTag Tag, const FGESOnePropertySignature& ReceivingFunction)\n{\n\tFString Domain;\n\tFString Event;\n\tConv_TagToDomainAndEvent(Tag, Domain, Event);\n\tGESUnbindDelegate(WorldContextObject, ReceivingFunction, Domain, Event);\n}\n\nvoid UGlobalEventSystemBPLibrary::GESBindEvent(UObject* WorldContextObject, const FString& Domain /*= TEXT(\"global.default\")*/, const FString& Event /*= TEXT(\"\")*/, const FString& ReceivingFunction /*= TEXT(\"\")*/)\n{\n\tFGESEventListener Listener;\n\tListener.ReceiverWCO = WorldContextObject;\n\tListener.FunctionName = ReceivingFunction;\n\tListener.LinkFunction();\t//this makes the function valid by finding a reference to it\n\n\tFGESHandler::DefaultHandler()->AddListener(Domain, Event, Listener);\n}\n\nvoid UGlobalEventSystemBPLibrary::GESBindTagEvent(UObject* WorldContextObject, FGameplayTag DomainedEventTag, const FString& ReceivingFunction /*= TEXT(\"\")*/)\n{\n\tFGESEventListener Listener;\n\tListener.ReceiverWCO = WorldContextObject;\n\tListener.FunctionName = ReceivingFunction;\n\tListener.LinkFunction();\t//this makes the function valid by finding a reference to it\n\n\tFString Domain;\n\tFString Event;\n\tConv_TagToDomainAndEvent(DomainedEventTag, Domain, Event);\n\n\tFGESHandler::DefaultHandler()->AddListener(Domain, Event, Listener);\n}\n\nvoid UGlobalEventSystemBPLibrary::GESBindTagEventToDelegate(UObject* WorldContextObject, FGameplayTag DomainedEventTag, const FGESOnePropertySignature& ReceivingFunction)\n{\n\tFString Domain;\n\tFString Event;\n\tConv_TagToDomainAndEvent(DomainedEventTag, Domain, Event);\n\tGESBindEventToDelegate(WorldContextObject, ReceivingFunction, Domain, Event);\n}\n\nvoid UGlobalEventSystemBPLibrary::GESBindEventToDelegate(UObject* WorldContextObject, const FGESOnePropertySignature& ReceivingFunction, const FString& Domain /*= TEXT(\"global.default\")*/, const FString& Event /*= TEXT(\"\")*/)\n{\n\tFGESEventListener Listener;\n\tListener.ReceiverWCO = WorldContextObject;\n\tif (ReceivingFunction.GetUObject()->IsValidLowLevelFast())\n\t{\n\t\tListener.FunctionName = WorldContextObject->GetName() + ReceivingFunction.GetUObject()->GetName();\n\t}\n\telse\n\t{\n\t\tListener.FunctionName = WorldContextObject->GetName() + TEXT(\".UnboundDelegate\");\n\t}\n\tListener.OnePropertyFunctionDelegate = ReceivingFunction;\n\tListener.bIsBoundToDelegate = true;\n\n\tFGESHandler::DefaultHandler()->AddListener(Domain, Event, Listener);\n}\n\nvoid UGlobalEventSystemBPLibrary::HandleEmit(const FGESPropertyEmitContext& FullEmitData)\n{\n\tFGESHandler::DefaultHandler()->EmitPropertyEvent(FullEmitData);\n}\n\nvoid UGlobalEventSystemBPLibrary::GESEmitEventOneParam(UObject* WorldContextObject, TFieldPath<FProperty> ParameterData, bool bPinned /*= false*/, const FString& Domain /*= TEXT(\"global.default\")*/, const FString& Event /*= TEXT(\"\")*/)\n{\n\t//this never gets called due to custom thunk\n}\n\nvoid UGlobalEventSystemBPLibrary::GESEmitEvent(UObject* WorldContextObject, bool bPinned /*= false*/, const FString& Domain /*= TEXT(\"global.default\")*/, const FString& EventName /*= TEXT(\"\")*/)\n{\n\tif (!WorldContextObject)\n\t{\n\t\treturn;\n\t}\n\n\tUWorld* World = WorldContextObject->GetWorld();\n\tif (!World)\n\t{\n\t\treturn;\n\t}\n\n\t// Only allow real gameplay worlds\n\tif (!World->IsGameWorld())\n\t{\n\t\treturn;\n\t}\n\t\n\tFGESEmitContext EmitData;\n\tEmitData.bPinned = bPinned;\n\tEmitData.Domain = Domain;\n\tEmitData.Event = EventName;\n\tEmitData.WorldContext = WorldContextObject;\n\tFGESHandler::DefaultHandler()->EmitEvent(EmitData);\n}\n\nvoid UGlobalEventSystemBPLibrary::GESEmitTagEvent(UObject* WorldContextObject, FGameplayTag DomainedEventTag, bool bPinned /*= false*/)\n{\n\tFGESEmitContext EmitData;\n\tEmitData.bPinned = bPinned;\n\tConv_TagToDomainAndEvent(DomainedEventTag, EmitData.Domain, EmitData.Event);\n\tEmitData.WorldContext = WorldContextObject;\n\tFGESHandler::DefaultHandler()->EmitEvent(EmitData);\n}\n\nvoid UGlobalEventSystemBPLibrary::GESEmitTagEventOneParam(UObject* WorldContextObject, TFieldPath<FProperty> ParameterData, FGameplayTag DomainedEventTag, bool bPinned /*= false*/)\n{\n\t//this never gets called due to custom thunk\n}\n\nvoid UGlobalEventSystemBPLibrary::GESUnpinEvent(UObject* WorldContextObject, const FString& Domain /*= TEXT(\"global.default\")*/, const FString& Event /*= TEXT(\"\")*/)\n{\n\tFGESHandler::DefaultHandler()->UnpinEvent(Domain, Event);\n}\n\nvoid UGlobalEventSystemBPLibrary::SetGESOptions(const FGESGlobalOptions& InOptions)\n{\n\tFGESHandler::DefaultHandler()->SetOptions(InOptions);\n}\n\nbool UGlobalEventSystemBPLibrary::Conv_PropToInt(const FGESWildcardProperty& InProp, int32& OutInt)\n{\n\tif (InProp.Property == nullptr)\n\t{\n\t\tUE_LOG(LogTemp, Warning, TEXT(\"UGlobalEventSystemBPLibrary::Conv_PropToInt InProp is a nullptr\"));\n\t\treturn false;\n\t}\n\n\tif (InProp.Property->IsA<FNumericProperty>())\n\t{\n\t\tFNumericProperty* Property = CastField<FNumericProperty>(InProp.Property.Get());\n\t\tif (!Property->IsFloatingPoint())\n\t\t{\n\t\t\tOutInt = Property->GetSignedIntPropertyValue(InProp.PropertyPtr);\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tUE_LOG(LogTemp, Warning, TEXT(\"UGlobalEventSystemBPLibrary::Conv_PropToInt %s is not an integer number, float truncated to int.\"), *InProp.Property->GetName());\n\t\t\tOutInt = Property->GetFloatingPointPropertyValue(InProp.PropertyPtr);\n\t\t\treturn false;\n\t\t}\n\t}\n\telse\n\t{\n\t\tUE_LOG(LogTemp, Warning, TEXT(\"UGlobalEventSystemBPLibrary::Conv_PropToInt %s is not an integer.\"), *InProp.Property->GetName());\n\t\treturn false;\n\t}\n}\n\nbool UGlobalEventSystemBPLibrary::Conv_PropToFloat(const FGESWildcardProperty& InProp, float& OutFloat)\n{\n\tif (InProp.Property == nullptr)\n\t{\n\t\tUE_LOG(LogTemp, Warning, TEXT(\"UGlobalEventSystemBPLibrary::Conv_PropToFloat InProp is a nullptr\"));\n\t\treturn false;\n\t}\n\n\tif (InProp.Property->IsA<FNumericProperty>())\n\t{\n\t\tFNumericProperty* Property = CastField<FNumericProperty>(InProp.Property.Get());\n\t\tif (Property->IsFloatingPoint())\n\t\t{\n\t\t\tOutFloat = Property->GetFloatingPointPropertyValue(InProp.PropertyPtr);\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tUE_LOG(LogTemp, Warning, TEXT(\"UGlobalEventSystemBPLibrary::Conv_PropToFloat %s is not a floating number, converted int to float.\"), *InProp.Property->GetName());\n\t\t\tOutFloat = Property->GetSignedIntPropertyValue(InProp.PropertyPtr);\n\t\t\treturn false;\n\t\t}\n\t}\n\telse\n\t{\n\t\tUE_LOG(LogTemp, Warning, TEXT(\"UGlobalEventSystemBPLibrary::Conv_PropToFloat %s is not a float.\"), *InProp.Property->GetName());\n\t\treturn false;\n\t}\n}\n\nbool UGlobalEventSystemBPLibrary::Conv_PropToBool(const FGESWildcardProperty& InProp, bool& OutBool)\n{\n\tif (InProp.Property == nullptr)\n\t{\n\t\tUE_LOG(LogTemp, Warning, TEXT(\"UGlobalEventSystemBPLibrary::Conv_PropToBool InProp is a nullptr\"));\n\t\treturn false;\n\t}\n\n\tif (InProp.Property->IsA<FBoolProperty>())\n\t{\n\t\tFBoolProperty* Property = CastField<FBoolProperty>(InProp.Property.Get());\n\t\tOutBool = Property->GetPropertyValue(InProp.PropertyPtr);\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\tUE_LOG(LogTemp, Warning, TEXT(\"UGlobalEventSystemBPLibrary::Conv_PropToBool %s is not a bool.\"), *InProp.Property->GetName());\n\t\treturn false;\n\t}\n}\n\nbool UGlobalEventSystemBPLibrary::Conv_PropToStringRef(const FGESWildcardProperty& InProp, FString& OutString)\n{\n\tif (InProp.Property == nullptr)\n\t{\n\t\tUE_LOG(LogTemp, Warning, TEXT(\"UGlobalEventSystemBPLibrary::Conv_PropToStringRef InProp is a nullptr\"));\n\t\treturn false;\n\t}\n\n\tif (InProp.Property->IsA<FStrProperty>())\n\t{\n\t\tFStrProperty* Property = CastField<FStrProperty>(InProp.Property.Get());\n\t\tOutString = Property->GetPropertyValue(InProp.PropertyPtr);\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\tUE_LOG(LogTemp, Warning, TEXT(\"UGlobalEventSystemBPLibrary::Conv_PropToString %s is not an FString, attempted best conversion for display purposes.\"), *InProp.Property->GetName());\n\n\t\t//Convert logic\n\t\tif (InProp.Property->IsA<FNumericProperty>())\n\t\t{\n\t\t\tFNumericProperty* Property = CastField<FNumericProperty>(InProp.Property.Get());\n\t\t\tif (Property->IsFloatingPoint())\n\t\t\t{\n\t\t\t\tOutString = FString::SanitizeFloat(Property->GetFloatingPointPropertyValue(InProp.PropertyPtr));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tOutString = FString::FromInt(Property->GetSignedIntPropertyValue(InProp.PropertyPtr));\n\t\t\t}\n\t\t}\n\t\telse if (InProp.Property->IsA<FBoolProperty>())\n\t\t{\n\t\t\tFBoolProperty* Property = CastField<FBoolProperty>(InProp.Property.Get());\n\t\t\tif (Property->GetPropertyValue(InProp.PropertyPtr))\n\t\t\t{\n\t\t\t\tOutString = TEXT(\"True\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tOutString = TEXT(\"False\");\n\t\t\t}\n\t\t}\n\t\telse if (InProp.Property->IsA<FNameProperty>())\n\t\t{\n\t\t\tFNameProperty* Property = CastField <FNameProperty>(InProp.Property.Get());\n\t\t\tOutString = Property->GetPropertyValue(InProp.PropertyPtr).ToString();\n\t\t}\n\t\telse if (InProp.Property->IsA<FObjectProperty>())\n\t\t{\n\t\t\tFObjectProperty* Property = CastField<FObjectProperty>(InProp.Property.Get());\n\t\t\tUObject* Object = Property->GetPropertyValue(InProp.PropertyPtr);\n\n\t\t\tif (Object->IsValidLowLevelFast())\n\t\t\t{\n\t\t\t\tOutString = Object->GetName() + TEXT(\", type: \") + Object->GetClass()->GetName();\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tOutString = TEXT(\"Null Object\");\n\t\t\t}\n\t\t}\n\t\telse if (InProp.Property->IsA<FStructProperty>())\n\t\t{\n\t\t\tFStructProperty* Property = CastField<FStructProperty>(InProp.Property.Get());\n\t\t\tOutString = Property->GetName() + TEXT(\", type: \") + Property->Struct->GetName();\n\t\t}\n\t\treturn false;\n\t}\n}\n\nFString UGlobalEventSystemBPLibrary::Conv_PropToString(const FGESWildcardProperty& InProp)\n{\n\n\tFString OutString;\n\tConv_PropToStringRef(InProp, OutString);\n\treturn OutString;\n}\n\nbool UGlobalEventSystemBPLibrary::Conv_PropToName(const FGESWildcardProperty& InProp, FName& OutName)\n{\n\tif (InProp.Property == nullptr)\n\t{\n\t\tUE_LOG(LogTemp, Warning, TEXT(\"UGlobalEventSystemBPLibrary::Conv_PropToName InProp is a nullptr\"));\n\t\treturn false;\n\t}\n\n\tif (InProp.Property->IsA<FNameProperty>())\n\t{\n\t\tFNameProperty* Property = CastField<FNameProperty>(InProp.Property.Get());\n\t\tOutName = Property->GetPropertyValue(InProp.PropertyPtr);\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\tUE_LOG(LogTemp, Warning, TEXT(\"UGlobalEventSystemBPLibrary::Conv_PropToName %s is not an FName.\"), *InProp.Property->GetName());\n\t\treturn false;\n\t}\n}\n\nbool UGlobalEventSystemBPLibrary::Conv_PropToStruct(const FGESWildcardProperty& InProp, TFieldPath<FProperty>& OutStruct)\n{\n\t//doesn't get called due to custom thunk\n\treturn false;\n}\n\nbool UGlobalEventSystemBPLibrary::HandlePropToStruct(const FGESWildcardProperty& InProp, FGESWildcardProperty& OutProp)\n{\n\tif (InProp.Property == nullptr)\n\t{\n\t\tUE_LOG(LogTemp, Warning, TEXT(\"UGlobalEventSystemBPLibrary::HandlePropToStruct InProp is a nullptr\"));\n\t\treturn false;\n\t}\n\tif (OutProp.Property == nullptr)\n\t{\n\t\tUE_LOG(LogTemp, Warning, TEXT(\"UGlobalEventSystemBPLibrary::HandlePropToStruct OutProp is a nullptr\"));\n\t\treturn false;\n\t}\n\n\tif (InProp.Property->IsA<FStructProperty>() && OutProp.Property->IsA<FStructProperty>())\n\t{\n\t\tFStructProperty* InStructProp = CastField<FStructProperty>(InProp.Property.Get());\n\t\tFStructProperty* OutStructProp = CastField<FStructProperty>(OutProp.Property.Get());\n\n\t\tOutStructProp->CopyCompleteValue(OutProp.PropertyPtr, InProp.PropertyPtr);\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\treturn false;\n\t}\n}\n\nbool UGlobalEventSystemBPLibrary::Conv_PropToObject(const FGESWildcardProperty& InProp, UObject*& OutObject)\n{\n\tif (InProp.Property == nullptr)\n\t{\n\t\treturn false;\n\t}\n\n\tif (InProp.Property->IsA<FObjectProperty>())\n\t{\n\t\tFObjectProperty* Property = CastField<FObjectProperty>(InProp.Property.Get());\n\t\tOutObject = Property->GetPropertyValue(InProp.PropertyPtr);\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\tUE_LOG(LogTemp, Warning, TEXT(\"UGlobalEventSystemBPLibrary::Conv_PropToObject %s is not an Object.\"), *InProp.Property->GetName());\n\t\treturn false;\n\t}\n}\n\nvoid UGlobalEventSystemBPLibrary::Conv_TagToDomainAndEvent(FGameplayTag InTag, FString& OutDomain, FString& OutEvent)\n{\n\tFString DomainAndEvent = InTag.GetTagName().ToString();\n\n\tbool bFound = DomainAndEvent.Split(TEXT(\".\"), &OutDomain, &OutEvent, ESearchCase::IgnoreCase, ESearchDir::FromEnd);\n\n\tif (!bFound)\n\t{\n\t\tOutDomain = TEXT(\"global.default\");\n\t\tOutEvent = DomainAndEvent;\n\t}\n}\n\n"
  },
  {
    "path": "Source/GlobalEventSystem/Public/GESBaseReceiverComponent.h",
    "content": "// Copyright 2019-current Getnamo. All Rights Reserved\n\n#pragma once\n\n#include \"Components/ActorComponent.h\"\n#include \"GESDataTypes.h\"\n#include \"GESHandlerDataTypes.h\"\n#include \"GESBaseReceiverComponent.generated.h\"\n\n/** Convenience base class for receiving GES events in an organized way for actors.*/\nUCLASS(BlueprintType, Blueprintable, ClassGroup = \"Utility\", meta = (BlueprintSpawnableComponent))\nclass GLOBALEVENTSYSTEM_API UGESBaseReceiverComponent : public UActorComponent\n{\n\tGENERATED_UCLASS_BODY()\npublic:\n\t//Wildcard receiver\n\tUPROPERTY(BlueprintAssignable, Category = \"GES Receiver\")\n\tFGESOnePropertyMCSignature OnEvent;\n\n\t//Domain, Event, and receiving function name\n\tUPROPERTY(BlueprintReadWrite, EditAnywhere, Category = \"GES Receiver\")\n\tFGESNameBind BindSettings;\n\n\t//For polling after having received an event\n\tUPROPERTY(BlueprintReadOnly, Category = \"GES Receiver\")\n\tFGESWildcardProperty LastReceivedProperty;\n\n\t//auto-bind as soon as this component begins play\n\tUPROPERTY(BlueprintReadWrite, EditAnywhere, Category = \"GES Receiver\")\n\tbool bBindOnBeginPlay;\n\n\t//Unbind the event automatically whenever gameplay ends for this component (e.g. destroyed)\n\tUPROPERTY(BlueprintReadWrite, EditAnywhere, Category = \"GES Receiver\")\n\tbool bUnbindOnEndPlay;\n\n\t/**\n\t* If event is the wildcard component one, this will pin data received for polling.\n\t* Turned off only for optimization generally.\n\t*/\n\tUPROPERTY(BlueprintReadWrite, EditAnywhere, Category = \"GES Receiver\")\n\tbool bPinInternalDataForPolling;\n\n\t/** Used to know if polling for last data will give valid results */\n\tUPROPERTY(BlueprintReadWrite, Category = \"GES Receiver\")\n\tbool bDidReceiveEventAtLeastOnce;\n\n\tvirtual void BeginPlay() override;\n\tvirtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;\n\nprotected:\n\n\t//Only used to route signal to MC variant\n\tUPROPERTY()\n\tFGESOnePropertySignature InternalListener;\n\n\tFGESPinnedData PinnedData;\n\n\tUFUNCTION()\n\tvoid HandleInternalEvent(const FGESWildcardProperty& WildcardProperty);\n};"
  },
  {
    "path": "Source/GlobalEventSystem/Public/GESDataTypes.h",
    "content": "#pragma once\n\n#include \"CoreMinimal.h\"\n#include \"GESDataTypes.generated.h\"\n\n/** \n* Global options for GESHandler. Used in BP library static calls.\n*/\nUSTRUCT(BlueprintType)\nstruct FGESGlobalOptions\n{\n\tGENERATED_BODY()\n\n\t/** Whether to ensure structs are exactly the same. Turn off for small performance boost. Default true.*/\n\tUPROPERTY(BlueprintReadWrite, Category = \"GES Global Options\")\n\tbool bValidateStructTypes;\n\n\t/** Will output logs for stale events and listeners that get removed. . Default true.*/\n\tUPROPERTY(BlueprintReadWrite, Category = \"GES Global Options\")\n\tbool bLogStaleRemovals;\n\n\tFGESGlobalOptions()\n\t{\n\t\tbValidateStructTypes = true;\n\t\tbLogStaleRemovals = true;\n\t}\n};\n\n/** Struct used to define a bind to a GES event by function name. (Used in GESBaseReceiverComponents) */\nUSTRUCT(BlueprintType)\nstruct FGESNameBind\n{\n\tGENERATED_BODY()\n\n\t/** Abstract Domain name used in GES, similar to a channel concept. */\n\tUPROPERTY(BlueprintReadWrite, EditAnywhere, Category = \"GES Local Bind\")\n\tFString Domain;\n\t\n\t/** Abstract event name used in GES. Unique when combined with Domain. */\n\tUPROPERTY(BlueprintReadWrite, EditAnywhere, Category = \"GES Local Bind\")\n\tFString Event;\n\n\t/** Name of function receiving event. */\n\tUPROPERTY(BlueprintReadWrite, EditAnywhere, Category = \"GES Local Bind\")\n\tFString ReceivingFunction;\n\n\tFGESNameBind()\n\t{\n\t\tDomain = TEXT(\"global.default\");\n\t\tEvent = TEXT(\"\");\n\t\tReceivingFunction = TEXT(\"\");\n\t}\n};\n\n/** \n*\tWrapper for lambda bind call data minus actual receiver function.\n*\tUsed in AddLambdaListener and remove variant.\n*/\nUSTRUCT()\nstruct FGESEventContext\n{\n\tGENERATED_BODY()\n\n\t/** Abstract Domain name used in GES, similar to a channel concept. */\n\tUPROPERTY()\n\tFString Domain;\n\n\t/** Abstract event name used in GES. Unique when combined with Domain. */\n\tUPROPERTY()\n\tFString Event;\n\n\t/** World context object. Used to determine max lifetime of event. */\n\tUPROPERTY()\n\tUObject* WorldContext;\n\n\tFGESEventContext()\n\t{\n\t\tDomain = TEXT(\"global.default\");\n\t\tEvent = TEXT(\"\");\n\t\tWorldContext = nullptr;\n\t}\n};\n\nUSTRUCT()\nstruct FGESEmitContext : public FGESEventContext\n{\n\tGENERATED_BODY()\n\n\t/** Pinned means an emitted event state should be accessible after it has been emitted. */\n\tUPROPERTY()\n\tbool bPinned;\n\n\tFGESEmitContext()\n\t{\n\t\tDomain = TEXT(\"global.default\");\n\t\tEvent = TEXT(\"\");\n\t\tWorldContext = nullptr;\n\t\tbPinned = false;\n\t}\n};\n\n\n/** \n* Wrapper struct for a wildcard property. Allows directly binding GES events to\n* delegate events with GESBPLibrary conversion casting used to specialize the property.\n*/\nUSTRUCT(BlueprintType)\nstruct FGESWildcardProperty\n{\n\tGENERATED_BODY()\n\n\t/** Property wrapper. Use GESBPLibrary conversion functions to obtained specialized conversions.*/\n\tUPROPERTY(BlueprintReadOnly, Category = \"GES Global Options\")\n\tTFieldPath<FProperty> Property;\n\n\tvoid* PropertyPtr;\n};\n\n/** No param Delegate */\nDECLARE_DYNAMIC_DELEGATE(FGESEmptySignature);\n\n/** Wildcard Delegate */\nDECLARE_DYNAMIC_DELEGATE_OneParam(FGESOnePropertySignature, const FGESWildcardProperty&, WildcardProperty);\n\n/** Multicast variant of Wildcard Delegate (used in BaseReceiverComponents) */\nDECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FGESOnePropertyMCSignature, const FGESWildcardProperty&, WildcardProperty);"
  },
  {
    "path": "Source/GlobalEventSystem/Public/GESHandler.h",
    "content": "#pragma once\n#include \"UObject/Object.h\"\n#include \"UObject/UnrealType.h\"\n#include \"GESWorldListenerActor.h\"\n#include \"GESDataTypes.h\"\n#include \"GESHandlerDataTypes.h\"\n\n//Text macro to handle TEXT(\"\") emits\n#if !defined(GES_RAW_TEXT)\n\t#if PLATFORM_TCHAR_IS_CHAR16\n\t\t#define GES_RAW_TEXT char16_t*\n\t#else\n\t\t#define GES_RAW_TEXT wchar_t*\n\t#endif\n#endif\n\n/** \nGESHandler Class usable in C++ with care. Private API may be a bit too exposed atm.\n*/\n\nclass GLOBALEVENTSYSTEM_API FGESHandler\n{\npublic:\n\n\t//Get the Global (default) handler to call all other functions\n\tstatic TSharedPtr<FGESHandler> DefaultHandler();\n\n\t/**\n\t*   Clear all listeners and reset state\n\t*/\n\tstatic void Clear();\n\n\t/**\n\t*\tCreate an event in TargetDomain.TargetFunction. Does nothing if already existing.\n\t*/\n\tvoid CreateEvent(const FString& Domain, const FString& Event, bool bPinned = false);\n\t\n\t/**\n\t*\tDelete an event in TargetDomain.TargetFunction. Does nothing if missing.\n\t*/\n\tvoid DeleteEvent(const FString& Domain, const FString& Event);\n\n\t/** \n\t*\tDelete an event in with DomainAndEvent defined as a single string. Does nothing if missing.\n\t*/\n\tvoid DeleteEvent(const FString& DomainAndEvent);\n\n\t/** \n\t*\tCheck if event exists\n\t*/\n\tbool HasEvent(const FString& Domain, const FString& Event);\n\n\t/** \n\t*\tRemoves the pinning of the event for future listeners.\n\t*/\n\tvoid UnpinEvent(const FString& Domain, const FString& Event);\n\n\t/** \n\t* Listen to an event in TargetDomain.TargetFunction via generic listener\n\t*/\n\tvoid AddListener(const FString& Domain, const FString& Event, const FGESEventListener& Listener);\n\n\t/**\n\t*\tListen to an event in TargetDomain.TargetFunction via passed in lambda\n\t*/\n\tFString AddLambdaListener(FGESEventContext EventInfo, TFunction<void(const FGESWildcardProperty&)> ReceivingLambda);\n\t\n\t/**\n\t* Stop listening to an event in TargetDomain.TargetFunction\n\t*/\n\tvoid RemoveListener(const FString& Domain, const FString& Event, const FGESEventListener& Listener);\n\n\t/**\n\t* Stop listening to all events for given receiver\n\t*/\n\tvoid RemoveAllListenersForReceiver(UObject* ReceiverWCO);\n\n\t/**\n\t*\tListen to an event in TargetDomain.TargetFunction via passed in lambda\n\t*/\n\tvoid RemoveLambdaListener(FGESEventContext EventInfo, TFunction<void(const FGESWildcardProperty&)> ReceivingLambda);\n\n\t/** \n\t*\tRemove lambda by function id string. Used in case of lambdas without ref to initial function.\n\t*/\n\tvoid RemoveLambdaListener(FGESEventContext EventInfo, const FString& LambdaName);\n\n\t//overloaded emits\n\tvoid EmitEvent(const FGESEmitContext& EmitData, UStruct* Struct, void* StructPtr);\n\tvoid EmitEvent(const FGESEmitContext& EmitData, const FString& ParamData);\n\tvoid EmitEvent(const FGESEmitContext& EmitData, UObject* ParamData);\n\tvoid EmitEvent(const FGESEmitContext& EmitData, float ParamData);\n\tvoid EmitEvent(const FGESEmitContext& EmitData, int32 ParamData);\n\tvoid EmitEvent(const FGESEmitContext& EmitData, bool ParamData);\n\tvoid EmitEvent(const FGESEmitContext& EmitData, const FName& ParamData);\n\tbool EmitEvent(const FGESEmitContext& EmitData);\n\t//GES_RAW_TEXT supports passing in TEXT(\"\") macros\n\tvoid EmitEvent(const FGESEmitContext& EmitData, const GES_RAW_TEXT RawStringMessage);\n\n\t//processed means the pointers have been filled\n\tbool EmitPropertyEvent(const FGESPropertyEmitContext& FullEmitData);\n\n\t//overloaded lambda binds\n\tFString AddLambdaListener(FGESEventContext EventInfo, TFunction<void(UStruct* Struct, void* StructPtr)> ReceivingLambda);\n\tFString AddLambdaListener(FGESEventContext EventInfo, TFunction<void(const FString&)> ReceivingLambda);\n\tFString AddLambdaListener(FGESEventContext EventInfo, TFunction<void(UObject*)> ReceivingLambda);\n\tFString AddLambdaListener(FGESEventContext EventInfo, TFunction<void(float)> ReceivingLambda);\n\tFString AddLambdaListener(FGESEventContext EventInfo, TFunction<void(const FName&)> ReceivingLambda);\n\tFString AddLambdaListener(FGESEventContext EventInfo, TFunction<void(void)> ReceivingLambda);\n\n\t//needed unique names due to ambiguity clash with float\n\tFString AddLambdaListenerInt(FGESEventContext EventInfo, TFunction<void(int32)> ReceivingLambda);\n\tFString AddLambdaListenerBool(FGESEventContext EventInfo, TFunction<void(bool)> ReceivingLambda);\n\n\t/**\n\t* Update global options\n\t*/\n\tvoid SetOptions(const FGESGlobalOptions& InOptions);\n\t\n\t/** \n\t* Convenience internal Key for domain and event string\n\t*/\n\tstatic FString Key(const FString& Domain, const FString& Event);\n\n\tFGESHandler();\n\t~FGESHandler();\n\nprivate:\n\tstatic TSharedPtr<FGESHandler> PrivateDefaultHandler;\n\n\t//internal helper for in-context data filling for listeners\n\tvoid EmitToListenersWithData(const FGESPropertyEmitContext& EmitData, TFunction<void(const FGESEventListener&)> DataFillCallback);\n\t//internal emitter to each listener\n\tbool EmitToListenerWithData(const FGESPropertyEmitContext& EmitData, const FGESEventListener& Listener,\n\t\tTFunction<void(const FGESEventListener&)>& DataFillCallback);\n\n\t//internal overloads\n\tvoid EmitSubPropertyEvent(const FGESPropertyEmitContext& EmitData);\n\n\t//can check function signature vs e.g. FString\n\tstatic bool FirstParamIsCppType(UFunction* Function, const FString& TypeString);\n\tstatic bool FirstParamIsSubclassOf(UFunction* Function, FFieldClass* ClassType);\n\tstatic FString ListenerLogString(const FGESEventListener& Listener);\n\tstatic FString EventLogString(const FGESEvent& Event);\n\tstatic FString EmitEventLogString(const FGESEmitContext& EmitData);\n\tstatic void FunctionParameters(UFunction* Function, TArray<FProperty*>& OutParamProperties);\n\n\t//this function logs warnings otherwise\n\tstatic bool FunctionHasValidParams(UFunction* Function, FFieldClass* ClassType, const FGESEmitContext& EmitData, const FGESEventListener& Listener);\n\n\t//Key == TargetDomain.TargetFunction\n\tTMap<FString, FGESEvent> EventMap;\n\tTMap<UObject*, TArray<FGESEventListenerWithContext>> ReceiverMap;\n\tTArray<FGESEventListener*> RemovalArray;\n\n\t//Toggles\n\tFGESGlobalOptions Options;\n\n\tTMap<UWorld*, AGESWorldListenerActor*> WorldMap;\n};"
  },
  {
    "path": "Source/GlobalEventSystem/Public/GESHandlerDataTypes.h",
    "content": "#pragma once\n\n#include \"GESDataTypes.h\"\n\n/** Struct to hold pinned property data */\nstruct FGESPinnedData\n{\n\tFProperty* Property;\n\tvoid* PropertyPtr;\n\tTArray<uint8> PropertyData;\n\tbool bHandlePropertyDeletion;\n\n\tFGESPinnedData()\n\t{\n\t\tProperty = nullptr;\n\t\tPropertyPtr = nullptr;\n\t\tbHandlePropertyDeletion = false;\n\t}\n\t~FGESPinnedData()\n\t{\n\t\tCleanupPinnedData();\n\t}\n\tvoid CopyPropertyToPinnedBuffer();\n\tvoid CleanupPinnedData();\n};\n\nstruct FGESDynamicArg\n{\n\tvoid* Arg01;\n};\n\n//Minimal definition to define a listener (for removal)\nstruct FGESMinimalEventListener\n{\n\tTWeakObjectPtr<UObject> ReceiverWCO;\t//WorldContextObject\n\tFString FunctionName;\n\n\tbool operator ==(FGESMinimalEventListener const& Other)\n\t{\n\t\treturn (Other.ReceiverWCO == ReceiverWCO) && (Other.FunctionName == FunctionName);\n\t}\n\tFGESMinimalEventListener();\n};\n\nstruct FGESEventListener : FGESMinimalEventListener\n{\n\t// Opt A) Bound UFunction, valid after calling LinkFunction\n\tUFunction* Function;\n\n\t// Opt B) Bound to a delegate\n\tbool bIsBoundToDelegate;\n\tFGESOnePropertySignature OnePropertyFunctionDelegate;\n\n\t// Opt C) Bound to a lambda function\n\tbool bIsBoundToLambda;\n\tTFunction<void(const FGESWildcardProperty&)> LambdaFunction;\n\n\tFGESEventListener(const FGESMinimalEventListener& Minimal);\n\tFGESEventListener();\n\tbool LinkFunction();\n\tbool IsValidListener() const;\n};\n\n//Wrapper struct for tracking event-receiver pairs in ReceiverMap\nstruct FGESEventListenerWithContext\n{\n\tFGESMinimalEventListener Listener;\n\tFString Domain;\n\tFString Event;\n\n\tFGESEventListenerWithContext()\n\t{\n\t\tDomain = TEXT(\"\");\n\t\tEvent = TEXT(\"\");\n\t}\n\n\tbool operator ==(FGESEventListenerWithContext const& Other)\n\t{\n\t\treturn (Other.Domain == Domain) && \n\t\t\t(Other.Event == Event) &&\n\t\t\t(Listener.FunctionName == Other.Listener.FunctionName) &&\n\t\t\t(Listener.ReceiverWCO == Other.Listener.ReceiverWCO);\n\t}\n};\n\n//Event specialization with pinned and listener data\nstruct FGESEvent : FGESEmitContext\n{\n\t//If pinned an event will emit the moment you add a listener if it has been already fired once\n\tFGESPinnedData PinnedData;\n\n\tTArray<FGESEventListener> Listeners;\n\n\tFGESEvent();\n\tFGESEvent(const FGESEmitContext& Other);\n};\n\n//Emit specialization with property pointers\nstruct FGESPropertyEmitContext : FGESEmitContext\n{\n\tFProperty* Property;\n\tvoid* PropertyPtr;\n\tbool bHandleAllocation;\n\n\t//NB: if we want a callback or pin emit\n\tFGESEventListener* SpecificTarget;\n\n\tFGESPropertyEmitContext();\n\tFGESPropertyEmitContext(const FGESEmitContext& Other);\n};"
  },
  {
    "path": "Source/GlobalEventSystem/Public/GESWorldListenerActor.h",
    "content": "// Copyright 2019-current Getnamo. All Rights Reserved\n\n#pragma once\n\n#include \"CoreMinimal.h\"\n#include \"GameFramework/Actor.h\"\n#include \"GESWorldListenerActor.generated.h\"\n\n/** \n* An actor spawned per world by FGESHandler in order to track when the world\n* gets torn down and we have to remove all listeners for that world.\n*/\nUCLASS()\nclass GLOBALEVENTSYSTEM_API AGESWorldListenerActor : public AActor\n{\n\tGENERATED_BODY()\n\t\npublic:\t\n\t// Sets default values for this actor's properties\n\tAGESWorldListenerActor();\n\n\t// Event to listen to in order to catch world ending\n\tTFunction<void()> OnEndPlay;\n\n\tTSet<FString> WorldEvents;\n\nprotected:\n\t// Called when the game starts or when spawned\n\tvirtual void BeginPlay() override;\n\tvirtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;\n};\n"
  },
  {
    "path": "Source/GlobalEventSystem/Public/GlobalEventSystem.h",
    "content": "// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.\n\n#pragma once\n\n#include \"Modules/ModuleManager.h\"\n\nclass FGlobalEventSystemModule : public IModuleInterface\n{\npublic:\n\n\t/** IModuleInterface implementation */\n\tvirtual void StartupModule() override;\n\tvirtual void ShutdownModule() override;\n\nprivate:\n\n#if WITH_EDITOR\n\tFDelegateHandle EndPieDelegate;\n#endif\n};\n"
  },
  {
    "path": "Source/GlobalEventSystem/Public/GlobalEventSystemBPLibrary.h",
    "content": "// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.\n\n#pragma once\n\n#include \"Kismet/BlueprintFunctionLibrary.h\"\n#include \"GESHandler.h\"\n#include \"GameplayTagContainer.h\"\n#include \"GlobalEventSystemBPLibrary.generated.h\"\n\n/* \n* Core Global Event System functions. Call anywhere.\n*/\nUCLASS()\nclass GLOBALEVENTSYSTEM_API UGlobalEventSystemBPLibrary : public UBlueprintFunctionLibrary\n{\n\tGENERATED_UCLASS_BODY()\n\n\t/** \n\t* Remove this listener from the specified GESEvent.\n\t*/\n\tUFUNCTION(BlueprintCallable, meta = (Keywords = \"ges sever stoplisten\", WorldContext = \"WorldContextObject\"), Category = \"GlobalEventSystem\")\n\tstatic void GESUnbindEvent(UObject* WorldContextObject, const FString& Domain = TEXT(\"global.default\"), const FString& Event = TEXT(\"\"), const FString& ReceivingFunction = TEXT(\"\"));\n\n\t/**\n\t* Remove this listener from the specified GESEvent given by GameplayTag.\n\t*/\n\tUFUNCTION(BlueprintCallable, meta = (Keywords = \"ges sever stoplisten\", WorldContext = \"WorldContextObject\"), Category = \"GlobalEventSystem\")\n\tstatic void GESUnbindTagEvent(UObject* WorldContextObject, FGameplayTag Tag, const FString& ReceivingFunction = TEXT(\"\"));\n\n\n\t/** \n\t* Call this on endplay to remove all events associated with this graph. If context isn't specified, graph context used (default use case).\n\t*/\n\tUFUNCTION(BlueprintCallable, meta = (Keywords = \"ges sever stoplisten\", WorldContext = \"WorldContextObject\"), Category = \"GlobalEventSystem\")\n\tstatic void GESUnbindAllEventsForContext(UObject* WorldContextObject, UObject* Context = nullptr);\n\n\n\tUFUNCTION(BlueprintCallable, meta = (Keywords = \"ges sever stoplisten\", WorldContext = \"WorldContextObject\"), Category = \"GlobalEventSystem\")\n\tstatic void GESUnbindDelegate(UObject* WorldContextObject, const FGESOnePropertySignature& ReceivingFunction, const FString& Domain = TEXT(\"global.default\"), const FString& Event = TEXT(\"\"));\n\n\tUFUNCTION(BlueprintCallable, meta = (Keywords = \"ges sever stoplisten\", WorldContext = \"WorldContextObject\"), Category = \"GlobalEventSystem\")\n\tstatic void GESUnbindTagDelegate(UObject* WorldContextObject, FGameplayTag Tag, const FGESOnePropertySignature& ReceivingFunction);\n\n\t/**\n\t* Bind a function (to current caller) to GES event. Make sure to match your receiving function parameters to the GESEvent ones.\n\t*/\n\tUFUNCTION(BlueprintCallable, meta = (Keywords = \"ges create listen\", WorldContext = \"WorldContextObject\"), Category = \"GlobalEventSystem\")\n\tstatic void GESBindEvent(UObject* WorldContextObject, const FString& Domain = TEXT(\"global.default\"), const FString& Event = TEXT(\"\"), const FString& ReceivingFunction = TEXT(\"\"));\n\n\t/**\n\t* Bind a function (to current caller) to GES event defined by a GamePlayTag\n\t*/\n\tUFUNCTION(BlueprintCallable, meta = (Keywords = \"ges create listen\", WorldContext = \"WorldContextObject\"), Category = \"GlobalEventSystem\")\n\tstatic void GESBindTagEvent(UObject* WorldContextObject, FGameplayTag DomainedEventTag, const FString& ReceivingFunction = TEXT(\"\"));\n\n\t/**\n\t* Bind a function (to current caller) to GES event defined by a GamePlayTag\n\t*/\n\tUFUNCTION(BlueprintCallable, meta = (Keywords = \"ges create listen\", WorldContext = \"WorldContextObject\"), Category = \"GlobalEventSystem\")\n\tstatic void GESBindTagEventToDelegate(UObject* WorldContextObject, FGameplayTag DomainedEventTag, const FGESOnePropertySignature& ReceivingFunction);\n\n\t/**\n\t* Bind an event delegate to GES event. Use blueprint utility to decode UProperty.\n\t*/\n\tUFUNCTION(BlueprintCallable, meta = (Keywords = \"ges create listen\", WorldContext = \"WorldContextObject\"), Category = \"GlobalEventSystem\")\n\tstatic void GESBindEventToDelegate(UObject* WorldContextObject, const FGESOnePropertySignature& ReceivingFunction, const FString& Domain = TEXT(\"global.default\"), const FString& Event = TEXT(\"\"));\n\n\t/** \n\t* Emit desired event with data. Data can be any single property (wrap arrays/maps etc in a struct or object)\n\t* Pinning an event means it will emit to future listeners even if the event has already been\n\t* emitted.\n\t*/\n\tUFUNCTION(BlueprintCallable, CustomThunk, Category = \"GlobalEventSystem\", meta = (CustomStructureParam = \"ParameterData\", WorldContext = \"WorldContextObject\"))\n\tstatic void GESEmitEventOneParam(UObject* WorldContextObject, TFieldPath<FProperty> ParameterData, bool bPinned = false, const FString& Domain = TEXT(\"global.default\"), const FString& Event = TEXT(\"\"));\n\n\t/** \n\t* Just emits the event with no additional data\n\t* Pinning an event means it will emit to future listeners even if the event has already been\n\t* emitted.\n\t*/\n\tUFUNCTION(BlueprintCallable, meta=(WorldContext = \"WorldContextObject\"), Category = \"GlobalEventSystem\")\n\tstatic void GESEmitEvent(UObject* WorldContextObject, bool bPinned = false, const FString& Domain = TEXT(\"global.default\"), const FString& Event = TEXT(\"\"));\n\n\t/**\n\t* Just emits the event with no additional data using GameplayTags to define domain and event.\n\t* Pinning an event means it will emit to future listeners even if the event has already been\n\t* emitted.\n\t*/\n\tUFUNCTION(BlueprintCallable, meta = (WorldContext = \"WorldContextObject\"), Category = \"GlobalEventSystem\")\n\tstatic void GESEmitTagEvent(UObject* WorldContextObject, FGameplayTag DomainedEventTag, bool bPinned = false);\n\n\t/**\n\t* Emit desired event with data using GameplayTags to define domain and event. Data can be any single\n\t* property (wrap arrays/maps etc in a struct or object).\n\t* Pinning an event means it will emit to future listeners even if the event has already been\n\t* emitted.\n\t*/\n\tUFUNCTION(BlueprintCallable, CustomThunk, Category = \"GlobalEventSystem\", meta = (CustomStructureParam = \"ParameterData\", WorldContext = \"WorldContextObject\"))\n\tstatic void GESEmitTagEventOneParam(UObject* WorldContextObject, TFieldPath<FProperty> ParameterData, FGameplayTag DomainedEventTag, bool bPinned = false);\n\n\t/** \n\t* If an event was pinned, this will unpin it. If you wish to re-pin a different event you need to unpin the old event first.\n\t*/\n\tUFUNCTION(BlueprintCallable, meta = (WorldContext = \"WorldContextObject\"), Category = \"GlobalEventSystem\")\n\tstatic void GESUnpinEvent(UObject* WorldContextObject, const FString& Domain = TEXT(\"global.default\"), const FString& Event = TEXT(\"\"));\n\n\t/**\n\t* GES Options are global and affect things like logging and param verification (performance options)\n\t*/\n\tUFUNCTION(BlueprintCallable, Category = \"GlobalEventSystemOptions\")\n\tstatic void SetGESOptions(const FGESGlobalOptions& InOptions);\n\n\t//Wildcard conversions, used in wildcard event delegates from GESBindEventToWildcardDelegate\n\n\t/** Convert wildcard property into a literal int */\n\tUFUNCTION(BlueprintPure, meta = (DisplayName = \"To Integer (Wildcard Property)\", BlueprintAutocast), Category = \"Utilities|GES\")\n\tstatic bool Conv_PropToInt(const FGESWildcardProperty& InProp, int32& OutInt);\n\n\t/** Convert wildcard property into a literal float */\n\tUFUNCTION(BlueprintPure, meta = (DisplayName = \"To Float (Wildcard Property)\", BlueprintAutocast), Category = \"Utilities|GES\")\n\tstatic bool Conv_PropToFloat(const FGESWildcardProperty& InProp, float& OutFloat);\n\n\t/** Convert wildcard property into a literal bool */\n\tUFUNCTION(BlueprintPure, meta = (DisplayName = \"To Bool (Wildcard Property)\", BlueprintAutocast), Category = \"Utilities|GES\")\n\tstatic bool Conv_PropToBool(const FGESWildcardProperty& InProp, bool& OutBool);\n\n\t/** Convert wildcard property into a string (reference) */\n\tUFUNCTION(BlueprintPure, meta = (DisplayName = \"To String Ref with status (Wildcard Property)\"), Category = \"Utilities|GES\")\n\tstatic bool Conv_PropToStringRef(const FGESWildcardProperty& InProp, FString& OutString);\n\n\t/** Will still warn, but won't return a boolean for conversion status, used for auto-casting to print strings for debugging */\n\tUFUNCTION(BlueprintPure, meta = (DisplayName = \"To String (Wildcard Property)\", BlueprintAutocast), Category = \"Utilities|GES\")\n\tstatic FString Conv_PropToString(const FGESWildcardProperty& InProp);\n\n\t/** Convert wildcard property into a literal Name */\n\tUFUNCTION(BlueprintPure, meta = (DisplayName = \"To Name (Wildcard Property)\", BlueprintAutocast), Category = \"Utilities|GES\")\n\tstatic bool Conv_PropToName(const FGESWildcardProperty& InProp, FName& OutName);\n\n\t/** Convert wildcard property into any struct */\n\tUFUNCTION(BlueprintPure, CustomThunk, meta = (DisplayName = \"To Struct (Wildcard Property)\", CustomStructureParam = \"OutStruct\", BlueprintAutocast), Category = \"Utilities|GES\")\n\tstatic bool Conv_PropToStruct(const FGESWildcardProperty& InProp, TFieldPath<FProperty>& OutStruct);\n\n\t/** Convert wildcard property into any Object */\n\tUFUNCTION(BlueprintPure, meta = (DisplayName = \"To Object (Wildcard Property)\", BlueprintAutocast), Category = \"Utilities|GES\")\n\tstatic bool Conv_PropToObject(const FGESWildcardProperty& InProp, UObject*& OutObject);\n\n\t/** Convert a GameplayTag into a Domain and Event string pair */\n\tUFUNCTION(BlueprintPure, meta = (DisplayName = \"To Domain and Event (GameplayTag)\", BlueprintAutocast), Category = \"Utilities|GES\")\n\tstatic void Conv_TagToDomainAndEvent(FGameplayTag InTag, FString& OutDomain, FString& OutEvent);\n\n\t//Convert property into c++ accessible form\n\tDECLARE_FUNCTION(execGESEmitEventOneParam)\n\t{\n\t\tStack.MostRecentProperty = nullptr;\n\t\tFGESPropertyEmitContext EmitData;\n\n\t\tStack.StepCompiledIn<FObjectProperty>(&EmitData.WorldContext);\n\n\t\t//Determine wildcard property\n\t\tStack.Step(Stack.Object, NULL);\n\t\tFProperty* ParameterProp = CastField<FProperty>(Stack.MostRecentProperty);\n\t\tvoid* PropPtr = Stack.MostRecentPropertyAddress;\n\n\t\tEmitData.Property = ParameterProp;\n\t\tEmitData.PropertyPtr = PropPtr;\n\n\t\tStack.StepCompiledIn<FBoolProperty>(&EmitData.bPinned);\n\t\tStack.StepCompiledIn<FStrProperty>(&EmitData.Domain);\n\t\tStack.StepCompiledIn<FStrProperty>(&EmitData.Event);\n\n\t\tP_FINISH;\n\t\tP_NATIVE_BEGIN;\n\t\tHandleEmit(EmitData);\n\t\tP_NATIVE_END;\n\t}\n\n\tDECLARE_FUNCTION(execGESEmitTagEventOneParam)\n\t{\n\t\tStack.MostRecentProperty = nullptr;\n\t\tFGESPropertyEmitContext EmitData;\n\n\t\tStack.StepCompiledIn<FObjectProperty>(&EmitData.WorldContext);\n\n\t\t//Determine wildcard property\n\t\tStack.Step(Stack.Object, NULL);\n\t\tif (Stack.MostRecentProperty != nullptr)\n\t\t{\n\t\t\tEmitData.Property = CastField<FProperty>(Stack.MostRecentProperty);\n\t\t\tEmitData.PropertyPtr = Stack.MostRecentPropertyAddress;\n\t\t}\n\t\t\n\t\tFGameplayTag Tag;\n\t\tStack.StepCompiledIn<FStructProperty>(&Tag);\n\t\tConv_TagToDomainAndEvent(Tag, EmitData.Domain, EmitData.Event);\n\n\t\t\n\t\tStack.StepCompiledIn<FBoolProperty>(&EmitData.bPinned);\n\n\t\tP_FINISH;\n\t\tP_NATIVE_BEGIN;\n\t\tHandleEmit(EmitData);\n\t\tP_NATIVE_END;\n\t}\n\n\tDECLARE_FUNCTION(execConv_PropToStruct)\n\t{\n\t\tStack.MostRecentProperty = nullptr;\n\t\tFGESWildcardProperty InProp;\n\t\tFGESWildcardProperty OutProp;\n\t\t\n\t\t//Determine copy wildcard property variables\n\t\tStack.StepCompiledIn<FStructProperty>(&InProp);\t\t\n\t\t//Stack.Step(Stack.Object, NULL);\n\t\t//InProp.Property = CastField<FProperty>(Stack.MostRecentProperty);\n\t\t//InProp.PropertyPtr = Stack.MostRecentPropertyAddress;\n\n\t\t//Copy the out struct property address\n\t\t//Stack.StepCompiledIn<FStructProperty>(&OutProp);\n\t\tStack.Step(Stack.Object, NULL);\n\t\tFProperty* ParameterProp = CastField<FProperty>(Stack.MostRecentProperty);\n\t\tvoid* PropPtr = Stack.MostRecentPropertyAddress;\n\n\t\tOutProp.Property = ParameterProp;\n\t\tOutProp.PropertyPtr = PropPtr;\n\t\tbool bDidCopy = false;\n\n\t\tP_FINISH;\n\t\tP_NATIVE_BEGIN;\n\t\tbDidCopy = HandlePropToStruct(InProp, OutProp);\n\t\tP_NATIVE_END;\n\n\t\t*(bool*)RESULT_PARAM = bDidCopy;\n\t}\n\n\nprivate:\n\tstatic void HandleEmit(const FGESPropertyEmitContext& EmitData);\n\tstatic bool HandlePropToStruct(const FGESWildcardProperty& InProp, FGESWildcardProperty& FullProp);\n};\n"
  }
]