diff --git a/CorruptedMemory/Config/DefaultEngine.ini b/CorruptedMemory/Config/DefaultEngine.ini
index 37e392a..c2a307f 100644
--- a/CorruptedMemory/Config/DefaultEngine.ini
+++ b/CorruptedMemory/Config/DefaultEngine.ini
@@ -11,7 +11,7 @@ bUseSplitscreen=True
TwoPlayerSplitscreenLayout=Horizontal
ThreePlayerSplitscreenLayout=FavorTop
GameInstanceClass=/Script/CorruptedMemory.CMGameInstance
-GameDefaultMap=/Game/FirstPerson/Maps/FirstPersonMap.FirstPersonMap
+GameDefaultMap=/Game/Levels/ClientEntry.ClientEntry
ServerDefaultMap=/Game/FirstPerson/Maps/FirstPersonMap.FirstPersonMap
GlobalDefaultGameMode=/Script/CorruptedMemory.CorruptedMemoryGameMode
GlobalDefaultServerGameMode=None
diff --git a/CorruptedMemory/Content/Levels/ClientEntry.umap b/CorruptedMemory/Content/Levels/ClientEntry.umap
index e72f7af..2eb03de 100644
--- a/CorruptedMemory/Content/Levels/ClientEntry.umap
+++ b/CorruptedMemory/Content/Levels/ClientEntry.umap
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c65d6201a37d6d02a79ddaffa38ed0a9779b665fec2aa9a97f81e45405565dad
-size 46902
+oid sha256:14f450331c03198d77da600afab5083c2a67a4a29c42eefa392499a8785fc5e7
+size 45651
diff --git a/CorruptedMemory/Content/StarterContent/HDRI/HDRI_Epic_Courtyard_Daylight.uasset b/CorruptedMemory/Content/StarterContent/HDRI/HDRI_Epic_Courtyard_Daylight.uasset
index 0f99058..1e83a0d 100644
--- a/CorruptedMemory/Content/StarterContent/HDRI/HDRI_Epic_Courtyard_Daylight.uasset
+++ b/CorruptedMemory/Content/StarterContent/HDRI/HDRI_Epic_Courtyard_Daylight.uasset
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:bc35eb2d43a47427d30aba0196f9eac90d089dd3abca319528c5d25c83510d0d
-size 72364642
+oid sha256:4955f0bbd20f97ba310648018c125a3ab32f0acd401451e3c6ee99a146c38766
+size 66790654
diff --git a/CorruptedMemory/Content/StarterContent/Particles/P_Ambient_Dust.uasset b/CorruptedMemory/Content/StarterContent/Particles/P_Ambient_Dust.uasset
index 20fff9d..bf2e439 100644
--- a/CorruptedMemory/Content/StarterContent/Particles/P_Ambient_Dust.uasset
+++ b/CorruptedMemory/Content/StarterContent/Particles/P_Ambient_Dust.uasset
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:593484020a07aeb851ff8366176138edba2aee783b44cc29bf84fa5d7e183078
-size 53232
+oid sha256:01ef616c7a8bd90cd1b7a13efb18a56f33346efbae51efa31f09804478b7621d
+size 43456
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Config/FilterPlugin.ini b/CorruptedMemory/Plugins/SocketIOClient/Config/FilterPlugin.ini
new file mode 100644
index 0000000..ccebca2
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Config/FilterPlugin.ini
@@ -0,0 +1,8 @@
+[FilterPlugin]
+; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
+; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
+;
+; Examples:
+; /README.txt
+; /Extras/...
+; /Binaries/ThirdParty/*.dll
diff --git a/CorruptedMemory/Plugins/SocketIOClient/LICENSE b/CorruptedMemory/Plugins/SocketIOClient/LICENSE
new file mode 100644
index 0000000..1b6b410
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/LICENSE
@@ -0,0 +1,28 @@
+The MIT License (MIT)
+
+Socket.IO Client Unreal
+Copyright (c) 2016-present, Jan Kaniewski (Getnamo) & Contributors
+
+VARest
+Copyright (c) 2014-present, Vladimir Alyamkin
+
+Socket.IO C++ Client
+Copyright (c) 2015-present, Melo Yao & Contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/CorruptedMemory/Plugins/SocketIOClient/README.md b/CorruptedMemory/Plugins/SocketIOClient/README.md
new file mode 100644
index 0000000..c01d172
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/README.md
@@ -0,0 +1,856 @@
+# SocketIOClient-Unreal
+Socket.IO client plugin for the Unreal Engine.
+
+[![GitHub release](https://img.shields.io/github/release/getnamo/SocketIOClient-Unreal.svg)](https://github.com/getnamo/SocketIOClient-Unreal/releases)
+[![Github All Releases](https://img.shields.io/github/downloads/getnamo/SocketIOClient-Unreal/total.svg)](https://github.com/getnamo/SocketIOClient-Unreal/releases)
+
+[Socket.IO](http://socket.io/) is a performant real-time bi-directional communication library. There are two parts, the server written in node.js and the client typically javascript for the web. There are alternative client implementations and this repo uses the [C++11 client library](https://github.com/socketio/socket.io-client-cpp) ported to Unreal.
+
+Socket.IO Lib uses _asio_, _rapidjson_, and _websocketpp_. SIOJson is originally forked from ufna's [VaRest](https://github.com/ufna/VaRest)
+
+[Unreal Forum Thread](https://forums.unrealengine.com/showthread.php?110680-Plugin-Socket-io-Client)
+
+[Discord Server](https://discord.gg/qfJUyxaW4s)
+
+Recommended socket.io server version: 3.0+
+
+*Tip: This is a sizeable readme, quickly find your topic with ```Ctrl+F``` and a search term e.g. namespaces*
+
+### Contribute! Current Main Issues:
+
+Current platform issues:
+
+* Xbox/PS platforms untested - see [issue 117](https://github.com/getnamo/SocketIOClient-Unreal/issues/117)
+
+Current TLS/SSL issues:
+
+* Certificate verification is not implemented; setting `bShouldVerifyTLSCertificate` will always fail - see [issue 303](https://github.com/getnamo/SocketIOClient-Unreal/issues/303)
+
+## Socket.IO Server Compatibility
+
+Some features in later versions of this plugin are not supported by earlier versions of the Socket.IO server API. See the compatibility table below for more details
+
+
+
+## Quick Install & Setup ##
+
+### Via Github Releases
+ 1. [Download Latest Release](https://github.com/getnamo/SocketIOClient-Unreal/releases)
+ 2. Create new or choose project.
+ 3. Browse to your project folder (typically found at Documents/Unreal Project/{Your Project Root})
+ 4. Copy *Plugins* folder into your Project root.
+ 5. Plugin should be now ready to use.
+
+ ### Via Unreal Engine Marketplace (helps support plugin development and maintenance)
+
+ Available at this link: [Socket.IO Client - Marketplace](https://www.unrealengine.com/marketplace/socket-io-client)
+
+ ### Via Git clone
+
+ 1. Create new or choose project.
+ 2. Browse to your project folder (typically found at Documents/Unreal Project/{Your Project Root})
+ 3. Create a *Plugins* in your project root folder and use that path for step 4. command.
+ 4. Git clone. Repository uses submodules, so recommended command is:
+
+```git clone https://github.com/getnamo/SocketIOClient-Unreal.git --recurse-submodules```
+
+ 5. Build your project (needs to be c++/mixed) and then the plugin will get built with it.
+
+## Example Project - Chat
+
+For an example project check out https://github.com/getnamo/SocketIOClient-Unreal-example which contains both server and client parts required to try out socket.io based chat, from Unreal to any other client and vice versa.
+
+## How to use - BP Basics
+
+Add the SocketIO Client Component to your blueprint actor of choice
+
+![IMG](http://i.imgur.com/lSkfHQ2.png)
+
+By default the component will auto connect *on begin play* to your default address and port [http://localhost:3000](http://localhost:3000). You can change this default address to connect to your service instead as well as add any query/header or other URL parameters you need.
+
+![IMG](https://i.imgur.com/qInVfuK.png)
+
+If you want to connect at your own time, you change the default variable *Should Auto Connect* to false and then call *Connect* with your address
+
+### Receiving an Event
+There are three main ways to receive socket.io events.
+
+#### Receive To Function
+The recommended way is to bind an event directly to a function or custom event. E.g. receiving the event "chatMessage" with a String parameter.
+
+![IMG](https://i.imgur.com/YHSf62f.png)
+
+Keep in mind that you can have this be a proper function instead of a custom event
+
+![IMG](https://i.imgur.com/QyItHsG.png)
+
+For the receiving type, if it's known, you can specify the exact type (like String in the example above see https://github.com/getnamo/SocketIOClient-Unreal#emit-with-callback for supported signatures), or if you're not sure or it's a complex type (e.g. a struct) you set it to a SIOJsonValue and use functions to decode it (see https://github.com/getnamo/SocketIOClient-Unreal#decoding-responses for details)
+
+![IMG](https://i.imgur.com/nNQTZ6j.png)
+
+#### Receive To Delegate
+
+The other recommended way (available since v2.2.0) is to bind your event directly to a delegate.
+
+![IMG](https://i.imgur.com/3DWxDi1.png)
+
+See https://github.com/getnamo/SocketIOClient-Unreal#decoding-responses for data conversion nodes from SIOJsonValues.
+
+#### Receive To Generic Event
+
+You can also receive an event to a generic unreal event. First you bind the socket.io event to the generic event.
+
+![IMG](https://i.imgur.com/mizdErw.png)
+
+and then you receive it and filter the results by checking against the Event Name.
+
+![IMG](https://i.imgur.com/KVZaKGh.png)
+
+### Sending data or Emitting Events to Server
+
+If you want to send information to the server, emit events on the SocketIO Client Component, e.g. pressing M to emit a 'chat message' string
+
+![IMG](http://i.imgur.com/nihMSPz.png)
+
+### Note on Printing Json Value
+
+A very common mistake is to drag from a ```SIOJsonValue``` to the print string for basic debug. By default the engine will pick ```Get Display Name```, this is incorrect as it will only print out the container objects engine name and nothing about the actual value. Instead you want to use either ```Encode Json``` or ```As String(SIOJson Value)```, either of the two will re-encode the value as a json string. Encode Json is also available for ```SIOJsonObject``` types.
+
+![printing a value](https://i.imgur.com/1kwpBYS.png)
+
+## Blueprint - Advanced
+
+### Simple Json
+
+You can formulate any *SIOJsonValue* directly in blueprint. Apart from native basic types which are supported directly via conversion and e.g. *Construct Json String Value*, you can construct *SIOJsonObject*s and fill their fields.
+
+![IMG](http://i.imgur.com/PnxD6Ui.png)
+
+Start with *Construct Json Object* then set any desired fields. In this instance we wanted to make the JSON *{"myString":"Hello"}* so we used *Set String Field* and then auto-convert the object into a message.
+
+### Complex Json
+
+By combining arrays and objects you can form almost any data type, nest away!
+
+![IMG](http://i.imgur.com/lj07Jsw.png)
+
+### Structs
+
+The plugin supports making *SIOJsonValue*s from any unreal structs you make, including ones defined purely in blueprints!
+
+An easy example of a familiar struct is the *Vector* type
+
+![IMG](http://i.imgur.com/mPHOw0C.png)
+
+But you can make a custom type and emit it or nest it inside other *SIOJsonValue*s which should make it easy to organize your data however you want it.
+
+![IMG](http://i.imgur.com/czi0AIF.png)
+
+### Binary
+
+Socket.IO spec supports raw binary data types and these should be capable of being mixed in with other JSON types as usual. This plugin supports binaries as a first class citizen in blueprints and any arrays of bytes can be embedded and decoded in the chain as expected.
+
+![IMG](http://i.imgur.com/PqxEJqI.png)
+
+Since v1.2.6 binaries (byte arrays) are fully supported inside structs as well.
+
+#### Binary to base64 string fallback (since v1.2.6)
+
+If you encode a ```SIOJsonValue``` or ```SIOJsonObject``` to JSON string (i.e. not using socket.io protocol for transmission) then binaries will get encoded in base64. Conversely passing in a ```SIOJsonValue``` of string type for decoding into a binary target (e.g. ```Get Binary Field```) will attempt base64 decoding of that string to allow for non-socket.io protocol fallback. Keep in mind that base64 encoding has a 33% overhead (6/8 useful bits).
+
+### Decoding Responses
+
+There are many ways to decode your *SIOJsonValue* message, it all depends on your data setup. You can even decode your *JsonObject*s directly into structs, if the JSON structure has matching variable names.
+
+![IMG](http://i.imgur.com/urAh2TH.png)
+
+Make sure your server is sending the correct type of data, you should not be encoding your data into json strings on the server side if you want to decode them directly into objects in the Unreal side, send the objects as is and the socket.io protocol will handle serialization on both ends.
+
+#### Json Object to Struct Example
+
+Keep in mind that you need to match your json object names (case doesn't matter) and if you are using objects of objects, the sub-objects will need their own structs to build the full main struct. For example if we have the following json object:
+
+```json
+{
+ "Title":
+ {
+ "Text":"Example",
+ "Caption":"Used to guide developers"
+ }
+}
+```
+
+##### Defined Struct
+
+Make a substruct for the _Title_ object with two string variables, _Text_ and _Caption_ matching your json object format.
+
+![title substruct](https://i.imgur.com/1L8T4Tl.png)
+
+and then make the main struct with the substruct and a member variable with the _Title_ property name to match your json.
+
+![main struct](https://i.imgur.com/gcyDK1l.png)
+
+##### Alternative Struct
+
+If you know that you will have a set of properties of the same type (blueprints only support maps of same type), then you can use a Map property. In this example our title sub-object only has String:String type properties so we could use a String:String map named _Title_ instead of the sub-struct.
+
+![alt struct](https://i.imgur.com/w6tttIh.png)
+
+If you wish to mix types in an object, you will need to use defined structs as before.
+
+##### Arrays of array
+An Array of arrays is not supported in Unreal structs, a workaround is to use an array of structs that contains an array of the data type you want. The server side will need to adapt what it sends or you can decode using json fields.
+
+### Conversion
+
+Most primitive types have auto-conversion nodes to simplify your workflow. E.g. if you wanted to emit a float literal you can get a reference to your float and simply drag to the *message* parameter to auto-convert into a *SIOJsonValue*.
+
+![IMG](http://i.imgur.com/4T79TUV.gif)
+
+Supported auto-conversion
+
+* Float
+* Int
+* Bool
+* SIOJsonObject
+* String *-technically supported but it will by default pick **Get Display Name** instead, use **As String** to get desired result*
+
+### Emit with Callback
+
+You can have a callback when, for example, you need an acknowledgement or if you're fetching data from the server. You can respond to this callback straight in your blueprint. Keep in mind that the server can only use the callback *once* per emit.
+
+![IMG](http://i.imgur.com/Ed01Jq0.png)
+
+Instead of using *Emit* you use *Emit With Callback* and by default the target is the calling blueprint so you can leave that parameter blank and simply type your function name e.g. *OnEcho* function.
+
+![IMG](http://i.imgur.com/hXMXDd2.png)
+
+If the function is missing or named incorrectly, your output log will warn you.
+
+![IMG](http://i.imgur.com/PQinDYy.gif)
+
+Your function expects a *SIOJsonValue* reference signature. By default this contains your first response value from you callback parameter. If your callback uses more than one parameter, make a second *SIOJsonValue* Input parameter which contains an array of all the responses.
+
+Since 0.6.8, if you know your data type you can use that signature directly in your function name. E.g. if you're sending a callback with a float value you can make a function with the matching name and only one float parameter as your signature.
+
+Supported Signatures:
+- SIOJsonValue
+- SIOJsonObject
+- String
+- Float
+- Int
+- Bool
+- Byte Array
+
+#### Emit With Graph Callback
+
+Since v1.1.0 you can get results directly to your calling graph function. Use the ```Emit with Graph Callback``` method and simply drag off from the completed node to receive your result when your server uses the callback. This feature should be useful for fetching data from the server without breaking your graph flow.
+
+![graph callback](https://i.imgur.com/CbFHxRj.png)
+
+Limitations:
+- Can only be used in Event Graphs (BP functions don't support latent actions)
+
+#### Emit with Callback node.js server example
+
+If you're unsure how the callbacks look like on the server side, here is a basic example:
+
+```javascript
+const io = require('socket.io')(http);
+
+io.on('connection', (socket) => {
+
+ //...
+
+ socket.on('getData', (msg, callback) => {
+ //let's say your data is an object
+ let result = {};
+
+ /* do something here to get your data */
+
+ //callback with the results, this will call your bound function inside your blueprint
+ callback(result);
+ });
+};
+```
+
+See https://socket.io/docs/server-api/#socket-on-eventName-callback for detailed API.
+
+### Binding Events to Functions
+
+Instead of using the event graph and comparing strings, you can bind an event directly to a function. The format to make the function is the same as [callbacks](https://github.com/getnamo/SocketIOClient-Unreal#emit-with-callback).
+
+![IMG](http://i.imgur.com/7fA1qca.png)
+
+#### Receiving Events on non-game thread
+
+Since v1.1.0 use ```Bind Event to Function``` and change the thread override option to ```Use Network Thread```.
+
+![](https://user-images.githubusercontent.com/542365/69472108-f95c1280-0d5a-11ea-9667-579b22d77ac3.png)
+
+NB: You cannot make or destroy uobjects on non-gamethreads and be mindful of your access patterns across threads to ensure you don't get a race condition. See https://github.com/getnamo/SocketIOClient-Unreal#blueprint-multithreading for other types of threading utility.
+
+
+### Namespaces
+
+Before v1.2.3 you can only join namespaces via using ```Emit``` and ```Bind Event``` with a namespace specified. This will auto-join your namespace of choice upon calling either function.
+
+![Example join emit](https://i.imgur.com/9rOy5Bp.png)
+
+A good place to bind your namespace functions is using the dedicated namespace connected/disconnected events
+
+![namespace events](https://i.imgur.com/ZyF41j4.png)
+
+Below is an example of binding a namespace event when you've connected to it. Keep in mind you'd need to join the namespace for this event to get bound and you can bind it elsewhere e.g. on beginplay instead if preferred.
+
+![namespace bind event](https://i.imgur.com/yWTOLB5.png)
+
+Since v1.2.3 you can now also join and leave a namespace explicitly using ```Join Namespace``` and ```Leave Namespace``` functions.
+
+![join and leave namespaces](https://i.imgur.com/bqBSp1q.png)
+
+### Rooms
+
+The Rooms functionality is solely based on server implementation, see Socket.IO api for details: https://socket.io/docs/v4/rooms/.
+
+Generally speaking you can have some kind of event to emit to your server specifying the unreal client wants to join or leave a room and then the server would handle that request for you. If you wanted to emit a message to a specific user in a room you'd need a way to get a list of possible users (e.g. get the list on joining the room or via a callback). Then selecting a user from the list and passing their id along with desired data in an emit call to the server which would forward the data to the user in the room you've joined.
+
+See [this basic tutorial](https://www.tutorialspoint.com/socket.io/socket.io_rooms.htm) for implementing rooms on the server.
+
+### Complex Connect
+
+You can fill the optional _Query_ and _Headers_ parameters to pass in e.g. your own headers for authentication.
+
+The input type for both fields is a _SIOJsonObject_ with purely string fields or leaving it empty for default.
+
+Here's an example of constructing a single header _X-Forwarded-Host: qnova.io_ and then connecting.
+
+![connectwithheader](https://cloud.githubusercontent.com/assets/542365/25309683/63bfe26e-27cb-11e7-877e-0590e40605f3.PNG)
+
+Since v2.3.0 you can also connect using a `SIOConnectParams` struct
+
+![connectwithparams](https://user-images.githubusercontent.com/542365/164123044-88af7b36-36b2-4364-abe6-75c133d21e8a.png)
+
+### Plugin Scoped Connection
+
+If you want your connection to survive level transitions, you can tick the class default option Plugin Scoped Connection. Then if another component has the same plugin scoped id, it will re-use the same connection. Note that if this option is enabled the connection will not auto-disconnect on *End Play* and you will need to either manually disconnect or the connection will finally disconnect when the program exits.
+
+![plugin scoped connection](https://i.imgur.com/lE8BHbN.png)
+
+This does mean that you may not receive events during times your actor does not have a world (such as a level transition without using a persistent parent map to which the socket.io component actor belongs). If this doesn't work for you consider switching to C++ and using [FSocketIONative](https://github.com/getnamo/SocketIOClient-Unreal#c-fsocketionative), which doesn't doesn't depend on using an actor component.
+
+### Statically Constructed SocketIOClient Component
+
+Since v1.1.0 there is a BPFunctionLibrary method ```Construct SocketIOComponent``` which creates and correctly initializes the component in various execution contexts. This allows you to add and reference a SocketIOClient component inside a non-actor blueprint. Below is an example use pattern. It's important to save the result from the construct function into a member variable of your blueprint or the component will be garbage collected.
+
+![static example](https://i.imgur.com/EX4anxd.png)
+
+#### Note on Auto-connect
+
+Game modes do have actor owners and will correctly respect ```bShouldAutoConnect```. The connection happens one tick after construction so you can disable the toggle and connect at own time.
+
+Game Instances do *not* have actor owners and therefore cannot register and initialize the component. The only drawback is that you must manually connect. ```bShouldAutoConnect``` is disabled in this context.
+
+#### Note on Emit with Graph Callback
+
+Non actor-owners such as Game Instances cannot receive the graph callbacks due to invalid world context. This only affects this one callback method, other methods work as usual.
+
+## TLS / SSL
+
+TLS is supported for both C++ and BP if your platform supports OpenSSL (see https://github.com/getnamo/SocketIOClient-Unreal/blob/master/Source/SocketIOLib/SocketIOLib.Build.cs#L64 for currently supported platforms). Simply use a `https` or `wss` URL for host target and it will use TLS by default. You can also force TLS on any URL by using `bForceTLS` set to true.
+
+Gist with example node.js TLS server and instructions for self signing certificate for testing purposes: https://gist.github.com/getnamo/fe6c9574dc971066813fd291c363ee04
+
+*NB: Certificate verification is currently not implemented; `bShouldVerifyTLSCertificate` is set to false by default, setting it to true will currently cause connections to fail. See [issue 303](https://github.com/getnamo/SocketIOClient-Unreal/issues/303).*
+
+## CoreUtility
+
+Plugin contains the CoreUtility module with a variety of useful C++ and blueprint utilities.
+
+#### CUFileComponent
+
+Provides and easy way to save/load files to common project directories. Example usecase: Encode a received message to JSON and pass the bytes in to ```SaveBytesToFile``` to store your response.
+
+See https://github.com/getnamo/SocketIOClient-Unreal/blob/master/Source/CoreUtility/Public/CUFileComponent.h for details.
+
+#### CULambdaRunnable
+
+A wrapper for simple multi-threading in C++. Used all over the plugin to handle threading with lambda captured scopes and simpler latent structs for bp calls. Below is an example of a call to a background thread and return to gamethread:
+
+```c++
+//Assuming you're in game thread
+FCULambdaRunnable::RunLambdaOnBackGroundThread([]
+{
+ //Now you're in a background thread
+ //... do some calculation and let's callback a result
+ int SomeResult;
+ FCULambdaRunnable::RunShortLambdaOnGameThread([SomeResult]
+ {
+ //You're back to the game thread
+ //... display or do something with the result safely
+ });
+});
+```
+
+See https://github.com/getnamo/SocketIOClient-Unreal/blob/master/Source/CoreUtility/Public/CULambdaRunnable.h for full API.
+
+For blueprint multi-threading see https://github.com/getnamo/SocketIOClient-Unreal#blueprint-multithreading.
+
+#### CUMeasureTimer
+
+Static string tagged measurement of durations. Useful for sprinkling your code with duration messages for optimization. CUBlueprintLibrary exposes this utility to blueprint.
+
+See https://github.com/getnamo/SocketIOClient-Unreal/blob/master/Source/CoreUtility/Public/CUBlueprintLibrary.h for details.
+
+#### CUBlueprintLibrary
+
+Global blueprint utilities.
+- Conversions: String<->Bytes, Texture2D<->Bytes, Opus<->Wav, Wav<->Soundwave
+- Time string
+- Unique ID
+- Measurement timers based on CUMeasureTimer
+- Blueprint multithreading calls
+
+##### Blueprint Multithreading
+
+Enables easy calling of blueprint functions on background threads and returning back to the game thread to deal with the results. This enables long running operations to not block the game thread while they work.
+
+Two variants are available as of v1.2.8: ```Call Function On Thread``` and ```Call Function on Thread Graph Return```. Generally the latent variant is recommended for easier function chaining and it will always return on the game thread when finished.
+
+In the example below we use the latent variant to call two background tasks in succession, return to read the result on the game thread and measure the overall time taken.
+
+[![latent multithreading](https://i.imgur.com/ryORGF5.png)](https://i.imgur.com/G4ZPtyt.mp4)
+(click image to see video of performance)
+
+The first task prepares an array with a million floats, the second sums them up. This should take ~1 sec to run, but from the video above you can see that it doesn't block the game thread at all. We use class member variables to pass data between threads. It's important to only have one function modifying the same data at any time and you cannot make or destroy UObjects on background threads so it may be necessary to callback to the gamethread to do allocations if you use UObjects.
+
+#### CUOpusCoder
+
+Standalone opus coder that doesn't depend on the online subsystem. Useful for custom VOIP solutions.
+
+See https://github.com/getnamo/SocketIOClient-Unreal/blob/master/Source/CoreUtility/Public/CUOpusCoder.h for details.
+
+## How to use - C++
+
+### Setup
+
+To use the C++ code from the plugin add _SocketIOClient_, _SocketIOLib_, and _Json_ as dependency modules in your project build.cs. If you're using conversion methods you will also have to add _SIOJson_.
+
+```c#
+PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "SocketIOClient", "SocketIOLib", "Json", "SIOJson" });
+```
+
+This guide assumes you want to use the client component method. See the [_FSocketIONative_](https://github.com/getnamo/SocketIOClient-Unreal#c-fsocketionative) section for non-actor based C++ details.
+
+```#include "SocketIOClientComponent.h"``` and add *USocketIoClientComponent* to your actor of choice via e.g. a UProperty
+
+and *CreateDefaultSubobject* in your constructor
+
+```c++
+SIOClientComponent = CreateDefaultSubobject(TEXT("SocketIOClientComponent"));
+```
+
+or reference it from another component by getting it on begin play e.g.
+
+```c++
+SIOClientComponent = Cast(this->GetOwner()->GetComponentByClass(USocketIOClientComponent::StaticClass()));
+if (!SIOClientComponent)
+{
+ UE_LOG(LogTemp, Warning, TEXT("No sister socket IO component found"));
+ return;
+}
+else
+{
+ UE_LOG(LogTemp, Log, TEXT("Found SIOClientComponent: %s"), *SIOComponent->GetDesc());
+}
+```
+
+### Connect / Disconnect
+
+To connect simply change your address, the component will auto-connect on component initialization.
+
+
+```c++
+USocketIOClientComponent* SIOClientComponent; //get a reference or add as subobject in your actor
+
+//the component will autoconnect, but you may wish to change the url before it does that via
+SIOClientComponent->AddressAndPort = TEXT("http://127.0.0.1:3000"); //change your address
+```
+
+You can also connect at your own time by disabling auto-connect and connecting either to the default address or a custom one
+
+```c++
+//you can also disable auto connect and connect it at your own time via
+SIOClientComponent->ShouldAutoConnect = false;
+SIOClientComponent->Connect();
+
+//You can also easily disconnect at some point, reconnect to another address
+SIOClientComponent->Disconnect();
+SIOClientComponent->Connect(TEXT("http://127.0.0.1:3000"));
+```
+
+### Receiving Events
+
+To receive events call _OnNativeEvent_ and pass in your expected event name and callback lambda or function with ```void(const FString&, const TSharedPtr&)``` signature. Optionally pass in another FString to specify namespace, omit if not using a namespace (default ```TEXT("/")```).
+
+```c++
+SIOClientComponent->OnNativeEvent(TEXT("MyEvent"), [](const FString& Event, const TSharedPtr& Message)
+{
+ //Called when the event is received. We can e.g. log what we got
+ UE_LOG(LogTemp, Log, TEXT("Received: %s"), *USIOJConvert::ToJsonString(Message));
+});
+```
+
+Message parameter is a [FJsonValue](http://api.unrealengine.com/INT/API/Runtime/Json/Dom/FJsonValue/), if you have a complex message you'll most commonly want to decode it into a [FJsonObject](http://api.unrealengine.com/INT/API/Runtime/Json/Dom/FJsonObject/) via ```AsObject()```.
+
+Note that this is equivalent to the blueprint ```BindEventToFunction``` function and should be typically called once e.g. on beginplay.
+
+### Emitting Events
+
+In C++ you can use *EmitNative*, *EmitRaw*, or *EmitRawBinary*. *EmitNative* is fully overloaded and expects all kinds of native Unreal data types and is the recommended method.
+
+#### String
+
+Emit an FString (or TEXT() macro).
+
+```c++
+SIOClientComponent->EmitNative(TEXT("nativeTest"), TEXT("hi"));
+```
+
+#### Number
+
+Emit a double
+
+```c++
+SIOClientComponent->EmitNative(TEXT("nativeTest"), -3.5f);
+```
+
+#### Boolean
+
+Emit a raw boolean
+
+```c++
+SIOClientComponent->EmitNative(TEXT("nativeTest"), true);
+```
+
+#### Binary or raw data
+
+Emit raw data via a TArray
+
+```c++
+TArray Buffer; //null terminated 'Hi!'
+Buffer.Add(0x48);
+Buffer.Add(0x69);
+Buffer.Add(0x21);
+Buffer.Add(0x00);
+
+SIOClientComponent->EmitNative(TEXT("nativeTest"), Buffer);
+```
+
+or
+
+```c++
+SIOComponent->EmitRawBinary(TEXT("myBinarySendEvent"), Buffer.GetData(), Buffer.Num());
+```
+
+#### FJsonObject - Simple
+
+Option 1 - Shorthand
+
+```c++
+//Basic one field object e.g. {"myKey":"myValue"}
+auto JsonObject = USIOJConvert::MakeJsonObject();
+JsonObject->SetStringField(TEXT("myKey"), TEXT("myValue"));
+
+SIOClientComponent->EmitNative(TEXT("nativeTest"), JsonObject);
+```
+
+Option 2 - Standard
+
+```c++
+TSharedPtr JsonObject = MakeShareable(new FJsonObject);
+```
+
+#### FJsonObject - Complex Example
+
+A nested example using various methods
+
+```c++
+//All types, nested
+TSharedPtr JsonObject = MakeShareable(new FJsonObject); //make object option2
+JsonObject->SetBoolField(TEXT("myBool"), false);
+JsonObject->SetStringField(TEXT("myString"), TEXT("Socket.io is easy"));
+JsonObject->SetNumberField(TEXT("myNumber"), 9001);
+
+JsonObject->SetField(TEXT("myBinary1"), USIOJConvert::ToJsonValue(Buffer)); //binary option1 - shorthand
+JsonObject->SetField(TEXT("myBinary2"), MakeShareable(new FJsonValueBinary(Buffer))); //binary option2
+
+JsonObject->SetArrayField(TEXT("myArray"), ArrayValue);
+JsonObject->SetObjectField(TEXT("myNestedObject"), SmallObject);
+
+SIOClientComponent->EmitNative(TEXT("nativeTest"), JsonObject);
+```
+
+#### Callback Example
+
+Below is an example of emitting a simple object with the server using the passed in callback to return a response or acknowledgement.
+
+```c++
+//Make an object {"myKey":"myValue"}
+TSharedPtr JsonObject = MakeShareable(new FJsonObject);
+JsonObject->SetStringField(TEXT("myKey"), TEXT("myValue"));
+
+//Show what we emitted
+UE_LOG(LogTemp, Log, TEXT("1) Made a simple object and emitted: %s"), *USIOJConvert::ToJsonString(JsonObject));
+
+//Emit event "callbackTest" expecting an echo callback with the object we sent
+SIOClientComponent->EmitNative(TEXT("callbackTest"), JsonObject, [&](auto Response)
+{
+ //Response is an array of JsonValues, in our case we expect an object response, grab first element as an object.
+ auto Message = Response[0]->AsObject();
+
+ //Show what we received
+ UE_LOG(LogTemp, Log, TEXT("2) Received a response: %s"), *USIOJConvert::ToJsonString(Message));
+});
+```
+
+#### UStruct
+
+Plugin supports automatic conversion to/from UStructs, below is an example of a struct roundtrip, being in Json format on the server side.
+
+```c++
+USTRUCT()
+struct FTestCppStruct
+{
+ GENERATED_BODY()
+
+ UPROPERTY()
+ int32 Index;
+
+ UPROPERTY()
+ float SomeNumber;
+
+ UPROPERTY()
+ FString Name;
+};
+```
+
+```c++
+//Set your struct variables
+FTestCppStruct TestStruct;
+TestStruct.Name = TEXT("George");
+TestStruct.Index = 5;
+TestStruct.SomeNumber = 5.123f;
+
+SIOClientComponent->EmitNative(TEXT("callbackTest"), FTestCppStruct::StaticStruct(), &TestStruct, [&](auto Response)
+{
+ auto Message = Response[0]->AsObject();
+
+ //Show what we received
+ UE_LOG(LogTemp, Log, TEXT("Received a response: %s"), *USIOJConvert::ToJsonString(Message));
+
+ //Set our second struct to the new values
+ USIOJConvert::JsonObjectToUStruct(Message, FTestCppStruct::StaticStruct(), &MemberStruct);
+
+ //Show that struct
+ UE_LOG(LogTemp, Log, TEXT("Our received member name is now: %s"), *MemberStruct.Name);
+});
+```
+
+## C++ FSocketIONative
+
+If you do not wish to use Unreal AActors or UObjects, you can use the native base class [FSocketIONative](https://github.com/getnamo/SocketIOClient-Unreal/blob/master/Source/SocketIOClient/Public/SocketIONative.h). Please see the class header for API. It generally follows a similar pattern to ```USocketIOClientComponent``` with the exception of native callbacks which you can for example see in use here: https://github.com/getnamo/SocketIOClient-Unreal/blob/master/Source/SocketIOClient/Private/SocketIOClientComponent.cpp#L81
+
+### Example FSocketIONative Custom Game Instance
+
+SIOTestGameInstance.h
+```c++
+#include "CoreMinimal.h"
+#include "Engine/GameInstance.h"
+#include "SocketIONative.h"
+#include "SIOTestGameInstance.generated.h"
+UCLASS()
+class SIOCLIENT_API USIOTestGameInstance : public UGameInstance
+{
+ GENERATED_BODY()
+
+ virtual void Init() override;
+ virtual void Shutdown() override;
+
+ TSharedPtr Socket;
+};
+```
+SIOTestGameInstance.cpp
+```c++
+#include "SIOTestGameInstance.h"
+#include "SocketIOClient.h"
+void USIOTestGameInstance::Init()
+{
+ Super::Init();
+
+ Socket= ISocketIOClientModule::Get().NewValidNativePointer();
+
+ Socket->Connect("http://localhost:3000", nullptr, nullptr);
+
+ Socket->OnEvent(TEXT("MyEvent"), [this](const FString& Event, const TSharedPtr& Message)
+ {
+ UE_LOG(LogTemp, Log, TEXT("Received: %s"), *USIOJConvert::ToJsonString(Message));
+ });
+
+ Socket->Emit(TEXT("MyEmit"), TEXT("hi"));
+}
+
+void USIOTestGameInstance::Shutdown()
+{
+ Super::Shutdown();
+
+ if (Socket.IsValid())
+ {
+ ISocketIOClientModule::Get().ReleaseNativePointer(Socket);
+ Socket = nullptr;
+ }
+}
+```
+
+## Alternative Raw C++ Complex message using sio::message
+
+see [sio::message](https://github.com/socketio/socket.io-client-cpp/blob/master/src/sio_message.h) for how to form a raw message. Generally it supports a lot of std:: variants e.g. std::string or more complex messages e.g. [socket.io c++ emit readme](https://github.com/socketio/socket.io-client-cpp#emit-an-event). Note that there are static helper functions attached to the component class to convert from std::string to FString and the reverse.
+
+```c++
+static std::string USIOMessageConvert::StdString(FString UEString);
+
+static FString USIOMessageConvert::FStringFromStd(std::string StdString);
+```
+
+and assuming
+
+```c++
+FSocketIONative* NativeClient;
+```
+
+e.g. emitting *{type:"image"}* object
+
+```c++
+//create object message
+auto message = sio::object_message::create();
+
+//set map property string
+message->get_map()["type"] = sio::string_message::create(std::string("image"));
+
+//emit message
+NativeClient->EmitRaw(ShareResourceEventName, message);
+```
+
+with a callback
+
+```c++
+NativeClient->EmitRawWithCallback(FString("myRawMessageEventWithAck"), string_message::create(username), [&](message::list const& msg) {
+ //got data, handle it here
+});
+```
+
+
+### Receiving Events
+
+To receive events you can bind lambdas which makes things awesomely easy e.g.
+
+#### Binary
+
+```c++
+NativeClient->OnBinaryEvent([&](const FString& Name, const TArray& Buffer)
+ {
+ //Do something with your buffer
+ }, FString(TEXT("myBinaryReceiveEvent")));
+```
+
+#### Complex message using sio::message
+
+See [sio::message](https://github.com/socketio/socket.io-client-cpp/blob/master/src/sio_message.h) or [socket.io c++ readme](https://github.com/socketio/socket.io-client-cpp#emit-an-event) for examples.
+
+e.g. expecting a result {type:"some type"}
+
+```c++
+NativeClient->OnRawEvent([&](const FString& Name, const sio::message::ptr& Message)
+ {
+ //if you expected an object e.g. {}
+ if (Message->get_flag() != sio::message::flag_object)
+ {
+ UE_LOG(LogTemp, Warning, TEXT("Warning! event did not receive expected Object."));
+ return;
+ }
+ auto MessageMap = Message->get_map();
+
+ //use the map to decode an object key e.g. type string
+ auto typeMessage = MessageMap["type"];
+ if (typeMessage->get_flag() == typeMessage->flag_string)
+ {
+ FString TypeValue = USocketIOClientComponent::FStringFromStd(typeMessage->get_string());
+
+ //do something with your received type value!
+ }
+
+ }, FString(TEXT("myArbitraryReceiveEvent")));
+```
+
+## Http JSON requests
+
+Using e.g. https://gist.github.com/getnamo/ced1e6fbee122b640169f5fb867ed540 server
+
+You can post simple JSON requests using the SIOJRequest (this is the same architecture as [VARest](https://github.com/ufna/VaRest)).
+
+![Sending a JSON post request](https://i.imgur.com/UOJHcP0.png)
+
+These request functions are available globally.
+
+## Packaging
+
+### C++
+Works out of the box.
+
+### Blueprint
+
+#### Marketplace
+Works out of the box.
+
+#### Github
+If you're using this as a project plugin you will need to convert your blueprint only project to mixed (bp and C++). Follow these instructions to do that: https://allarsblog.com/2015/11/04/converting-bp-project-to-cpp/
+
+![Converting project to C++](https://i.imgur.com/Urwx2TF.png)
+
+e.g. Using the File menu option to convert your project to mixed by adding a C++ file.
+
+### iOS
+
+If you're using non-ssl connections (which as of 1.0 is all that is supported), then you need to enable ```Allow web connections to non-HTTPS websites```
+
+![IOS platform setting](https://i.imgur.com/J7Xzy2j.png)
+
+Its possible you may also need to convert your IP4 to IP6, see https://github.com/getnamo/SocketIOClient-Unreal/issues/136#issuecomment-515337500
+
+### Android
+
+Minimum/Target SDK 21 or higher is recommended, but not required.
+
+
+## License
+
+[![license](https://img.shields.io/github/license/getnamo/SocketIOClient-Unreal.svg)](https://github.com/getnamo/SocketIOClient-Unreal/blob/master/LICENSE)
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Resources/Icon128.png b/CorruptedMemory/Plugins/SocketIOClient/Resources/Icon128.png
new file mode 100644
index 0000000..c23b9e2
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Resources/Icon128.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9e74629fc13925f7415503670731b99a4a402ca35f5ac83736e56ad01047a56a
+size 13131
diff --git a/CorruptedMemory/Plugins/SocketIOClient/SocketIOClient.uplugin b/CorruptedMemory/Plugins/SocketIOClient/SocketIOClient.uplugin
new file mode 100644
index 0000000..5875bd5
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/SocketIOClient.uplugin
@@ -0,0 +1,78 @@
+{
+ "FileVersion": 3,
+ "Version": 1,
+ "VersionName": "2.5.8",
+ "EngineVersion": "5.1",
+ "FriendlyName": "Socket.IO Client",
+ "Description": "Real-time WebSocket networking via Socket.IO protocol usable from blueprints and c++.",
+ "Category": "Networking",
+ "CreatedBy": "Getnamo",
+ "CreatedByURL": "http://getnamo.com",
+ "DocsURL": "https://github.com/getnamo/SocketIOClient-Unreal",
+ "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/slug/socket-io-client",
+ "SupportURL": "https://github.com/getnamo/SocketIOClient-Unreal/issues",
+ "EnabledByDefault": true,
+ "CanContainContent": false,
+ "IsBetaVersion": false,
+ "Installed": false,
+ "Modules": [
+ {
+ "Name": "SocketIOClient",
+ "Type": "Runtime",
+ "LoadingPhase": "PreDefault",
+ "WhitelistPlatforms": [
+ "Win64",
+ "Linux",
+ "Mac",
+ "Android",
+ "IOS"
+ ]
+ },
+ {
+ "Name": "SIOJson",
+ "Type": "Runtime",
+ "LoadingPhase": "PreDefault",
+ "WhitelistPlatforms": [
+ "Win64",
+ "Linux",
+ "Mac",
+ "Android",
+ "IOS"
+ ]
+ },
+ {
+ "Name": "SocketIOLib",
+ "Type": "Runtime",
+ "LoadingPhase": "PreDefault",
+ "WhitelistPlatforms": [
+ "Win64",
+ "Linux",
+ "Mac",
+ "Android",
+ "IOS"
+ ]
+ },
+ {
+ "Name": "CoreUtility",
+ "Type": "Runtime",
+ "LoadingPhase": "PreDefault",
+ "WhitelistPlatforms": [
+ "Win64",
+ "Linux",
+ "Mac",
+ "Android",
+ "IOS"
+ ]
+ },
+ {
+ "Name": "SIOJEditorPlugin",
+ "Type": "Editor",
+ "LoadingPhase": "Default",
+ "WhitelistPlatforms": [
+ "Win64",
+ "Linux",
+ "Mac"
+ ]
+ }
+ ]
+}
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/CoreUtility.Build.cs b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/CoreUtility.Build.cs
new file mode 100644
index 0000000..d27d1dc
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/CoreUtility.Build.cs
@@ -0,0 +1,54 @@
+// Copyright 2018-current Getnamo. All Rights Reserved
+
+
+using UnrealBuildTool;
+using System.IO;
+
+public class CoreUtility : ModuleRules
+{
+ public CoreUtility(ReadOnlyTargetRules Target) : base(Target)
+ {
+ PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
+
+ PublicIncludePaths.AddRange(
+ new string[] {
+ Path.Combine(ModuleDirectory, "Public"),
+ }
+ );
+
+
+ PrivateIncludePaths.AddRange(
+ new string[] {
+ Path.Combine(ModuleDirectory, "Private"),
+ }
+ );
+
+
+ PublicDependencyModuleNames.AddRange(
+ new string[]
+ {
+ "Core"
+ }
+ );
+
+
+ PrivateDependencyModuleNames.AddRange(
+ new string[]
+ {
+ "CoreUObject",
+ "Engine",
+ "RHI",
+ "RenderCore",
+ "libOpus",
+ "UEOgg",
+ }
+ );
+
+
+ DynamicallyLoadedModuleNames.AddRange(
+ new string[]
+ {
+ }
+ );
+ }
+}
\ No newline at end of file
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Private/CUBlueprintLibrary.cpp b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Private/CUBlueprintLibrary.cpp
new file mode 100644
index 0000000..c7413cd
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Private/CUBlueprintLibrary.cpp
@@ -0,0 +1,556 @@
+// Copyright 2018-current Getnamo. All Rights Reserved
+
+
+#include "CUBlueprintLibrary.h"
+#include "IImageWrapper.h"
+#include "IImageWrapperModule.h"
+#include "Core/Public/Modules/ModuleManager.h"
+#include "Core/Public/Async/Async.h"
+#include "Engine/Classes/Engine/Texture2D.h"
+#include "Core/Public/HAL/ThreadSafeBool.h"
+#include "RHI/Public/RHI.h"
+#include "Core/Public/Misc/FileHelper.h"
+#include "Engine/Public/OpusAudioInfo.h"
+#include "Launch/Resources/Version.h"
+#include "Developer/TargetPlatform/Public/Interfaces/IAudioFormat.h"
+#include "CoreMinimal.h"
+#include "Engine/Engine.h"
+#include "CULambdaRunnable.h"
+#include "CUOpusCoder.h"
+#include "CUMeasureTimer.h"
+#include "Hash/CityHash.h"
+
+#pragma warning( push )
+#pragma warning( disable : 5046)
+
+//Render thread wrapper struct
+struct FUpdateTextureData
+{
+ UTexture2D* Texture2D;
+ FUpdateTextureRegion2D Region;
+ uint32 Pitch;
+ TArray64 BufferArray;
+ TSharedPtr Wrapper; //to keep the uncompressed data alive
+};
+
+FString UCUBlueprintLibrary::Conv_BytesToString(const TArray& InArray)
+{
+ FString ResultString;
+ FFileHelper::BufferToString(ResultString, InArray.GetData(), InArray.Num());
+ return ResultString;
+}
+
+TArray UCUBlueprintLibrary::Conv_StringToBytes(FString InString)
+{
+ TArray ResultBytes;
+ ResultBytes.Append((uint8*)TCHAR_TO_UTF8(*InString), InString.Len());
+ return ResultBytes;
+}
+
+UTexture2D* UCUBlueprintLibrary::Conv_BytesToTexture(const TArray& InBytes)
+{
+ //Convert the UTexture2D back to an image
+ UTexture2D* Texture = nullptr;
+
+ IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked(FName("ImageWrapper"));
+ EImageFormat DetectedFormat = ImageWrapperModule.DetectImageFormat(InBytes.GetData(), InBytes.Num());
+
+ TSharedPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper(DetectedFormat);
+
+ //Set the compressed bytes - we need this information on game thread to be able to determine texture size, otherwise we'll need a complete async callback
+ if (ImageWrapper.IsValid() && ImageWrapper->SetCompressed(InBytes.GetData(), InBytes.Num()))
+ {
+ //Create image given sizes
+ Texture = UTexture2D::CreateTransient(ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), PF_B8G8R8A8);
+ Texture->UpdateResource();
+
+ //Uncompress on a background thread pool
+ FCULambdaRunnable::RunLambdaOnBackGroundThreadPool([ImageWrapper, Texture] {
+ TArray64 UncompressedBGRA;
+ if (ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, UncompressedBGRA))
+ {
+
+ FUpdateTextureData* UpdateData = new FUpdateTextureData;
+ UpdateData->Texture2D = Texture;
+ UpdateData->Region = FUpdateTextureRegion2D(0, 0, 0, 0, Texture->GetSizeX(), Texture->GetSizeY());
+ UpdateData->BufferArray = UncompressedBGRA;
+ UpdateData->Pitch = Texture->GetSizeX() * 4;
+ UpdateData->Wrapper = ImageWrapper;
+
+ //enqueue texture copy
+ ENQUEUE_RENDER_COMMAND(BytesToTextureCommand)(
+ [UpdateData](FRHICommandList& CommandList)
+ {
+ RHIUpdateTexture2D(
+ ((FTextureResource*)UpdateData->Texture2D->GetResource())->TextureRHI->GetTexture2D(),
+ 0,
+ UpdateData->Region,
+ UpdateData->Pitch,
+ UpdateData->BufferArray.GetData()
+ );
+ delete UpdateData; //now that we've updated the texture data, we can finally release any data we're holding on to
+ });//End Enqueue
+ }
+ });
+ }
+ else
+ {
+ UE_LOG(LogTemp, Warning, TEXT("Invalid image format cannot decode %d"), (int32)DetectedFormat);
+ }
+
+ return Texture;
+}
+
+//one static coder, created based on need
+TSharedPtr OpusCoder;
+
+TArray UCUBlueprintLibrary::Conv_OpusBytesToWav(const TArray& InBytes)
+{
+ //FCUScopeTimer Timer(TEXT("Conv_OpusBytesToWav"));
+
+ TArray WavBytes;
+ //Early exit condition
+ if (InBytes.Num() == 0)
+ {
+ return WavBytes;
+ }
+ if (!OpusCoder)
+ {
+ OpusCoder = MakeShareable(new FCUOpusCoder());
+ }
+
+ TArray PCMBytes;
+ FCUOpusMinimalStream OpusStream;
+ OpusCoder->DeserializeMinimal(InBytes, OpusStream);
+ if (OpusCoder->DecodeStream(OpusStream, PCMBytes))
+ {
+ SerializeWaveFile(WavBytes, PCMBytes.GetData(), PCMBytes.Num(), OpusCoder->Channels, OpusCoder->SampleRate);
+ }
+ else
+ {
+ UE_LOG(LogTemp, Warning, TEXT("OpusMinimal to Wave Failed. DecodeStream returned false"));
+ }
+
+ return WavBytes;
+}
+
+TArray UCUBlueprintLibrary::Conv_WavBytesToOpus(const TArray& InBytes)
+{
+ //FCUScopeTimer Timer(TEXT("Conv_WavBytesToOpus"));
+
+ TArray OpusBytes;
+
+ FWaveModInfo WaveInfo;
+
+ if (!WaveInfo.ReadWaveInfo(InBytes.GetData(), InBytes.Num()))
+ {
+ return OpusBytes;
+ }
+
+ if (!OpusCoder)
+ {
+ OpusCoder = MakeShareable(new FCUOpusCoder());
+ }
+
+ TArray PCMBytes = TArray(WaveInfo.SampleDataStart, WaveInfo.SampleDataSize);
+
+ FCUOpusMinimalStream OpusStream;
+
+ OpusCoder->EncodeStream(PCMBytes, OpusStream);
+
+ TArray SerializedBytes;
+ OpusCoder->SerializeMinimal(OpusStream, SerializedBytes);
+
+ return SerializedBytes;
+}
+
+USoundWave* UCUBlueprintLibrary::Conv_WavBytesToSoundWave(const TArray& InBytes)
+{
+ USoundWave* SoundWave;
+
+ //Allocate based on thread
+ if (IsInGameThread())
+ {
+ SoundWave = NewObject(USoundWaveProcedural::StaticClass());
+ SetSoundWaveFromWavBytes((USoundWaveProcedural*)SoundWave, InBytes);
+ }
+ else
+ {
+ //We will go to another thread, copy our bytes
+ TArray CopiedBytes = InBytes;
+ FThreadSafeBool bAllocationComplete = false;
+ AsyncTask(ENamedThreads::GameThread, [&bAllocationComplete, &SoundWave]
+ {
+ SoundWave = NewObject(USoundWaveProcedural::StaticClass());
+ bAllocationComplete = true;
+ });
+
+ //block while not complete
+ while (!bAllocationComplete)
+ {
+ //100micros sleep, this should be very quick
+ FPlatformProcess::Sleep(0.0001f);
+ };
+
+ SetSoundWaveFromWavBytes((USoundWaveProcedural*)SoundWave, CopiedBytes);
+ }
+
+ return SoundWave;
+}
+
+TArray UCUBlueprintLibrary::Conv_SoundWaveToWavBytes(USoundWave* SoundWave)
+{
+ TArray PCMBytes;
+ TArray WavBytes;
+
+ //memcpy raw data from soundwave, hmm this won't work for procedurals...
+ const void* LockedData = SoundWave->GetResourceData();
+ PCMBytes.SetNumUninitialized(SoundWave->GetResourceSize());
+ FMemory::Memcpy(PCMBytes.GetData(), LockedData, PCMBytes.Num());
+
+ //add wav header
+ SerializeWaveFile(WavBytes, PCMBytes.GetData(), PCMBytes.Num(), SoundWave->NumChannels, SoundWave->GetSampleRateForCurrentPlatform());
+
+ return WavBytes;
+}
+
+void UCUBlueprintLibrary::Conv_CompactBytesToTransforms(const TArray& InCompactBytes, TArray& OutTransforms)
+{
+ TArray FloatView;
+ FloatView.SetNumUninitialized(InCompactBytes.Num() / 4);
+ FPlatformMemory::Memcpy(FloatView.GetData(), InCompactBytes.GetData(), InCompactBytes.Num());
+
+ //is our float array exactly divisible by 9?
+ if (FloatView.Num() % 9 != 0)
+ {
+ UE_LOG(LogTemp, Log, TEXT("Conv_CompactBytesToTransforms::float array is not divisible by 9"));
+ return;
+ }
+
+ int32 TransformNum = FloatView.Num() / 9;
+ OutTransforms.SetNumUninitialized(TransformNum);
+
+ for (int i = 0; i < FloatView.Num() - 8; i += 9)
+ {
+ OutTransforms[i/9] = FTransform(FRotator(FloatView[i], FloatView[i+1], FloatView[i+2]), FVector(FloatView[i+3], FloatView[i + 4], FloatView[i + 5]), FVector(FloatView[i+6], FloatView[i+7], FloatView[i+8]));
+ }
+}
+
+void UCUBlueprintLibrary::Conv_CompactPositionBytesToTransforms(const TArray& InCompactBytes, TArray& OutTransforms)
+{
+ TArray FloatView;
+ FloatView.SetNumUninitialized(InCompactBytes.Num() / 4);
+ FPlatformMemory::Memcpy(FloatView.GetData(), InCompactBytes.GetData(), InCompactBytes.Num());
+
+ //is our float array exactly divisible by 3?
+ if (FloatView.Num() % 3 != 0)
+ {
+ UE_LOG(LogTemp, Log, TEXT("Conv_CompactPositionBytesToTransforms::float array is not divisible by 3"));
+ return;
+ }
+
+ int32 TransformNum = FloatView.Num() / 3;
+ OutTransforms.SetNumUninitialized(TransformNum);
+
+ for (int i = 0; i < FloatView.Num() - 2; i += 3)
+ {
+ OutTransforms[i / 3] = FTransform(FVector(FloatView[i], FloatView[i + 1], FloatView[i + 2]));
+ }
+}
+
+void UCUBlueprintLibrary::SetSoundWaveFromWavBytes(USoundWaveProcedural* InSoundWave, const TArray& InBytes)
+{
+ FWaveModInfo WaveInfo;
+
+ FString ErrorReason;
+ if (WaveInfo.ReadWaveInfo(InBytes.GetData(), InBytes.Num(), &ErrorReason))
+ {
+ //copy header info
+ int32 DurationDiv = *WaveInfo.pChannels * *WaveInfo.pBitsPerSample * *WaveInfo.pSamplesPerSec;
+ if (DurationDiv)
+ {
+ InSoundWave->Duration = *WaveInfo.pWaveDataSize * 8.0f / DurationDiv;
+ }
+ else
+ {
+ InSoundWave->Duration = 0.0f;
+ }
+
+ InSoundWave->SetSampleRate(*WaveInfo.pSamplesPerSec);
+ InSoundWave->NumChannels = *WaveInfo.pChannels;
+ //SoundWaveProc->RawPCMDataSize = WaveInfo.SampleDataSize;
+ InSoundWave->bLooping = false;
+ InSoundWave->SoundGroup = ESoundGroup::SOUNDGROUP_Default;
+
+ //Queue actual audio data
+ InSoundWave->QueueAudio(WaveInfo.SampleDataStart, WaveInfo.SampleDataSize);
+ }
+ else
+ {
+ UE_LOG(LogTemp, Log, TEXT("SetSoundWaveFromWavBytes::WaveRead error: %s"), *ErrorReason);
+ }
+}
+
+TFuture UCUBlueprintLibrary::Conv_BytesToTexture_Async(const TArray& InBytes)
+{
+ //Running this on a background thread
+ return Async(EAsyncExecution::Thread, [InBytes]
+ {
+ //Create wrapper pointer we can share easily across threads
+ struct FDataHolder
+ {
+ UTexture2D* Texture = nullptr;
+ };
+ TSharedPtr Holder = MakeShareable(new FDataHolder);
+
+ FThreadSafeBool bLoadModuleComplete = false;
+ IImageWrapperModule* ImageWrapperModule;
+
+ AsyncTask(ENamedThreads::GameThread, [&bLoadModuleComplete, Holder, &ImageWrapperModule]
+ {
+ ImageWrapperModule = &FModuleManager::LoadModuleChecked(FName("ImageWrapper"));
+ bLoadModuleComplete = true;
+ });
+ while (!bLoadModuleComplete)
+ {
+ FPlatformProcess::Sleep(0.001f);
+ }
+
+ EImageFormat DetectedFormat = ImageWrapperModule->DetectImageFormat(InBytes.GetData(), InBytes.Num());
+
+ TSharedPtr ImageWrapper = ImageWrapperModule->CreateImageWrapper(DetectedFormat);
+
+ if (!(ImageWrapper.IsValid() && ImageWrapper->SetCompressed(InBytes.GetData(), InBytes.Num())))
+ {
+ UE_LOG(LogTemp, Warning, TEXT("Invalid image format cannot decode %d"), (int32)DetectedFormat);
+ return (UTexture2D*)nullptr;
+ }
+
+ //Create image given sizes
+
+ //Creation of UTexture needs to happen on game thread
+ FThreadSafeBool bAllocationComplete = false;
+ FIntPoint Size;
+ Size.X = ImageWrapper->GetWidth();
+ Size.Y = ImageWrapper->GetHeight();
+ AsyncTask(ENamedThreads::GameThread, [&bAllocationComplete, Holder, Size]
+ {
+ Holder->Texture = UTexture2D::CreateTransient(Size.X, Size.Y, PF_B8G8R8A8);
+ Holder->Texture->UpdateResource();
+ bAllocationComplete = true;
+ });
+
+ while (!bAllocationComplete)
+ {
+ //sleep 10ms intervals
+ FPlatformProcess::Sleep(0.001f);
+ }
+
+ //Uncompress on a background thread pool
+ TArray64 UncompressedBGRA;
+ if (!ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, UncompressedBGRA))
+ {
+ return (UTexture2D*)nullptr;
+ }
+
+ FUpdateTextureData* UpdateData = new FUpdateTextureData;
+ UpdateData->Texture2D = Holder->Texture;
+ UpdateData->Region = FUpdateTextureRegion2D(0, 0, 0, 0, Size.X, Size.Y);
+ UpdateData->BufferArray = UncompressedBGRA;
+ UpdateData->Pitch = Size.X * 4;
+ UpdateData->Wrapper = ImageWrapper;
+
+ //This command sends it to the render thread
+ ENQUEUE_RENDER_COMMAND(BytesToTextureAsyncCommand)(
+ [UpdateData](FRHICommandList& CommandList)
+ {
+ RHIUpdateTexture2D(
+ ((FTextureResource*)UpdateData->Texture2D->GetResource())->TextureRHI->GetTexture2D(),
+ 0,
+ UpdateData->Region,
+ UpdateData->Pitch,
+ UpdateData->BufferArray.GetData()
+ );
+ delete UpdateData; //now that we've updated the texture data, we can finally release any data we're holding on to
+ });//End Enqueue
+
+ return Holder->Texture;
+ });//End async
+}
+
+bool UCUBlueprintLibrary::Conv_TextureToBytes(UTexture2D* Texture, TArray& OutBuffer, EImageFormatBPType Format /*= EImageFormatBPType::JPEG*/)
+{
+ if (!Texture || !Texture->IsValidLowLevel())
+ {
+ return false;
+ }
+
+ //Get our wrapper module
+ IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked(FName("ImageWrapper"));
+ TSharedPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper((EImageFormat)Format);
+
+ int32 Width = Texture->GetPlatformData()->Mips[0].SizeX;
+ int32 Height = Texture->GetPlatformData()->Mips[0].SizeY;
+ int32 DataLength = Width * Height * 4;
+
+ void* TextureDataPointer = Texture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_ONLY);
+
+ ImageWrapper->SetRaw(TextureDataPointer, DataLength, Width, Height, ERGBFormat::BGRA, 8);
+
+ //This part can take a while, has performance implications
+ OutBuffer = ImageWrapper->GetCompressed();
+
+ Texture->GetPlatformData()->Mips[0].BulkData.Unlock();
+
+ return true;
+}
+
+FString UCUBlueprintLibrary::NowUTCString()
+{
+ return FDateTime::UtcNow().ToString();
+}
+
+FString UCUBlueprintLibrary::GetLoginId()
+{
+ return FPlatformMisc::GetLoginId();
+}
+
+int32 UCUBlueprintLibrary::ToHashCode(const FString& String)
+{
+ return (int32)CityHash32(TCHAR_TO_ANSI(*String), String.Len());
+}
+
+void UCUBlueprintLibrary::MeasureTimerStart(const FString& Category /*= TEXT("TimeTaken")*/)
+{
+ FCUMeasureTimer::Tick(Category);
+}
+
+float UCUBlueprintLibrary::MeasureTimerStop(const FString& Category /*= TEXT("TimeTaken")*/, bool bShouldLogResult /*= true*/)
+{
+ return (float)FCUMeasureTimer::Tock(Category, bShouldLogResult);
+}
+
+void UCUBlueprintLibrary::CallFunctionOnThread(const FString& FunctionName, ESIOCallbackType ThreadType, UObject* WorldContextObject /*= nullptr*/)
+{
+ UObject* Target = WorldContextObject;
+
+ if (!Target->IsValidLowLevel())
+ {
+ UE_LOG(LogTemp, Warning, TEXT("CallFunctionOnThread: Target not found for '%s'"), *FunctionName);
+ return;
+ }
+
+ UFunction* Function = Target->FindFunction(FName(*FunctionName));
+ if (nullptr == Function)
+ {
+ UE_LOG(LogTemp, Warning, TEXT("CallFunctionOnThread: Function not found '%s'"), *FunctionName);
+ return;
+ }
+
+ switch (ThreadType)
+ {
+ case CALLBACK_GAME_THREAD:
+ if (IsInGameThread())
+ {
+ Target->ProcessEvent(Function, nullptr);
+ }
+ else
+ {
+ FCULambdaRunnable::RunShortLambdaOnGameThread([Function, Target]
+ {
+ if (Target->IsValidLowLevel())
+ {
+ Target->ProcessEvent(Function, nullptr);
+ }
+ });
+ }
+ break;
+ case CALLBACK_BACKGROUND_THREADPOOL:
+ FCULambdaRunnable::RunLambdaOnBackGroundThreadPool([Function, Target]
+ {
+ if (Target->IsValidLowLevel())
+ {
+ Target->ProcessEvent(Function, nullptr);
+ }
+ });
+ break;
+ case CALLBACK_BACKGROUND_TASKGRAPH:
+ FCULambdaRunnable::RunShortLambdaOnBackGroundTask([Function, Target]
+ {
+ if (Target->IsValidLowLevel())
+ {
+ Target->ProcessEvent(Function, nullptr);
+ }
+ });
+ break;
+ default:
+ break;
+ }
+}
+
+void UCUBlueprintLibrary::CallFunctionOnThreadGraphReturn(const FString& FunctionName, ESIOCallbackType ThreadType, struct FLatentActionInfo LatentInfo, UObject* WorldContextObject /*= nullptr*/)
+{
+ UObject* Target = WorldContextObject;
+ FCULatentAction* LatentAction = FCULatentAction::CreateLatentAction(LatentInfo, Target);
+
+ if (!Target->IsValidLowLevel())
+ {
+ UE_LOG(LogTemp, Warning, TEXT("CallFunctionOnThread: Target not found for '%s'"), *FunctionName);
+ LatentAction->Call();
+ return;
+ }
+
+ UFunction* Function = Target->FindFunction(FName(*FunctionName));
+ if (nullptr == Function)
+ {
+ UE_LOG(LogTemp, Warning, TEXT("CallFunctionOnThread: Function not found '%s'"), *FunctionName);
+ LatentAction->Call();
+ return;
+ }
+
+ switch (ThreadType)
+ {
+ case CALLBACK_GAME_THREAD:
+ if (IsInGameThread())
+ {
+ Target->ProcessEvent(Function, nullptr);
+ LatentAction->Call();
+ }
+ else
+ {
+ FCULambdaRunnable::RunShortLambdaOnGameThread([Function, Target, LatentAction]
+ {
+ if (Target->IsValidLowLevel())
+ {
+ Target->ProcessEvent(Function, nullptr);
+ LatentAction->Call();
+ }
+ });
+ }
+ break;
+ case CALLBACK_BACKGROUND_THREADPOOL:
+ FCULambdaRunnable::RunLambdaOnBackGroundThreadPool([Function, Target, LatentAction]
+ {
+ if (Target->IsValidLowLevel())
+ {
+ Target->ProcessEvent(Function, nullptr);
+ LatentAction->Call();
+ }
+ });
+ break;
+ case CALLBACK_BACKGROUND_TASKGRAPH:
+ FCULambdaRunnable::RunShortLambdaOnBackGroundTask([Function, Target, LatentAction]
+ {
+ if (Target->IsValidLowLevel())
+ {
+ Target->ProcessEvent(Function, nullptr);
+ LatentAction->Call();
+ }
+ });
+ break;
+ default:
+ break;
+ }
+}
+
+#pragma warning( pop )
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Private/CUFileComponent.cpp b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Private/CUFileComponent.cpp
new file mode 100644
index 0000000..57e024b
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Private/CUFileComponent.cpp
@@ -0,0 +1,113 @@
+// Copyright 2018-current Getnamo. All Rights Reserved
+
+
+#include "CUFileComponent.h"
+#include "HAL/PlatformFileManager.h"
+#include "Misc/FileHelper.h"
+#include "Misc/Paths.h"
+
+#if PLATFORM_ANDROID
+#include "Android/AndroidPlatformMisc.h"
+#endif
+
+UCUFileComponent::UCUFileComponent(const FObjectInitializer &init) : UActorComponent(init)
+{
+ bWantsInitializeComponent = true;
+ bAutoActivate = true;
+}
+
+FString UCUFileComponent::ProjectContentsDirectory()
+{
+ return FPaths::ProjectContentDir();
+}
+
+FString UCUFileComponent::ProjectDirectory()
+{
+ return FPaths::ProjectDir();
+}
+
+FString UCUFileComponent::ProjectSavedDirectory()
+{
+ return FPaths::ProjectSavedDir();
+}
+
+FString UCUFileComponent::ExternalSaveDirectory()
+{
+#if PLATFORM_ANDROID
+ return FString(FAndroidMisc::GamePersistentDownloadDir());
+#else
+ return FPaths::ProjectSavedDir();
+#endif
+}
+
+void UCUFileComponent::SplitFullPath(const FString& InFullPath, FString& OutDirectory, FString& OutFileName)
+{
+ bool bDidSplit = InFullPath.Split(TEXT("/"), &OutDirectory, &OutFileName, ESearchCase::CaseSensitive, ESearchDir::FromEnd);
+
+ if (!bDidSplit)
+ {
+ //search by backslash
+ InFullPath.Split(TEXT("\\\\"), &OutDirectory, &OutFileName, ESearchCase::CaseSensitive, ESearchDir::FromEnd);
+ }
+}
+
+void UCUFileComponent::ProjectRelativePath(const FString& InFullPath, FString& OutProjectRelativePath)
+{
+ const FString PathToProject = ProjectDirectory();
+
+ FString Before, After;
+
+ InFullPath.Split(PathToProject, &Before, &After);
+
+ OutProjectRelativePath = After;
+}
+
+bool UCUFileComponent::SaveBytesToFile(const TArray& Bytes, const FString& Directory, const FString& FileName, bool bLogSave)
+{
+ //bool AllowOverwriting = false;
+
+ IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
+
+ if (PlatformFile.CreateDirectoryTree(*Directory))
+ {
+ FString AbsoluteFilePath;
+
+ // Get absolute file path
+ if (Directory.EndsWith(TEXT("/")))
+ {
+ AbsoluteFilePath = FPaths::ConvertRelativePathToFull(Directory + FileName);
+ }
+ else
+ {
+ AbsoluteFilePath = FPaths::ConvertRelativePathToFull(Directory + "/" + FileName);
+ }
+
+ // Allow overwriting or file doesn't already exist
+ bool bSaveSuccesful = FFileHelper::SaveArrayToFile(Bytes, *AbsoluteFilePath);
+
+ if (bLogSave)
+ {
+ if (bSaveSuccesful)
+ {
+ UE_LOG(LogTemp, Log, TEXT("Saved: %s with %d bytes"), *AbsoluteFilePath, Bytes.Num());
+ }
+ else
+ {
+ UE_LOG(LogTemp, Log, TEXT("Failed to save: %s"), *AbsoluteFilePath);
+ }
+ }
+
+ return bSaveSuccesful;
+ }
+
+ return false;
+}
+
+bool UCUFileComponent::ReadBytesFromFile(const FString& Directory, const FString& FileName, TArray& OutBytes)
+{
+ // Get absolute file path
+ FString AbsoluteFilePath = Directory + "/" + FileName;
+
+ // Allow overwriting or file doesn't already exist
+ return FFileHelper::LoadFileToArray(OutBytes, *AbsoluteFilePath);
+}
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Private/CUFileSubsystem.cpp b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Private/CUFileSubsystem.cpp
new file mode 100644
index 0000000..48d1fab
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Private/CUFileSubsystem.cpp
@@ -0,0 +1,141 @@
+#include "CUFileSubsystem.h"
+#include "HAL/PlatformFileManager.h"
+#include "Misc/FileHelper.h"
+#include "Misc/Paths.h"
+
+#if PLATFORM_ANDROID
+#include "Android/AndroidPlatformMisc.h"
+#endif
+
+void UCUFileSubsystem::Initialize(FSubsystemCollectionBase& Collection)
+{
+
+}
+
+void UCUFileSubsystem::Deinitialize()
+{
+
+}
+
+FString UCUFileSubsystem::ProjectContentsDirectory()
+{
+ return FPaths::ProjectContentDir();
+}
+
+FString UCUFileSubsystem::ProjectDirectory()
+{
+ return FPaths::ProjectDir();
+}
+
+FString UCUFileSubsystem::ProjectSavedDirectory()
+{
+ return FPaths::ProjectSavedDir();
+}
+
+FString UCUFileSubsystem::ExternalSaveDirectory()
+{
+#if PLATFORM_ANDROID
+ return FString(FAndroidMisc::GamePersistentDownloadDir());
+#else
+ return FPaths::ProjectSavedDir();
+#endif
+}
+
+void UCUFileSubsystem::SplitFullPath(const FString& InFullPath, FString& OutDirectory, FString& OutFileName)
+{
+ bool bDidSplit = InFullPath.Split(TEXT("/"), &OutDirectory, &OutFileName, ESearchCase::CaseSensitive, ESearchDir::FromEnd);
+
+ if (!bDidSplit)
+ {
+ //search by backslash
+ InFullPath.Split(TEXT("\\\\"), &OutDirectory, &OutFileName, ESearchCase::CaseSensitive, ESearchDir::FromEnd);
+ }
+}
+
+void UCUFileSubsystem::ProjectRelativePath(const FString& InFullPath, FString& OutProjectRelativePath)
+{
+ const FString PathToProject = ProjectDirectory();
+
+ FString Before, After;
+
+ InFullPath.Split(PathToProject, &Before, &After);
+
+ OutProjectRelativePath = After;
+}
+
+bool UCUFileSubsystem::SaveBytesToFile(const TArray& Bytes, const FString& Directory, const FString& FileName, bool bLogSave)
+{
+ //bool AllowOverwriting = false;
+
+ IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
+
+ if (PlatformFile.CreateDirectoryTree(*Directory))
+ {
+ FString AbsoluteFilePath;
+
+ // Get absolute file path
+ if (Directory.EndsWith(TEXT("/")))
+ {
+ AbsoluteFilePath = FPaths::ConvertRelativePathToFull(Directory + FileName);
+ }
+ else
+ {
+ AbsoluteFilePath = FPaths::ConvertRelativePathToFull(Directory + "/" + FileName);
+ }
+
+ // Allow overwriting or file doesn't already exist
+ bool bSaveSuccesful = FFileHelper::SaveArrayToFile(Bytes, *AbsoluteFilePath);
+
+ if (bLogSave)
+ {
+ if (bSaveSuccesful)
+ {
+ UE_LOG(LogTemp, Log, TEXT("Saved: %s with %d bytes"), *AbsoluteFilePath, Bytes.Num());
+ }
+ else
+ {
+ UE_LOG(LogTemp, Log, TEXT("Failed to save: %s"), *AbsoluteFilePath);
+ }
+ }
+
+ return bSaveSuccesful;
+ }
+
+ return false;
+}
+
+bool UCUFileSubsystem::SaveBytesToPath(const TArray& Bytes, const FString& Path, bool bLogSave /*= false*/)
+{
+ //we use the split file overload due to some potential directory automation checks
+ FString Directory, FileName;
+ SplitFullPath(Path, Directory, FileName);
+ return SaveBytesToFile(Bytes, Directory, FileName, bLogSave);
+}
+
+bool UCUFileSubsystem::ReadBytesFromFile(const FString& Directory, const FString& FileName, TArray& OutBytes)
+{
+ // Get absolute file path
+ const FString AbsoluteFilePath = Directory + "/" + FileName;
+
+ return ReadBytesFromPath(AbsoluteFilePath, OutBytes);
+}
+
+bool UCUFileSubsystem::ReadBytesFromPath(const FString& Path, TArray& OutBytes)
+{
+ return FFileHelper::LoadFileToArray(OutBytes, *Path);
+}
+
+bool UCUFileSubsystem::DeleteFileAtPath(const FString& Path)
+{
+ if (!Path.IsEmpty())
+ {
+ if (FPaths::ValidatePath(Path) && FPaths::FileExists(Path))
+ {
+ IFileManager& FileManager = IFileManager::Get();
+ FileManager.Delete(*Path);
+ return true;
+ }
+ }
+ return false;
+}
+
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Private/CULambdaRunnable.cpp b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Private/CULambdaRunnable.cpp
new file mode 100644
index 0000000..5095c32
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Private/CULambdaRunnable.cpp
@@ -0,0 +1,116 @@
+// Copyright 2018-current Getnamo. All Rights Reserved
+
+
+#include "CULambdaRunnable.h"
+#include "Async/Async.h"
+#include "Engine/Engine.h"
+
+void FCULambdaRunnable::RunLambdaOnBackGroundThread(TFunction< void()> InFunction)
+{
+ Async(EAsyncExecution::Thread, InFunction);
+}
+
+void FCULambdaRunnable::RunLambdaOnBackGroundThreadPool(TFunction< void()> InFunction)
+{
+ Async(EAsyncExecution::ThreadPool, InFunction);
+}
+
+FGraphEventRef FCULambdaRunnable::RunShortLambdaOnGameThread(TFunction< void()> InFunction)
+{
+ return FFunctionGraphTask::CreateAndDispatchWhenReady(InFunction, TStatId(), nullptr, ENamedThreads::GameThread);
+}
+
+FGraphEventRef FCULambdaRunnable::RunShortLambdaOnBackGroundTask(TFunction< void()> InFunction)
+{
+ return FFunctionGraphTask::CreateAndDispatchWhenReady(InFunction, TStatId(), nullptr, ENamedThreads::AnyThread);
+}
+
+void FCULambdaRunnable::SetTimeout(TFunctionOnDone, float DurationInSec, bool bCallbackOnGameThread /*=true*/)
+{
+ RunLambdaOnBackGroundThread([OnDone, DurationInSec, bCallbackOnGameThread]()
+ {
+ FPlatformProcess::Sleep(DurationInSec);
+
+ if (bCallbackOnGameThread)
+ {
+ RunShortLambdaOnGameThread(OnDone);
+ }
+ else
+ {
+ OnDone();
+ }
+ });
+}
+
+
+FCULatentAction* FCULatentAction::CreateLatentAction(struct FLatentActionInfo& LatentInfo, UObject* WorldContext)
+{
+ UWorld* World = GEngine->GetWorldFromContextObject(WorldContext, EGetWorldErrorMode::LogAndReturnNull);
+ if (!World)
+ {
+ return nullptr;
+ }
+ FLatentActionManager& LatentActionManager = World->GetLatentActionManager();
+ FCULatentAction *LatentAction = LatentActionManager.FindExistingAction(LatentInfo.CallbackTarget, LatentInfo.UUID);
+ LatentAction = new FCULatentAction(LatentInfo); //safe to use new since latentactionmanager will delete it
+ int32 UUID = LatentInfo.UUID;
+ LatentAction->OnCancelNotification = [UUID]()
+ {
+ UE_LOG(LogTemp, Log, TEXT("%d graph callback cancelled."), UUID);
+ };
+ LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, LatentAction);
+ return LatentAction;
+}
+
+FCULatentAction::FCULatentAction(const FLatentActionInfo& LatentInfo) :
+ ExecutionFunction(LatentInfo.ExecutionFunction),
+ OutputLink(LatentInfo.Linkage),
+ CallbackTarget(LatentInfo.CallbackTarget),
+ Called(false)
+{
+
+}
+
+void FCULatentAction::UpdateOperation(FLatentResponse& Response)
+{
+ Response.FinishAndTriggerIf(Called, ExecutionFunction, OutputLink, CallbackTarget);
+}
+
+void FCULatentAction::Call()
+{
+ Called = true;
+}
+
+void FCULatentAction::Cancel()
+{
+ if (OnCancelNotification)
+ {
+ OnCancelNotification();
+ }
+}
+
+void FCULatentAction::NotifyObjectDestroyed()
+{
+ Cancel();
+}
+
+void FCULatentAction::NotifyActionAborted()
+{
+ Cancel();
+}
+
+#if WITH_EDITOR
+FString FCULatentAction::GetDescription() const
+{
+ {
+ if (Called)
+ {
+ return TEXT("Done.");
+ }
+ else
+ {
+ return TEXT("Pending.");
+ }
+ };
+}
+#endif
\ No newline at end of file
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Private/CUMeasureTimer.cpp b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Private/CUMeasureTimer.cpp
new file mode 100644
index 0000000..61df6aa
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Private/CUMeasureTimer.cpp
@@ -0,0 +1,48 @@
+#include "CUMeasureTimer.h"
+#include "HAL/PlatformTime.h"
+
+#if ENABLE_CUPRECISE_TIMER
+static TMap> FPreciseTimerInternalMap;
+#endif
+
+void FCUMeasureTimer::Tick(const FString& LogMsg /*= TEXT("TimeTaken")*/)
+{
+#if ENABLE_CUPRECISE_TIMER
+ TSharedPtr Timer = MakeShareable(new FCUMeasureTimer);
+ FPreciseTimerInternalMap.Add(LogMsg, Timer);
+ Timer->Then = FPlatformTime::Seconds(); //start timer last so we don't measure anything else
+#endif
+}
+
+double FCUMeasureTimer::Tock(const FString& LogMsg /*= TEXT("TimeTaken")*/, bool bShouldLogResult /*= true*/)
+{
+#if ENABLE_CUPRECISE_TIMER
+ double Now = FPlatformTime::Seconds();
+ if (!FPreciseTimerInternalMap.Contains(LogMsg))
+ {
+ UE_LOG(LogTemp, Warning, TEXT("FCUMeasureTimer::Tock error: <%s> no such category ticked."), *LogMsg);
+ return 0.0;
+ }
+ TSharedPtr Timer = FPreciseTimerInternalMap[LogMsg];
+ double Elapsed = (Now - Timer->Then) * 1000.0;
+ if (bShouldLogResult)
+ {
+ UE_LOG(LogTemp, Log, TEXT("%s %1.3f ms"), *LogMsg, Elapsed);
+ }
+ FPreciseTimerInternalMap.Remove(LogMsg);
+#else
+ return 0.0;
+#endif
+ return Elapsed;
+}
+
+FCUScopeTimer::FCUScopeTimer(const FString& LogMsg)
+{
+ LogMessage = LogMsg;
+ FCUMeasureTimer::Tick(LogMsg);
+}
+
+FCUScopeTimer::~FCUScopeTimer()
+{
+ FCUMeasureTimer::Tock(LogMessage);
+}
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Private/CUOpusCoder.cpp b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Private/CUOpusCoder.cpp
new file mode 100644
index 0000000..89d3ade
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Private/CUOpusCoder.cpp
@@ -0,0 +1,428 @@
+#include "CUOpusCoder.h"
+#include "CoreMinimal.h"
+
+#define DEBUG_OPUS_LOG 0
+
+FCUOpusCoder::FCUOpusCoder()
+{
+#if WITH_OPUS
+ Encoder = nullptr;
+ Decoder = nullptr;
+#endif
+ SampleRate = 16000;
+ Channels = 1;
+ BitRate = 24000;
+ MaxPacketSize = (3 * 1276);
+ SetFrameSizeMs(60);
+ bResetBetweenEncoding = true;
+ bApplicationVoip = true;
+ bLowestPossibleLatency = false;
+}
+
+FCUOpusCoder::~FCUOpusCoder()
+{
+#if WITH_OPUS
+ if (Encoder)
+ {
+ opus_encoder_destroy(Encoder);
+ Encoder = nullptr;
+ }
+ if (Decoder)
+ {
+ opus_decoder_destroy(Decoder);
+ Decoder = nullptr;
+ }
+#endif
+}
+
+void FCUOpusCoder::SetSampleRate(int32 InSamplesPerSec)
+{
+ SampleRate = InSamplesPerSec;
+ SetFrameSizeMs(FrameSizeMs);
+ ResetCoderIfInitialized();
+}
+
+void FCUOpusCoder::SetChannels(int32 InChannels)
+{
+ Channels = InChannels;
+ ResetCoderIfInitialized();
+}
+
+void FCUOpusCoder::SetBitrate(int32 InBitrate)
+{
+ BitRate = InBitrate;
+ ResetCoderIfInitialized();
+}
+
+void FCUOpusCoder::SetFrameSizeMs(int32 Ms)
+{
+ FrameSizeMs = Ms;
+ FrameSize = (SampleRate * FrameSizeMs) / 1000;
+ MaxFrameSize = FrameSize * 6;
+ ResetCoderIfInitialized();
+}
+
+bool FCUOpusCoder::EncodeStream(const TArray& InPCMBytes, FCUOpusMinimalStream& OutStream)
+{
+ if (bResetBetweenEncoding)
+ {
+ ResetCoderIfInitialized();
+ }
+ if (!InitEncoderIfNeeded())
+ {
+ return false;
+ }
+#if WITH_OPUS
+
+#if DEBUG_OPUS_LOG
+ DebugLogEncoder();
+#endif
+
+ int32 BytesLeft = InPCMBytes.Num();
+ int32 Offset = 0;
+ int32 BytesWritten = 0;
+ const int32 BytesPerFrame = FrameSize * Channels * sizeof(opus_int16);
+ TArray TempBuffer;
+ TArray FinalPacket;
+ TempBuffer.SetNumUninitialized(MaxPacketSize);
+ int32 EncodedBytes = 0;
+ opus_int16* PCMDataPtr = 0;
+
+ while (BytesLeft > 0)
+ {
+ //Final packet requires zero padding
+ if(BytesLeft& OutPCMFrame)
+{
+ if (!InitDecoderIfNeeded())
+ {
+ return false;
+ }
+#if WITH_OPUS
+
+#if DEBUG_OPUS_LOG
+ DebugLogDecoder();
+#endif
+
+ const int32 BytesPerFrame = FrameSize * Channels * sizeof(opus_int16);
+
+ TArray TempBuffer;
+ TempBuffer.SetNum(MaxFrameSize);
+
+ int32 CompressedOffset = 0;
+
+ for (int FrameIndex = 0; CompressedOffset < InStream.CompressedBytes.Num(); FrameIndex++)
+ {
+
+ int32 DecodedSamples = opus_decode(Decoder, InStream.CompressedBytes.GetData() + CompressedOffset, InStream.PacketSizes[FrameIndex], (opus_int16*)TempBuffer.GetData(), FrameSize, 0);
+
+#if DEBUG_OPUS_LOG
+ DebugLogFrame(InStream.CompressedBytes.GetData(), InStream.PacketSizes[FrameIndex], SampleRate, false);
+ UE_LOG(LogTemp, Log, TEXT("Decoded Samples: %d"), DecodedSamples);
+#endif
+
+ if (DecodedSamples > 0)
+ {
+ OutPCMFrame.Append(TempBuffer.GetData(), DecodedSamples*Channels*sizeof(opus_int16));
+ }
+ else if (DecodedSamples < 0)
+ {
+ UE_LOG(LogTemp, Log, TEXT("%s"), opus_strerror(DecodedSamples));
+ return false;
+ }
+
+ CompressedOffset += InStream.PacketSizes[FrameIndex];
+ }
+
+#if DEBUG_OPUS_LOG
+ UE_LOG(LogTemp, Log, TEXT("decoded into %d bytes"), OutPCMFrame.Num());
+#endif
+
+#endif
+ return true;
+}
+
+static void WriteUInt32ToByteArrayLE(TArray& InByteArray, int32& Index, const uint32 Value)
+{
+ InByteArray[Index++] = (uint8)(Value >> 0);
+ InByteArray[Index++] = (uint8)(Value >> 8);
+ InByteArray[Index++] = (uint8)(Value >> 16);
+ InByteArray[Index++] = (uint8)(Value >> 24);
+}
+
+bool FCUOpusCoder::SerializeMinimal(const FCUOpusMinimalStream& InStream, TArray& OutSerializedBytes)
+{
+ //Preset array size
+ OutSerializedBytes.SetNumUninitialized(sizeof(int32) + (InStream.PacketSizes.Num() * sizeof(int16)) + InStream.CompressedBytes.Num());
+
+ //Write total number of packets as int32 first
+ int32 Index = 0;
+ WriteUInt32ToByteArrayLE(OutSerializedBytes, Index, InStream.PacketSizes.Num());
+
+ //write the compressed frame sizes
+ int32 Offset = sizeof(int32);
+ FMemory::Memcpy(&OutSerializedBytes[Offset], InStream.PacketSizes.GetData(), InStream.PacketSizes.Num() * sizeof(int16));
+
+ //write the compressed bytes
+ Offset += (InStream.PacketSizes.Num() * sizeof(int16));
+ FMemory::Memcpy(&OutSerializedBytes[Offset], InStream.CompressedBytes.GetData(), InStream.CompressedBytes.Num());
+
+ return true;
+}
+
+bool FCUOpusCoder::DeserializeMinimal(const TArray& InSerializedMinimalBytes, FCUOpusMinimalStream& OutStream)
+{
+ int32 PacketCount = InSerializedMinimalBytes[0];
+
+ //get our packet info
+ OutStream.PacketSizes.SetNumUninitialized(PacketCount);
+ int32 Offset = sizeof(int32);
+ FMemory::Memcpy(OutStream.PacketSizes.GetData(), &InSerializedMinimalBytes[Offset], PacketCount*sizeof(int16));
+
+ //get our compressed data
+ Offset += ((PacketCount) * sizeof(int16));
+ int32 RemainingBytes = InSerializedMinimalBytes.Num() - Offset;
+ OutStream.CompressedBytes.Append(&InSerializedMinimalBytes[Offset], RemainingBytes);
+
+ return true;
+}
+
+int32 FCUOpusCoder::EncodeFrame(const TArray& InPCMFrame, TArray& OutCompressed)
+{
+#if WITH_OPUS
+ return opus_encode(Encoder, (const opus_int16*)InPCMFrame.GetData(), FrameSize, OutCompressed.GetData(), MaxPacketSize);
+#endif
+ return 0;
+}
+
+int32 FCUOpusCoder::DecodeFrame(const TArray& InCompressedFrame, TArray& OutPCMFrame)
+{
+#if WITH_OPUS
+ return opus_decode(Decoder, InCompressedFrame.GetData(), InCompressedFrame.Num(), (opus_int16*)OutPCMFrame.GetData(), FrameSize, 0);
+#endif
+ return 0;
+}
+
+
+bool FCUOpusCoder::InitEncoderIfNeeded()
+{
+#if WITH_OPUS
+
+ if (!Encoder)
+ {
+ int32 ErrorCode;
+ if (!Encoder)
+ {
+ int32 ApplicationCode = OPUS_APPLICATION_AUDIO;
+ if (bApplicationVoip)
+ {
+ ApplicationCode = OPUS_APPLICATION_VOIP;
+ }
+ if (bLowestPossibleLatency)
+ {
+ ApplicationCode = OPUS_APPLICATION_RESTRICTED_LOWDELAY;
+ }
+ Encoder = opus_encoder_create(SampleRate, Channels, ApplicationCode, &ErrorCode);
+ if (ErrorCode < 0)
+ {
+ UE_LOG(LogTemp, Warning, TEXT("opus_encoder_create err: %d"), ErrorCode);
+ return false;
+ }
+
+ //Turn on some settings
+ //opus_encoder_ctl(Encoder, OPUS_SET_BITRATE(BitRate));
+ /*opus_encoder_ctl(Encoder, OPUS_SET_VBR(1)); //variable bit rate encoding
+ opus_encoder_ctl(Encoder, OPUS_SET_VBR_CONSTRAINT(0)); //constrained VBR
+ opus_encoder_ctl(Encoder, OPUS_SET_COMPLEXITY(1)); //complexity
+ opus_encoder_ctl(Encoder, OPUS_SET_INBAND_FEC(0)); //forward error correction
+ */
+ }
+ }
+
+#endif
+ return true;
+}
+
+bool FCUOpusCoder::InitDecoderIfNeeded()
+{
+#if WITH_OPUS
+ if (!Decoder)
+ {
+ int32 ErrorCode;
+ Decoder = opus_decoder_create(SampleRate, Channels, &ErrorCode);
+ if (ErrorCode < 0)
+ {
+ UE_LOG(LogTemp, Warning, TEXT("opus_decoder_create err: %d"), ErrorCode);
+ return false;
+ }
+ }
+#endif
+ return true;
+}
+
+void FCUOpusCoder::ResetCoderIfInitialized()
+{
+#if WITH_OPUS
+ if (Encoder)
+ {
+ opus_encoder_destroy(Encoder);
+ Encoder = nullptr;
+ InitEncoderIfNeeded();
+ }
+ if (Decoder)
+ {
+ opus_decoder_destroy(Decoder);
+ Decoder = nullptr;
+ InitDecoderIfNeeded();
+ }
+#endif
+}
+
+//Debug utilities
+void FCUOpusCoder::DebugLogEncoder()
+{
+#if WITH_OPUS
+ int32 ErrCode = 0;
+ int32 BitRateLocal = 0;
+ int32 Vbr = 0;
+ int32 SampleRateLocal = 0;
+ int32 Application = 0;
+ int32 Signal = 0;
+ int32 Complexity = 0;
+
+ ErrCode = opus_encoder_ctl(Encoder, OPUS_GET_BITRATE(&BitRateLocal));
+ ErrCode = opus_encoder_ctl(Encoder, OPUS_GET_VBR(&Vbr));
+ ErrCode = opus_encoder_ctl(Encoder, OPUS_GET_SAMPLE_RATE(&SampleRateLocal));
+ ErrCode = opus_encoder_ctl(Encoder, OPUS_GET_APPLICATION(&Application));
+ ErrCode = opus_encoder_ctl(Encoder, OPUS_GET_SIGNAL(&Signal));
+ ErrCode = opus_encoder_ctl(Encoder, OPUS_GET_COMPLEXITY(&Complexity));
+
+ UE_LOG(LogTemp, Log, TEXT("Opus Encoder Details"));
+ UE_LOG(LogTemp, Log, TEXT("- Application: %d"), Application);
+ UE_LOG(LogTemp, Log, TEXT("- Signal: %d"), Signal);
+ UE_LOG(LogTemp, Log, TEXT("- BitRate: %d"), BitRateLocal);
+ UE_LOG(LogTemp, Log, TEXT("- SampleRate: %d"), SampleRateLocal);
+ UE_LOG(LogTemp, Log, TEXT("- Vbr: %d"), Vbr);
+ UE_LOG(LogTemp, Log, TEXT("- Complexity: %d"), Complexity);
+#endif
+}
+
+void FCUOpusCoder::DebugLogDecoder()
+{
+#if WITH_OPUS
+ int32 ErrCode = 0;
+ int32 Gain = 0;
+ int32 Pitch = 0;
+
+ ErrCode = opus_decoder_ctl(Decoder, OPUS_GET_GAIN(&Gain));
+ ErrCode = opus_decoder_ctl(Decoder, OPUS_GET_PITCH(&Pitch));
+
+ UE_LOG(LogTemp, Log, TEXT("Opus Decoder Details"));
+ UE_LOG(LogTemp, Log, TEXT("- Gain: %d"), Gain);
+ UE_LOG(LogTemp, Log, TEXT("- Pitch: %d"), Pitch);
+#endif
+}
+
+void FCUOpusCoder::DebugLogFrame(const uint8* PacketData, uint32 PacketLength, uint32 InSampleRate, bool bEncode)
+{
+#if WITH_OPUS
+ // Frame Encoding see http://tools.ietf.org/html/rfc6716#section-3.1
+ int32 NumFrames = opus_packet_get_nb_frames(PacketData, PacketLength);
+ if (NumFrames == OPUS_BAD_ARG || NumFrames == OPUS_INVALID_PACKET)
+ {
+ UE_LOG(LogTemp, Warning, TEXT("opus_packet_get_nb_frames: Invalid voice packet data!"));
+ }
+
+ int32 NumSamples = opus_packet_get_nb_samples(PacketData, PacketLength, InSampleRate);
+ if (NumSamples == OPUS_BAD_ARG || NumSamples == OPUS_INVALID_PACKET)
+ {
+ UE_LOG(LogTemp, Warning, TEXT("opus_packet_get_nb_samples: Invalid voice packet data!"));
+ }
+
+ int32 NumSamplesPerFrame = opus_packet_get_samples_per_frame(PacketData, InSampleRate);
+ int32 Bandwidth = opus_packet_get_bandwidth(PacketData);
+
+ const TCHAR* BandwidthStr = nullptr;
+ switch (Bandwidth)
+ {
+ case OPUS_BANDWIDTH_NARROWBAND: // Narrowband (4kHz bandpass)
+ BandwidthStr = TEXT("NB");
+ break;
+ case OPUS_BANDWIDTH_MEDIUMBAND: // Mediumband (6kHz bandpass)
+ BandwidthStr = TEXT("MB");
+ break;
+ case OPUS_BANDWIDTH_WIDEBAND: // Wideband (8kHz bandpass)
+ BandwidthStr = TEXT("WB");
+ break;
+ case OPUS_BANDWIDTH_SUPERWIDEBAND: // Superwideband (12kHz bandpass)
+ BandwidthStr = TEXT("SWB");
+ break;
+ case OPUS_BANDWIDTH_FULLBAND: // Fullband (20kHz bandpass)
+ BandwidthStr = TEXT("FB");
+ break;
+ case OPUS_INVALID_PACKET:
+ default:
+ BandwidthStr = TEXT("Invalid");
+ break;
+ }
+
+ uint8 TOC = 0;
+ const uint8* frames[48];
+ int16 size[48];
+ int32 payload_offset = 0;
+ int32 NumFramesParsed = opus_packet_parse(PacketData, PacketLength, &TOC, frames, size, &payload_offset);
+
+ int32 TOCEncoding = (TOC & 0xf8) >> 3;
+ bool TOCStereo = (TOC & 0x4) != 0 ? true : false;
+ int32 TOCMode = TOC & 0x3;
+
+ if (bEncode)
+ {
+ UE_LOG(LogTemp, Log, TEXT("PacketLength: %d NumFrames: %d NumSamples: %d Bandwidth: %s Encoding: %d Stereo: %d FrameDesc: %d"),
+ PacketLength, NumFrames, NumSamples, BandwidthStr, TOCEncoding, TOCStereo, TOCMode);
+ }
+ else
+ {
+ UE_LOG(LogTemp, Log, TEXT("PacketLength: %d NumFrames: %d NumSamples: %d Bandwidth: %s Encoding: %d Stereo: %d FrameDesc: %d"),
+ PacketLength, NumFrames, NumSamples, BandwidthStr, TOCEncoding, TOCStereo, TOCMode);
+ }
+#endif
+}
+
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Private/FCoreUtility.cpp b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Private/FCoreUtility.cpp
new file mode 100644
index 0000000..d1e3e53
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Private/FCoreUtility.cpp
@@ -0,0 +1,18 @@
+// Copyright 2018-current Getnamo. All Rights Reserved
+
+
+#include "ICoreUtility.h"
+
+DEFINE_LOG_CATEGORY(CoreUtilityLog);
+
+class FCoreUtility : public ICoreUtility
+{
+public:
+
+ /** IModuleInterface implementation */
+ virtual void StartupModule() {};
+ virtual void ShutdownModule() {};
+};
+
+
+IMPLEMENT_MODULE(FCoreUtility, CoreUtility)
\ No newline at end of file
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Public/CUBlueprintLibrary.h b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Public/CUBlueprintLibrary.h
new file mode 100644
index 0000000..0cfdfa9
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Public/CUBlueprintLibrary.h
@@ -0,0 +1,177 @@
+// Copyright 2018-current Getnamo. All Rights Reserved
+
+
+#pragma once
+
+#include "Kismet/BlueprintFunctionLibrary.h"
+#include "Async/Future.h"
+#include "Engine/Classes/Sound/SoundWaveProcedural.h"
+#include "CUBlueprintLibrary.generated.h"
+
+/** Wrapper for EImageFormat::Type for BP */
+UENUM()
+enum class EImageFormatBPType : uint8
+{
+ /** Invalid or unrecognized format. */
+ Invalid = 254,
+
+ /** Portable Network Graphics. */
+ PNG = 0,
+
+ /** Joint Photographic Experts Group. */
+ JPEG,
+
+ /** Single channel JPEG. */
+ GrayscaleJPEG,
+
+ /** Windows Bitmap. */
+ BMP,
+
+ /** Windows Icon resource. */
+ ICO,
+
+ /** OpenEXR (HDR) image file format. */
+ EXR,
+
+ /** Mac icon. */
+ ICNS
+};
+
+/** Callback threading option */
+UENUM(BlueprintType)
+enum ESIOCallbackType
+{
+ CALLBACK_GAME_THREAD,
+ CALLBACK_BACKGROUND_THREADPOOL,
+ CALLBACK_BACKGROUND_TASKGRAPH
+};
+
+/**
+ * Useful generic blueprint functions, mostly conversion
+ */
+UCLASS()
+class COREUTILITY_API UCUBlueprintLibrary : public UBlueprintFunctionLibrary
+{
+ GENERATED_BODY()
+
+public:
+
+ //Conversion Nodes
+
+ /**
+ * Convert any unicode bytes to string
+ */
+ UFUNCTION(BlueprintPure, meta = (DisplayName = "To String (Bytes)", BlueprintAutocast), Category = "CoreUtility|Conversion")
+ static FString Conv_BytesToString(const TArray& InBytes);
+
+ /**
+ * Convert string to UTF8 bytes
+ */
+ UFUNCTION(BlueprintPure, meta = (DisplayName = "To Bytes (String)", BlueprintAutocast), Category = "CoreUtility|Conversion")
+ static TArray Conv_StringToBytes(FString InString);
+
+ /**
+ * Convert bytes to UTexture2D using auto-detection - optimized, but can still have performance implication
+ */
+ UFUNCTION(BlueprintPure, meta = (DisplayName = "To Texture2D (Bytes)", BlueprintAutocast), Category = "CoreUtility|Conversion")
+ static UTexture2D* Conv_BytesToTexture(const TArray& InBytes);
+
+ /**
+ * Audio decompression - Convert opus (currently raw serialized) to wav
+ */
+ UFUNCTION(BlueprintPure, meta = (DisplayName = "To Wav Bytes (Opus Bytes)", BlueprintAutocast), Category = "CoreUtility|Conversion")
+ static TArray Conv_OpusBytesToWav(const TArray& InBytes);
+
+ /**
+ * Audio compression - Convert wav to opus (currently raw serialized)
+ */
+ UFUNCTION(BlueprintPure, meta = (DisplayName = "To Opus Bytes (Wav Bytes)", BlueprintAutocast), Category = "CoreUtility|Conversion")
+ static TArray Conv_WavBytesToOpus(const TArray& InBytes);
+
+ /**
+ * Assumes .wav chunks - handles async alloc, callable from any thread
+ */
+ UFUNCTION(BlueprintPure, meta = (DisplayName = "To SoundWave (Wav Bytes)", BlueprintAutocast), Category = "CoreUtility|Conversion")
+ static USoundWave* Conv_WavBytesToSoundWave(const TArray& InBytes);
+
+ /**
+ * convert a soundwave into wav bytes
+ */
+ UFUNCTION(BlueprintPure, meta = (DisplayName = "To Bytes (SoundWave)", BlueprintAutocast), Category = "CoreUtility|Conversion")
+ static TArray Conv_SoundWaveToWavBytes(USoundWave* SoundWave);
+
+
+ /**
+ * Compact Transform bytes are [[pitch,yaw,roll,x,y,z,sx,sy,sz],...]
+ */
+ UFUNCTION(BlueprintPure, meta = (DisplayName = "To Transforms (Bytes)", BlueprintAutocast), Category = "CoreUtility|Conversion")
+ static void Conv_CompactBytesToTransforms(const TArray& InCompactBytes, TArray& OutTransforms);
+
+ /**
+ * Compact Position bytes are [[x,y,z],...]
+ */
+ UFUNCTION(BlueprintPure, meta = (DisplayName = "To Transforms (Location Bytes)", BlueprintAutocast), Category = "CoreUtility|Conversion")
+ static void Conv_CompactPositionBytesToTransforms(const TArray& InCompactBytes, TArray& OutTransforms);
+
+ /**
+ * Sets and updates soundwave if needed from incoming bytes. Callable on background threads
+ */
+ UFUNCTION(BlueprintCallable, Category = "CoreUtility|Conversion")
+ static void SetSoundWaveFromWavBytes(USoundWaveProcedural* InSoundWave, const TArray& InBytes);
+
+ /**
+ * Fully Async texture conversion from bytes will auto-detect format, depends on TFuture, cannot be called in blueprint. Latent graph callback may be used
+ */
+ static TFuture Conv_BytesToTexture_Async(const TArray& InBytes);
+
+ /**
+ * Convert UTexture2D to bytes in given format - can have performance implication
+ */
+ UFUNCTION(BlueprintPure, meta = (DisplayName = "To Bytes (Texture2D)", BlueprintAutocast), Category = "CoreUtility|Conversion")
+ static bool Conv_TextureToBytes(UTexture2D* Texture, TArray& OutBuffer, EImageFormatBPType Format = EImageFormatBPType::PNG);
+
+ /**
+ * Current UTC time in string format
+ */
+ UFUNCTION(BlueprintPure, Category = "CoreUtility|Misc")
+ static FString NowUTCString();
+
+ /**
+ * Returns a type of Unique Hardware ID
+ */
+ UFUNCTION(BlueprintPure, Category = "CoreUtility|Misc")
+ static FString GetLoginId();
+
+ /**
+ * Return a somewhat unique int for given string
+ */
+ UFUNCTION(BlueprintPure, Category = "CoreUtility|Misc")
+ static int32 ToHashCode(const FString& String);
+
+ /**
+ * Time inter-tick durations for simple
+ */
+ UFUNCTION(BlueprintCallable, Category = "CoreUtility|Misc")
+ static void MeasureTimerStart(const FString& Category = TEXT("TimeTaken"));
+
+ /**
+ * Stops the timer started for this category and returns the duration taken in milliseconds
+ */
+ UFUNCTION(BlueprintCallable, Category = "CoreUtility|Misc")
+ static float MeasureTimerStop(const FString& Category = TEXT("TimeTaken"), bool bShouldLogResult = true);
+
+ /**
+ * Calls function by name given calling context on thread specified. Use e.g. delay (0) to return to game thread
+ * or use game thread callback for threadtype. This allows you to run certain functions on a background thread or
+ * taskgraph in blueprints. Keep in mind that you should not create or destroy UObjects non-game threads.
+ */
+ UFUNCTION(BlueprintCallable, Category = "CoreUtility|Threading", meta = (WorldContext = "WorldContextObject"))
+ static void CallFunctionOnThread(const FString& Function, ESIOCallbackType ThreadType, UObject* WorldContextObject = nullptr);
+
+ /**
+ * Calls specified function on thread type with a latent graph return. This allows you to run certain functions on a background thread or
+ * taskgraph in blueprints. Keep in mind that you should not create or destroy UObjects non-game threads.
+ */
+ UFUNCTION(BlueprintCallable, Category = "CoreUtility|Threading", meta = (Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject"))
+ static void CallFunctionOnThreadGraphReturn(const FString& Function, ESIOCallbackType ThreadType, struct FLatentActionInfo LatentInfo, UObject* WorldContextObject = nullptr);
+};
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Public/CUFileComponent.h b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Public/CUFileComponent.h
new file mode 100644
index 0000000..dc7d584
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Public/CUFileComponent.h
@@ -0,0 +1,47 @@
+// Copyright 2018-current Getnamo. All Rights Reservedfrom UCUFileSubsystem. This component will be removed in the next stable version."
+
+
+#pragma once
+
+#include "Components/ActorComponent.h"
+#include "CUFileComponent.generated.h"
+
+/**
+* Convenience component to save/load data from files
+*/
+UCLASS(ClassGroup = "Utility", meta = (BlueprintSpawnableComponent))
+class COREUTILITY_API UCUFileComponent : public UActorComponent
+{
+ GENERATED_UCLASS_BODY()
+public:
+
+ /** Get the current project contents directory path*/
+ UFUNCTION(BlueprintPure, Category = FileUtility, meta = (DeprecatedFunction, DeprecationMessage = "UCUFileComponent has been deprecated, Please use the same functions from UCUFileSubsystem. This component will be removed in the next stable version."))
+ FString ProjectContentsDirectory();
+
+ /** Get the current project directory path*/
+ UFUNCTION(BlueprintPure, Category = FileUtility, meta = (DeprecatedFunction, DeprecationMessage = "UCUFileComponent has been deprecated, Please use the same functions from UCUFileSubsystem. This component will be removed in the next stable version."))
+ FString ProjectDirectory();
+
+ /** Get the current project saved directory path*/
+ UFUNCTION(BlueprintPure, Category = FileUtility, meta = (DeprecatedFunction, DeprecationMessage = "UCUFileComponent has been deprecated, Please use the same functions from UCUFileSubsystem. This component will be removed in the next stable version."))
+ FString ProjectSavedDirectory();
+
+ /** External storage in android context, otherwise uses project saved directory*/
+ UFUNCTION(BlueprintPure, Category = FileUtility, meta = (DeprecatedFunction, DeprecationMessage = "UCUFileComponent has been deprecated, Please use the same functions from UCUFileSubsystem. This component will be removed in the next stable version."))
+ FString ExternalSaveDirectory();
+
+ UFUNCTION(BlueprintPure, Category = FileUtility, meta = (DeprecatedFunction, DeprecationMessage = "UCUFileComponent has been deprecated, Please use the same functions from UCUFileSubsystem. This component will be removed in the next stable version."))
+ void SplitFullPath(const FString& InFullPath, FString& OutDirectory, FString& OutFileName);
+
+ UFUNCTION(BlueprintPure, Category = FileUtility, meta = (DeprecatedFunction, DeprecationMessage = "UCUFileComponent has been deprecated, Please use the same functions from UCUFileSubsystem. This component will be removed in the next stable version."))
+ void ProjectRelativePath(const FString& InFullPath, FString& OutProjectRelativePath);
+
+ /** Save array of bytes to file at specified directory */
+ UFUNCTION(BlueprintCallable, Category = FileUtility, meta = (DeprecatedFunction, DeprecationMessage = "UCUFileComponent has been deprecated, Please use the same functions from UCUFileSubsystem. This component will be removed in the next stable version."))
+ bool SaveBytesToFile(const TArray& Bytes, const FString& Directory, const FString& FileName, bool bLogSave = false);
+
+ /** Read array of bytes from file at specified directory */
+ UFUNCTION(BlueprintCallable, Category = FileUtility, meta = (DeprecatedFunction, DeprecationMessage = "UCUFileComponent has been deprecated, Please use the same functions from UCUFileSubsystem. This component will be removed in the next stable version."))
+ bool ReadBytesFromFile(const FString& Directory, const FString& FileName, TArray& OutBytes);
+};
\ No newline at end of file
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Public/CUFileSubsystem.h b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Public/CUFileSubsystem.h
new file mode 100644
index 0000000..c2e4b92
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Public/CUFileSubsystem.h
@@ -0,0 +1,58 @@
+#pragma once
+
+#include "Subsystems/EngineSubsystem.h"
+#include "CUFileSubsystem.generated.h"
+
+UCLASS(ClassGroup = "Utility")
+class COREUTILITY_API UCUFileSubsystem : public UEngineSubsystem
+{
+ GENERATED_BODY()
+
+public:
+
+ /** Get the current project contents directory path*/
+ UFUNCTION(BlueprintPure, Category = FileUtility)
+ FString ProjectContentsDirectory();
+
+ /** Get the current project directory path*/
+ UFUNCTION(BlueprintPure, Category = FileUtility)
+ FString ProjectDirectory();
+
+ /** Get the current project saved directory path*/
+ UFUNCTION(BlueprintPure, Category = FileUtility)
+ FString ProjectSavedDirectory();
+
+ /** External storage in android context, otherwise uses project saved directory*/
+ UFUNCTION(BlueprintPure, Category = FileUtility)
+ FString ExternalSaveDirectory();
+
+ UFUNCTION(BlueprintPure, Category = FileUtility)
+ void SplitFullPath(const FString& InFullPath, FString& OutDirectory, FString& OutFileName);
+
+ UFUNCTION(BlueprintPure, Category = FileUtility)
+ void ProjectRelativePath(const FString& InFullPath, FString& OutProjectRelativePath);
+
+ /** Save array of bytes to file at specified directory */
+ UFUNCTION(BlueprintCallable, Category = FileUtility)
+ bool SaveBytesToFile(const TArray& Bytes, const FString& Directory, const FString& FileName, bool bLogSave = false);
+
+ /** Full path variant of SaveBytesToFile */
+ UFUNCTION(BlueprintCallable, Category = FileUtility)
+ bool SaveBytesToPath(const TArray& Bytes, const FString& Path, bool bLogSave = false);
+
+
+ /** Read array of bytes from file at specified directory */
+ UFUNCTION(BlueprintCallable, Category = FileUtility)
+ bool ReadBytesFromFile(const FString& Directory, const FString& FileName, TArray& OutBytes);
+
+ /** Full path variant of ReadBytesFromFile */
+ UFUNCTION(BlueprintCallable, Category = FileUtility)
+ bool ReadBytesFromPath(const FString& Path, TArray& OutBytes);
+
+ UFUNCTION(BlueprintCallable, Category = FileUtility)
+ bool DeleteFileAtPath(const FString& Path);
+
+ //Lifetime
+ virtual void Initialize(FSubsystemCollectionBase& Collection) override;
+ virtual void Deinitialize() override;
+};
\ No newline at end of file
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Public/CULambdaRunnable.h b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Public/CULambdaRunnable.h
new file mode 100644
index 0000000..ad7d91b
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Public/CULambdaRunnable.h
@@ -0,0 +1,72 @@
+// Copyright 2018-current Getnamo. All Rights Reserved
+
+
+#pragma once
+#include "Engine/Classes/Engine/LatentActionManager.h"
+#include "LatentActions.h"
+#include "Async/TaskGraphInterfaces.h"
+
+/** A simple latent action where we don't hold the value, expect capturing value in lambdas */
+class COREUTILITY_API FCULatentAction : public FPendingLatentAction
+{
+public:
+ TFunction OnCancelNotification = nullptr;
+
+ /** Note that you need to use the resulting call to cleanly exit */
+ static FCULatentAction* CreateLatentAction(struct FLatentActionInfo& LatentInfo, UObject* WorldContext);
+
+ FCULatentAction(const FLatentActionInfo& LatentInfo);
+
+ virtual void UpdateOperation(FLatentResponse& Response) override;
+
+ void Call();
+ void Cancel();
+
+ virtual void NotifyObjectDestroyed() override;
+ virtual void NotifyActionAborted() override;
+
+#if WITH_EDITOR
+ virtual FString GetDescription() const override;
+#endif
+
+ const FName ExecutionFunction;
+ const int32 OutputLink;
+ const FWeakObjectPtr CallbackTarget;
+
+private:
+ bool Called;
+};
+
+/**
+* Convenience wrappers for common thread/task work flow. Run background task on thread, callback via task graph on game thread
+*/
+class COREUTILITY_API FCULambdaRunnable
+{
+public:
+
+ /**
+ * Runs the passed lambda on the background thread, new thread per call
+ */
+ static void RunLambdaOnBackGroundThread(TFunction< void()> InFunction);
+
+ /**
+ * Runs the passed lambda on the background thread pool
+ */
+ static void RunLambdaOnBackGroundThreadPool(TFunction< void()> InFunction);
+
+ /**
+ * Runs a short lambda on the game thread via task graph system
+ */
+ static FGraphEventRef RunShortLambdaOnGameThread(TFunction< void()> InFunction);
+
+ /**
+ * Runs a short lambda on background thread via task graph system
+ */
+ static FGraphEventRef RunShortLambdaOnBackGroundTask(TFunction< void()> InFunction);
+
+ /**
+ * Runs a thread with idle for duration before calling back on game thread.
+ * Due to context cost recommended for >0.1sec durations.
+ */
+ static void SetTimeout(TFunctionOnDone, float DurationInSec, bool bCallbackOnGameThread = true);
+};
\ No newline at end of file
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Public/CUMeasureTimer.h b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Public/CUMeasureTimer.h
new file mode 100644
index 0000000..eb60354
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Public/CUMeasureTimer.h
@@ -0,0 +1,58 @@
+// Copyright 2019-current Getnamo. All Rights Reserved
+
+
+#pragma once
+#include "CoreMinimal.h"
+
+//Toggle to enable/disable timing code logs
+#define ENABLE_CUPRECISE_TIMER 1
+
+/**
+* C++ Utility Timer class. Multiple categories can be used simultaneously.
+*
+* Usage:
+* FCUMeasureTimer::Tick(TEXT("MyMeasurementCategory"));
+* //Your code
+* FCUMeasureTimer::Tock(TEXT("MyMeasurementCategory")); //This will log the time taken in miliseconds
+*
+* optionally get the result and handle logging manually
+* double Elapsed = FCUMeasureTimer::Tock(TEXT("MyMeasurementCategory", false);
+*/
+class COREUTILITY_API FCUMeasureTimer
+{
+public:
+ /**
+ * Start a timer for given category
+ */
+ static void Tick(const FString& LogMsg = TEXT("TimeTaken"));
+
+ /**
+ * Returns time taken in milliseconds (to micro precision). This function will also log the time taken
+ */
+ static double Tock(const FString& LogMsg = TEXT("TimeTaken"), bool bShouldLogResult = true);
+
+private:
+ double Then;
+};
+
+/**
+* Wrapper for FCUMeasureTimer calls.
+*
+* Usage:
+* {
+* //code you do not wish to measure
+*
+* FCUScopeTimer Timer(TEXT("My Message"));
+*
+* //your code
+* }
+* It will log duration when you exit the scope
+*/
+class COREUTILITY_API FCUScopeTimer
+{
+public:
+ FCUScopeTimer(const FString& InLogMsg);
+ ~FCUScopeTimer();
+private:
+ FString LogMessage;
+};
\ No newline at end of file
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Public/CUOpusCoder.h b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Public/CUOpusCoder.h
new file mode 100644
index 0000000..2badc6d
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Public/CUOpusCoder.h
@@ -0,0 +1,84 @@
+// Copyright 2019-current Getnamo. All Rights Reserved
+
+#pragma once
+#define WITH_OPUS (PLATFORM_WINDOWS || PLATFORM_UNIX || PLATFORM_ANDROID)
+
+#if WITH_OPUS
+#include "ThirdParty/libOpus/opus-1.1/include/opus.h"
+//#include "ogg/ogg.h"
+#endif
+
+//Bare minimum struct for transferring opus bytes.
+struct FCUOpusMinimalStream
+{
+ TArray PacketSizes; // Maximum packet size is 32768 (much larger than typical packet)
+ TArray CompressedBytes;
+};
+
+
+/**
+Symmetric coder for e.g. voip written from raw libopus due to how hidden the opus
+coder is in the engine (this one doesn't require an online subsystem)
+*/
+class FCUOpusCoder
+{
+public:
+ FCUOpusCoder();
+ ~FCUOpusCoder();
+
+ /** Set Encoder and Decoder Samples per sec */
+ void SetSampleRate(int32 InSampleRate);
+ void SetChannels(int32 Channels);
+ void SetBitrate(int32 Bitrate);
+ void SetFrameSizeMs(int32 FrameSizeInMs);
+
+ /** Expects raw PCM data, outputs compressed raw opus data along with compressed frame sizes*/
+ bool EncodeStream(const TArray& InPCMBytes, FCUOpusMinimalStream& OutStream);
+ bool DecodeStream(const FCUOpusMinimalStream& InStream, TArray& OutPCMFrame);
+
+
+ //Format: PacketCount, PacketSizes, CompressedOpusBytes. Expects settings set in a different stream
+ bool SerializeMinimal(const FCUOpusMinimalStream& InStream, TArray& OutSerializedBytes);
+ bool DeserializeMinimal(const TArray& InSerializedMinimalBytes, FCUOpusMinimalStream& OutStream);
+
+ //Handle a single frame
+ int32 EncodeFrame(const TArray& InPCMFrame, TArray& OutCompressed);
+ int32 DecodeFrame(const TArray& InCompressedFrame, TArray& OutPCMFrame);
+
+ //Todo: add ogg file format wrapper for raw opus bytes
+
+ //Intended to be Read-only
+ int32 Channels;
+ int32 SampleRate;
+
+ //Whether we should reset the coder per stream to keep byte size small. Costs ~0.01ms
+ bool bResetBetweenEncoding;
+ bool bApplicationVoip;
+ bool bLowestPossibleLatency; //trade-off is compression size; ~3ms/sec of audio if on, ~10ms/sec if off. For ~ 33% more bytes.
+
+ void ResetCoderIfInitialized();
+
+protected:
+ //Call this if some settings need to be reflected (all setters all this)
+ bool InitEncoderIfNeeded();
+ bool InitDecoderIfNeeded();
+
+private:
+
+#if WITH_OPUS
+ OpusEncoder* Encoder;
+ OpusDecoder* Decoder;
+#endif
+
+ int32 BitRate;
+ int32 MaxPacketSize;
+ int32 FrameSizeMs;
+ int32 FrameSize;
+ int32 MaxFrameSize;
+
+
+ //Debug utilities
+ void DebugLogEncoder();
+ void DebugLogDecoder();
+ void DebugLogFrame(const uint8* PacketData, uint32 PacketLength, uint32 SampleRate, bool bEncode);
+};
\ No newline at end of file
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Public/ICoreUtility.h b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Public/ICoreUtility.h
new file mode 100644
index 0000000..46730bd
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Source/CoreUtility/Public/ICoreUtility.h
@@ -0,0 +1,44 @@
+// Copyright 2018-current Getnamo. All Rights Reserved
+
+
+#pragma once
+
+
+DECLARE_LOG_CATEGORY_EXTERN(CoreUtilityLog, Log, All);
+#include "Modules/ModuleManager.h"
+
+
+/**
+* The public interface to this module. In most cases, this interface is only public to sibling modules
+* within this plugin.
+*/
+class ICoreUtility : public IModuleInterface
+{
+
+public:
+
+ /**
+ * Singleton-like access to this module's interface. This is just for convenience!
+ * Beware of calling this during the shutdown phase, though. Your module might have been unloaded already.
+ *
+ * @return Returns singleton instance, loading the module on demand if needed
+ */
+ static inline ICoreUtility& Get()
+ {
+ return FModuleManager::LoadModuleChecked< ICoreUtility >("CoreUtility");
+ }
+
+ /**
+ * Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true.
+ *
+ * @return True if the module is loaded and ready to use
+ */
+ static inline bool IsAvailable()
+ {
+ return FModuleManager::Get().IsModuleLoaded("CoreUtility");
+ }
+
+ /** IModuleInterface implementation */
+ virtual void StartupModule() {};
+ virtual void ShutdownModule() {};
+};
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJEditorPlugin/Private/SIOJEditorPlugin.cpp b/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJEditorPlugin/Private/SIOJEditorPlugin.cpp
new file mode 100644
index 0000000..ce2c23a
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJEditorPlugin/Private/SIOJEditorPlugin.cpp
@@ -0,0 +1,24 @@
+// Modifications Copyright 2018-current Getnamo. All Rights Reserved
+
+
+// Copyright 2015 Vladimir Alyamkin. All Rights Reserved.
+// Original code by https://github.com/unktomi
+
+#include "SIOJEditorPlugin.h"
+
+#define LOCTEXT_NAMESPACE "FSIOJEditorPluginModule"
+
+void FSIOJEditorPluginModule::StartupModule()
+{
+ // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
+}
+
+void FSIOJEditorPluginModule::ShutdownModule()
+{
+ // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
+ // we call this function before unloading the module.
+}
+
+#undef LOCTEXT_NAMESPACE
+
+IMPLEMENT_MODULE(FSIOJEditorPluginModule, SIOJEditorPlugin)
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJEditorPlugin/Private/SIOJ_BreakJson.cpp b/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJEditorPlugin/Private/SIOJ_BreakJson.cpp
new file mode 100644
index 0000000..1056c22
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJEditorPlugin/Private/SIOJ_BreakJson.cpp
@@ -0,0 +1,306 @@
+// Modifications Copyright 2018-current Getnamo. All Rights Reserved
+
+
+// Copyright 2015 Vladimir Alyamkin. All Rights Reserved.
+// Original code by https://github.com/unktomi
+
+#include "SIOJ_BreakJson.h"
+#include "EdGraphUtilities.h"
+#include "KismetCompiler.h"
+#include "EditorCategoryUtils.h"
+#include "EdGraph/EdGraph.h"
+#include "EdGraph/EdGraphNodeUtils.h" // for FNodeTextCache
+#include "EdGraphSchema_K2.h"
+#include "BlueprintNodeSpawner.h"
+#include "BlueprintActionDatabaseRegistrar.h"
+#include "BlueprintFieldNodeSpawner.h"
+#include "EditorCategoryUtils.h"
+#include "BlueprintActionFilter.h"
+#include "Launch/Resources/Version.h"
+
+#define LOCTEXT_NAMESPACE "SIOJ_BreakJson"
+
+class FKCHandler_BreakJson : public FNodeHandlingFunctor
+{
+
+public:
+ FKCHandler_BreakJson(FKismetCompilerContext& InCompilerContext)
+ : FNodeHandlingFunctor(InCompilerContext)
+ {
+ }
+
+ virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override
+ {
+ UEdGraphPin* InputPin = NULL;
+
+ for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex)
+ {
+ UEdGraphPin* Pin = Node->Pins[PinIndex];
+ if (Pin && (EGPD_Input == Pin->Direction))
+ {
+ InputPin = Pin;
+ break;
+ }
+ }
+
+ UEdGraphPin *InNet = FEdGraphUtilities::GetNetFromPin(InputPin);
+ UClass *Class = Cast(StaticLoadObject(UClass::StaticClass(), NULL, TEXT("class'SIOJPlugin.SIOJJsonObject'")));
+
+ FBPTerminal **SourceTerm = Context.NetMap.Find(InNet);
+ if (SourceTerm == nullptr)
+ {
+ return;
+ }
+
+ for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex)
+ {
+ UEdGraphPin* Pin = Node->Pins[PinIndex];
+ if (Pin && (EGPD_Output == Pin->Direction))
+ {
+ if (Pin->LinkedTo.Num() < 1)
+ {
+ continue;
+ }
+
+ FBPTerminal **Target = Context.NetMap.Find(Pin);
+
+ const FName&FieldName = Pin->PinName;
+ const FName&FieldType = Pin->PinType.PinCategory;
+
+ FBPTerminal* FieldNameTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal);
+ FieldNameTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_String;
+ FieldNameTerm->SourcePin = Pin;
+ //FieldNameTerm->Source = Pin;
+ FieldNameTerm->Name = FieldName.ToString();
+ FieldNameTerm->TextLiteral = FText::FromName(FieldName);
+
+ FBlueprintCompiledStatement& Statement = Context.AppendStatementForNode(Node);
+ FName FunctionName;
+
+ bool bIsArray = Pin->PinType.IsArray();
+
+ if (FieldType == CompilerContext.GetSchema()->PC_Boolean)
+ {
+ FunctionName = bIsArray ? TEXT("GetBoolArrayField") : TEXT("GetBoolField");
+ }
+ else if (FieldType == CompilerContext.GetSchema()->PC_Float)
+ {
+ FunctionName = bIsArray ? TEXT("GetNumberArrayField") : TEXT("GetNumberField");
+ }
+ else if (FieldType == CompilerContext.GetSchema()->PC_String)
+ {
+ FunctionName = bIsArray ? TEXT("GetStringArrayField") : TEXT("GetStringField");
+ }
+ else if (FieldType == CompilerContext.GetSchema()->PC_Object)
+ {
+ FunctionName = bIsArray ? TEXT("GetObjectArrayField") : TEXT("GetObjectField");
+ }
+ else
+ {
+ continue;
+ }
+
+ UFunction *FunctionPtr = Class->FindFunctionByName(FunctionName);
+ Statement.Type = KCST_CallFunction;
+ Statement.FunctionToCall = FunctionPtr;
+ Statement.FunctionContext = *SourceTerm;
+ Statement.bIsParentContext = false;
+ Statement.LHS = *Target;
+ Statement.RHS.Add(FieldNameTerm);
+ }
+ }
+ }
+
+ FBPTerminal* RegisterInputTerm(FKismetFunctionContext& Context, USIOJ_BreakJson* Node)
+ {
+ // Find input pin
+ UEdGraphPin* InputPin = NULL;
+ for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex)
+ {
+ UEdGraphPin* Pin = Node->Pins[PinIndex];
+ if (Pin && (EGPD_Input == Pin->Direction))
+ {
+ InputPin = Pin;
+ break;
+ }
+ }
+ check(NULL != InputPin);
+
+ // Find structure source net
+ UEdGraphPin* Net = FEdGraphUtilities::GetNetFromPin(InputPin);
+ FBPTerminal **TermPtr = Context.NetMap.Find(Net);
+
+ if (!TermPtr)
+ {
+ FBPTerminal *Term = Context.CreateLocalTerminalFromPinAutoChooseScope(Net, Context.NetNameMap->MakeValidName(Net));
+
+ Context.NetMap.Add(Net, Term);
+
+ return Term;
+ }
+
+ return *TermPtr;
+ }
+
+ void RegisterOutputTerm(FKismetFunctionContext& Context, UEdGraphPin* OutputPin, FBPTerminal* ContextTerm)
+ {
+ FBPTerminal *Term = Context.CreateLocalTerminalFromPinAutoChooseScope(OutputPin, Context.NetNameMap->MakeValidName(OutputPin));
+ Context.NetMap.Add(OutputPin, Term);
+ }
+
+ virtual void RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* InNode) override
+ {
+ USIOJ_BreakJson* Node = Cast(InNode);
+ FNodeHandlingFunctor::RegisterNets(Context, Node);
+
+ check(NULL != Node);
+
+ if (FBPTerminal* StructContextTerm = RegisterInputTerm(Context, Node))
+ {
+ for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex)
+ {
+ UEdGraphPin* Pin = Node->Pins[PinIndex];
+ if (NULL != Pin && EGPD_Output == Pin->Direction)
+ {
+ RegisterOutputTerm(Context, Pin, StructContextTerm);
+ }
+ }
+ }
+ }
+};
+
+/**
+ * Main node class
+ */
+USIOJ_BreakJson::USIOJ_BreakJson(const FObjectInitializer &ObjectInitializer)
+ : Super(ObjectInitializer)
+{
+}
+
+FNodeHandlingFunctor* USIOJ_BreakJson::CreateNodeHandler(class FKismetCompilerContext& CompilerContext) const
+{
+ return new FKCHandler_BreakJson(CompilerContext);
+}
+
+void USIOJ_BreakJson::AllocateDefaultPins()
+{
+ const UEdGraphSchema_K2* K2Schema = GetDefault();
+
+ UClass *Class = Cast(StaticLoadObject(UClass::StaticClass(), NULL, TEXT("class'SIOJPlugin.SIOJJsonObject'")));
+ UEdGraphPin* Pin = CreatePin(EGPD_Input, K2Schema->PC_Object, TEXT(""), Class, TEXT("Target"));
+
+ K2Schema->SetPinAutogeneratedDefaultValueBasedOnType(Pin);
+
+ CreateProjectionPins(Pin);
+}
+
+FLinearColor USIOJ_BreakJson::GetNodeTitleColor() const
+{
+ return FLinearColor(255.0f, 255.0f, 0.0f);
+}
+
+void USIOJ_BreakJson::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
+{
+ bool bIsDirty = false;
+
+ FName PropertyName = (PropertyChangedEvent.Property != NULL) ? PropertyChangedEvent.Property->GetFName() : NAME_None;
+ if (true || PropertyName == TEXT("Outputs"))
+ {
+ bIsDirty = true;
+ }
+
+ if (bIsDirty)
+ {
+ ReconstructNode();
+ GetGraph()->NotifyGraphChanged();
+ }
+
+ Super::PostEditChangeProperty(PropertyChangedEvent);
+}
+
+void USIOJ_BreakJson::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
+{
+ // actions get registered under specific object-keys; the idea is that
+ // actions might have to be updated (or deleted) if their object-key is
+ // mutated (or removed)... here we use the node's class (so if the node
+ // type disappears, then the action should go with it)
+ UClass* ActionKey = GetClass();
+
+ // to keep from needlessly instantiating a UBlueprintNodeSpawner, first
+ // check to make sure that the registrar is looking for actions of this type
+ // (could be regenerating actions for a specific asset, and therefore the
+ // registrar would only accept actions corresponding to that asset)
+ if (ActionRegistrar.IsOpenForRegistration(ActionKey))
+ {
+ UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
+ check(NodeSpawner != nullptr);
+
+ ActionRegistrar.AddBlueprintAction(ActionKey, NodeSpawner);
+ }
+}
+
+FText USIOJ_BreakJson::GetMenuCategory() const
+{
+ static FNodeTextCache CachedCategory;
+
+ if (CachedCategory.IsOutOfDate(this))
+ {
+ // FText::Format() is slow, so we cache this to save on performance
+ CachedCategory.SetCachedText(FEditorCategoryUtils::BuildCategoryString(FCommonEditorCategory::Utilities, LOCTEXT("ActionMenuCategory", "Va Rest")), this);
+ }
+ return CachedCategory;
+}
+
+void USIOJ_BreakJson::CreateProjectionPins(UEdGraphPin *Source)
+{
+ const UEdGraphSchema_K2* K2Schema = GetDefault();
+ UClass *Class = Cast(StaticLoadObject(UClass::StaticClass(), NULL, TEXT("class'SIOJPlugin.SIOJJsonObject'")));
+
+ for (TArray::TIterator it(Outputs); it; ++it)
+ {
+ FName Type;
+ UObject *Subtype = nullptr;
+ FString FieldName = (*it).Name;
+
+ switch ((*it).Type) {
+ case ESIOJ_JsonType::JSON_Bool:
+ Type = K2Schema->PC_Boolean;
+ break;
+
+ case ESIOJ_JsonType::JSON_Number:
+ Type = K2Schema->PC_Float;
+ break;
+
+ case ESIOJ_JsonType::JSON_String:
+ Type = K2Schema->PC_String;
+ break;
+
+ case ESIOJ_JsonType::JSON_Object:
+ Type = K2Schema->PC_Object;
+ Subtype = Class;
+ break;
+ }
+
+ FCreatePinParams PinParams;
+ if ((*it).bIsArray)
+ {
+ PinParams.ContainerType = EPinContainerType::Array;
+ }
+ else
+ {
+ PinParams.ContainerType = EPinContainerType::None;
+ }
+ PinParams.bIsConst = false;
+ PinParams.bIsReference = false;
+ FName Name = FName(*(*it).Name);
+
+ UEdGraphPin *OutputPin = CreatePin(EGPD_Output, Type, TEXT(""), Subtype, Name, PinParams);
+ }
+}
+
+FText USIOJ_BreakJson::GetNodeTitle(ENodeTitleType::Type TitleType) const
+{
+ return LOCTEXT("SIOJ_Break_Json.NodeTitle", "Break Json");
+}
+
+#undef LOCTEXT_NAMESPACE
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJEditorPlugin/Public/SIOJEditorPlugin.h b/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJEditorPlugin/Public/SIOJEditorPlugin.h
new file mode 100644
index 0000000..fcb022b
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJEditorPlugin/Public/SIOJEditorPlugin.h
@@ -0,0 +1,19 @@
+// Modifications Copyright 2018-current Getnamo. All Rights Reserved
+
+
+// Copyright 2015 Vladimir Alyamkin. All Rights Reserved.
+// Original code by https://github.com/unktomi
+
+#pragma once
+
+#include "Modules/ModuleManager.h"
+
+class FSIOJEditorPluginModule : public IModuleInterface
+{
+
+public:
+ /** IModuleInterface implementation */
+ virtual void StartupModule() override;
+ virtual void ShutdownModule() override;
+
+};
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJEditorPlugin/Public/SIOJ_BreakJson.h b/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJEditorPlugin/Public/SIOJ_BreakJson.h
new file mode 100644
index 0000000..f935dfd
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJEditorPlugin/Public/SIOJ_BreakJson.h
@@ -0,0 +1,73 @@
+// Modifications Copyright 2018-current Getnamo. All Rights Reserved
+
+
+// Copyright 2015 Vladimir Alyamkin. All Rights Reserved.
+// Original code by https://github.com/unktomi
+
+#pragma once
+
+#include "K2Node.h"
+#include "ISIOJson.h"
+#include "SIOJConvert.h"
+#include "SIOJsonObject.h"
+#include "SIOJsonValue.h"
+#include "SIOJLibrary.h"
+#include "SIOJRequestJSON.h"
+#include "SIOJTypes.h"
+
+#include "SIOJ_BreakJson.generated.h"
+
+UENUM(BlueprintType)
+enum class ESIOJ_JsonType : uint8
+{
+ //JSON_Null UMETA(DisplayName = "Null"),
+ JSON_Bool UMETA(DisplayName = "Boolean"),
+ JSON_Number UMETA(DisplayName = "Number"),
+ JSON_String UMETA(DisplayName = "String"),
+ JSON_Object UMETA(DisplayName = "Object")
+};
+
+USTRUCT(BlueprintType)
+struct FSIOJ_NamedType
+{
+ GENERATED_USTRUCT_BODY();
+
+ UPROPERTY(EditAnywhere, Category = NamedType)
+ FString Name;
+
+ UPROPERTY(EditAnywhere, Category = NamedType)
+ ESIOJ_JsonType Type;
+
+ UPROPERTY(EditAnywhere, Category = NamedType)
+ bool bIsArray;
+};
+
+UCLASS(BlueprintType, Blueprintable)
+class SIOJEDITORPLUGIN_API USIOJ_BreakJson : public UK2Node
+{
+ GENERATED_UCLASS_BODY()
+
+public:
+ // Begin UEdGraphNode interface.
+ virtual void AllocateDefaultPins() override;
+ virtual FLinearColor GetNodeTitleColor() const override;
+ virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override;
+ // End UEdGraphNode interface.
+
+ // Begin UK2Node interface
+ virtual bool IsNodePure() const { return true; }
+ virtual bool ShouldShowNodeProperties() const { return true; }
+ void GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const override;
+ virtual FText GetMenuCategory() const override;
+ virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
+ virtual class FNodeHandlingFunctor* CreateNodeHandler(class FKismetCompilerContext& CompilerContext) const override;
+ // End UK2Node interface.
+
+protected:
+ virtual void CreateProjectionPins(UEdGraphPin *Source);
+
+public:
+ UPROPERTY(EditAnywhere, Category = PinOptions)
+ TArray Outputs;
+
+};
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJEditorPlugin/SIOJEditorPlugin.Build.cs b/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJEditorPlugin/SIOJEditorPlugin.Build.cs
new file mode 100644
index 0000000..5e4b6d7
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJEditorPlugin/SIOJEditorPlugin.Build.cs
@@ -0,0 +1,72 @@
+// Modifications Copyright 2018-current Getnamo. All Rights Reserved
+
+
+// Copyright 2015 Vladimir Alyamkin. All Rights Reserved.
+
+using UnrealBuildTool;
+using System.IO;
+
+public class SIOJEditorPlugin : ModuleRules
+{
+ public SIOJEditorPlugin(ReadOnlyTargetRules Target) : base(Target)
+ {
+ PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
+
+ PublicIncludePaths.AddRange(
+ new string[] {
+ Path.Combine(ModuleDirectory, "Public"),
+ Path.Combine(ModuleDirectory, "../SIOJson", "Public"),
+
+ // ... add public include paths required here ...
+ });
+
+
+ PrivateIncludePaths.AddRange(
+ new string[] {
+ "SIOJEditorPlugin/Private",
+
+ // ... add other private include paths required here ...
+ });
+
+
+ PublicDependencyModuleNames.AddRange(
+ new string[]
+ {
+ "Core",
+ "SIOJson"
+
+ // ... add other public dependencies that you statically link with here ...
+ });
+
+
+ PrivateDependencyModuleNames.AddRange(
+ new string[]
+ {
+ "CoreUObject",
+ "Engine",
+ "Slate",
+ "SlateCore",
+ "InputCore",
+ "AssetTools",
+ "UnrealEd", // for FAssetEditorManager
+ "KismetWidgets",
+ "KismetCompiler",
+ "BlueprintGraph",
+ "GraphEditor",
+ "Kismet", // for FWorkflowCentricApplication
+ "PropertyEditor",
+ "EditorStyle",
+ "Sequencer",
+ "DetailCustomizations",
+ "Settings",
+ "RenderCore"
+ });
+
+
+ DynamicallyLoadedModuleNames.AddRange(
+ new string[]
+ {
+ // ... add any modules that your module loads dynamically here ...
+ });
+ }
+}
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJson/Private/SIOJConvert.cpp b/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJson/Private/SIOJConvert.cpp
new file mode 100644
index 0000000..115e333
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJson/Private/SIOJConvert.cpp
@@ -0,0 +1,1083 @@
+// Copyright 2018-current Getnamo. All Rights Reserved
+
+
+#include "SIOJConvert.h"
+//#include "Json.h"
+#include "UObject/TextProperty.h"
+#include "JsonGlobals.h"
+#include "Policies/CondensedJsonPrintPolicy.h"
+#include "Misc/FileHelper.h"
+#include "SIOJsonValue.h"
+#include "SIOJsonObject.h"
+#include "JsonObjectConverter.h"
+#include "UObject/PropertyPortFlags.h"
+#include "Misc/Base64.h"
+
+typedef TJsonWriterFactory< TCHAR, TCondensedJsonPrintPolicy > FCondensedJsonStringWriterFactory;
+typedef TJsonWriter< TCHAR, TCondensedJsonPrintPolicy > FCondensedJsonStringWriter;
+
+//The one key that will break
+#define TMAP_STRING TEXT("!__!INTERNAL_TMAP")
+
+namespace
+{
+ FJsonObjectConverter::CustomExportCallback EnumOverrideExportCallback;
+
+ //Begin partial copy of FJsonObjectConverter for BP enum workaround
+ bool JsonValueToFPropertyWithContainer(const TSharedPtr& JsonValue, FProperty* Property, void* OutValue, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags);
+ bool JsonAttributesToUStructWithContainer(const TMap< FString, TSharedPtr >& JsonAttributes, const UStruct* StructDefinition, void* OutStruct, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags);
+
+ /** Convert JSON to property, assuming either the property is not an array or the value is an individual array element */
+ bool ConvertScalarJsonValueToFPropertyWithContainer(const TSharedPtr& JsonValue, FProperty* Property, void* OutValue, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags)
+ {
+ if (FEnumProperty* EnumProperty = CastField(Property))
+ {
+ if (JsonValue->Type == EJson::String)
+ {
+ // see if we were passed a string for the enum
+ const UEnum* Enum = EnumProperty->GetEnum();
+ check(Enum);
+ FString StrValue = JsonValue->AsString();
+ int64 IntValue = Enum->GetValueByName(FName(*StrValue));
+ if (IntValue == INDEX_NONE)
+ {
+ UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Unable import enum %s from string value %s for property %s"), *Enum->CppType, *StrValue, *Property->GetNameCPP());
+ return false;
+ }
+ EnumProperty->GetUnderlyingProperty()->SetIntPropertyValue(OutValue, IntValue);
+ }
+ else
+ {
+ // AsNumber will log an error for completely inappropriate types (then give us a default)
+ EnumProperty->GetUnderlyingProperty()->SetIntPropertyValue(OutValue, (int64)JsonValue->AsNumber());
+ }
+ }
+ else if (FNumericProperty *NumericProperty = CastField(Property))
+ {
+ if (NumericProperty->IsEnum() && JsonValue->Type == EJson::String)
+ {
+ // see if we were passed a string for the enum
+ const UEnum* Enum = NumericProperty->GetIntPropertyEnum();
+ check(Enum); // should be assured by IsEnum()
+ FString StrValue = JsonValue->AsString();
+ int64 IntValue = Enum->GetValueByName(FName(*StrValue));
+
+ //BEGIN WORKAROUND MODIFICATION
+ if (IntValue == INDEX_NONE)
+ {
+ //Failed 'NewEnumeratorX' lookup, try via DisplayNames
+ const FString LowerStrValue = StrValue.ToLower();
+
+ //blueprints only support int8 sized enums
+ int8 MaxEnum = (int8)Enum->GetMaxEnumValue();
+ for (int32 i = 0; i < MaxEnum; i++)
+ {
+ //Case insensitive match
+ if (LowerStrValue.Equals(Enum->GetDisplayNameTextByIndex(i).ToString().ToLower()))
+ {
+ IntValue = i;
+ }
+ }
+
+ //END WORKAROUND MODIFICATION
+
+ if (IntValue == INDEX_NONE)
+ {
+ UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Unable import enum %s from string value %s for property %s"), *Enum->CppType, *StrValue, *Property->GetNameCPP());
+ return false;
+ }
+ }
+ NumericProperty->SetIntPropertyValue(OutValue, IntValue);
+ }
+ else if (NumericProperty->IsFloatingPoint())
+ {
+ // AsNumber will log an error for completely inappropriate types (then give us a default)
+ NumericProperty->SetFloatingPointPropertyValue(OutValue, JsonValue->AsNumber());
+ }
+ else if (NumericProperty->IsInteger())
+ {
+ if (JsonValue->Type == EJson::String)
+ {
+ // parse string -> int64 ourselves so we don't lose any precision going through AsNumber (aka double)
+ NumericProperty->SetIntPropertyValue(OutValue, FCString::Atoi64(*JsonValue->AsString()));
+ }
+ else
+ {
+ // AsNumber will log an error for completely inappropriate types (then give us a default)
+ NumericProperty->SetIntPropertyValue(OutValue, (int64)JsonValue->AsNumber());
+ }
+ }
+ else
+ {
+ UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Unable to set numeric property type %s for property %s"), *Property->GetClass()->GetName(), *Property->GetNameCPP());
+ return false;
+ }
+ }
+ else if (FBoolProperty *BoolProperty = CastField(Property))
+ {
+ // AsBool will log an error for completely inappropriate types (then give us a default)
+ BoolProperty->SetPropertyValue(OutValue, JsonValue->AsBool());
+ }
+ else if (FStrProperty *StringProperty = CastField(Property))
+ {
+ // AsString will log an error for completely inappropriate types (then give us a default)
+ StringProperty->SetPropertyValue(OutValue, JsonValue->AsString());
+ }
+ else if (FArrayProperty *ArrayProperty = CastField(Property))
+ {
+ if (JsonValue->Type == EJson::Array)
+ {
+ TArray< TSharedPtr > ArrayValue = JsonValue->AsArray();
+ int32 ArrLen = ArrayValue.Num();
+
+ // make the output array size match
+ FScriptArrayHelper Helper(ArrayProperty, OutValue);
+ Helper.Resize(ArrLen);
+
+ // set the property values
+ for (int32 i = 0; i < ArrLen; ++i)
+ {
+ const TSharedPtr& ArrayValueItem = ArrayValue[i];
+ if (ArrayValueItem.IsValid() && !ArrayValueItem->IsNull())
+ {
+ if (!JsonValueToFPropertyWithContainer(ArrayValueItem, ArrayProperty->Inner, Helper.GetRawPtr(i), ContainerStruct, Container, CheckFlags & (~CPF_ParmFlags), SkipFlags))
+ {
+ UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Unable to deserialize array element [%d] for property %s"), i, *Property->GetNameCPP());
+ return false;
+ }
+ }
+ }
+ }
+ else
+ {
+ UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Attempted to import TArray from non-array JSON key for property %s"), *Property->GetNameCPP());
+ return false;
+ }
+ }
+ else if (FMapProperty* MapProperty = CastField(Property))
+ {
+ if (JsonValue->Type == EJson::Object)
+ {
+ TSharedPtr ObjectValue = JsonValue->AsObject();
+
+ FScriptMapHelper Helper(MapProperty, OutValue);
+
+ check(ObjectValue);
+
+ int32 MapSize = ObjectValue->Values.Num();
+ Helper.EmptyValues(MapSize);
+
+ // set the property values
+ for (const auto& Entry : ObjectValue->Values)
+ {
+ if (Entry.Value.IsValid() && !Entry.Value->IsNull())
+ {
+ int32 NewIndex = Helper.AddDefaultValue_Invalid_NeedsRehash();
+
+ TSharedPtr TempKeyValue = MakeShared(Entry.Key);
+
+ const bool bKeySuccess = JsonValueToFPropertyWithContainer(TempKeyValue, MapProperty->KeyProp, Helper.GetKeyPtr(NewIndex), ContainerStruct, Container, CheckFlags & (~CPF_ParmFlags), SkipFlags);
+ const bool bValueSuccess = JsonValueToFPropertyWithContainer(Entry.Value, MapProperty->ValueProp, Helper.GetValuePtr(NewIndex), ContainerStruct, Container, CheckFlags & (~CPF_ParmFlags), SkipFlags);
+
+ if (!(bKeySuccess && bValueSuccess))
+ {
+ UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Unable to deserialize map element [key: %s] for property %s"), *Entry.Key, *Property->GetNameCPP());
+ return false;
+ }
+ }
+ }
+
+ Helper.Rehash();
+ }
+ else
+ {
+ UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Attempted to import TMap from non-object JSON key for property %s"), *Property->GetNameCPP());
+ return false;
+ }
+ }
+ else if (FSetProperty* SetProperty = CastField(Property))
+ {
+ if (JsonValue->Type == EJson::Array)
+ {
+ TArray< TSharedPtr > ArrayValue = JsonValue->AsArray();
+ int32 ArrLen = ArrayValue.Num();
+
+ FScriptSetHelper Helper(SetProperty, OutValue);
+
+ // set the property values
+ for (int32 i = 0; i < ArrLen; ++i)
+ {
+ const TSharedPtr& ArrayValueItem = ArrayValue[i];
+ if (ArrayValueItem.IsValid() && !ArrayValueItem->IsNull())
+ {
+ int32 NewIndex = Helper.AddDefaultValue_Invalid_NeedsRehash();
+ if (!JsonValueToFPropertyWithContainer(ArrayValueItem, SetProperty->ElementProp, Helper.GetElementPtr(NewIndex), ContainerStruct, Container, CheckFlags & (~CPF_ParmFlags), SkipFlags))
+ {
+ UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Unable to deserialize set element [%d] for property %s"), i, *Property->GetNameCPP());
+ return false;
+ }
+ }
+ }
+
+ Helper.Rehash();
+ }
+ else
+ {
+ UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Attempted to import TSet from non-array JSON key for property %s"), *Property->GetNameCPP());
+ return false;
+ }
+ }
+ else if (FTextProperty* TextProperty = CastField(Property))
+ {
+ if (JsonValue->Type == EJson::String)
+ {
+ // assume this string is already localized, so import as invariant
+ TextProperty->SetPropertyValue(OutValue, FText::FromString(JsonValue->AsString()));
+ }
+ else if (JsonValue->Type == EJson::Object)
+ {
+ TSharedPtr Obj = JsonValue->AsObject();
+ check(Obj.IsValid()); // should not fail if Type == EJson::Object
+
+ // import the subvalue as a culture invariant string
+ FText Text;
+ if (!FJsonObjectConverter::GetTextFromObject(Obj.ToSharedRef(), Text))
+ {
+ UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Attempted to import FText from JSON object with invalid keys for property %s"), *Property->GetNameCPP());
+ return false;
+ }
+ TextProperty->SetPropertyValue(OutValue, Text);
+ }
+ else
+ {
+ UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Attempted to import FText from JSON that was neither string nor object for property %s"), *Property->GetNameCPP());
+ return false;
+ }
+ }
+ else if (FStructProperty *StructProperty = CastField(Property))
+ {
+ static const FName NAME_DateTime(TEXT("DateTime"));
+ static const FName NAME_Color_Local(TEXT("Color"));
+ static const FName NAME_LinearColor_Local(TEXT("LinearColor"));
+ if (JsonValue->Type == EJson::Object)
+ {
+ TSharedPtr Obj = JsonValue->AsObject();
+ check(Obj.IsValid()); // should not fail if Type == EJson::Object
+ if (!JsonAttributesToUStructWithContainer(Obj->Values, StructProperty->Struct, OutValue, ContainerStruct, Container, CheckFlags & (~CPF_ParmFlags), SkipFlags))
+ {
+ UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - FJsonObjectConverter::JsonObjectToUStruct failed for property %s"), *Property->GetNameCPP());
+ return false;
+ }
+ }
+ else if (JsonValue->Type == EJson::String && StructProperty->Struct->GetFName() == NAME_LinearColor_Local)
+ {
+ FLinearColor& ColorOut = *(FLinearColor*)OutValue;
+ FString ColorString = JsonValue->AsString();
+
+ FColor IntermediateColor;
+ IntermediateColor = FColor::FromHex(ColorString);
+
+ ColorOut = IntermediateColor;
+ }
+ else if (JsonValue->Type == EJson::String && StructProperty->Struct->GetFName() == NAME_Color)
+ {
+ FColor& ColorOut = *(FColor*)OutValue;
+ FString ColorString = JsonValue->AsString();
+
+ ColorOut = FColor::FromHex(ColorString);
+ }
+ else if (JsonValue->Type == EJson::String && StructProperty->Struct->GetFName() == NAME_DateTime)
+ {
+ FString DateString = JsonValue->AsString();
+ FDateTime& DateTimeOut = *(FDateTime*)OutValue;
+ if (DateString == TEXT("min"))
+ {
+ // min representable value for our date struct. Actual date may vary by platform (this is used for sorting)
+ DateTimeOut = FDateTime::MinValue();
+ }
+ else if (DateString == TEXT("max"))
+ {
+ // max representable value for our date struct. Actual date may vary by platform (this is used for sorting)
+ DateTimeOut = FDateTime::MaxValue();
+ }
+ else if (DateString == TEXT("now"))
+ {
+ // this value's not really meaningful from json serialization (since we don't know timezone) but handle it anyway since we're handling the other keywords
+ DateTimeOut = FDateTime::UtcNow();
+ }
+ else if (FDateTime::ParseIso8601(*DateString, DateTimeOut))
+ {
+ // ok
+ }
+ else if (FDateTime::Parse(DateString, DateTimeOut))
+ {
+ // ok
+ }
+ else
+ {
+ UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Unable to import FDateTime for property %s"), *Property->GetNameCPP());
+ return false;
+ }
+ }
+ else if (JsonValue->Type == EJson::String && StructProperty->Struct->GetCppStructOps() && StructProperty->Struct->GetCppStructOps()->HasImportTextItem())
+ {
+ UScriptStruct::ICppStructOps* TheCppStructOps = StructProperty->Struct->GetCppStructOps();
+
+ FString ImportTextString = JsonValue->AsString();
+ const TCHAR* ImportTextPtr = *ImportTextString;
+ if (!TheCppStructOps->ImportTextItem(ImportTextPtr, OutValue, PPF_None, nullptr, (FOutputDevice*)GWarn))
+ {
+ // Fall back to trying the tagged property approach if custom ImportTextItem couldn't get it done
+ Property->ImportText_Direct(ImportTextPtr, OutValue, nullptr, PPF_None);
+ }
+ }
+ else if (JsonValue->Type == EJson::String)
+ {
+ FString ImportTextString = JsonValue->AsString();
+ const TCHAR* ImportTextPtr = *ImportTextString;
+ Property->ImportText_Direct(ImportTextPtr, OutValue, nullptr, PPF_None);
+ }
+ else
+ {
+ UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Attempted to import UStruct from non-object JSON key for property %s"), *Property->GetNameCPP());
+ return false;
+ }
+ }
+ else if (FObjectProperty *ObjectProperty = CastField(Property))
+ {
+ if (JsonValue->Type == EJson::Object)
+ {
+ UObject* Outer = GetTransientPackage();
+ if (ContainerStruct->IsChildOf(UObject::StaticClass()))
+ {
+ Outer = (UObject*)Container;
+ }
+
+ UClass* PropertyClass = ObjectProperty->PropertyClass;
+ UObject* createdObj = StaticAllocateObject(PropertyClass, Outer, NAME_None, EObjectFlags::RF_NoFlags, EInternalObjectFlags::None, false);
+ (*PropertyClass->ClassConstructor)(FObjectInitializer(createdObj, PropertyClass->ClassDefaultObject, EObjectInitializerOptions::None));
+
+ ObjectProperty->SetObjectPropertyValue(OutValue, createdObj);
+
+ TSharedPtr Obj = JsonValue->AsObject();
+ check(Obj.IsValid()); // should not fail if Type == EJson::Object
+ if (!JsonAttributesToUStructWithContainer(Obj->Values, ObjectProperty->PropertyClass, createdObj, ObjectProperty->PropertyClass, createdObj, CheckFlags & (~CPF_ParmFlags), SkipFlags))
+ {
+ UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - FJsonObjectConverter::JsonObjectToUStruct failed for property %s"), *Property->GetNameCPP());
+ return false;
+ }
+ }
+ else if (JsonValue->Type == EJson::String)
+ {
+ // Default to expect a string for everything else
+ if (Property->ImportText_Direct(*JsonValue->AsString(), OutValue, nullptr, PPF_None) == nullptr)
+ {
+ UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Unable import property type %s from string value for property %s"), *Property->GetClass()->GetName(), *Property->GetNameCPP());
+ return false;
+ }
+ }
+ }
+ else
+ {
+ // Default to expect a string for everything else
+ if (Property->ImportText_Direct(*JsonValue->AsString(), OutValue, nullptr, PPF_None) == nullptr)
+ {
+ UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Unable import property type %s from string value for property %s"), *Property->GetClass()->GetName(), *Property->GetNameCPP());
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+ bool JsonValueToFPropertyWithContainer(const TSharedPtr& JsonValue, FProperty* Property, void* OutValue, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags)
+ {
+ if (!JsonValue.IsValid())
+ {
+ UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Invalid value JSON key"));
+ return false;
+ }
+
+ bool bArrayOrSetProperty = Property->IsA() || Property->IsA();
+ bool bJsonArray = JsonValue->Type == EJson::Array;
+
+ if (!bJsonArray)
+ {
+ if (bArrayOrSetProperty)
+ {
+ //Begin custom workaround - support string -> binary array conversion
+ FArrayProperty* ArrayProperty = CastField(Property);
+ if (ArrayProperty->Inner->IsA())
+ {
+ //Did we get a direct binary?
+ TArray ByteArray;
+ if (FJsonValueBinary::IsBinary(JsonValue))
+ {
+ ByteArray = FJsonValueBinary::AsBinary(JsonValue);
+ }
+ //it's a string, convert use base64 to bytes
+ else if(JsonValue->Type == EJson::String)
+ {
+ bool bDidDecodeCorrectly = FBase64::Decode(JsonValue->AsString(), ByteArray);
+ if (!bDidDecodeCorrectly)
+ {
+ UE_LOG(LogJson, Warning, TEXT("FBase64::Decode failed on %s"), *Property->GetName());
+ return false;
+ }
+ }
+ else
+ {
+ UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Attempted to import TArray from unsupported non-array JSON key: %s"), *Property->GetName());
+ return false;
+ }
+ //Memcpy raw arrays
+ FScriptArrayHelper ArrayHelper(ArrayProperty, OutValue);
+ ArrayHelper.EmptyAndAddUninitializedValues(ByteArray.Num());
+ FGenericPlatformMemory::Memcpy(ArrayHelper.GetRawPtr(), ByteArray.GetData(), ByteArray.Num());
+ return true;
+ }
+ //End custom workaround
+ UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - Attempted to import TArray from non-array JSON key"));
+ return false;
+ }
+
+ if (Property->ArrayDim != 1)
+ {
+ UE_LOG(LogJson, Warning, TEXT("Ignoring excess properties when deserializing %s"), *Property->GetName());
+ }
+
+ return ConvertScalarJsonValueToFPropertyWithContainer(JsonValue, Property, OutValue, ContainerStruct, Container, CheckFlags, SkipFlags);
+ }
+
+ // In practice, the ArrayDim == 1 check ought to be redundant, since nested arrays of UPropertys are not supported
+ if (bArrayOrSetProperty && Property->ArrayDim == 1)
+ {
+ // Read into TArray
+ return ConvertScalarJsonValueToFPropertyWithContainer(JsonValue, Property, OutValue, ContainerStruct, Container, CheckFlags, SkipFlags);
+ }
+
+ // We're deserializing a JSON array
+ const auto& ArrayValue = JsonValue->AsArray();
+ if (Property->ArrayDim < ArrayValue.Num())
+ {
+ UE_LOG(LogJson, Warning, TEXT("BPEnumWA-Ignoring excess properties when deserializing %s"), *Property->GetName());
+ }
+
+ // Read into native array
+ int ItemsToRead = FMath::Clamp(ArrayValue.Num(), 0, Property->ArrayDim);
+ for (int Index = 0; Index != ItemsToRead; ++Index)
+ {
+ if (!ConvertScalarJsonValueToFPropertyWithContainer(ArrayValue[Index], Property, (char*)OutValue + Index * Property->ElementSize, ContainerStruct, Container, CheckFlags, SkipFlags))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool JsonAttributesToUStructWithContainer(const TMap< FString, TSharedPtr >& JsonAttributes, const UStruct* StructDefinition, void* OutStruct, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags)
+ {
+ if (StructDefinition == FJsonObjectWrapper::StaticStruct())
+ {
+ // Just copy it into the object
+ FJsonObjectWrapper* ProxyObject = (FJsonObjectWrapper *)OutStruct;
+ ProxyObject->JsonObject = MakeShared();
+ ProxyObject->JsonObject->Values = JsonAttributes;
+ return true;
+ }
+
+ int32 NumUnclaimedProperties = JsonAttributes.Num();
+ if (NumUnclaimedProperties <= 0)
+ {
+ return true;
+ }
+
+ // iterate over the struct properties
+ for (TFieldIterator PropIt(StructDefinition); PropIt; ++PropIt)
+ {
+ FProperty* Property = *PropIt;
+
+ // Check to see if we should ignore this property
+ if (CheckFlags != 0 && !Property->HasAnyPropertyFlags(CheckFlags))
+ {
+ continue;
+ }
+ if (Property->HasAnyPropertyFlags(SkipFlags))
+ {
+ continue;
+ }
+
+ // find a json value matching this property name
+ const TSharedPtr* JsonValue = JsonAttributes.Find(Property->GetName());
+ if (!JsonValue)
+ {
+ // we allow values to not be found since this mirrors the typical UObject mantra that all the fields are optional when deserializing
+ continue;
+ }
+
+ if (JsonValue->IsValid() && !(*JsonValue)->IsNull())
+ {
+ void* Value = Property->ContainerPtrToValuePtr(OutStruct);
+ if (!JsonValueToFPropertyWithContainer(*JsonValue, Property, Value, ContainerStruct, Container, CheckFlags, SkipFlags))
+ {
+ UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonObjectToUStruct - Unable to parse %s.%s from JSON"), *StructDefinition->GetName(), *Property->GetName());
+ return false;
+ }
+ }
+
+ if (--NumUnclaimedProperties <= 0)
+ {
+ // If we found all properties that were in the JsonAttributes map, there is no reason to keep looking for more.
+ break;
+ }
+ }
+
+ return true;
+ }
+ //End FJsonObjectConverter BPEnum Workaround
+
+ class FJsonObjectConverterBPEnum : public FJsonObjectConverter
+ {
+ public:
+ static bool JsonObjectToUStruct(const TSharedRef& JsonObject, const UStruct* StructDefinition, void* OutStruct, int64 CheckFlags, int64 SkipFlags)
+ {
+ return JsonAttributesToUStructWithContainer(JsonObject->Values, StructDefinition, OutStruct, StructDefinition, OutStruct, CheckFlags, SkipFlags);
+ }
+ };
+}
+
+
+FString FTrimmedKeyMap::ToString()
+{
+ FString SubMapString;
+ for (auto Pair : SubMap)
+ {
+ FString PairString = FString::Printf(TEXT("{%s:%s}"), *Pair.Key, *Pair.Value->ToString());
+ SubMapString.Append(PairString);
+ SubMapString.Append(",");
+ }
+ return FString::Printf(TEXT("{%s:%s}"), *LongKey, *SubMapString);
+}
+
+FString USIOJConvert::ToJsonString(const TSharedPtr& JsonObject)
+{
+ FString OutputString;
+ TSharedRef< FCondensedJsonStringWriter > Writer = FCondensedJsonStringWriterFactory::Create(&OutputString);
+ FJsonSerializer::Serialize(JsonObject.ToSharedRef(), Writer);
+ return OutputString;
+}
+
+FString USIOJConvert::ToJsonString(const TArray>& JsonValueArray)
+{
+ FString OutputString;
+ TSharedRef< FCondensedJsonStringWriter > Writer = FCondensedJsonStringWriterFactory::Create(&OutputString);
+ FJsonSerializer::Serialize(JsonValueArray, Writer);
+ return OutputString;
+}
+
+FString USIOJConvert::ToJsonString(const TSharedPtr& JsonValue)
+{
+ if (JsonValue->Type == EJson::None)
+ {
+ return FString();
+ }
+ else if (JsonValue->Type == EJson::Null)
+ {
+ return FString();
+ }
+ else if (JsonValue->Type == EJson::String)
+ {
+ return JsonValue->AsString();
+ }
+ else if (JsonValue->Type == EJson::Number)
+ {
+ return FString::Printf(TEXT("%f"), JsonValue->AsNumber());
+ }
+ else if (JsonValue->Type == EJson::Boolean)
+ {
+ return FString::Printf(TEXT("%d"), JsonValue->AsBool());
+ }
+ else if (JsonValue->Type == EJson::Array)
+ {
+ return ToJsonString(JsonValue->AsArray());
+ }
+ else if (JsonValue->Type == EJson::Object)
+ {
+ return ToJsonString(JsonValue->AsObject());
+ }
+ else
+ {
+ return FString();
+ }
+}
+
+USIOJsonValue* USIOJConvert::ToSIOJsonValue(const TArray>& JsonValueArray)
+{
+ TArray< TSharedPtr > ValueArray;
+ for (auto InVal : JsonValueArray)
+ {
+ ValueArray.Add(InVal);
+ }
+
+ USIOJsonValue* ResultValue = NewObject();
+ TSharedPtr NewVal = MakeShareable(new FJsonValueArray(ValueArray));
+ ResultValue->SetRootValue(NewVal);
+
+ return ResultValue;
+}
+
+#if PLATFORM_WINDOWS
+#pragma endregion ToJsonValue
+#endif
+
+TSharedPtr USIOJConvert::JsonStringToJsonValue(const FString& JsonString)
+{
+ //Null
+ if (JsonString.IsEmpty())
+ {
+ return MakeShareable(new FJsonValueNull);
+ }
+
+ //Number
+ if (JsonString.IsNumeric())
+ {
+ //convert to double
+ return MakeShareable(new FJsonValueNumber(FCString::Atod(*JsonString)));
+ }
+
+ //Object
+ if (JsonString.StartsWith(FString(TEXT("{"))))
+ {
+ TSharedPtr< FJsonObject > JsonObject = ToJsonObject(JsonString);
+ return MakeShareable(new FJsonValueObject(JsonObject));
+ }
+
+ //Array
+ if (JsonString.StartsWith(FString(TEXT("["))))
+ {
+ TArray < TSharedPtr> RawJsonValueArray;
+ TSharedRef< TJsonReader<> > Reader = TJsonReaderFactory<>::Create(*JsonString);
+ bool success = FJsonSerializer::Deserialize(Reader, RawJsonValueArray);
+
+ if (success)
+ {
+ return MakeShareable(new FJsonValueArray(RawJsonValueArray));
+ }
+ }
+
+ //Bool
+ if (JsonString == FString("true") || JsonString == FString("false"))
+ {
+ bool BooleanValue = (JsonString == FString("true"));
+ return MakeShareable(new FJsonValueBoolean(BooleanValue));
+ }
+
+ //String
+ return MakeShareable(new FJsonValueString(JsonString));
+}
+
+TSharedPtr USIOJConvert::ToJsonValue(const TSharedPtr& JsonObject)
+{
+ return MakeShareable(new FJsonValueObject(JsonObject));
+}
+
+TSharedPtr USIOJConvert::ToJsonValue(const FString& StringValue)
+{
+ return MakeShareable(new FJsonValueString(StringValue));
+}
+
+TSharedPtr USIOJConvert::ToJsonValue(double NumberValue)
+{
+ return MakeShareable(new FJsonValueNumber(NumberValue));
+}
+
+TSharedPtr USIOJConvert::ToJsonValue(bool BoolValue)
+{
+ return MakeShareable(new FJsonValueBoolean(BoolValue));
+}
+
+TSharedPtr USIOJConvert::ToJsonValue(const TArray& BinaryValue)
+{
+ return MakeShareable(new FJsonValueBinary(BinaryValue));
+}
+
+TSharedPtr USIOJConvert::ToJsonValue(const TArray>& ArrayValue)
+{
+ return MakeShareable(new FJsonValueArray(ArrayValue));
+}
+
+#if PLATFORM_WINDOWS
+#pragma endregion ToJsonValue
+#endif
+
+TArray> USIOJConvert::JsonStringToJsonArray(const FString& JsonString)
+{
+ TArray < TSharedPtr> RawJsonValueArray;
+ TSharedRef< TJsonReader<> > Reader = TJsonReaderFactory<>::Create(*JsonString);
+ FJsonSerializer::Deserialize(Reader, RawJsonValueArray);
+
+ return RawJsonValueArray;
+}
+
+TSharedPtr USIOJConvert::ToJsonObject(const FString& JsonString)
+{
+ TSharedPtr< FJsonObject > JsonObject = MakeShareable(new FJsonObject);
+ TSharedRef< TJsonReader<> > Reader = TJsonReaderFactory<>::Create(*JsonString);
+ FJsonSerializer::Deserialize(Reader, JsonObject);
+ return JsonObject;
+}
+
+
+
+TSharedPtr USIOJConvert::ToJsonObject(UStruct* StructDefinition, void* StructPtr, bool IsBlueprintStruct, bool BinaryStructCppSupport /*= false */)
+{
+ TSharedRef JsonObject = MakeShareable(new FJsonObject);
+
+ if (IsBlueprintStruct || BinaryStructCppSupport)
+ {
+ //Handle BP enum override
+ if (!EnumOverrideExportCallback.IsBound())
+ {
+ EnumOverrideExportCallback.BindLambda([](FProperty* Property, const void* Value)
+ {
+ if (FByteProperty* BPEnumProperty = CastField(Property))
+ {
+ //Override default enum behavior by fetching display name text
+ UEnum* EnumDef = BPEnumProperty->Enum;
+
+ uint8 IntValue = *(uint8*)Value;
+
+ //It's an enum byte
+ if (EnumDef)
+ {
+ FString StringValue = EnumDef->GetDisplayNameTextByIndex(IntValue).ToString();
+ return (TSharedPtr)MakeShared(StringValue);
+ }
+ //it's a regular byte, convert to number
+ else
+ {
+ return (TSharedPtr)MakeShared(IntValue);
+ }
+ }
+ //byte array special case
+ else if (FArrayProperty* ArrayProperty = CastField(Property))
+ {
+ //is it a byte array?
+ if (ArrayProperty->Inner->IsA())
+ {
+ FScriptArrayHelper ArrayHelper(ArrayProperty, Value);
+ TArray ByteArray(ArrayHelper.GetRawPtr(), ArrayHelper.Num());
+ return USIOJConvert::ToJsonValue(ByteArray);
+ }
+ }
+
+ // invalid
+ return TSharedPtr();
+ });
+ }
+
+ //Get the object keys
+ FJsonObjectConverter::UStructToJsonObject(StructDefinition, StructPtr, JsonObject, 0, 0, &EnumOverrideExportCallback);
+
+ //Wrap it into a value and pass it into the trimmer
+ TSharedPtr JsonValue = MakeShareable(new FJsonValueObject(JsonObject));
+ TrimValueKeyNames(JsonValue);
+
+ //Return object with trimmed names
+ return JsonValue->AsObject();
+ }
+ else
+ {
+ FJsonObjectConverter::UStructToJsonObject(StructDefinition, StructPtr, JsonObject, 0, 0);
+ return JsonObject;
+ }
+}
+
+TSharedPtr USIOJConvert::MakeJsonObject()
+{
+ return MakeShareable(new FJsonObject);
+}
+
+bool USIOJConvert::JsonObjectToUStruct(TSharedPtr JsonObject, UStruct* Struct, void* StructPtr, bool IsBlueprintStruct /*= false*/, bool BinaryStructCppSupport /*= false*/)
+{
+ if (IsBlueprintStruct || BinaryStructCppSupport)
+ {
+ //Json object we pass will have their trimmed BP names, e.g. boolKey vs boolKey_8_EDBB36654CF43866C376DE921373AF23
+ //so we have to match them to the verbose versions, get a map of the names
+
+ TSharedPtr KeyMap = MakeShareable(new FTrimmedKeyMap);
+ SetTrimmedKeyMapForStruct(KeyMap, Struct);
+
+ //Print our keymap for debug
+ //UE_LOG(LogTemp, Log, TEXT("Keymap: %s"), *KeyMap->ToString());
+
+ //Adjust our passed in JsonObject to use the long key names
+ TSharedPtr JsonValue = MakeShareable(new FJsonValueObject(JsonObject));
+ ReplaceJsonValueNamesWithMap(JsonValue, KeyMap);
+
+ /*Todo: add support for enums by pretty name and not by NewEnumeratorX
+ Will require re-writing FJsonObjectConverter::JsonObjectToUStruct to lookup by display name in numeric case
+ of https://github.com/EpicGames/UnrealEngine/blob/release/Engine/Source/Runtime/JsonUtilities/Private/JsonObjectConverter.cpp#L377,
+ or getting engine pull request merge.
+ */
+
+ //Use custom blueprint JsonObjectToUStruct to fix BPEnums
+ return FJsonObjectConverterBPEnum::JsonObjectToUStruct(JsonObject.ToSharedRef(), Struct, StructPtr, 0, 0);
+ }
+ else
+ {
+ return FJsonObjectConverter::JsonObjectToUStruct(JsonObject.ToSharedRef(), Struct, StructPtr, 0, 0);
+ }
+}
+
+bool USIOJConvert::JsonFileToUStruct(const FString& FilePath, UStruct* Struct, void* StructPtr, bool IsBlueprintStruct /*= false*/)
+{
+ //Read bytes from file
+ TArray OutBytes;
+ if (!FFileHelper::LoadFileToArray(OutBytes, *FilePath))
+ {
+ return false;
+ }
+
+ //Convert to json string
+ FString JsonString;
+ FFileHelper::BufferToString(JsonString, OutBytes.GetData(), OutBytes.Num());
+
+ //Read into struct
+ return JsonObjectToUStruct(ToJsonObject(JsonString), Struct, StructPtr, IsBlueprintStruct);
+}
+
+
+bool USIOJConvert::ToJsonFile(const FString& FilePath, UStruct* Struct, void* StructPtr, bool IsBlueprintStruct /*= false*/)
+{
+ //Get json object with trimmed values
+ TSharedPtr JsonObject = ToJsonObject(Struct, StructPtr, IsBlueprintStruct);
+ TSharedPtr TrimmedValue = MakeShareable(new FJsonValueObject(JsonObject));
+ TrimValueKeyNames(TrimmedValue);
+
+ //Convert to string
+ FString JsonString = ToJsonString(TrimmedValue);
+ FTCHARToUTF8 Utf8String(*JsonString);
+
+ TArray Bytes;
+ Bytes.Append((uint8*)Utf8String.Get(), Utf8String.Length());
+
+ //flush to disk
+ return FFileHelper::SaveArrayToFile(Bytes, *FilePath);
+}
+
+void USIOJConvert::TrimValueKeyNames(const TSharedPtr& JsonValue)
+{
+ //Array?
+ if (JsonValue->Type == EJson::Array)
+ {
+ auto Array = JsonValue->AsArray();
+
+ for (auto SubValue : Array)
+ {
+ TrimValueKeyNames(SubValue);
+ }
+ }
+ //Object?
+ else if (JsonValue->Type == EJson::Object)
+ {
+ auto JsonObject = JsonValue->AsObject();
+ for (auto Pair : JsonObject->Values)
+ {
+ const FString& Key = Pair.Key;
+ FString TrimmedKey;
+
+ bool DidNeedTrimming = TrimKey(Key, TrimmedKey);
+
+ //keep attempting sub keys even if we have a valid string
+ auto SubValue = Pair.Value;
+ TrimValueKeyNames(SubValue);
+
+ if (DidNeedTrimming)
+ {
+ //Replace field names with the trimmed key
+ JsonObject->SetField(TrimmedKey, SubValue);
+ JsonObject->RemoveField(Key);
+ }
+ }
+ }
+ else
+ {
+ //UE_LOG(LogTemp, Warning, TEXT("TrimValueKeyNames:: uncaught type is: %d"), (int)JsonValue->Type);
+ }
+}
+
+bool USIOJConvert::TrimKey(const FString& InLongKey, FString& OutTrimmedKey)
+{
+ //Look for the position of the 2nd '_'
+ int32 LastIndex = InLongKey.Find(TEXT("_"), ESearchCase::IgnoreCase, ESearchDir::FromEnd);
+ LastIndex = InLongKey.Find(TEXT("_"), ESearchCase::IgnoreCase, ESearchDir::FromEnd, LastIndex);
+
+ if (LastIndex >= 0)
+ {
+ OutTrimmedKey = InLongKey.Mid(0, LastIndex);;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void USIOJConvert::SetTrimmedKeyMapForStruct(TSharedPtr& InMap, UStruct* Struct)
+{
+ //Get the child fields
+ FField* FieldPtr = Struct->ChildProperties;
+
+ //If it hasn't been set, the long key is the json standardized long name
+ if (InMap->LongKey.IsEmpty())
+ {
+ InMap->LongKey = FJsonObjectConverter::StandardizeCase(Struct->GetName());
+ }
+
+ //For each child field...
+ while (FieldPtr != nullptr)
+ {
+ //Map our trimmed name to our full name
+ const FString& LowerKey = FJsonObjectConverter::StandardizeCase(FieldPtr->GetName());
+ FString TrimmedKey;
+ bool DidTrim = TrimKey(LowerKey, TrimmedKey);
+
+ //Set the key map
+ TSharedPtr SubMap = MakeShareable(new FTrimmedKeyMap);
+ SubMap->LongKey = LowerKey;
+
+ //No-trim case, trim = long
+ if (!DidTrim)
+ {
+ TrimmedKey = SubMap->LongKey;
+ }
+
+ //Did we get a substructure?
+ FStructProperty* SubStruct = CastField(FieldPtr);
+ FArrayProperty* ArrayProp = CastField(FieldPtr);
+ FMapProperty* MapProperty = CastField(FieldPtr);
+
+ if (SubStruct != nullptr)
+ {
+ //We did, embed the sub-map
+ SetTrimmedKeyMapForStruct(SubMap, SubStruct->Struct);
+ }
+
+ //Did we get a sub-array?
+ else if (ArrayProp != nullptr)
+ {
+ //set the inner map for the inner property
+ //UE_LOG(LogTemp, Log, TEXT("found array: %s"), *ArrayProp->GetName());
+ SetTrimmedKeyMapForProp(SubMap, ArrayProp->Inner);
+ }
+ else if (MapProperty != nullptr)
+ {
+ //UE_LOG(LogTemp, Log, TEXT("I'm a tmap: %s"), *MapProperty->GetName());
+ SetTrimmedKeyMapForProp(SubMap, MapProperty);
+ }
+
+ //Debug types
+ /*
+ UProperty* ObjectProp = Cast(FieldPtr);
+ if (ObjectProp)
+ {
+ UE_LOG(LogTemp, Log, TEXT("found map: %s, %s, type: %s, %s"),
+ *ObjectProp->GetName(),
+ *ObjectProp->GetNameCPP(),
+ *ObjectProp->GetClass()->GetFName().ToString(),
+ *ObjectProp->GetCPPType());
+ }*/
+
+ InMap->SubMap.Add(TrimmedKey, SubMap);
+ //UE_LOG(LogTemp, Log, TEXT("long: %s, trim: %s, is struct: %d"), *SubMap->LongKey, *TrimmedKey, SubStruct != NULL);
+
+ FieldPtr = FieldPtr->Next;
+ }
+
+ //UE_LOG(LogTemp, Log, TEXT("Final map: %d"), InMap->SubMap.Num());
+}
+
+void USIOJConvert::SetTrimmedKeyMapForProp(TSharedPtr& InMap, FProperty* InnerProperty)
+{
+
+ //UE_LOG(LogTemp, Log, TEXT("got prop: %s"), *InnerProperty->GetName());
+ FStructProperty* SubStruct = CastField(InnerProperty);
+ FArrayProperty* ArrayProp = CastField(InnerProperty);
+ FMapProperty* MapProperty = CastField(InnerProperty);
+
+ if (SubStruct != nullptr)
+ {
+ //We did, embed the sub-map
+ SetTrimmedKeyMapForStruct(InMap, SubStruct->Struct);
+ }
+ //Did we get a sub-array?
+ else if (ArrayProp != nullptr)
+ {
+ SetTrimmedKeyMapForProp(InMap, ArrayProp->Inner);
+ }
+ else if (MapProperty != nullptr)
+ {
+ //Make a special submap with special TMAP identifier key
+ TSharedPtr SubMap = MakeShareable(new FTrimmedKeyMap);
+ SubMap->LongKey = TMAP_STRING;
+ InMap->SubMap.Add(SubMap->LongKey, SubMap);
+
+ //Take the value property and set it as it's unique child
+ SetTrimmedKeyMapForProp(SubMap, MapProperty->ValueProp);
+
+ //Each child in the JSON object map will use the same structure (it's a UE4 limitation of maps anyway
+ }
+}
+
+void USIOJConvert::ReplaceJsonValueNamesWithMap(TSharedPtr& JsonValue, TSharedPtr KeyMap)
+{
+ if (JsonValue->Type == EJson::Object)
+ {
+ //Go through each key in the object
+ auto Object = JsonValue->AsObject();
+ auto SubMap = KeyMap->SubMap;
+ auto AllValues = Object->Values;
+
+ FString PreviewPreValue = USIOJConvert::ToJsonString(Object);
+ //UE_LOG(LogTemp, Log, TEXT("Rep::PreObject: <%s>"), *PreviewPreValue);
+
+ for (auto Pair : AllValues)
+ {
+ if (SubMap.Contains(TMAP_STRING))
+ {
+ FString TMapString = FString(TMAP_STRING);
+ //If we found a tmap, replace each sub key with list of keys
+ ReplaceJsonValueNamesWithMap(Pair.Value, SubMap[TMapString]);
+ }
+ else if (SubMap.Num() > 0 && SubMap.Contains(Pair.Key))
+ {
+ //Get the long key for entry
+ const FString& LongKey = SubMap[Pair.Key]->LongKey;
+
+ //loop nested structures
+ ReplaceJsonValueNamesWithMap(Pair.Value, SubMap[Pair.Key]);
+
+ if (Pair.Key != LongKey)
+ {
+ //finally set the field and remove the old field
+ Object->SetField(LongKey, Pair.Value);
+ Object->RemoveField(Pair.Key);
+ }
+ }
+ }
+
+ FString PreviewPostValue = USIOJConvert::ToJsonString(Object);
+ //UE_LOG(LogTemp, Log, TEXT("Rep::PostObject: <%s>"), *PreviewPostValue);
+ }
+ else if (JsonValue->Type == EJson::Array)
+ {
+ auto Array = JsonValue->AsArray();
+ for (auto Item : Array)
+ {
+ //UE_LOG(LogTemp, Log, TEXT("%s"), *Item->AsString());
+ ReplaceJsonValueNamesWithMap(Item, KeyMap);
+ }
+ }
+}
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJson/Private/SIOJLibrary.cpp b/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJson/Private/SIOJLibrary.cpp
new file mode 100644
index 0000000..b300ae9
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJson/Private/SIOJLibrary.cpp
@@ -0,0 +1,293 @@
+// Modifications Copyright 2018-current Getnamo. All Rights Reserved
+
+
+// Copyright 2016 Vladimir Alyamkin. All Rights Reserved.
+
+#include "SIOJLibrary.h"
+#include "ISIOJson.h"
+#include "Json/Public/Dom/JsonObject.h"
+#include "Json/Public/Dom/JsonValue.h"
+#include "SIOJsonValue.h"
+#include "SIOJsonObject.h"
+#include "Misc/Base64.h"
+#include "Engine/Engine.h"
+
+//////////////////////////////////////////////////////////////////////////
+// Helpers
+
+FString USIOJLibrary::PercentEncode(const FString& Source)
+{
+ FString OutText = Source;
+
+ OutText = OutText.Replace(TEXT(" "), TEXT("%20"));
+ OutText = OutText.Replace(TEXT("!"), TEXT("%21"));
+ OutText = OutText.Replace(TEXT("\""), TEXT("%22"));
+ OutText = OutText.Replace(TEXT("#"), TEXT("%23"));
+ OutText = OutText.Replace(TEXT("$"), TEXT("%24"));
+ OutText = OutText.Replace(TEXT("&"), TEXT("%26"));
+ OutText = OutText.Replace(TEXT("'"), TEXT("%27"));
+ OutText = OutText.Replace(TEXT("("), TEXT("%28"));
+ OutText = OutText.Replace(TEXT(")"), TEXT("%29"));
+ OutText = OutText.Replace(TEXT("*"), TEXT("%2A"));
+ OutText = OutText.Replace(TEXT("+"), TEXT("%2B"));
+ OutText = OutText.Replace(TEXT(","), TEXT("%2C"));
+ OutText = OutText.Replace(TEXT("/"), TEXT("%2F"));
+ OutText = OutText.Replace(TEXT(":"), TEXT("%3A"));
+ OutText = OutText.Replace(TEXT(";"), TEXT("%3B"));
+ OutText = OutText.Replace(TEXT("="), TEXT("%3D"));
+ OutText = OutText.Replace(TEXT("?"), TEXT("%3F"));
+ OutText = OutText.Replace(TEXT("@"), TEXT("%40"));
+ OutText = OutText.Replace(TEXT("["), TEXT("%5B"));
+ OutText = OutText.Replace(TEXT("]"), TEXT("%5D"));
+ OutText = OutText.Replace(TEXT("{"), TEXT("%7B"));
+ OutText = OutText.Replace(TEXT("}"), TEXT("%7D"));
+
+ return OutText;
+}
+
+FString USIOJLibrary::Base64Encode(const FString& Source)
+{
+ return FBase64::Encode(Source);
+}
+
+FString USIOJLibrary::Base64EncodeBytes(const TArray& Source)
+{
+ return FBase64::Encode(Source);
+}
+
+bool USIOJLibrary::Base64Decode(const FString& Source, FString& Dest)
+{
+ return FBase64::Decode(Source, Dest);
+}
+
+
+bool USIOJLibrary::Base64DecodeBytes(const FString& Source, TArray& Dest)
+{
+ return FBase64::Decode(Source, Dest);
+}
+
+bool USIOJLibrary::StringToJsonValueArray(const FString& JsonString, TArray& OutJsonValueArray)
+{
+ TArray < TSharedPtr> RawJsonValueArray;
+ TSharedRef< TJsonReader<> > Reader = TJsonReaderFactory<>::Create(*JsonString);
+ FJsonSerializer::Deserialize(Reader, RawJsonValueArray);
+
+ for (auto Value : RawJsonValueArray)
+ {
+ auto SJsonValue = NewObject();
+ SJsonValue->SetRootValue(Value);
+ OutJsonValueArray.Add(SJsonValue);
+ }
+
+ return OutJsonValueArray.Num() > 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Easy URL processing
+
+TMap USIOJLibrary::RequestMap;
+
+
+FString USIOJLibrary::Conv_SIOJsonObjectToString(USIOJsonObject* InObject)
+{
+ if(InObject)
+ {
+ return InObject->EncodeJson();
+ }
+
+ return "";
+}
+
+
+USIOJsonObject* USIOJLibrary::Conv_JsonValueToJsonObject(class USIOJsonValue* InValue)
+{
+ if(InValue)
+ {
+ return InValue->AsObject();
+ }
+
+ return nullptr;
+}
+
+USIOJsonValue* USIOJLibrary::Conv_ArrayToJsonValue(const TArray& InArray)
+{
+ return USIOJsonValue::ConstructJsonValueArray(nullptr, InArray);
+}
+
+
+USIOJsonValue* USIOJLibrary::Conv_JsonObjectToJsonValue(USIOJsonObject* InObject)
+{
+ return USIOJsonValue::ConstructJsonValueObject(InObject, nullptr);
+}
+
+
+USIOJsonValue* USIOJLibrary::Conv_BytesToJsonValue(const TArray& InBytes)
+{
+ return USIOJsonValue::ConstructJsonValueBinary(nullptr, InBytes);
+}
+
+
+USIOJsonValue* USIOJLibrary::Conv_StringToJsonValue(const FString& InString)
+{
+ return USIOJsonValue::ConstructJsonValueString(nullptr, InString);
+}
+
+
+USIOJsonValue* USIOJLibrary::Conv_IntToJsonValue(int32 InInt)
+{
+ TSharedPtr NewVal = MakeShareable(new FJsonValueNumber(InInt));
+
+ USIOJsonValue* NewValue = NewObject();
+ NewValue->SetRootValue(NewVal);
+
+ return NewValue;
+}
+
+USIOJsonValue* USIOJLibrary::Conv_FloatToJsonValue(float InFloat)
+{
+ return USIOJsonValue::ConstructJsonValueNumber(nullptr, InFloat);
+}
+
+
+USIOJsonValue* USIOJLibrary::Conv_BoolToJsonValue(bool InBool)
+{
+ return USIOJsonValue::ConstructJsonValueBool(nullptr, InBool);
+}
+
+
+FString USIOJLibrary::Conv_SIOJsonValueToString(class USIOJsonValue* InValue)
+{
+ if (InValue)
+ {
+ return InValue->AsString();
+ }
+
+ return TEXT("");
+}
+
+int32 USIOJLibrary::Conv_JsonValueToInt(USIOJsonValue* InValue)
+{
+ if(InValue)
+ {
+ return (int32)InValue->AsNumber();
+ }
+
+ return 0;
+}
+
+
+float USIOJLibrary::Conv_JsonValueToFloat(USIOJsonValue* InValue)
+{
+ if (InValue)
+ {
+ return InValue->AsNumber();
+ }
+
+ return 0.f;
+}
+
+
+bool USIOJLibrary::Conv_JsonValueToBool(USIOJsonValue* InValue)
+{
+ if (InValue)
+ {
+ return InValue->AsBool();
+ }
+
+ return false;
+}
+
+
+TArray USIOJLibrary::Conv_JsonValueToBytes(USIOJsonValue* InValue)
+{
+ if (InValue)
+ {
+ return InValue->AsBinary();
+ }
+
+ return TArray();
+}
+
+void USIOJLibrary::CallURL(UObject* WorldContextObject, const FString& URL, ESIORequestVerb Verb, ESIORequestContentType ContentType, USIOJsonObject* SIOJJson, const FSIOJCallDelegate& Callback)
+{
+ UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
+ if (World == nullptr)
+ {
+ UE_LOG(LogSIOJ, Error, TEXT("USIOJLibrary: Wrong world context"))
+ return;
+ }
+
+ // Check we have valid data json
+ if (SIOJJson == nullptr)
+ {
+ SIOJJson = USIOJsonObject::ConstructJsonObject(WorldContextObject);
+ }
+
+ USIOJRequestJSON* Request = NewObject();
+
+ Request->SetVerb(Verb);
+ Request->SetContentType(ContentType);
+ Request->SetRequestObject(SIOJJson);
+
+ FSIOJCallResponse Response;
+ Response.Request = Request;
+ Response.WorldContextObject = WorldContextObject;
+ Response.Callback = Callback;
+
+ Response.CompleteDelegateHandle = Request->OnStaticRequestComplete.AddStatic(&USIOJLibrary::OnCallComplete);
+ Response.FailDelegateHandle = Request->OnStaticRequestFail.AddStatic(&USIOJLibrary::OnCallComplete);
+
+ RequestMap.Add(Request, Response);
+
+ Request->ResetResponseData();
+ Request->ProcessURL(URL);
+}
+
+void USIOJLibrary::GetURLBinary(UObject* WorldContextObject, const FString& URL, ESIORequestVerb Verb, ESIORequestContentType ContentType, TArray& OutResultData, struct FLatentActionInfo LatentInfo)
+{
+ UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
+ if (World == nullptr)
+ {
+ UE_LOG(LogSIOJ, Error, TEXT("USIOJLibrary: Wrong world context"))
+ return;
+ }
+
+ USIOJRequestJSON* Request = NewObject();
+ FCULatentAction *LatentAction = FCULatentAction::CreateLatentAction(LatentInfo, WorldContextObject);
+
+ Request->SetVerb(Verb);
+ Request->SetContentType(ContentType);
+ Request->bShouldHaveBinaryResponse = true;
+ Request->OnProcessURLCompleteCallback = [LatentAction, &OutResultData](TArray& ResultData)
+ {
+ if (LatentAction)
+ {
+ OutResultData = ResultData;
+ LatentAction->Call(); //resume the latent action
+ }
+ };
+
+ Request->ResetResponseData();
+ Request->ProcessURL(URL);
+
+
+}
+
+void USIOJLibrary::OnCallComplete(USIOJRequestJSON* Request)
+{
+ if (!RequestMap.Contains(Request))
+ {
+ return;
+ }
+
+ FSIOJCallResponse* Response = RequestMap.Find(Request);
+
+ Request->OnStaticRequestComplete.Remove(Response->CompleteDelegateHandle);
+ Request->OnStaticRequestFail.Remove(Response->FailDelegateHandle);
+
+ Response->Callback.ExecuteIfBound(Request);
+
+ Response->WorldContextObject = nullptr;
+ Response->Request = nullptr;
+ RequestMap.Remove(Request);
+}
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJson/Private/SIOJRequestJSON.cpp b/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJson/Private/SIOJRequestJSON.cpp
new file mode 100644
index 0000000..6befb6a
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJson/Private/SIOJRequestJSON.cpp
@@ -0,0 +1,501 @@
+// Modifications Copyright 2018-current Getnamo. All Rights Reserved
+
+
+// Copyright 2014 Vladimir Alyamkin. All Rights Reserved.
+
+#include "SIOJRequestJSON.h"
+#include "ISIOJson.h"
+#include "SIOJLibrary.h"
+#include "SIOJsonValue.h"
+#include "SIOJsonObject.h"
+
+template void FSIOJLatentAction::Cancel()
+{
+ UObject *Obj = Request.Get();
+ if (Obj != nullptr)
+ {
+ ((USIOJRequestJSON*)Obj)->Cancel();
+ }
+}
+
+USIOJRequestJSON::USIOJRequestJSON(const class FObjectInitializer& PCIP)
+ : Super(PCIP),
+ BinaryContentType(TEXT("application/octet-stream"))
+{
+ RequestVerb = ESIORequestVerb::GET;
+ RequestContentType = ESIORequestContentType::x_www_form_urlencoded_url;
+ bShouldHaveBinaryResponse = false;
+ OnProcessURLCompleteCallback = nullptr;
+
+ ResetData();
+}
+
+USIOJRequestJSON* USIOJRequestJSON::ConstructRequest(UObject* WorldContextObject)
+{
+ return NewObject();
+}
+
+USIOJRequestJSON* USIOJRequestJSON::ConstructRequestExt(
+ UObject* WorldContextObject,
+ ESIORequestVerb Verb,
+ ESIORequestContentType ContentType)
+{
+ USIOJRequestJSON* Request = ConstructRequest(WorldContextObject);
+
+ Request->SetVerb(Verb);
+ Request->SetContentType(ContentType);
+
+ return Request;
+}
+
+void USIOJRequestJSON::SetVerb(ESIORequestVerb Verb)
+{
+ RequestVerb = Verb;
+}
+
+void USIOJRequestJSON::SetCustomVerb(FString Verb)
+{
+ CustomVerb = Verb;
+}
+
+void USIOJRequestJSON::SetContentType(ESIORequestContentType ContentType)
+{
+ RequestContentType = ContentType;
+}
+
+void USIOJRequestJSON::SetBinaryContentType(const FString &ContentType)
+{
+ BinaryContentType = ContentType;
+}
+
+void USIOJRequestJSON::SetBinaryRequestContent(const TArray &Bytes)
+{
+ RequestBytes = Bytes;
+}
+
+void USIOJRequestJSON::SetHeader(const FString& HeaderName, const FString& HeaderValue)
+{
+ RequestHeaders.Add(HeaderName, HeaderValue);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// Destruction and reset
+
+void USIOJRequestJSON::ResetData()
+{
+ ResetRequestData();
+ ResetResponseData();
+}
+
+void USIOJRequestJSON::ResetRequestData()
+{
+ if (RequestJsonObj != nullptr)
+ {
+ RequestJsonObj->Reset();
+ }
+ else
+ {
+ RequestJsonObj = NewObject();
+ }
+
+ HttpRequest = FHttpModule::Get().CreateRequest();
+}
+
+void USIOJRequestJSON::ResetResponseData()
+{
+ if (ResponseJsonObj != nullptr)
+ {
+ ResponseJsonObj->Reset();
+ }
+ else
+ {
+ ResponseJsonObj = NewObject();
+ }
+
+ ResponseHeaders.Empty();
+ ResponseCode = -1;
+
+ bIsValidJsonResponse = false;
+}
+
+void USIOJRequestJSON::Cancel()
+{
+ ContinueAction = nullptr;
+
+ ResetResponseData();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// JSON data accessors
+
+USIOJsonObject* USIOJRequestJSON::GetRequestObject()
+{
+ return RequestJsonObj;
+}
+
+void USIOJRequestJSON::SetRequestObject(USIOJsonObject* JsonObject)
+{
+ RequestJsonObj = JsonObject;
+}
+
+USIOJsonObject* USIOJRequestJSON::GetResponseObject()
+{
+ return ResponseJsonObj;
+}
+
+void USIOJRequestJSON::SetResponseObject(USIOJsonObject* JsonObject)
+{
+ ResponseJsonObj = JsonObject;
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+// Response data access
+
+FString USIOJRequestJSON::GetURL()
+{
+ return HttpRequest->GetURL();
+}
+
+ESIORequestStatus USIOJRequestJSON::GetStatus()
+{
+ return ESIORequestStatus((uint8)HttpRequest->GetStatus());
+}
+
+int32 USIOJRequestJSON::GetResponseCode()
+{
+ return ResponseCode;
+}
+
+FString USIOJRequestJSON::GetResponseHeader(const FString HeaderName)
+{
+ FString Result;
+
+ FString* Header = ResponseHeaders.Find(HeaderName);
+ if (Header != nullptr)
+ {
+ Result = *Header;
+ }
+
+ return Result;
+}
+
+TArray USIOJRequestJSON::GetAllResponseHeaders()
+{
+ TArray Result;
+ for (TMap::TConstIterator It(ResponseHeaders); It; ++It)
+ {
+ Result.Add(It.Key() + TEXT(": ") + It.Value());
+ }
+ return Result;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// URL processing
+
+void USIOJRequestJSON::ProcessURL(const FString& Url)
+{
+ HttpRequest->SetURL(Url);
+
+ ProcessRequest();
+}
+
+void USIOJRequestJSON::ApplyURL(const FString& Url, USIOJsonObject *&Result, UObject* WorldContextObject, FLatentActionInfo LatentInfo)
+{
+ HttpRequest->SetURL(Url);
+
+ // Prepare latent action
+ if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull))
+ {
+ FLatentActionManager& LatentActionManager = World->GetLatentActionManager();
+ FSIOJLatentAction *Kont = LatentActionManager.FindExistingAction>(LatentInfo.CallbackTarget, LatentInfo.UUID);
+
+
+ if (Kont != nullptr)
+ {
+ Kont->Cancel();
+ LatentActionManager.RemoveActionsForObject(LatentInfo.CallbackTarget);
+ }
+
+ LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, ContinueAction = new FSIOJLatentAction(this, Result, LatentInfo));
+ }
+
+ ProcessRequest();
+}
+
+void USIOJRequestJSON::ProcessRequest()
+{
+ // Set verb
+ switch (RequestVerb)
+ {
+ case ESIORequestVerb::GET:
+ HttpRequest->SetVerb(TEXT("GET"));
+ break;
+
+ case ESIORequestVerb::POST:
+ HttpRequest->SetVerb(TEXT("POST"));
+ break;
+
+ case ESIORequestVerb::PUT:
+ HttpRequest->SetVerb(TEXT("PUT"));
+ break;
+
+ case ESIORequestVerb::DEL:
+ HttpRequest->SetVerb(TEXT("DELETE"));
+ break;
+
+ case ESIORequestVerb::CUSTOM:
+ HttpRequest->SetVerb(CustomVerb);
+ break;
+
+ default:
+ break;
+ }
+
+ // Set content-type
+ switch (RequestContentType)
+ {
+ case ESIORequestContentType::x_www_form_urlencoded_url:
+ {
+ HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/x-www-form-urlencoded"));
+
+ FString UrlParams = "";
+ uint16 ParamIdx = 0;
+
+ // Loop through all the values and prepare additional url part
+ for (auto RequestIt = RequestJsonObj->GetRootObject()->Values.CreateIterator(); RequestIt; ++RequestIt)
+ {
+ FString Key = RequestIt.Key();
+ FString Value = RequestIt.Value().Get()->AsString();
+
+ if (!Key.IsEmpty() && !Value.IsEmpty())
+ {
+ UrlParams += ParamIdx == 0 ? "?" : "&";
+ UrlParams += USIOJLibrary::PercentEncode(Key) + "=" + USIOJLibrary::PercentEncode(Value);
+ }
+
+ ParamIdx++;
+ }
+
+ // Apply params
+ HttpRequest->SetURL(HttpRequest->GetURL() + UrlParams);
+
+ UE_LOG(LogSIOJ, Log, TEXT("Request (urlencoded): %s %s %s"), *HttpRequest->GetVerb(), *HttpRequest->GetURL(), *UrlParams);
+
+ break;
+ }
+ case ESIORequestContentType::x_www_form_urlencoded_body:
+ {
+ HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/x-www-form-urlencoded"));
+
+ FString UrlParams = "";
+ uint16 ParamIdx = 0;
+
+ // Loop through all the values and prepare additional url part
+ for (auto RequestIt = RequestJsonObj->GetRootObject()->Values.CreateIterator(); RequestIt; ++RequestIt)
+ {
+ FString Key = RequestIt.Key();
+ FString Value = RequestIt.Value().Get()->AsString();
+
+ if (!Key.IsEmpty() && !Value.IsEmpty())
+ {
+ UrlParams += ParamIdx == 0 ? "" : "&";
+ UrlParams += USIOJLibrary::PercentEncode(Key) + "=" + USIOJLibrary::PercentEncode(Value);
+ }
+
+ ParamIdx++;
+ }
+
+ // Apply params
+ HttpRequest->SetContentAsString(UrlParams);
+
+ UE_LOG(LogSIOJ, Log, TEXT("Request (url body): %s %s %s"), *HttpRequest->GetVerb(), *HttpRequest->GetURL(), *UrlParams);
+
+ break;
+ }
+ case ESIORequestContentType::binary:
+ {
+ HttpRequest->SetHeader(TEXT("Content-Type"), BinaryContentType);
+ HttpRequest->SetContent(RequestBytes);
+
+ UE_LOG(LogSIOJ, Log, TEXT("Request (binary): %s %s"), *HttpRequest->GetVerb(), *HttpRequest->GetURL());
+
+ break;
+ }
+ case ESIORequestContentType::json:
+ {
+ HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
+
+ // Serialize data to json string
+ FString OutputString;
+ TSharedRef< TJsonWriter<> > Writer = TJsonWriterFactory<>::Create(&OutputString);
+ FJsonSerializer::Serialize(RequestJsonObj->GetRootObject().ToSharedRef(), Writer);
+
+ // Set Json content
+ HttpRequest->SetContentAsString(OutputString);
+
+ UE_LOG(LogSIOJ, Log, TEXT("Request (json): %s %s %s"), *HttpRequest->GetVerb(), *HttpRequest->GetURL(), *OutputString);
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ // Apply additional headers
+ for (TMap::TConstIterator It(RequestHeaders); It; ++It)
+ {
+ HttpRequest->SetHeader(It.Key(), It.Value());
+ }
+
+ // Bind event
+ if (bShouldHaveBinaryResponse)
+ {
+ HttpRequest->OnProcessRequestComplete().BindUObject(this, &USIOJRequestJSON::OnProcessRequestCompleteBinaryResult);
+ }
+ else
+ {
+ HttpRequest->OnProcessRequestComplete().BindUObject(this, &USIOJRequestJSON::OnProcessRequestComplete);
+ }
+
+ // Execute the request
+ HttpRequest->ProcessRequest();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// Request callbacks
+
+void USIOJRequestJSON::OnProcessRequestComplete(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
+{
+ // Be sure that we have no data from previous response
+ ResetResponseData();
+
+ // Check we have a response and save response code as int32
+ if(Response.IsValid())
+ {
+ ResponseCode = Response->GetResponseCode();
+ }
+
+ // Check we have result to process futher
+ if (!bWasSuccessful || !Response.IsValid())
+ {
+ UE_LOG(LogSIOJ, Error, TEXT("Request failed (%d): %s"), ResponseCode, *Request->GetURL());
+
+ // Broadcast the result event
+ OnRequestFail.Broadcast(this);
+ OnStaticRequestFail.Broadcast(this);
+
+ return;
+ }
+
+ // Save response data as a string
+ ResponseContent = Response->GetContentAsString();
+
+ // Log response state
+ UE_LOG(LogSIOJ, Log, TEXT("Response (%d): %s"), ResponseCode, *ResponseContent);
+
+ // Process response headers
+ TArray Headers = Response->GetAllHeaders();
+ for (FString Header : Headers)
+ {
+ FString Key;
+ FString Value;
+ if (Header.Split(TEXT(": "), &Key, &Value))
+ {
+ ResponseHeaders.Add(Key, Value);
+ }
+ }
+
+ // Try to deserialize data to JSON
+ TSharedRef> JsonReader = TJsonReaderFactory::Create(ResponseContent);
+ FJsonSerializer::Deserialize(JsonReader, ResponseJsonObj->GetRootObject());
+
+ // Decide whether the request was successful
+ bIsValidJsonResponse = bWasSuccessful && ResponseJsonObj->GetRootObject().IsValid();
+
+ // Log errors
+ if (!bIsValidJsonResponse)
+ {
+ if (!ResponseJsonObj->GetRootObject().IsValid())
+ {
+ // As we assume it's recommended way to use current class, but not the only one,
+ // it will be the warning instead of error
+ UE_LOG(LogSIOJ, Warning, TEXT("JSON could not be decoded!"));
+ }
+ }
+
+ // Broadcast the result event
+ OnRequestComplete.Broadcast(this);
+ OnStaticRequestComplete.Broadcast(this);
+
+ // Finish the latent action
+ if (ContinueAction)
+ {
+ FSIOJLatentAction *K = ContinueAction;
+ ContinueAction = nullptr;
+
+ K->Call(ResponseJsonObj);
+ }
+}
+
+
+void USIOJRequestJSON::OnProcessRequestCompleteBinaryResult(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
+{
+ // Be sure that we have no data from previous response
+ ResetResponseData();
+
+ // Check we have a response and save response code as int32
+ if (Response.IsValid())
+ {
+ ResponseCode = Response->GetResponseCode();
+ }
+
+ // Check we have result to process futher
+ if (!bWasSuccessful || !Response.IsValid())
+ {
+ UE_LOG(LogSIOJ, Error, TEXT("Request failed (%d): %s"), ResponseCode, *Request->GetURL());
+
+ // Broadcast the result event
+ OnRequestFail.Broadcast(this);
+ OnStaticRequestFail.Broadcast(this);
+
+ return;
+ }
+
+ ResultBinaryData = Response->GetContent();
+
+ // Broadcast the result event
+ OnRequestComplete.Broadcast(this);
+ OnStaticRequestComplete.Broadcast(this);
+
+ if (OnProcessURLCompleteCallback)
+ {
+ OnProcessURLCompleteCallback(ResultBinaryData);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Tags
+
+void USIOJRequestJSON::AddTag(FName Tag)
+{
+ if (Tag != NAME_None)
+ {
+ Tags.AddUnique(Tag);
+ }
+}
+
+int32 USIOJRequestJSON::RemoveTag(FName Tag)
+{
+ return Tags.Remove(Tag);
+}
+
+bool USIOJRequestJSON::HasTag(FName Tag) const
+{
+ return (Tag != NAME_None) && Tags.Contains(Tag);
+}
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJson/Private/SIOJson.cpp b/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJson/Private/SIOJson.cpp
new file mode 100644
index 0000000..a16621f
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJson/Private/SIOJson.cpp
@@ -0,0 +1,30 @@
+// Modifications Copyright 2018-current Getnamo. All Rights Reserved
+
+
+// Copyright 2014 Vladimir Alyamkin. All Rights Reserved.
+
+#include "ISIOJson.h"
+#include "SIOJsonObject.h"
+#include "SIOJsonValue.h"
+#include "SIOJRequestJSON.h"
+
+class FSIOJson : public ISIOJson
+{
+ /** IModuleInterface implementation */
+ virtual void StartupModule() override
+ {
+ // @HACK Force classes to be compiled on shipping build
+ USIOJsonObject::StaticClass();
+ USIOJsonValue::StaticClass();
+ USIOJRequestJSON::StaticClass();
+ }
+
+ virtual void ShutdownModule() override
+ {
+
+ }
+};
+
+IMPLEMENT_MODULE(FSIOJson, SIOJson)
+
+DEFINE_LOG_CATEGORY(LogSIOJ);
diff --git a/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJson/Private/SIOJsonObject.cpp b/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJson/Private/SIOJsonObject.cpp
new file mode 100644
index 0000000..0a7dc82
--- /dev/null
+++ b/CorruptedMemory/Plugins/SocketIOClient/Source/SIOJson/Private/SIOJsonObject.cpp
@@ -0,0 +1,576 @@
+// Modifications Copyright 2018-current Getnamo. All Rights Reserved
+
+
+// Copyright 2014 Vladimir Alyamkin. All Rights Reserved.
+
+#include "SIOJsonObject.h"
+#include "SIOJsonValue.h"
+#include "ISIOJson.h"
+#include "Misc/Base64.h"
+#include "Policies/CondensedJsonPrintPolicy.h"
+#include "Serialization/JsonWriter.h"
+#include "Serialization/JsonSerializer.h"
+
+typedef TJsonWriterFactory< TCHAR, TCondensedJsonPrintPolicy > FCondensedJsonStringWriterFactory;
+typedef TJsonWriter< TCHAR, TCondensedJsonPrintPolicy > FCondensedJsonStringWriter;
+
+USIOJsonObject::USIOJsonObject(const class FObjectInitializer& PCIP)
+ : Super(PCIP)
+{
+ Reset();
+}
+
+USIOJsonObject* USIOJsonObject::ConstructJsonObject(UObject* WorldContextObject)
+{
+ return NewObject();
+}
+
+void USIOJsonObject::Reset()
+{
+ if (JsonObj.IsValid())
+ {
+ JsonObj.Reset();
+ }
+
+ JsonObj = MakeShareable(new FJsonObject());
+}
+
+TSharedPtr& USIOJsonObject::GetRootObject()
+{
+ return JsonObj;
+}
+
+void USIOJsonObject::SetRootObject(const TSharedPtr& JsonObject)
+{
+ JsonObj = JsonObject;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// Serialization
+
+FString USIOJsonObject::EncodeJson() const
+{
+ if (!JsonObj.IsValid())
+ {
+ return TEXT("");
+ }
+
+ FString OutputString;
+ TSharedRef< FCondensedJsonStringWriter > Writer = FCondensedJsonStringWriterFactory::Create(&OutputString);
+ FJsonSerializer::Serialize(JsonObj.ToSharedRef(), Writer);
+
+ return OutputString;
+}
+
+FString USIOJsonObject::EncodeJsonToSingleString() const
+{
+ FString OutputString = EncodeJson();
+
+ // Remove line terminators
+ (void)OutputString.Replace(LINE_TERMINATOR, TEXT(""));
+
+ // Remove tabs
+ (void)OutputString.Replace(LINE_TERMINATOR, TEXT("\t"));
+
+ return OutputString;
+}
+
+bool USIOJsonObject::DecodeJson(const FString& JsonString)
+{
+ TSharedRef< TJsonReader<> > Reader = TJsonReaderFactory<>::Create(*JsonString);
+ if (FJsonSerializer::Deserialize(Reader, JsonObj) && JsonObj.IsValid())
+ {
+ return true;
+ }
+
+ // If we've failed to deserialize the string, we should clear our internal data
+ Reset();
+
+ UE_LOG(LogSIOJ, Error, TEXT("Json decoding failed for: %s"), *JsonString);
+
+ return false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// FJsonObject API
+
+TArray USIOJsonObject::GetFieldNames()
+{
+ TArray Result;
+
+ if (!JsonObj.IsValid())
+ {
+ return Result;
+ }
+
+ JsonObj->Values.GetKeys(Result);
+
+ return Result;
+}
+
+bool USIOJsonObject::HasField(const FString& FieldName) const
+{
+ if (!JsonObj.IsValid() || FieldName.IsEmpty())
+ {
+ return false;
+ }
+
+ return JsonObj->HasField(FieldName);
+}
+
+void USIOJsonObject::RemoveField(const FString& FieldName)
+{
+ if (!JsonObj.IsValid() || FieldName.IsEmpty())
+ {
+ return;
+ }
+
+ JsonObj->RemoveField(FieldName);
+}
+
+USIOJsonValue* USIOJsonObject::GetField(const FString& FieldName) const
+{
+ if (!JsonObj.IsValid() || FieldName.IsEmpty())
+ {
+ return nullptr;
+ }
+
+ TSharedPtr