Add Socket.io Plugin for Socket Implementation
This commit is contained in:
parent
19150f7fdd
commit
1b64b02c0d
@ -11,7 +11,7 @@ bUseSplitscreen=True
|
|||||||
TwoPlayerSplitscreenLayout=Horizontal
|
TwoPlayerSplitscreenLayout=Horizontal
|
||||||
ThreePlayerSplitscreenLayout=FavorTop
|
ThreePlayerSplitscreenLayout=FavorTop
|
||||||
GameInstanceClass=/Script/CorruptedMemory.CMGameInstance
|
GameInstanceClass=/Script/CorruptedMemory.CMGameInstance
|
||||||
GameDefaultMap=/Game/FirstPerson/Maps/FirstPersonMap.FirstPersonMap
|
GameDefaultMap=/Game/Levels/ClientEntry.ClientEntry
|
||||||
ServerDefaultMap=/Game/FirstPerson/Maps/FirstPersonMap.FirstPersonMap
|
ServerDefaultMap=/Game/FirstPerson/Maps/FirstPersonMap.FirstPersonMap
|
||||||
GlobalDefaultGameMode=/Script/CorruptedMemory.CorruptedMemoryGameMode
|
GlobalDefaultGameMode=/Script/CorruptedMemory.CorruptedMemoryGameMode
|
||||||
GlobalDefaultServerGameMode=None
|
GlobalDefaultServerGameMode=None
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:c65d6201a37d6d02a79ddaffa38ed0a9779b665fec2aa9a97f81e45405565dad
|
oid sha256:14f450331c03198d77da600afab5083c2a67a4a29c42eefa392499a8785fc5e7
|
||||||
size 46902
|
size 45651
|
||||||
|
BIN
CorruptedMemory/Content/StarterContent/HDRI/HDRI_Epic_Courtyard_Daylight.uasset
(Stored with Git LFS)
BIN
CorruptedMemory/Content/StarterContent/HDRI/HDRI_Epic_Courtyard_Daylight.uasset
(Stored with Git LFS)
Binary file not shown.
BIN
CorruptedMemory/Content/StarterContent/Particles/P_Ambient_Dust.uasset
(Stored with Git LFS)
BIN
CorruptedMemory/Content/StarterContent/Particles/P_Ambient_Dust.uasset
(Stored with Git LFS)
Binary file not shown.
@ -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
|
28
CorruptedMemory/Plugins/SocketIOClient/LICENSE
Normal file
28
CorruptedMemory/Plugins/SocketIOClient/LICENSE
Normal file
@ -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.
|
856
CorruptedMemory/Plugins/SocketIOClient/README.md
Normal file
856
CorruptedMemory/Plugins/SocketIOClient/README.md
Normal file
@ -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
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th rowspan="2">UE4 Socket.IO plugin version</th>
|
||||||
|
<th colspan="2">Socket.IO server version</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center">1.x / 2.x</td>
|
||||||
|
<td align="center">3.x / 4.x</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><a href="https://github.com/getnamo/SocketIOClient-Unreal/releases/tag/1.5.5">v1.5.5 and earlier</a></td>
|
||||||
|
<td align="center">YES</td>
|
||||||
|
<td align="center">NO</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><a href="https://github.com/getnamo/SocketIOClient-Unreal/releases/tag/1.6.0">v1.6.0 and later</a></td>
|
||||||
|
<td align="center">NO</td>
|
||||||
|
<td align="center">YES</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
## 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<USocketIOClientComponent>(TEXT("SocketIOClientComponent"));
|
||||||
|
```
|
||||||
|
|
||||||
|
or reference it from another component by getting it on begin play e.g.
|
||||||
|
|
||||||
|
```c++
|
||||||
|
SIOClientComponent = Cast<USocketIOClientComponent>(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<FJsonValue>&)``` 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<FJsonValue>& 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<uint8>
|
||||||
|
|
||||||
|
```c++
|
||||||
|
TArray<uint8> 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<FJsonObject> JsonObject = MakeShareable(new FJsonObject);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### FJsonObject - Complex Example
|
||||||
|
|
||||||
|
A nested example using various methods
|
||||||
|
|
||||||
|
```c++
|
||||||
|
//All types, nested
|
||||||
|
TSharedPtr<FJsonObject> 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<FJsonObject> 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<FSocketIONative> 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<FJsonValue>& 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<uint8>& 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)
|
BIN
CorruptedMemory/Plugins/SocketIOClient/Resources/Icon128.png
(Stored with Git LFS)
Normal file
BIN
CorruptedMemory/Plugins/SocketIOClient/Resources/Icon128.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -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[]
|
||||||
|
{
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -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<uint8> BufferArray;
|
||||||
|
TSharedPtr<IImageWrapper> Wrapper; //to keep the uncompressed data alive
|
||||||
|
};
|
||||||
|
|
||||||
|
FString UCUBlueprintLibrary::Conv_BytesToString(const TArray<uint8>& InArray)
|
||||||
|
{
|
||||||
|
FString ResultString;
|
||||||
|
FFileHelper::BufferToString(ResultString, InArray.GetData(), InArray.Num());
|
||||||
|
return ResultString;
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<uint8> UCUBlueprintLibrary::Conv_StringToBytes(FString InString)
|
||||||
|
{
|
||||||
|
TArray<uint8> ResultBytes;
|
||||||
|
ResultBytes.Append((uint8*)TCHAR_TO_UTF8(*InString), InString.Len());
|
||||||
|
return ResultBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
UTexture2D* UCUBlueprintLibrary::Conv_BytesToTexture(const TArray<uint8>& InBytes)
|
||||||
|
{
|
||||||
|
//Convert the UTexture2D back to an image
|
||||||
|
UTexture2D* Texture = nullptr;
|
||||||
|
|
||||||
|
IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
|
||||||
|
EImageFormat DetectedFormat = ImageWrapperModule.DetectImageFormat(InBytes.GetData(), InBytes.Num());
|
||||||
|
|
||||||
|
TSharedPtr<IImageWrapper> 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<uint8> 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<FCUOpusCoder> OpusCoder;
|
||||||
|
|
||||||
|
TArray<uint8> UCUBlueprintLibrary::Conv_OpusBytesToWav(const TArray<uint8>& InBytes)
|
||||||
|
{
|
||||||
|
//FCUScopeTimer Timer(TEXT("Conv_OpusBytesToWav"));
|
||||||
|
|
||||||
|
TArray<uint8> WavBytes;
|
||||||
|
//Early exit condition
|
||||||
|
if (InBytes.Num() == 0)
|
||||||
|
{
|
||||||
|
return WavBytes;
|
||||||
|
}
|
||||||
|
if (!OpusCoder)
|
||||||
|
{
|
||||||
|
OpusCoder = MakeShareable(new FCUOpusCoder());
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<uint8> 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<uint8> UCUBlueprintLibrary::Conv_WavBytesToOpus(const TArray<uint8>& InBytes)
|
||||||
|
{
|
||||||
|
//FCUScopeTimer Timer(TEXT("Conv_WavBytesToOpus"));
|
||||||
|
|
||||||
|
TArray<uint8> OpusBytes;
|
||||||
|
|
||||||
|
FWaveModInfo WaveInfo;
|
||||||
|
|
||||||
|
if (!WaveInfo.ReadWaveInfo(InBytes.GetData(), InBytes.Num()))
|
||||||
|
{
|
||||||
|
return OpusBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!OpusCoder)
|
||||||
|
{
|
||||||
|
OpusCoder = MakeShareable(new FCUOpusCoder());
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<uint8> PCMBytes = TArray<uint8>(WaveInfo.SampleDataStart, WaveInfo.SampleDataSize);
|
||||||
|
|
||||||
|
FCUOpusMinimalStream OpusStream;
|
||||||
|
|
||||||
|
OpusCoder->EncodeStream(PCMBytes, OpusStream);
|
||||||
|
|
||||||
|
TArray<uint8> SerializedBytes;
|
||||||
|
OpusCoder->SerializeMinimal(OpusStream, SerializedBytes);
|
||||||
|
|
||||||
|
return SerializedBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
USoundWave* UCUBlueprintLibrary::Conv_WavBytesToSoundWave(const TArray<uint8>& InBytes)
|
||||||
|
{
|
||||||
|
USoundWave* SoundWave;
|
||||||
|
|
||||||
|
//Allocate based on thread
|
||||||
|
if (IsInGameThread())
|
||||||
|
{
|
||||||
|
SoundWave = NewObject<USoundWaveProcedural>(USoundWaveProcedural::StaticClass());
|
||||||
|
SetSoundWaveFromWavBytes((USoundWaveProcedural*)SoundWave, InBytes);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//We will go to another thread, copy our bytes
|
||||||
|
TArray<uint8> CopiedBytes = InBytes;
|
||||||
|
FThreadSafeBool bAllocationComplete = false;
|
||||||
|
AsyncTask(ENamedThreads::GameThread, [&bAllocationComplete, &SoundWave]
|
||||||
|
{
|
||||||
|
SoundWave = NewObject<USoundWaveProcedural>(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<uint8> UCUBlueprintLibrary::Conv_SoundWaveToWavBytes(USoundWave* SoundWave)
|
||||||
|
{
|
||||||
|
TArray<uint8> PCMBytes;
|
||||||
|
TArray<uint8> 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<uint8>& InCompactBytes, TArray<FTransform>& OutTransforms)
|
||||||
|
{
|
||||||
|
TArray<float> 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<uint8>& InCompactBytes, TArray<FTransform>& OutTransforms)
|
||||||
|
{
|
||||||
|
TArray<float> 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<uint8>& 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<UTexture2D*> UCUBlueprintLibrary::Conv_BytesToTexture_Async(const TArray<uint8>& 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<FDataHolder> Holder = MakeShareable(new FDataHolder);
|
||||||
|
|
||||||
|
FThreadSafeBool bLoadModuleComplete = false;
|
||||||
|
IImageWrapperModule* ImageWrapperModule;
|
||||||
|
|
||||||
|
AsyncTask(ENamedThreads::GameThread, [&bLoadModuleComplete, Holder, &ImageWrapperModule]
|
||||||
|
{
|
||||||
|
ImageWrapperModule = &FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
|
||||||
|
bLoadModuleComplete = true;
|
||||||
|
});
|
||||||
|
while (!bLoadModuleComplete)
|
||||||
|
{
|
||||||
|
FPlatformProcess::Sleep(0.001f);
|
||||||
|
}
|
||||||
|
|
||||||
|
EImageFormat DetectedFormat = ImageWrapperModule->DetectImageFormat(InBytes.GetData(), InBytes.Num());
|
||||||
|
|
||||||
|
TSharedPtr<IImageWrapper> 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<uint8> 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<uint8>& OutBuffer, EImageFormatBPType Format /*= EImageFormatBPType::JPEG*/)
|
||||||
|
{
|
||||||
|
if (!Texture || !Texture->IsValidLowLevel())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get our wrapper module
|
||||||
|
IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
|
||||||
|
TSharedPtr<IImageWrapper> 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 )
|
@ -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<uint8>& 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<uint8>& OutBytes)
|
||||||
|
{
|
||||||
|
// Get absolute file path
|
||||||
|
FString AbsoluteFilePath = Directory + "/" + FileName;
|
||||||
|
|
||||||
|
// Allow overwriting or file doesn't already exist
|
||||||
|
return FFileHelper::LoadFileToArray(OutBytes, *AbsoluteFilePath);
|
||||||
|
}
|
@ -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<uint8>& 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<uint8>& 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<uint8>& OutBytes)
|
||||||
|
{
|
||||||
|
// Get absolute file path
|
||||||
|
const FString AbsoluteFilePath = Directory + "/" + FileName;
|
||||||
|
|
||||||
|
return ReadBytesFromPath(AbsoluteFilePath, OutBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UCUFileSubsystem::ReadBytesFromPath(const FString& Path, TArray<uint8>& 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;
|
||||||
|
}
|
||||||
|
|
@ -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(TFunction<void()>OnDone, 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<FCULatentAction>(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
|
@ -0,0 +1,48 @@
|
|||||||
|
#include "CUMeasureTimer.h"
|
||||||
|
#include "HAL/PlatformTime.h"
|
||||||
|
|
||||||
|
#if ENABLE_CUPRECISE_TIMER
|
||||||
|
static TMap<FString, TSharedPtr<FCUMeasureTimer>> FPreciseTimerInternalMap;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void FCUMeasureTimer::Tick(const FString& LogMsg /*= TEXT("TimeTaken")*/)
|
||||||
|
{
|
||||||
|
#if ENABLE_CUPRECISE_TIMER
|
||||||
|
TSharedPtr<FCUMeasureTimer> 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<FCUMeasureTimer> 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);
|
||||||
|
}
|
@ -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<uint8>& 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<uint8> TempBuffer;
|
||||||
|
TArray<uint8> FinalPacket;
|
||||||
|
TempBuffer.SetNumUninitialized(MaxPacketSize);
|
||||||
|
int32 EncodedBytes = 0;
|
||||||
|
opus_int16* PCMDataPtr = 0;
|
||||||
|
|
||||||
|
while (BytesLeft > 0)
|
||||||
|
{
|
||||||
|
//Final packet requires zero padding
|
||||||
|
if(BytesLeft<BytesPerFrame)
|
||||||
|
{
|
||||||
|
FinalPacket.Append(InPCMBytes.GetData() + Offset, BytesLeft);
|
||||||
|
FinalPacket.AddZeroed(BytesPerFrame - BytesLeft);
|
||||||
|
PCMDataPtr = (opus_int16*)FinalPacket.GetData();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PCMDataPtr = (opus_int16*)(InPCMBytes.GetData() + Offset);
|
||||||
|
}
|
||||||
|
EncodedBytes = opus_encode(Encoder, (const opus_int16*)PCMDataPtr, FrameSize, TempBuffer.GetData(), MaxPacketSize);
|
||||||
|
|
||||||
|
#if DEBUG_OPUS_LOG
|
||||||
|
DebugLogFrame(TempBuffer.GetData(), EncodedBytes, SampleRate, true);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (EncodedBytes < 0)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("opus_encode err: %s"), opus_strerror(EncodedBytes));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
OutStream.CompressedBytes.Append(TempBuffer.GetData(), EncodedBytes);
|
||||||
|
|
||||||
|
OutStream.PacketSizes.Add(EncodedBytes);
|
||||||
|
Offset += BytesPerFrame;
|
||||||
|
BytesLeft -= BytesPerFrame;
|
||||||
|
BytesWritten += EncodedBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG_OPUS_LOG
|
||||||
|
UE_LOG(LogTemp, Log, TEXT("Total packets encoded: %d, total bytes: %d=%d"), OutStream.PacketSizes.Num(), BytesWritten, OutStream.CompressedBytes.Num());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif //with opus
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FCUOpusCoder::DecodeStream(const FCUOpusMinimalStream& InStream, TArray<uint8>& OutPCMFrame)
|
||||||
|
{
|
||||||
|
if (!InitDecoderIfNeeded())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#if WITH_OPUS
|
||||||
|
|
||||||
|
#if DEBUG_OPUS_LOG
|
||||||
|
DebugLogDecoder();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const int32 BytesPerFrame = FrameSize * Channels * sizeof(opus_int16);
|
||||||
|
|
||||||
|
TArray<uint8> 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<uint8>& 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<uint8>& 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<uint8>& 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<uint8>& InPCMFrame, TArray<uint8>& 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<uint8>& InCompressedFrame, TArray<uint8>& 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
|
||||||
|
}
|
||||||
|
|
@ -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)
|
@ -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<uint8>& InBytes);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert string to UTF8 bytes
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintPure, meta = (DisplayName = "To Bytes (String)", BlueprintAutocast), Category = "CoreUtility|Conversion")
|
||||||
|
static TArray<uint8> 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<uint8>& 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<uint8> Conv_OpusBytesToWav(const TArray<uint8>& 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<uint8> Conv_WavBytesToOpus(const TArray<uint8>& 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<uint8>& InBytes);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert a soundwave into wav bytes
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintPure, meta = (DisplayName = "To Bytes (SoundWave)", BlueprintAutocast), Category = "CoreUtility|Conversion")
|
||||||
|
static TArray<uint8> 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<uint8>& InCompactBytes, TArray<FTransform>& 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<uint8>& InCompactBytes, TArray<FTransform>& 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<uint8>& 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<UTexture2D*> Conv_BytesToTexture_Async(const TArray<uint8>& 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<uint8>& 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);
|
||||||
|
};
|
@ -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<uint8>& 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<uint8>& OutBytes);
|
||||||
|
};
|
@ -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<uint8>& Bytes, const FString& Directory, const FString& FileName, bool bLogSave = false);
|
||||||
|
|
||||||
|
/** Full path variant of SaveBytesToFile */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = FileUtility)
|
||||||
|
bool SaveBytesToPath(const TArray<uint8>& 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<uint8>& OutBytes);
|
||||||
|
|
||||||
|
/** Full path variant of ReadBytesFromFile */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = FileUtility)
|
||||||
|
bool ReadBytesFromPath(const FString& Path, TArray<uint8>& OutBytes);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = FileUtility)
|
||||||
|
bool DeleteFileAtPath(const FString& Path);
|
||||||
|
|
||||||
|
//Lifetime
|
||||||
|
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||||
|
virtual void Deinitialize() override;
|
||||||
|
};
|
@ -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<void()> 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(TFunction<void()>OnDone, float DurationInSec, bool bCallbackOnGameThread = true);
|
||||||
|
};
|
@ -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;
|
||||||
|
};
|
@ -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<int16> PacketSizes; // Maximum packet size is 32768 (much larger than typical packet)
|
||||||
|
TArray<uint8> 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<uint8>& InPCMBytes, FCUOpusMinimalStream& OutStream);
|
||||||
|
bool DecodeStream(const FCUOpusMinimalStream& InStream, TArray<uint8>& OutPCMFrame);
|
||||||
|
|
||||||
|
|
||||||
|
//Format: PacketCount, PacketSizes, CompressedOpusBytes. Expects settings set in a different stream
|
||||||
|
bool SerializeMinimal(const FCUOpusMinimalStream& InStream, TArray<uint8>& OutSerializedBytes);
|
||||||
|
bool DeserializeMinimal(const TArray<uint8>& InSerializedMinimalBytes, FCUOpusMinimalStream& OutStream);
|
||||||
|
|
||||||
|
//Handle a single frame
|
||||||
|
int32 EncodeFrame(const TArray<uint8>& InPCMFrame, TArray<uint8>& OutCompressed);
|
||||||
|
int32 DecodeFrame(const TArray<uint8>& InCompressedFrame, TArray<uint8>& 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);
|
||||||
|
};
|
@ -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() {};
|
||||||
|
};
|
@ -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)
|
@ -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<UClass>(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<USIOJ_BreakJson>(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<UEdGraphSchema_K2>();
|
||||||
|
|
||||||
|
UClass *Class = Cast<UClass>(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<UEdGraphSchema_K2>();
|
||||||
|
UClass *Class = Cast<UClass>(StaticLoadObject(UClass::StaticClass(), NULL, TEXT("class'SIOJPlugin.SIOJJsonObject'")));
|
||||||
|
|
||||||
|
for (TArray<FSIOJ_NamedType>::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
|
@ -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;
|
||||||
|
|
||||||
|
};
|
@ -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<FSIOJ_NamedType> Outputs;
|
||||||
|
|
||||||
|
};
|
@ -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 ...
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -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<uint8>& 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<uint8>& Dest)
|
||||||
|
{
|
||||||
|
return FBase64::Decode(Source, Dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool USIOJLibrary::StringToJsonValueArray(const FString& JsonString, TArray<USIOJsonValue*>& OutJsonValueArray)
|
||||||
|
{
|
||||||
|
TArray < TSharedPtr<FJsonValue>> RawJsonValueArray;
|
||||||
|
TSharedRef< TJsonReader<> > Reader = TJsonReaderFactory<>::Create(*JsonString);
|
||||||
|
FJsonSerializer::Deserialize(Reader, RawJsonValueArray);
|
||||||
|
|
||||||
|
for (auto Value : RawJsonValueArray)
|
||||||
|
{
|
||||||
|
auto SJsonValue = NewObject<USIOJsonValue>();
|
||||||
|
SJsonValue->SetRootValue(Value);
|
||||||
|
OutJsonValueArray.Add(SJsonValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return OutJsonValueArray.Num() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Easy URL processing
|
||||||
|
|
||||||
|
TMap<USIOJRequestJSON*, FSIOJCallResponse> 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<USIOJsonValue*>& InArray)
|
||||||
|
{
|
||||||
|
return USIOJsonValue::ConstructJsonValueArray(nullptr, InArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
USIOJsonValue* USIOJLibrary::Conv_JsonObjectToJsonValue(USIOJsonObject* InObject)
|
||||||
|
{
|
||||||
|
return USIOJsonValue::ConstructJsonValueObject(InObject, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
USIOJsonValue* USIOJLibrary::Conv_BytesToJsonValue(const TArray<uint8>& 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<FJsonValue> NewVal = MakeShareable(new FJsonValueNumber(InInt));
|
||||||
|
|
||||||
|
USIOJsonValue* NewValue = NewObject<USIOJsonValue>();
|
||||||
|
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<uint8> USIOJLibrary::Conv_JsonValueToBytes(USIOJsonValue* InValue)
|
||||||
|
{
|
||||||
|
if (InValue)
|
||||||
|
{
|
||||||
|
return InValue->AsBinary();
|
||||||
|
}
|
||||||
|
|
||||||
|
return TArray<uint8>();
|
||||||
|
}
|
||||||
|
|
||||||
|
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<USIOJRequestJSON>();
|
||||||
|
|
||||||
|
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<uint8>& 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<USIOJRequestJSON>();
|
||||||
|
FCULatentAction *LatentAction = FCULatentAction::CreateLatentAction(LatentInfo, WorldContextObject);
|
||||||
|
|
||||||
|
Request->SetVerb(Verb);
|
||||||
|
Request->SetContentType(ContentType);
|
||||||
|
Request->bShouldHaveBinaryResponse = true;
|
||||||
|
Request->OnProcessURLCompleteCallback = [LatentAction, &OutResultData](TArray<uint8>& 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);
|
||||||
|
}
|
@ -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 <class T> void FSIOJLatentAction<T>::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* 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<uint8> &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<USIOJsonObject>();
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpRequest = FHttpModule::Get().CreateRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
void USIOJRequestJSON::ResetResponseData()
|
||||||
|
{
|
||||||
|
if (ResponseJsonObj != nullptr)
|
||||||
|
{
|
||||||
|
ResponseJsonObj->Reset();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ResponseJsonObj = NewObject<USIOJsonObject>();
|
||||||
|
}
|
||||||
|
|
||||||
|
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<FString> USIOJRequestJSON::GetAllResponseHeaders()
|
||||||
|
{
|
||||||
|
TArray<FString> Result;
|
||||||
|
for (TMap<FString, FString>::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<USIOJsonObject*> *Kont = LatentActionManager.FindExistingAction<FSIOJLatentAction<USIOJsonObject*>>(LatentInfo.CallbackTarget, LatentInfo.UUID);
|
||||||
|
|
||||||
|
|
||||||
|
if (Kont != nullptr)
|
||||||
|
{
|
||||||
|
Kont->Cancel();
|
||||||
|
LatentActionManager.RemoveActionsForObject(LatentInfo.CallbackTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, ContinueAction = new FSIOJLatentAction<USIOJsonObject*>(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<FString, FString>::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<FString> 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<TJsonReader<TCHAR>> JsonReader = TJsonReaderFactory<TCHAR>::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<USIOJsonObject*> *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);
|
||||||
|
}
|
@ -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);
|
@ -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<TCHAR> > FCondensedJsonStringWriterFactory;
|
||||||
|
typedef TJsonWriter< TCHAR, TCondensedJsonPrintPolicy<TCHAR> > FCondensedJsonStringWriter;
|
||||||
|
|
||||||
|
USIOJsonObject::USIOJsonObject(const class FObjectInitializer& PCIP)
|
||||||
|
: Super(PCIP)
|
||||||
|
{
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
USIOJsonObject* USIOJsonObject::ConstructJsonObject(UObject* WorldContextObject)
|
||||||
|
{
|
||||||
|
return NewObject<USIOJsonObject>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void USIOJsonObject::Reset()
|
||||||
|
{
|
||||||
|
if (JsonObj.IsValid())
|
||||||
|
{
|
||||||
|
JsonObj.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObj = MakeShareable(new FJsonObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedPtr<FJsonObject>& USIOJsonObject::GetRootObject()
|
||||||
|
{
|
||||||
|
return JsonObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void USIOJsonObject::SetRootObject(const TSharedPtr<FJsonObject>& 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<FString> USIOJsonObject::GetFieldNames()
|
||||||
|
{
|
||||||
|
TArray<FString> 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<FJsonValue> NewVal = JsonObj->TryGetField(FieldName);
|
||||||
|
if (NewVal.IsValid())
|
||||||
|
{
|
||||||
|
USIOJsonValue* NewValue = NewObject<USIOJsonValue>();
|
||||||
|
NewValue->SetRootValue(NewVal);
|
||||||
|
|
||||||
|
return NewValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void USIOJsonObject::SetField(const FString& FieldName, USIOJsonValue* JsonValue)
|
||||||
|
{
|
||||||
|
if (!JsonObj.IsValid() || FieldName.IsEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObj->SetField(FieldName, JsonValue->GetRootValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// FJsonObject API Helpers (easy to use with simple Json objects)
|
||||||
|
|
||||||
|
float USIOJsonObject::GetNumberField(const FString& FieldName) const
|
||||||
|
{
|
||||||
|
if (!JsonObj.IsValid() || !JsonObj->HasTypedField<EJson::Number>(FieldName))
|
||||||
|
{
|
||||||
|
UE_LOG(LogSIOJ, Warning, TEXT("No field with name %s of type Number"), *FieldName);
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonObj->GetNumberField(FieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USIOJsonObject::SetNumberField(const FString& FieldName, float Number)
|
||||||
|
{
|
||||||
|
if (!JsonObj.IsValid() || FieldName.IsEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObj->SetNumberField(FieldName, Number);
|
||||||
|
}
|
||||||
|
|
||||||
|
FString USIOJsonObject::GetStringField(const FString& FieldName) const
|
||||||
|
{
|
||||||
|
if (!JsonObj.IsValid() || !JsonObj->HasTypedField<EJson::String>(FieldName))
|
||||||
|
{
|
||||||
|
UE_LOG(LogSIOJ, Warning, TEXT("No field with name %s of type String"), *FieldName);
|
||||||
|
return TEXT("");
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonObj->GetStringField(FieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USIOJsonObject::SetStringField(const FString& FieldName, const FString& StringValue)
|
||||||
|
{
|
||||||
|
if (!JsonObj.IsValid() || FieldName.IsEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObj->SetStringField(FieldName, StringValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool USIOJsonObject::GetBoolField(const FString& FieldName) const
|
||||||
|
{
|
||||||
|
if (!JsonObj.IsValid() || !JsonObj->HasTypedField<EJson::Boolean>(FieldName))
|
||||||
|
{
|
||||||
|
UE_LOG(LogSIOJ, Warning, TEXT("No field with name %s of type Boolean"), *FieldName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonObj->GetBoolField(FieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USIOJsonObject::SetBoolField(const FString& FieldName, bool InValue)
|
||||||
|
{
|
||||||
|
if (!JsonObj.IsValid() || FieldName.IsEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObj->SetBoolField(FieldName, InValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<USIOJsonValue*> USIOJsonObject::GetArrayField(const FString& FieldName)
|
||||||
|
{
|
||||||
|
if (!JsonObj->HasTypedField<EJson::Array>(FieldName))
|
||||||
|
{
|
||||||
|
UE_LOG(LogSIOJ, Warning, TEXT("No field with name %s of type Array"), *FieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<USIOJsonValue*> OutArray;
|
||||||
|
if (!JsonObj.IsValid() || FieldName.IsEmpty())
|
||||||
|
{
|
||||||
|
return OutArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray< TSharedPtr<FJsonValue> > ValArray = JsonObj->GetArrayField(FieldName);
|
||||||
|
for (auto Value : ValArray)
|
||||||
|
{
|
||||||
|
USIOJsonValue* NewValue = NewObject<USIOJsonValue>();
|
||||||
|
NewValue->SetRootValue(Value);
|
||||||
|
|
||||||
|
OutArray.Add(NewValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return OutArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
void USIOJsonObject::SetArrayField(const FString& FieldName, const TArray<USIOJsonValue*>& InArray)
|
||||||
|
{
|
||||||
|
if (!JsonObj.IsValid() || FieldName.IsEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray< TSharedPtr<FJsonValue> > ValArray;
|
||||||
|
|
||||||
|
// Process input array and COPY original values
|
||||||
|
for (auto InVal : InArray)
|
||||||
|
{
|
||||||
|
TSharedPtr<FJsonValue> JsonVal = InVal->GetRootValue();
|
||||||
|
|
||||||
|
switch (InVal->GetType())
|
||||||
|
{
|
||||||
|
case ESIOJson::None:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ESIOJson::Null:
|
||||||
|
ValArray.Add(MakeShareable(new FJsonValueNull()));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ESIOJson::String:
|
||||||
|
ValArray.Add(MakeShareable(new FJsonValueString(JsonVal->AsString())));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ESIOJson::Number:
|
||||||
|
ValArray.Add(MakeShareable(new FJsonValueNumber(JsonVal->AsNumber())));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ESIOJson::Boolean:
|
||||||
|
ValArray.Add(MakeShareable(new FJsonValueBoolean(JsonVal->AsBool())));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ESIOJson::Array:
|
||||||
|
ValArray.Add(MakeShareable(new FJsonValueArray(JsonVal->AsArray())));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ESIOJson::Object:
|
||||||
|
ValArray.Add(MakeShareable(new FJsonValueObject(JsonVal->AsObject())));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObj->SetArrayField(FieldName, ValArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USIOJsonObject::MergeJsonObject(USIOJsonObject* InJsonObject, bool Overwrite)
|
||||||
|
{
|
||||||
|
TArray<FString> Keys = InJsonObject->GetFieldNames();
|
||||||
|
|
||||||
|
for (auto Key : Keys)
|
||||||
|
{
|
||||||
|
if (Overwrite == false && HasField(Key))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetField(Key, InJsonObject->GetField(Key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
USIOJsonObject* USIOJsonObject::GetObjectField(const FString& FieldName) const
|
||||||
|
{
|
||||||
|
if (!JsonObj.IsValid() || !JsonObj->HasTypedField<EJson::Object>(FieldName))
|
||||||
|
{
|
||||||
|
UE_LOG(LogSIOJ, Warning, TEXT("No field with name %s of type Object"), *FieldName);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedPtr<FJsonObject> JsonObjField = JsonObj->GetObjectField(FieldName);
|
||||||
|
|
||||||
|
USIOJsonObject* OutRestJsonObj = NewObject<USIOJsonObject>();
|
||||||
|
OutRestJsonObj->SetRootObject(JsonObjField);
|
||||||
|
|
||||||
|
return OutRestJsonObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void USIOJsonObject::SetObjectField(const FString& FieldName, USIOJsonObject* JsonObject)
|
||||||
|
{
|
||||||
|
if (!JsonObj.IsValid() || FieldName.IsEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObj->SetObjectField(FieldName, JsonObject->GetRootObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
void USIOJsonObject::GetBinaryField(const FString& FieldName, TArray<uint8>& OutBinary) const
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!JsonObj->HasTypedField<EJson::String>(FieldName))
|
||||||
|
{
|
||||||
|
UE_LOG(LogSIOJ, Warning, TEXT("No field with name %s of type String"), *FieldName);
|
||||||
|
}
|
||||||
|
TSharedPtr<FJsonValue> JsonValue = JsonObj->TryGetField(FieldName);
|
||||||
|
|
||||||
|
if (!JsonValue)
|
||||||
|
{
|
||||||
|
UE_LOG(LogSIOJ, Warning, TEXT("JsonValue is null for %s, aborting parse."), *FieldName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FJsonValueBinary::IsBinary(JsonValue))
|
||||||
|
{
|
||||||
|
OutBinary = FJsonValueBinary::AsBinary(JsonValue);
|
||||||
|
}
|
||||||
|
else if (JsonValue->Type == EJson::String)
|
||||||
|
{
|
||||||
|
//If we got a string that isn't detected as a binary via socket.io protocol hack
|
||||||
|
//then we need to decode this string as base 64
|
||||||
|
TArray<uint8> DecodedArray;
|
||||||
|
bool bDidDecodeCorrectly = FBase64::Decode(JsonValue->AsString(), DecodedArray);
|
||||||
|
if (!bDidDecodeCorrectly)
|
||||||
|
{
|
||||||
|
UE_LOG(LogSIOJ, Warning, TEXT("USIOJsonObject::GetBinaryField couldn't decode %s as a binary."), *JsonValue->AsString());
|
||||||
|
}
|
||||||
|
OutBinary = DecodedArray;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TArray<uint8> EmptyArray;
|
||||||
|
OutBinary = EmptyArray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void USIOJsonObject::SetBinaryField(const FString& FieldName, const TArray<uint8>& Bytes)
|
||||||
|
{
|
||||||
|
if (!JsonObj.IsValid() || FieldName.IsEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TSharedPtr<FJsonValueBinary> JsonValue = MakeShareable(new FJsonValueBinary(Bytes));
|
||||||
|
JsonObj->SetField(FieldName, JsonValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Array fields helpers (uniform arrays)
|
||||||
|
|
||||||
|
TArray<float> USIOJsonObject::GetNumberArrayField(const FString& FieldName)
|
||||||
|
{
|
||||||
|
if (!JsonObj->HasTypedField<EJson::Array>(FieldName))
|
||||||
|
{
|
||||||
|
UE_LOG(LogSIOJ, Warning, TEXT("No field with name %s of type Array"), *FieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<float> NumberArray;
|
||||||
|
if (!JsonObj.IsValid() || FieldName.IsEmpty())
|
||||||
|
{
|
||||||
|
return NumberArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<TSharedPtr<FJsonValue> > JsonArrayValues = JsonObj->GetArrayField(FieldName);
|
||||||
|
for (TArray<TSharedPtr<FJsonValue> >::TConstIterator It(JsonArrayValues); It; ++It)
|
||||||
|
{
|
||||||
|
auto Value = (*It).Get();
|
||||||
|
if (Value->Type != EJson::Number)
|
||||||
|
{
|
||||||
|
UE_LOG(LogSIOJ, Error, TEXT("Not Number element in array with field name %s"), *FieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberArray.Add((*It)->AsNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
return NumberArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
void USIOJsonObject::SetNumberArrayField(const FString& FieldName, const TArray<float>& NumberArray)
|
||||||
|
{
|
||||||
|
if (!JsonObj.IsValid() || FieldName.IsEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray< TSharedPtr<FJsonValue> > EntriesArray;
|
||||||
|
|
||||||
|
for (auto Number : NumberArray)
|
||||||
|
{
|
||||||
|
EntriesArray.Add(MakeShareable(new FJsonValueNumber(Number)));
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObj->SetArrayField(FieldName, EntriesArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<FString> USIOJsonObject::GetStringArrayField(const FString& FieldName)
|
||||||
|
{
|
||||||
|
if (!JsonObj->HasTypedField<EJson::Array>(FieldName))
|
||||||
|
{
|
||||||
|
UE_LOG(LogSIOJ, Warning, TEXT("No field with name %s of type Array"), *FieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<FString> StringArray;
|
||||||
|
if (!JsonObj.IsValid() || FieldName.IsEmpty())
|
||||||
|
{
|
||||||
|
return StringArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<TSharedPtr<FJsonValue> > JsonArrayValues = JsonObj->GetArrayField(FieldName);
|
||||||
|
for (TArray<TSharedPtr<FJsonValue> >::TConstIterator It(JsonArrayValues); It; ++It)
|
||||||
|
{
|
||||||
|
auto Value = (*It).Get();
|
||||||
|
if (Value->Type != EJson::String)
|
||||||
|
{
|
||||||
|
UE_LOG(LogSIOJ, Error, TEXT("Not String element in array with field name %s"), *FieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringArray.Add((*It)->AsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return StringArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
void USIOJsonObject::SetStringArrayField(const FString& FieldName, const TArray<FString>& StringArray)
|
||||||
|
{
|
||||||
|
if (!JsonObj.IsValid() || FieldName.IsEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray< TSharedPtr<FJsonValue> > EntriesArray;
|
||||||
|
for (auto String : StringArray)
|
||||||
|
{
|
||||||
|
EntriesArray.Add(MakeShareable(new FJsonValueString(String)));
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObj->SetArrayField(FieldName, EntriesArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<bool> USIOJsonObject::GetBoolArrayField(const FString& FieldName)
|
||||||
|
{
|
||||||
|
if (!JsonObj->HasTypedField<EJson::Array>(FieldName))
|
||||||
|
{
|
||||||
|
UE_LOG(LogSIOJ, Warning, TEXT("No field with name %s of type Array"), *FieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<bool> BoolArray;
|
||||||
|
if (!JsonObj.IsValid() || FieldName.IsEmpty())
|
||||||
|
{
|
||||||
|
return BoolArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<TSharedPtr<FJsonValue> > JsonArrayValues = JsonObj->GetArrayField(FieldName);
|
||||||
|
for (TArray<TSharedPtr<FJsonValue> >::TConstIterator It(JsonArrayValues); It; ++It)
|
||||||
|
{
|
||||||
|
auto Value = (*It).Get();
|
||||||
|
if (Value->Type != EJson::Boolean)
|
||||||
|
{
|
||||||
|
UE_LOG(LogSIOJ, Error, TEXT("Not Boolean element in array with field name %s"), *FieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
BoolArray.Add((*It)->AsBool());
|
||||||
|
}
|
||||||
|
|
||||||
|
return BoolArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
void USIOJsonObject::SetBoolArrayField(const FString& FieldName, const TArray<bool>& BoolArray)
|
||||||
|
{
|
||||||
|
if (!JsonObj.IsValid() || FieldName.IsEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray< TSharedPtr<FJsonValue> > EntriesArray;
|
||||||
|
for (auto Boolean : BoolArray)
|
||||||
|
{
|
||||||
|
EntriesArray.Add(MakeShareable(new FJsonValueBoolean(Boolean)));
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObj->SetArrayField(FieldName, EntriesArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<USIOJsonObject*> USIOJsonObject::GetObjectArrayField(const FString& FieldName)
|
||||||
|
{
|
||||||
|
if (!JsonObj->HasTypedField<EJson::Array>(FieldName))
|
||||||
|
{
|
||||||
|
UE_LOG(LogSIOJ, Warning, TEXT("No field with name %s of type Array"), *FieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<USIOJsonObject*> OutArray;
|
||||||
|
if (!JsonObj.IsValid() || FieldName.IsEmpty())
|
||||||
|
{
|
||||||
|
return OutArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray< TSharedPtr<FJsonValue> > ValArray = JsonObj->GetArrayField(FieldName);
|
||||||
|
for (auto Value : ValArray)
|
||||||
|
{
|
||||||
|
if (Value->Type != EJson::Object)
|
||||||
|
{
|
||||||
|
UE_LOG(LogSIOJ, Error, TEXT("Not Object element in array with field name %s"), *FieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedPtr<FJsonObject> NewObj = Value->AsObject();
|
||||||
|
|
||||||
|
USIOJsonObject* NewJson = NewObject<USIOJsonObject>();
|
||||||
|
NewJson->SetRootObject(NewObj);
|
||||||
|
|
||||||
|
OutArray.Add(NewJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
return OutArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
void USIOJsonObject::SetObjectArrayField(const FString& FieldName, const TArray<USIOJsonObject*>& ObjectArray)
|
||||||
|
{
|
||||||
|
if (!JsonObj.IsValid() || FieldName.IsEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray< TSharedPtr<FJsonValue> > EntriesArray;
|
||||||
|
for (auto Value : ObjectArray)
|
||||||
|
{
|
||||||
|
EntriesArray.Add(MakeShareable(new FJsonValueObject(Value->GetRootObject())));
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObj->SetArrayField(FieldName, EntriesArray);
|
||||||
|
}
|
@ -0,0 +1,396 @@
|
|||||||
|
// Modifications Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
|
||||||
|
// Copyright 2014 Vladimir Alyamkin. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "SIOJsonValue.h"
|
||||||
|
#include "SIOJConvert.h"
|
||||||
|
#include "SIOJsonObject.h"
|
||||||
|
#include "ISIOJson.h"
|
||||||
|
#include "Misc/Base64.h"
|
||||||
|
|
||||||
|
#if PLATFORM_WINDOWS
|
||||||
|
#pragma region FJsonValueBinary
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
TArray<uint8> FJsonValueBinary::AsBinary(const TSharedPtr<FJsonValue>& InJsonValue)
|
||||||
|
{
|
||||||
|
if (FJsonValueBinary::IsBinary(InJsonValue))
|
||||||
|
{
|
||||||
|
TSharedPtr<FJsonValueBinary> BinaryValue = StaticCastSharedPtr<FJsonValueBinary>(InJsonValue);
|
||||||
|
return BinaryValue->AsBinary();
|
||||||
|
}
|
||||||
|
else if (InJsonValue->Type == EJson::String)
|
||||||
|
{
|
||||||
|
//If we got a string that isn't detected as a binary via socket.io protocol hack
|
||||||
|
//then we need to decode this string as base 64
|
||||||
|
TArray<uint8> DecodedArray;
|
||||||
|
bool bDidDecodeCorrectly = FBase64::Decode(InJsonValue->AsString(), DecodedArray);
|
||||||
|
if (!bDidDecodeCorrectly)
|
||||||
|
{
|
||||||
|
UE_LOG(LogSIOJ, Warning, TEXT("FJsonValueBinary::AsBinary couldn't decode %s as a binary."), *InJsonValue->AsString());
|
||||||
|
}
|
||||||
|
return DecodedArray;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TArray<uint8> EmptyArray;
|
||||||
|
return EmptyArray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool FJsonValueBinary::IsBinary(const TSharedPtr<FJsonValue>& InJsonValue)
|
||||||
|
{
|
||||||
|
if (!InJsonValue.IsValid())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//use our hackery to determine if we got a binary string
|
||||||
|
bool IgnoreBool;
|
||||||
|
return !InJsonValue->TryGetBool(IgnoreBool);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if PLATFORM_WINDOWS
|
||||||
|
#pragma endregion FJsonValueBinary
|
||||||
|
#pragma region USIOJsonValue
|
||||||
|
#endif
|
||||||
|
|
||||||
|
USIOJsonValue::USIOJsonValue(const class FObjectInitializer& PCIP)
|
||||||
|
: Super(PCIP)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
USIOJsonValue* USIOJsonValue::ConstructJsonValueNumber(UObject* WorldContextObject, float Number)
|
||||||
|
{
|
||||||
|
TSharedPtr<FJsonValue> NewVal = MakeShareable(new FJsonValueNumber(Number));
|
||||||
|
|
||||||
|
USIOJsonValue* NewValue = NewObject<USIOJsonValue>();
|
||||||
|
NewValue->SetRootValue(NewVal);
|
||||||
|
|
||||||
|
return NewValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
USIOJsonValue* USIOJsonValue::ConstructJsonValueString(UObject* WorldContextObject, const FString& StringValue)
|
||||||
|
{
|
||||||
|
TSharedPtr<FJsonValue> NewVal = MakeShareable(new FJsonValueString(StringValue));
|
||||||
|
|
||||||
|
USIOJsonValue* NewValue = NewObject<USIOJsonValue>();
|
||||||
|
NewValue->SetRootValue(NewVal);
|
||||||
|
|
||||||
|
return NewValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
USIOJsonValue* USIOJsonValue::ConstructJsonValueBool(UObject* WorldContextObject, bool InValue)
|
||||||
|
{
|
||||||
|
TSharedPtr<FJsonValue> NewVal = MakeShareable(new FJsonValueBoolean(InValue));
|
||||||
|
|
||||||
|
USIOJsonValue* NewValue = NewObject<USIOJsonValue>();
|
||||||
|
NewValue->SetRootValue(NewVal);
|
||||||
|
|
||||||
|
return NewValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
USIOJsonValue* USIOJsonValue::ConstructJsonValueArray(UObject* WorldContextObject, const TArray<USIOJsonValue*>& InArray)
|
||||||
|
{
|
||||||
|
// Prepare data array to create new value
|
||||||
|
TArray< TSharedPtr<FJsonValue> > ValueArray;
|
||||||
|
for (auto InVal : InArray)
|
||||||
|
{
|
||||||
|
ValueArray.Add(InVal->GetRootValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedPtr<FJsonValue> NewVal = MakeShareable(new FJsonValueArray(ValueArray));
|
||||||
|
|
||||||
|
USIOJsonValue* NewValue = NewObject<USIOJsonValue>();
|
||||||
|
NewValue->SetRootValue(NewVal);
|
||||||
|
|
||||||
|
return NewValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
USIOJsonValue* USIOJsonValue::ConstructJsonValueObject(USIOJsonObject *JsonObject, UObject* WorldContextObject)
|
||||||
|
{
|
||||||
|
TSharedPtr<FJsonValue> NewVal = MakeShareable(new FJsonValueObject(JsonObject->GetRootObject()));
|
||||||
|
|
||||||
|
USIOJsonValue* NewValue = NewObject<USIOJsonValue>();
|
||||||
|
NewValue->SetRootValue(NewVal);
|
||||||
|
|
||||||
|
return NewValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
USIOJsonValue* USIOJsonValue::ConstructJsonValueBinary(UObject* WorldContextObject, TArray<uint8> ByteArray)
|
||||||
|
{
|
||||||
|
TSharedPtr<FJsonValue> NewVal = MakeShareable(new FJsonValueBinary(ByteArray));
|
||||||
|
|
||||||
|
USIOJsonValue* NewValue = NewObject<USIOJsonValue>();
|
||||||
|
NewValue->SetRootValue(NewVal);
|
||||||
|
|
||||||
|
return NewValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
USIOJsonValue* USIOJsonValue::ConstructJsonValue(UObject* WorldContextObject, const TSharedPtr<FJsonValue>& InValue)
|
||||||
|
{
|
||||||
|
TSharedPtr<FJsonValue> NewVal = InValue;
|
||||||
|
|
||||||
|
USIOJsonValue* NewValue = NewObject<USIOJsonValue>();
|
||||||
|
NewValue->SetRootValue(NewVal);
|
||||||
|
|
||||||
|
return NewValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
USIOJsonValue* USIOJsonValue::ValueFromJsonString(UObject* WorldContextObject, const FString& StringValue)
|
||||||
|
{
|
||||||
|
TSharedPtr<FJsonValue> NewVal = USIOJConvert::JsonStringToJsonValue(StringValue);
|
||||||
|
|
||||||
|
USIOJsonValue* NewValue = NewObject<USIOJsonValue>();
|
||||||
|
NewValue->SetRootValue(NewVal);
|
||||||
|
|
||||||
|
return NewValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedPtr<FJsonValue>& USIOJsonValue::GetRootValue()
|
||||||
|
{
|
||||||
|
return JsonVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void USIOJsonValue::SetRootValue(TSharedPtr<FJsonValue>& JsonValue)
|
||||||
|
{
|
||||||
|
JsonVal = JsonValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// FJsonValue API
|
||||||
|
|
||||||
|
ESIOJson::Type USIOJsonValue::GetType() const
|
||||||
|
{
|
||||||
|
if (!JsonVal.IsValid())
|
||||||
|
{
|
||||||
|
return ESIOJson::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (JsonVal->Type)
|
||||||
|
{
|
||||||
|
case EJson::None:
|
||||||
|
return ESIOJson::None;
|
||||||
|
|
||||||
|
case EJson::Null:
|
||||||
|
return ESIOJson::Null;
|
||||||
|
|
||||||
|
case EJson::String:
|
||||||
|
if (FJsonValueBinary::IsBinary(JsonVal))
|
||||||
|
{
|
||||||
|
return ESIOJson::Binary;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ESIOJson::String;
|
||||||
|
}
|
||||||
|
case EJson::Number:
|
||||||
|
return ESIOJson::Number;
|
||||||
|
|
||||||
|
case EJson::Boolean:
|
||||||
|
return ESIOJson::Boolean;
|
||||||
|
|
||||||
|
case EJson::Array:
|
||||||
|
return ESIOJson::Array;
|
||||||
|
|
||||||
|
case EJson::Object:
|
||||||
|
return ESIOJson::Object;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return ESIOJson::None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FString USIOJsonValue::GetTypeString() const
|
||||||
|
{
|
||||||
|
if (!JsonVal.IsValid())
|
||||||
|
{
|
||||||
|
return "None";
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (JsonVal->Type)
|
||||||
|
{
|
||||||
|
case EJson::None:
|
||||||
|
return TEXT("None");
|
||||||
|
|
||||||
|
case EJson::Null:
|
||||||
|
return TEXT("Null");
|
||||||
|
|
||||||
|
case EJson::String:
|
||||||
|
return TEXT("String");
|
||||||
|
|
||||||
|
case EJson::Number:
|
||||||
|
return TEXT("Number");
|
||||||
|
|
||||||
|
case EJson::Boolean:
|
||||||
|
return TEXT("Boolean");
|
||||||
|
|
||||||
|
case EJson::Array:
|
||||||
|
return TEXT("Array");
|
||||||
|
|
||||||
|
case EJson::Object:
|
||||||
|
return TEXT("Object");
|
||||||
|
|
||||||
|
default:
|
||||||
|
return TEXT("None");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool USIOJsonValue::IsNull() const
|
||||||
|
{
|
||||||
|
if (!JsonVal.IsValid())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonVal->IsNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
float USIOJsonValue::AsNumber() const
|
||||||
|
{
|
||||||
|
if (!JsonVal.IsValid())
|
||||||
|
{
|
||||||
|
ErrorMessage(TEXT("Number"));
|
||||||
|
return 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonVal->AsNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
FString USIOJsonValue::AsString() const
|
||||||
|
{
|
||||||
|
if (!JsonVal.IsValid())
|
||||||
|
{
|
||||||
|
ErrorMessage(TEXT("String"));
|
||||||
|
return FString();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Auto-convert non-strings instead of getting directly
|
||||||
|
if (JsonVal->Type != EJson::String)
|
||||||
|
{
|
||||||
|
return EncodeJson();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return JsonVal->AsString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool USIOJsonValue::AsBool() const
|
||||||
|
{
|
||||||
|
if (!JsonVal.IsValid())
|
||||||
|
{
|
||||||
|
ErrorMessage(TEXT("Boolean"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonVal->AsBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<USIOJsonValue*> USIOJsonValue::AsArray() const
|
||||||
|
{
|
||||||
|
TArray<USIOJsonValue*> OutArray;
|
||||||
|
|
||||||
|
if (!JsonVal.IsValid())
|
||||||
|
{
|
||||||
|
ErrorMessage(TEXT("Array"));
|
||||||
|
return OutArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray< TSharedPtr<FJsonValue> > ValArray = JsonVal->AsArray();
|
||||||
|
for (auto Value : ValArray)
|
||||||
|
{
|
||||||
|
USIOJsonValue* NewValue = NewObject<USIOJsonValue>();
|
||||||
|
NewValue->SetRootValue(Value);
|
||||||
|
|
||||||
|
OutArray.Add(NewValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return OutArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
USIOJsonObject* USIOJsonValue::AsObject()
|
||||||
|
{
|
||||||
|
if (!JsonVal.IsValid())
|
||||||
|
{
|
||||||
|
ErrorMessage(TEXT("Object"));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedPtr<FJsonObject> NewObj = JsonVal->AsObject();
|
||||||
|
|
||||||
|
USIOJsonObject* JsonObj = NewObject<USIOJsonObject>();
|
||||||
|
JsonObj->SetRootObject(NewObj);
|
||||||
|
|
||||||
|
return JsonObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TArray<uint8> USIOJsonValue::AsBinary()
|
||||||
|
{
|
||||||
|
if (!JsonVal.IsValid())
|
||||||
|
{
|
||||||
|
ErrorMessage(TEXT("Binary"));
|
||||||
|
TArray<uint8> ByteArray;
|
||||||
|
return ByteArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
//binary object pretending & starts with non-json format? it's our disguise binary
|
||||||
|
if (JsonVal->Type == EJson::String)
|
||||||
|
{
|
||||||
|
//it's a legit binary
|
||||||
|
if (FJsonValueBinary::IsBinary(JsonVal))
|
||||||
|
{
|
||||||
|
//Valid binary available
|
||||||
|
return FJsonValueBinary::AsBinary(JsonVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
//It's a string, decode as if hex encoded binary
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const FString& HexString = JsonVal->AsString();
|
||||||
|
|
||||||
|
TArray<uint8> ByteArray;
|
||||||
|
ByteArray.AddUninitialized(HexString.Len() / 2);
|
||||||
|
|
||||||
|
bool DidConvert = FString::ToHexBlob(HexString, ByteArray.GetData(), ByteArray.Num());
|
||||||
|
|
||||||
|
//Empty our array if conversion failed
|
||||||
|
if (!DidConvert)
|
||||||
|
{
|
||||||
|
ByteArray.Empty();
|
||||||
|
}
|
||||||
|
return ByteArray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Not a binary nor binary string, return empty array
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//Empty array
|
||||||
|
TArray<uint8> ByteArray;
|
||||||
|
return ByteArray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FString USIOJsonValue::EncodeJson() const
|
||||||
|
{
|
||||||
|
return USIOJConvert::ToJsonString(JsonVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Helpers
|
||||||
|
|
||||||
|
void USIOJsonValue::ErrorMessage(const FString& InType) const
|
||||||
|
{
|
||||||
|
UE_LOG(LogSIOJ, Error, TEXT("Json Value of type '%s' used as a '%s'."), *GetTypeString(), *InType);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if PLATFORM_WINDOWS
|
||||||
|
#pragma endregion USIOJsonValue
|
||||||
|
#endif
|
@ -0,0 +1,43 @@
|
|||||||
|
// Modifications Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
|
||||||
|
// Copyright 2014 Vladimir Alyamkin. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Runtime/Core/Public/Modules/ModuleManager.h"
|
||||||
|
|
||||||
|
DECLARE_LOG_CATEGORY_EXTERN(LogSIOJ, Log, All);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The public interface to this module. In most cases, this interface is only public to sibling modules
|
||||||
|
* within this plugin.
|
||||||
|
*/
|
||||||
|
class ISIOJson : 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 ISIOJson& Get()
|
||||||
|
{
|
||||||
|
return FModuleManager::LoadModuleChecked< ISIOJson >( "SIOJson" );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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( "SIOJson" );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,75 @@
|
|||||||
|
// Modifications Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "UObject/Package.h"
|
||||||
|
#include "UObject/ObjectMacros.h"
|
||||||
|
#include "Runtime/Json/Public/Dom/JsonObject.h"
|
||||||
|
#include "Runtime/Json/Public/Dom/JsonValue.h"
|
||||||
|
#include "SIOJConvert.generated.h"
|
||||||
|
|
||||||
|
struct FTrimmedKeyMap
|
||||||
|
{
|
||||||
|
FString LongKey;
|
||||||
|
TMap<FString, TSharedPtr<FTrimmedKeyMap>> SubMap;
|
||||||
|
|
||||||
|
FString ToString();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
UCLASS()
|
||||||
|
class SIOJSON_API USIOJConvert : public UObject
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
public:
|
||||||
|
|
||||||
|
//encode/decode json convenience wrappers
|
||||||
|
static FString ToJsonString(const TSharedPtr<FJsonObject>& JsonObject);
|
||||||
|
static FString ToJsonString(const TSharedPtr<FJsonValue>& JsonValue);
|
||||||
|
static FString ToJsonString(const TArray<TSharedPtr<FJsonValue>>& JsonValueArray);
|
||||||
|
|
||||||
|
static TSharedPtr<FJsonObject> ToJsonObject(const FString& JsonString);
|
||||||
|
static TSharedPtr<FJsonObject> MakeJsonObject();
|
||||||
|
|
||||||
|
|
||||||
|
//Structs
|
||||||
|
|
||||||
|
//Will trim names if specified as blueprint
|
||||||
|
static TSharedPtr<FJsonObject> ToJsonObject(UStruct* Struct, void* StructPtr, bool IsBlueprintStruct = false, bool BinaryStructCppSupport = false);
|
||||||
|
|
||||||
|
//Expects a JsonObject, if blueprint struct it will lengthen the names to fill properly
|
||||||
|
static bool JsonObjectToUStruct(TSharedPtr<FJsonObject> JsonObject, UStruct* Struct, void* StructPtr, bool IsBlueprintStruct = false, bool BinaryStructCppSupport = false);
|
||||||
|
|
||||||
|
//Files - convenience read/write files
|
||||||
|
static bool JsonFileToUStruct(const FString& FilePath, UStruct* Struct, void* StructPtr, bool IsBlueprintStruct = false);
|
||||||
|
static bool ToJsonFile(const FString& FilePath, UStruct* Struct, void* StructPtr, bool IsBlueprintStruct = false);
|
||||||
|
|
||||||
|
|
||||||
|
//typically from callbacks
|
||||||
|
static class USIOJsonValue* ToSIOJsonValue(const TArray<TSharedPtr<FJsonValue>>& JsonValueArray);
|
||||||
|
|
||||||
|
//Convenience overrides for JsonValues
|
||||||
|
static TSharedPtr<FJsonValue> ToJsonValue(const TSharedPtr<FJsonObject>& JsonObject);
|
||||||
|
static TSharedPtr<FJsonValue> ToJsonValue(const FString& StringValue);
|
||||||
|
static TSharedPtr<FJsonValue> ToJsonValue(double NumberValue);
|
||||||
|
static TSharedPtr<FJsonValue> ToJsonValue(bool BoolValue);
|
||||||
|
static TSharedPtr<FJsonValue> ToJsonValue(const TArray<uint8>& BinaryValue);
|
||||||
|
static TSharedPtr<FJsonValue> ToJsonValue(const TArray<TSharedPtr<FJsonValue>>& ArrayValue);
|
||||||
|
|
||||||
|
static TSharedPtr<FJsonValue> JsonStringToJsonValue(const FString& JsonString);
|
||||||
|
static TArray<TSharedPtr<FJsonValue>> JsonStringToJsonArray(const FString& JsonString);
|
||||||
|
|
||||||
|
|
||||||
|
//internal utility, exposed for modularity
|
||||||
|
static void TrimValueKeyNames(const TSharedPtr<FJsonValue>& JsonValue);
|
||||||
|
static bool TrimKey(const FString& InLongKey, FString& OutTrimmedKey);
|
||||||
|
static void SetTrimmedKeyMapForStruct(TSharedPtr<FTrimmedKeyMap>& InMap, UStruct* Struct);
|
||||||
|
static void SetTrimmedKeyMapForProp(TSharedPtr<FTrimmedKeyMap>& InMap, FProperty* ArrayInnerProp);
|
||||||
|
static void ReplaceJsonValueNamesWithMap(TSharedPtr<FJsonValue>& InValue, TSharedPtr<FTrimmedKeyMap> KeyMap);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
@ -0,0 +1,301 @@
|
|||||||
|
// Modifications Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
|
||||||
|
// Copyright 2016 Vladimir Alyamkin. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||||
|
|
||||||
|
#include "SIOJTypes.h"
|
||||||
|
#include "SIOJConvert.h"
|
||||||
|
#include "SIOJsonObject.h"
|
||||||
|
#include "SIOJRequestJSON.h"
|
||||||
|
#include "SIOJsonValue.h"
|
||||||
|
#include "SIOJLibrary.generated.h"
|
||||||
|
|
||||||
|
DECLARE_DYNAMIC_DELEGATE_OneParam(FSIOJCallDelegate, USIOJRequestJSON*, Request);
|
||||||
|
|
||||||
|
USTRUCT()
|
||||||
|
struct FSIOJCallResponse
|
||||||
|
{
|
||||||
|
GENERATED_USTRUCT_BODY()
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
USIOJRequestJSON* Request;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
UObject* WorldContextObject;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
FSIOJCallDelegate Callback;
|
||||||
|
|
||||||
|
FDelegateHandle CompleteDelegateHandle;
|
||||||
|
FDelegateHandle FailDelegateHandle;
|
||||||
|
|
||||||
|
FSIOJCallResponse()
|
||||||
|
: Request(nullptr)
|
||||||
|
, WorldContextObject(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Usefull tools for REST communications
|
||||||
|
*/
|
||||||
|
UCLASS()
|
||||||
|
class SIOJSON_API USIOJLibrary : public UBlueprintFunctionLibrary
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Helpers
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** Applies percent-encoding to text */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Utility")
|
||||||
|
static FString PercentEncode(const FString& Source);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a FString into a Base64 string
|
||||||
|
*
|
||||||
|
* @param Source The string data to convert
|
||||||
|
* @return A string that encodes the binary data in a way that can be safely transmitted via various Internet protocols
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintPure, Category = "SIOJ|Utility", meta = (DisplayName = "Base64 Encode (String)"))
|
||||||
|
static FString Base64Encode(const FString& Source);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a Byte array into a Base64 string
|
||||||
|
*
|
||||||
|
* @param Source The string data to convert
|
||||||
|
* @return A string that encodes the binary data in a way that can be safely transmitted via various Internet protocols
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintPure, Category = "SIOJ|Utility", meta = (DisplayName = "Base64 Encode (Bytes)"))
|
||||||
|
static FString Base64EncodeBytes(const TArray<uint8>& Source);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a Base64 string into a FString
|
||||||
|
*
|
||||||
|
* @param Source The stringified data to convert
|
||||||
|
* @param Dest The out buffer that will be filled with the decoded data
|
||||||
|
* @return True if the buffer was decoded, false if it failed to decode
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintPure, Category = "SIOJ|Utility", meta = (DisplayName = "Base64 Decode (To String)"))
|
||||||
|
static bool Base64Decode(const FString& Source, FString& Dest);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a Base64 string into a Byte array
|
||||||
|
*
|
||||||
|
* @param Source The stringified data to convert
|
||||||
|
* @param Dest The out buffer that will be filled with the decoded data
|
||||||
|
* @return True if the buffer was decoded, false if it failed to decode
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintPure, Category = "SIOJ|Utility", meta = (DisplayName = "Base64 Decode (To Bytes)"))
|
||||||
|
static bool Base64DecodeBytes(const FString& Source, TArray<uint8>& Dest);
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Easy URL processing
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a json string into an array of stringified json Values
|
||||||
|
*
|
||||||
|
* @param JsonString Input stringified json
|
||||||
|
* @param OutJsonValueArray The decoded Array of JsonValue
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintPure, Category = "SIOJ|Utility")
|
||||||
|
static bool StringToJsonValueArray(const FString& JsonString, TArray<USIOJsonValue*>& OutJsonValueArray);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the reflection system to convert an unreal struct into a JsonObject
|
||||||
|
*
|
||||||
|
* @param AnyStruct The struct you wish to convert
|
||||||
|
* @return Converted Json Object
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintPure, Category = "SocketIOFunctions", CustomThunk, meta = (CustomStructureParam = "AnyStruct"))
|
||||||
|
static USIOJsonObject* StructToJsonObject(TFieldPath<FProperty> AnyStruct);
|
||||||
|
|
||||||
|
//Convert property into c++ accessible form
|
||||||
|
DECLARE_FUNCTION(execStructToJsonObject)
|
||||||
|
{
|
||||||
|
//Get properties and pointers from stack
|
||||||
|
Stack.Step(Stack.Object, NULL);
|
||||||
|
FStructProperty* StructProperty = CastField<FStructProperty>(Stack.MostRecentProperty);
|
||||||
|
void* StructPtr = Stack.MostRecentPropertyAddress;
|
||||||
|
|
||||||
|
// We need this to wrap up the stack
|
||||||
|
P_FINISH;
|
||||||
|
|
||||||
|
auto BPJsonObject = NewObject<USIOJsonObject>();
|
||||||
|
|
||||||
|
auto JsonObject = USIOJConvert::ToJsonObject(StructProperty->Struct, StructPtr, true);
|
||||||
|
BPJsonObject->SetRootObject(JsonObject);
|
||||||
|
|
||||||
|
*(USIOJsonObject**)RESULT_PARAM = BPJsonObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintPure, CustomThunk, meta = (DisplayName = "To JsonValue (Struct)", BlueprintAutocast, CustomStructureParam = "AnyStruct"), Category = "Utilities|SocketIO")
|
||||||
|
static USIOJsonValue* StructToJsonValue(TFieldPath<FProperty> AnyStruct);
|
||||||
|
|
||||||
|
DECLARE_FUNCTION(execStructToJsonValue)
|
||||||
|
{
|
||||||
|
//Get properties and pointers from stack
|
||||||
|
Stack.Step(Stack.Object, NULL);
|
||||||
|
FStructProperty* StructProperty = CastField<FStructProperty>(Stack.MostRecentProperty);
|
||||||
|
void* StructPtr = Stack.MostRecentPropertyAddress;
|
||||||
|
|
||||||
|
// We need this to wrap up the stack
|
||||||
|
P_FINISH;
|
||||||
|
|
||||||
|
auto JsonObject = USIOJConvert::ToJsonObject(StructProperty->Struct, StructPtr, true);
|
||||||
|
|
||||||
|
TSharedPtr<FJsonValue> JsonValue = MakeShareable(new FJsonValueObject(JsonObject));
|
||||||
|
USIOJsonValue* BPJsonValue = NewObject<USIOJsonValue>();
|
||||||
|
BPJsonValue->SetRootValue(JsonValue);
|
||||||
|
|
||||||
|
*(USIOJsonValue**)RESULT_PARAM = BPJsonValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the reflection system to fill an unreal struct from data defined in JsonObject.
|
||||||
|
*
|
||||||
|
* @param JsonObject The source JsonObject for properties to fill
|
||||||
|
* @param AnyStruct The struct you wish to fill
|
||||||
|
* @return Whether all properties filled correctly
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SocketIOFunctions", CustomThunk, meta = (CustomStructureParam = "AnyStruct"))
|
||||||
|
static bool JsonObjectToStruct(USIOJsonObject* JsonObject, TFieldPath<FProperty> AnyStruct);
|
||||||
|
|
||||||
|
DECLARE_FUNCTION(execJsonObjectToStruct)
|
||||||
|
{
|
||||||
|
//Get properties and pointers from stack
|
||||||
|
P_GET_OBJECT(USIOJsonObject, JsonObject);
|
||||||
|
|
||||||
|
Stack.Step(Stack.Object, NULL);
|
||||||
|
FStructProperty* StructProperty = CastField<FStructProperty>(Stack.MostRecentProperty);
|
||||||
|
void* StructPtr = Stack.MostRecentPropertyAddress;
|
||||||
|
|
||||||
|
P_FINISH;
|
||||||
|
|
||||||
|
//Pass in the reference to the json object
|
||||||
|
bool Success = USIOJConvert::JsonObjectToUStruct(JsonObject->GetRootObject(), StructProperty->Struct, StructPtr, true);
|
||||||
|
|
||||||
|
*(bool*)RESULT_PARAM = Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Convenience - Saving/Loading structs from files
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SocketIOFunctions", CustomThunk, meta = (CustomStructureParam = "AnyStruct"))
|
||||||
|
static bool SaveStructToJsonFile(TFieldPath<FProperty> AnyStruct, const FString& FilePath);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SocketIOFunctions", CustomThunk, meta = (CustomStructureParam = "AnyStruct"))
|
||||||
|
static bool LoadJsonFileToStruct(const FString& FilePath, TFieldPath<FProperty> AnyStruct);
|
||||||
|
|
||||||
|
//custom thunk needed to handle wildcard structs
|
||||||
|
DECLARE_FUNCTION(execSaveStructToJsonFile)
|
||||||
|
{
|
||||||
|
Stack.StepCompiledIn<FStructProperty>(NULL);
|
||||||
|
FStructProperty* StructProp = CastField<FStructProperty>(Stack.MostRecentProperty);
|
||||||
|
void* StructPtr = Stack.MostRecentPropertyAddress;
|
||||||
|
|
||||||
|
FString FilePath;
|
||||||
|
Stack.StepCompiledIn<FStrProperty>(&FilePath);
|
||||||
|
P_FINISH;
|
||||||
|
|
||||||
|
P_NATIVE_BEGIN;
|
||||||
|
*(bool*)RESULT_PARAM = USIOJConvert::ToJsonFile(FilePath, StructProp->Struct, StructPtr);
|
||||||
|
P_NATIVE_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
//custom thunk needed to handle wildcard structs
|
||||||
|
DECLARE_FUNCTION(execLoadJsonFileToStruct)
|
||||||
|
{
|
||||||
|
FString FilePath;
|
||||||
|
Stack.StepCompiledIn<FStrProperty>(&FilePath);
|
||||||
|
Stack.StepCompiledIn<FStructProperty>(NULL);
|
||||||
|
FStructProperty* StructProp = CastField<FStructProperty>(Stack.MostRecentProperty);
|
||||||
|
void* StructPtr = Stack.MostRecentPropertyAddress;
|
||||||
|
P_FINISH;
|
||||||
|
|
||||||
|
P_NATIVE_BEGIN;
|
||||||
|
*(bool*)RESULT_PARAM = USIOJConvert::JsonFileToUStruct(FilePath, StructProp->Struct, StructPtr, true);
|
||||||
|
P_NATIVE_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Conversion Nodes - comments added for blueprint hover with compact nodes
|
||||||
|
|
||||||
|
//To JsonValue (Array)
|
||||||
|
UFUNCTION(BlueprintPure, meta = (DisplayName = "To JsonValue (Array)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Utilities|SocketIO")
|
||||||
|
static USIOJsonValue* Conv_ArrayToJsonValue(const TArray<USIOJsonValue*>& InArray);
|
||||||
|
|
||||||
|
//To JsonValue (JsonObject)
|
||||||
|
UFUNCTION(BlueprintPure, meta = (DisplayName = "To JsonValue (JsonObject)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Utilities|SocketIO")
|
||||||
|
static USIOJsonValue* Conv_JsonObjectToJsonValue(USIOJsonObject* InObject);
|
||||||
|
|
||||||
|
//To JsonValue (Bytes)
|
||||||
|
UFUNCTION(BlueprintPure, meta = (DisplayName = "To JsonValue (Bytes)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Utilities|SocketIO")
|
||||||
|
static USIOJsonValue* Conv_BytesToJsonValue(const TArray<uint8>& InBytes);
|
||||||
|
|
||||||
|
//To JsonValue (String)
|
||||||
|
UFUNCTION(BlueprintPure, meta = (DisplayName = "To JsonValue (String)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Utilities|SocketIO")
|
||||||
|
static USIOJsonValue* Conv_StringToJsonValue(const FString& InString);
|
||||||
|
|
||||||
|
//To JsonValue (Integer)
|
||||||
|
UFUNCTION(BlueprintPure, meta = (DisplayName = "To JsonValue (Integer)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Utilities|SocketIO")
|
||||||
|
static USIOJsonValue* Conv_IntToJsonValue(int32 InInt);
|
||||||
|
|
||||||
|
//To JsonValue (Float)
|
||||||
|
UFUNCTION(BlueprintPure, meta = (DisplayName = "To JsonValue (Float)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Utilities|SocketIO")
|
||||||
|
static USIOJsonValue* Conv_FloatToJsonValue(float InFloat);
|
||||||
|
|
||||||
|
//To JsonValue (Bool)
|
||||||
|
UFUNCTION(BlueprintPure, meta = (DisplayName = "To JsonValue (Bool)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Utilities|SocketIO")
|
||||||
|
static USIOJsonValue* Conv_BoolToJsonValue(bool InBool);
|
||||||
|
|
||||||
|
//To String (JsonValue) - doesn't autocast due to get display name
|
||||||
|
UFUNCTION(BlueprintPure, meta = (DisplayName = "To String (JsonValue)", BlueprintAutocast), Category = "Utilities|SocketIO")
|
||||||
|
static FString Conv_SIOJsonValueToString(class USIOJsonValue* InValue);
|
||||||
|
|
||||||
|
//To Integer (JsonValue)
|
||||||
|
UFUNCTION(BlueprintPure, meta = (DisplayName = "To Integer (JsonValue)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Utilities|SocketIO")
|
||||||
|
static int32 Conv_JsonValueToInt(class USIOJsonValue* InValue);
|
||||||
|
|
||||||
|
//To Float (JsonValue)
|
||||||
|
UFUNCTION(BlueprintPure, meta = (DisplayName = "To Float (JsonValue)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Utilities|SocketIO")
|
||||||
|
static float Conv_JsonValueToFloat(class USIOJsonValue* InValue);
|
||||||
|
|
||||||
|
//To Bool (JsonValue)
|
||||||
|
UFUNCTION(BlueprintPure, meta = (DisplayName = "To Bool (JsonValue)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Utilities|SocketIO")
|
||||||
|
static bool Conv_JsonValueToBool(class USIOJsonValue* InValue);
|
||||||
|
|
||||||
|
//To Bytes (JsonValue)
|
||||||
|
UFUNCTION(BlueprintPure, meta = (DisplayName = "To Bytes (JsonValue)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Utilities|SocketIO")
|
||||||
|
static TArray<uint8> Conv_JsonValueToBytes(class USIOJsonValue* InValue);
|
||||||
|
|
||||||
|
//To String (JsonObject) - doesn't autocast due to get display name
|
||||||
|
UFUNCTION(BlueprintPure, meta = (DisplayName = "To String (JsonObject)", BlueprintAutocast), Category = "Utilities|SocketIO")
|
||||||
|
static FString Conv_SIOJsonObjectToString(class USIOJsonObject* InObject);
|
||||||
|
|
||||||
|
//To Object (JsonValue)
|
||||||
|
UFUNCTION(BlueprintPure, meta = (DisplayName = "To Object (JsonValue)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Utilities|SocketIO")
|
||||||
|
static USIOJsonObject* Conv_JsonValueToJsonObject(class USIOJsonValue* InValue);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** Easy way to process http requests */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Utility", meta = (WorldContext = "WorldContextObject"))
|
||||||
|
static void CallURL(UObject* WorldContextObject, const FString& URL, ESIORequestVerb Verb, ESIORequestContentType ContentType, USIOJsonObject* SIOJJson, const FSIOJCallDelegate& Callback);
|
||||||
|
|
||||||
|
/** Easy way to fetch resources using get */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Utility", meta = (Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject"))
|
||||||
|
static void GetURLBinary(UObject* WorldContextObject, const FString& URL, ESIORequestVerb Verb, ESIORequestContentType ContentType, TArray<uint8>& OutResultData, struct FLatentActionInfo LatentInfo);
|
||||||
|
|
||||||
|
/** Called when URL is processed (one for both success/unsuccess events)*/
|
||||||
|
static void OnCallComplete(USIOJRequestJSON* Request);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static TMap<USIOJRequestJSON*, FSIOJCallResponse> RequestMap;
|
||||||
|
|
||||||
|
};
|
@ -0,0 +1,316 @@
|
|||||||
|
// Modifications Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
|
||||||
|
// Copyright 2014 Vladimir Alyamkin. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Delegates/Delegate.h"
|
||||||
|
#include "Http.h"
|
||||||
|
#include "Runtime/Core/Public/Containers/Map.h"
|
||||||
|
#include "Runtime/Json/Public/Dom/JsonObject.h"
|
||||||
|
#include "Runtime/Json/Public/Dom/JsonValue.h"
|
||||||
|
#include "LatentActions.h"
|
||||||
|
#include "CULambdaRunnable.h"
|
||||||
|
#include "Engine/LatentActionManager.h"
|
||||||
|
#include "SIOJTypes.h"
|
||||||
|
#include "SIOJRequestJSON.generated.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Original latent action class by https://github.com/unktomi
|
||||||
|
*/
|
||||||
|
template <class T> class SIOJSON_API FSIOJLatentAction : public FPendingLatentAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void Call(const T &Value)
|
||||||
|
{
|
||||||
|
Result = Value;
|
||||||
|
Called = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const T &Value)
|
||||||
|
{
|
||||||
|
Call(Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cancel();
|
||||||
|
|
||||||
|
FSIOJLatentAction(FWeakObjectPtr RequestObj, T& ResultParam, const FLatentActionInfo& LatentInfo) :
|
||||||
|
Called(false),
|
||||||
|
Request(RequestObj),
|
||||||
|
ExecutionFunction(LatentInfo.ExecutionFunction),
|
||||||
|
OutputLink(LatentInfo.Linkage),
|
||||||
|
CallbackTarget(LatentInfo.CallbackTarget),
|
||||||
|
Result(ResultParam)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void UpdateOperation(FLatentResponse& Response) override
|
||||||
|
{
|
||||||
|
Response.FinishAndTriggerIf(Called, ExecutionFunction, OutputLink, CallbackTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void NotifyObjectDestroyed()
|
||||||
|
{
|
||||||
|
Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void NotifyActionAborted()
|
||||||
|
{
|
||||||
|
Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool Called;
|
||||||
|
FWeakObjectPtr Request;
|
||||||
|
|
||||||
|
public:
|
||||||
|
const FName ExecutionFunction;
|
||||||
|
const int32 OutputLink;
|
||||||
|
const FWeakObjectPtr CallbackTarget;
|
||||||
|
T &Result;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** Generate a delegates for callback events */
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRequestComplete, class USIOJRequestJSON*, Request);
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRequestFail, class USIOJRequestJSON*, Request);
|
||||||
|
|
||||||
|
DECLARE_MULTICAST_DELEGATE_OneParam(FOnStaticRequestComplete, class USIOJRequestJSON*);
|
||||||
|
DECLARE_MULTICAST_DELEGATE_OneParam(FOnStaticRequestFail, class USIOJRequestJSON*);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General helper class http requests via blueprints
|
||||||
|
*/
|
||||||
|
UCLASS(BlueprintType, Blueprintable)
|
||||||
|
class SIOJSON_API USIOJRequestJSON : public UObject
|
||||||
|
{
|
||||||
|
GENERATED_UCLASS_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Construction
|
||||||
|
|
||||||
|
/** Creates new request (totally empty) */
|
||||||
|
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Construct Json Request (Empty)", HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"), Category = "SIOJ|Request")
|
||||||
|
static USIOJRequestJSON* ConstructRequest(UObject* WorldContextObject);
|
||||||
|
|
||||||
|
/** Creates new request with defined verb and content type */
|
||||||
|
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Construct Json Request", HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"), Category = "SIOJ|Request")
|
||||||
|
static USIOJRequestJSON* ConstructRequestExt(UObject* WorldContextObject, ESIORequestVerb Verb, ESIORequestContentType ContentType);
|
||||||
|
|
||||||
|
/** Set verb to the request */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Request")
|
||||||
|
void SetVerb(ESIORequestVerb Verb);
|
||||||
|
|
||||||
|
/** Set custom verb to the request */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Request")
|
||||||
|
void SetCustomVerb(FString Verb);
|
||||||
|
|
||||||
|
/** Set content type to the request. If you're using the x-www-form-urlencoded,
|
||||||
|
* params/constaints should be defined as key=ValueString pairs from Json data */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Request")
|
||||||
|
void SetContentType(ESIORequestContentType ContentType);
|
||||||
|
|
||||||
|
/** Set content type of the request for binary post data */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Request")
|
||||||
|
void SetBinaryContentType(const FString &ContentType);
|
||||||
|
|
||||||
|
/** Set content of the request for binary post data */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Request")
|
||||||
|
void SetBinaryRequestContent(const TArray<uint8> &Content);
|
||||||
|
|
||||||
|
/** Sets optional header info */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Request")
|
||||||
|
void SetHeader(const FString& HeaderName, const FString& HeaderValue);
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Destruction and reset
|
||||||
|
|
||||||
|
/** Reset all internal saved data */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Utility")
|
||||||
|
void ResetData();
|
||||||
|
|
||||||
|
/** Reset saved request data */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Request")
|
||||||
|
void ResetRequestData();
|
||||||
|
|
||||||
|
/** Reset saved response data */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Response")
|
||||||
|
void ResetResponseData();
|
||||||
|
|
||||||
|
/** Cancel latent response waiting */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Response")
|
||||||
|
void Cancel();
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// JSON data accessors
|
||||||
|
|
||||||
|
/** Get the Request Json object */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Request")
|
||||||
|
USIOJsonObject* GetRequestObject();
|
||||||
|
|
||||||
|
/** Set the Request Json object */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Request")
|
||||||
|
void SetRequestObject(USIOJsonObject* JsonObject);
|
||||||
|
|
||||||
|
/** Get the Response Json object */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Response")
|
||||||
|
USIOJsonObject* GetResponseObject();
|
||||||
|
|
||||||
|
/** Set the Response Json object */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Response")
|
||||||
|
void SetResponseObject(USIOJsonObject* JsonObject);
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Request/response data access
|
||||||
|
|
||||||
|
/** Get url of http request */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Request")
|
||||||
|
FString GetURL();
|
||||||
|
|
||||||
|
/** Get status of http request */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Request")
|
||||||
|
ESIORequestStatus GetStatus();
|
||||||
|
|
||||||
|
/** Get the response code of the last query */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Response")
|
||||||
|
int32 GetResponseCode();
|
||||||
|
|
||||||
|
/** Get value of desired response header */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Response")
|
||||||
|
FString GetResponseHeader(const FString HeaderName);
|
||||||
|
|
||||||
|
/** Get list of all response headers */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Response")
|
||||||
|
TArray<FString> GetAllResponseHeaders();
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// URL processing
|
||||||
|
|
||||||
|
/** Open URL with current setup */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Request")
|
||||||
|
virtual void ProcessURL(const FString& Url = TEXT("http://alyamkin.com"));
|
||||||
|
|
||||||
|
/** Open URL in latent mode */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Request", meta = (Latent, LatentInfo = "LatentInfo", HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
|
||||||
|
virtual void ApplyURL(const FString& Url, USIOJsonObject *&Result, UObject* WorldContextObject, struct FLatentActionInfo LatentInfo);
|
||||||
|
|
||||||
|
/** Apply current internal setup to request and process it */
|
||||||
|
void ProcessRequest();
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Request callbacks
|
||||||
|
|
||||||
|
private:
|
||||||
|
/** Internal bind function for the IHTTPRequest::OnProcessRequestCompleted() event */
|
||||||
|
void OnProcessRequestComplete(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
|
||||||
|
|
||||||
|
void OnProcessRequestCompleteBinaryResult(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** Event occured when the request has been completed */
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "SIOJ|Event")
|
||||||
|
FOnRequestComplete OnRequestComplete;
|
||||||
|
|
||||||
|
/** Event occured when the request wasn't successfull */
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "SIOJ|Event")
|
||||||
|
FOnRequestFail OnRequestFail;
|
||||||
|
|
||||||
|
/** Event occured when the request has been completed */
|
||||||
|
FOnStaticRequestComplete OnStaticRequestComplete;
|
||||||
|
|
||||||
|
/** Event occured when the request wasn't successfull */
|
||||||
|
FOnStaticRequestFail OnStaticRequestFail;
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Tags
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** Add tag to this request */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Utility")
|
||||||
|
void AddTag(FName Tag);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove tag from this request
|
||||||
|
*
|
||||||
|
* @return Number of removed elements
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Utility")
|
||||||
|
int32 RemoveTag(FName Tag);
|
||||||
|
|
||||||
|
/** See if this request contains the supplied tag */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Utility")
|
||||||
|
bool HasTag(FName Tag) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** Array of tags that can be used for grouping and categorizing */
|
||||||
|
TArray<FName> Tags;
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Data
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** Request response stored as a string */
|
||||||
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "SIOJ|Response")
|
||||||
|
FString ResponseContent;
|
||||||
|
|
||||||
|
/** Is the response valid JSON? */
|
||||||
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "SIOJ|Response")
|
||||||
|
bool bIsValidJsonResponse;
|
||||||
|
|
||||||
|
/** If this is true it will call back on the binary callback instead of json */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SIOJ|Response")
|
||||||
|
bool bShouldHaveBinaryResponse;
|
||||||
|
|
||||||
|
TFunction<void(TArray<uint8>&)> OnProcessURLCompleteCallback;
|
||||||
|
|
||||||
|
TArray<uint8> ResultBinaryData;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** Latent action helper */
|
||||||
|
FSIOJLatentAction<USIOJsonObject*>* ContinueAction;
|
||||||
|
|
||||||
|
/** Internal request data stored as JSON */
|
||||||
|
UPROPERTY()
|
||||||
|
USIOJsonObject* RequestJsonObj;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
TArray<uint8> RequestBytes;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
FString BinaryContentType;
|
||||||
|
|
||||||
|
/** Response data stored as JSON */
|
||||||
|
UPROPERTY()
|
||||||
|
USIOJsonObject* ResponseJsonObj;
|
||||||
|
|
||||||
|
/** Verb for making request (GET,POST,etc) */
|
||||||
|
ESIORequestVerb RequestVerb;
|
||||||
|
|
||||||
|
/** Content type to be applied for request */
|
||||||
|
ESIORequestContentType RequestContentType;
|
||||||
|
|
||||||
|
/** Mapping of header section to values. Used to generate final header string for request */
|
||||||
|
TMap<FString, FString> RequestHeaders;
|
||||||
|
|
||||||
|
/** Cached key/value header pairs. Parsed once request completes */
|
||||||
|
TMap<FString, FString> ResponseHeaders;
|
||||||
|
|
||||||
|
/** Http Response code */
|
||||||
|
int32 ResponseCode;
|
||||||
|
|
||||||
|
/** Custom verb that will be used with RequestContentType == CUSTOM */
|
||||||
|
FString CustomVerb;
|
||||||
|
|
||||||
|
/** Request we're currently processing */
|
||||||
|
FHttpRequestRef HttpRequest = FHttpModule::Get().CreateRequest();
|
||||||
|
|
||||||
|
};
|
@ -0,0 +1,44 @@
|
|||||||
|
// Modifications Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
|
||||||
|
// Copyright 2016 Vladimir Alyamkin. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/** Verb (GET, PUT, POST) used by the request */
|
||||||
|
UENUM(BlueprintType)
|
||||||
|
enum class ESIORequestVerb : uint8
|
||||||
|
{
|
||||||
|
GET,
|
||||||
|
POST,
|
||||||
|
PUT,
|
||||||
|
DEL UMETA(DisplayName = "DELETE"),
|
||||||
|
/** Set CUSTOM verb by SetCustomVerb() function */
|
||||||
|
CUSTOM
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Content type (json, urlencoded, etc.) used by the request */
|
||||||
|
UENUM(BlueprintType)
|
||||||
|
enum class ESIORequestContentType : uint8
|
||||||
|
{
|
||||||
|
x_www_form_urlencoded_url UMETA(DisplayName = "x-www-form-urlencoded (URL)"),
|
||||||
|
x_www_form_urlencoded_body UMETA(DisplayName = "x-www-form-urlencoded (Request Body)"),
|
||||||
|
json,
|
||||||
|
binary
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Enumerates the current state of an Http request */
|
||||||
|
UENUM(BlueprintType)
|
||||||
|
enum class ESIORequestStatus : uint8
|
||||||
|
{
|
||||||
|
/** Has not been started via ProcessRequest() */
|
||||||
|
NotStarted,
|
||||||
|
/** Currently being ticked and processed */
|
||||||
|
Processing,
|
||||||
|
/** Finished but failed */
|
||||||
|
Failed,
|
||||||
|
/** Failed because it was unable to connect (safe to retry) */
|
||||||
|
Failed_ConnectionError,
|
||||||
|
/** Finished and was successful */
|
||||||
|
Succeeded
|
||||||
|
};
|
@ -0,0 +1,180 @@
|
|||||||
|
// Modifications Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
|
||||||
|
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
|
||||||
|
// Copyright 2014 Vladimir Alyamkin. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Runtime/Json/Public/Dom/JsonObject.h"
|
||||||
|
#include "SIOJsonObject.generated.h"
|
||||||
|
|
||||||
|
class USIOJsonValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blueprintable FJsonObject wrapper
|
||||||
|
*/
|
||||||
|
UCLASS(BlueprintType, Blueprintable)
|
||||||
|
class SIOJSON_API USIOJsonObject : public UObject
|
||||||
|
{
|
||||||
|
GENERATED_UCLASS_BODY()
|
||||||
|
|
||||||
|
/** Create new Json object, cannot be pure */
|
||||||
|
UFUNCTION(BlueprintCallable , meta = (DisplayName = "Construct Json Object", KeyWords = "create make", HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"), Category = "SIOJ|Json")
|
||||||
|
static USIOJsonObject* ConstructJsonObject(UObject* WorldContextObject);
|
||||||
|
|
||||||
|
/** Reset all internal data */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
void Reset();
|
||||||
|
|
||||||
|
/** Get the root Json object */
|
||||||
|
TSharedPtr<FJsonObject>& GetRootObject();
|
||||||
|
|
||||||
|
/** Set the root Json object */
|
||||||
|
void SetRootObject(const TSharedPtr<FJsonObject>& JsonObject);
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Serialization
|
||||||
|
|
||||||
|
/** Serialize Json to string (formatted with line breaks) */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
FString EncodeJson() const;
|
||||||
|
|
||||||
|
/** Serialize Json to string (single string without line breaks) */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
FString EncodeJsonToSingleString() const;
|
||||||
|
|
||||||
|
/** Construct Json object from string */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
bool DecodeJson(const FString& JsonString);
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// FJsonObject API
|
||||||
|
|
||||||
|
/** Returns a list of field names that exist in the object */
|
||||||
|
UFUNCTION(BlueprintPure, Category = "SIOJ|Json")
|
||||||
|
TArray<FString> GetFieldNames();
|
||||||
|
|
||||||
|
/** Checks to see if the FieldName exists in the object */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
bool HasField(const FString& FieldName) const;
|
||||||
|
|
||||||
|
/** Remove field named FieldName */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
void RemoveField(const FString& FieldName);
|
||||||
|
|
||||||
|
/** Get the field named FieldName as a JsonValue */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
USIOJsonValue* GetField(const FString& FieldName) const;
|
||||||
|
|
||||||
|
/** Add a field named FieldName with a Value */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
void SetField(const FString& FieldName, USIOJsonValue* JsonValue);
|
||||||
|
|
||||||
|
/** Get the field named FieldName as a Json Array */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
TArray<USIOJsonValue*> GetArrayField(const FString& FieldName);
|
||||||
|
|
||||||
|
/** Set an ObjectField named FieldName and value of Json Array */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
void SetArrayField(const FString& FieldName, const TArray<USIOJsonValue*>& InArray);
|
||||||
|
|
||||||
|
/** Adds all of the fields from one json object to this one */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
void MergeJsonObject(USIOJsonObject* InJsonObject, bool Overwrite);
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// FJsonObject API Helpers (easy to use with simple Json objects)
|
||||||
|
|
||||||
|
/** Get the field named FieldName as a number. Ensures that the field is present and is of type Json number.
|
||||||
|
* Attn.!! float used instead of double to make the function blueprintable! */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
float GetNumberField(const FString& FieldName) const;
|
||||||
|
|
||||||
|
/** Add a field named FieldName with Number as value
|
||||||
|
* Attn.!! float used instead of double to make the function blueprintable! */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
void SetNumberField(const FString& FieldName, float Number);
|
||||||
|
|
||||||
|
/** Get the field named FieldName as a string. */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
FString GetStringField(const FString& FieldName) const;
|
||||||
|
|
||||||
|
/** Add a field named FieldName with value of StringValue */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
void SetStringField(const FString& FieldName, const FString& StringValue);
|
||||||
|
|
||||||
|
/** Get the field named FieldName as a boolean. */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
bool GetBoolField(const FString& FieldName) const;
|
||||||
|
|
||||||
|
/** Set a boolean field named FieldName and value of InValue */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
void SetBoolField(const FString& FieldName, bool InValue);
|
||||||
|
|
||||||
|
/** Get the field named FieldName as a Json object. */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
USIOJsonObject* GetObjectField(const FString& FieldName) const;
|
||||||
|
|
||||||
|
/** Set an ObjectField named FieldName and value of JsonObject */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
void SetObjectField(const FString& FieldName, USIOJsonObject* JsonObject);
|
||||||
|
|
||||||
|
/** Get the field named FieldName as a binary buffer array. */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
void GetBinaryField(const FString& FieldName, TArray<uint8>& OutBinary) const;
|
||||||
|
|
||||||
|
/** Set an BinaryField named FieldName and binary buffer array */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
void SetBinaryField(const FString& FieldName, const TArray<uint8>& Bytes);
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Array fields helpers (uniform arrays)
|
||||||
|
|
||||||
|
/** Get the field named FieldName as a Number Array. Use it only if you're sure that array is uniform!
|
||||||
|
* Attn.!! float used instead of double to make the function blueprintable! */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
TArray<float> GetNumberArrayField(const FString& FieldName);
|
||||||
|
|
||||||
|
/** Set an ObjectField named FieldName and value of Number Array
|
||||||
|
* Attn.!! float used instead of double to make the function blueprintable! */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
void SetNumberArrayField(const FString& FieldName, const TArray<float>& NumberArray);
|
||||||
|
|
||||||
|
/** Get the field named FieldName as a String Array. Use it only if you're sure that array is uniform! */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
TArray<FString> GetStringArrayField(const FString& FieldName);
|
||||||
|
|
||||||
|
/** Set an ObjectField named FieldName and value of String Array */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
void SetStringArrayField(const FString& FieldName, const TArray<FString>& StringArray);
|
||||||
|
|
||||||
|
/** Get the field named FieldName as a Bool Array. Use it only if you're sure that array is uniform! */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
TArray<bool> GetBoolArrayField(const FString& FieldName);
|
||||||
|
|
||||||
|
/** Set an ObjectField named FieldName and value of Bool Array */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
void SetBoolArrayField(const FString& FieldName, const TArray<bool>& BoolArray);
|
||||||
|
|
||||||
|
/** Get the field named FieldName as an Object Array. Use it only if you're sure that array is uniform! */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
TArray<USIOJsonObject*> GetObjectArrayField(const FString& FieldName);
|
||||||
|
|
||||||
|
/** Set an ObjectField named FieldName and value of Ob Array */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
void SetObjectArrayField(const FString& FieldName, const TArray<USIOJsonObject*>& ObjectArray);
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Data
|
||||||
|
|
||||||
|
private:
|
||||||
|
/** Internal JSON data */
|
||||||
|
TSharedPtr<FJsonObject> JsonObj;
|
||||||
|
|
||||||
|
};
|
@ -0,0 +1,177 @@
|
|||||||
|
// Modifications Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
|
||||||
|
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
|
||||||
|
// Copyright 2014 Vladimir Alyamkin. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Runtime/Json/Public/Dom/JsonValue.h"
|
||||||
|
#include "Runtime/Core/Public/Misc/Base64.h"
|
||||||
|
#include "Runtime/Core/Public/Templates/SharedPointer.h"
|
||||||
|
#include "SIOJsonValue.generated.h"
|
||||||
|
|
||||||
|
class USIOJsonObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents all the types a Json Value can be.
|
||||||
|
*/
|
||||||
|
UENUM(BlueprintType)
|
||||||
|
namespace ESIOJson
|
||||||
|
{
|
||||||
|
enum Type
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Null,
|
||||||
|
String,
|
||||||
|
Number,
|
||||||
|
Boolean,
|
||||||
|
Array,
|
||||||
|
Object,
|
||||||
|
Binary
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class SIOJSON_API FJsonValueBinary : public FJsonValue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FJsonValueBinary(const TArray<uint8>& InBinary) : Value(InBinary) { Type = EJson::String; } //pretends to be none
|
||||||
|
|
||||||
|
virtual bool TryGetString(FString& OutString) const override
|
||||||
|
{
|
||||||
|
//OutString = FString::FromHexBlob(Value.GetData(), Value.Num()); //HEX encoding
|
||||||
|
OutString = FBase64::Encode(Value); //Base64 encoding
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
virtual bool TryGetNumber(double& OutDouble) const override
|
||||||
|
{
|
||||||
|
OutDouble = Value.Num();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//hackery: we use this as an indicator we have a binary (strings don't normally do this)
|
||||||
|
virtual bool TryGetBool(bool& OutBool) const override { return false; }
|
||||||
|
|
||||||
|
/** Return our binary data from this value */
|
||||||
|
TArray<uint8> AsBinary() { return Value; }
|
||||||
|
|
||||||
|
/** Convenience method to determine if passed FJsonValue is a FJsonValueBinary or not. */
|
||||||
|
static bool IsBinary(const TSharedPtr<FJsonValue>& InJsonValue);
|
||||||
|
|
||||||
|
/** Convenience method to get binary array from unknown JsonValue, test with IsBinary first. */
|
||||||
|
static TArray<uint8> AsBinary(const TSharedPtr<FJsonValue>& InJsonValue);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
TArray<uint8> Value;
|
||||||
|
|
||||||
|
virtual FString GetType() const override { return TEXT("Binary"); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blueprintable FJsonValue wrapper
|
||||||
|
*/
|
||||||
|
UCLASS(BlueprintType, Blueprintable)
|
||||||
|
class SIOJSON_API USIOJsonValue : public UObject
|
||||||
|
{
|
||||||
|
GENERATED_UCLASS_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** Create new Json Number value
|
||||||
|
* Attn.!! float used instead of double to make the function blueprintable! */
|
||||||
|
UFUNCTION(BlueprintPure, meta = (DisplayName = "Construct Json Number Value", HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"), Category = "SIOJ|Json")
|
||||||
|
static USIOJsonValue* ConstructJsonValueNumber(UObject* WorldContextObject, float Number);
|
||||||
|
|
||||||
|
/** Create new Json String value */
|
||||||
|
UFUNCTION(BlueprintPure, meta = (DisplayName = "Construct Json String Value", HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"), Category = "SIOJ|Json")
|
||||||
|
static USIOJsonValue* ConstructJsonValueString(UObject* WorldContextObject, const FString& StringValue);
|
||||||
|
|
||||||
|
/** Create new Json Bool value */
|
||||||
|
UFUNCTION(BlueprintPure, meta = (DisplayName = "Construct Json Bool Value", HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"), Category = "SIOJ|Json")
|
||||||
|
static USIOJsonValue* ConstructJsonValueBool(UObject* WorldContextObject, bool InValue);
|
||||||
|
|
||||||
|
/** Create new Json Array value */
|
||||||
|
UFUNCTION(BlueprintPure, meta = (DisplayName = "Construct Json Array Value", HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"), Category = "SIOJ|Json")
|
||||||
|
static USIOJsonValue* ConstructJsonValueArray(UObject* WorldContextObject, const TArray<USIOJsonValue*>& InArray);
|
||||||
|
|
||||||
|
/** Create new Json Object value */
|
||||||
|
UFUNCTION(BlueprintPure, meta = (DisplayName = "Construct Json Object Value", HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"), Category = "SIOJ|Json")
|
||||||
|
static USIOJsonValue* ConstructJsonValueObject(USIOJsonObject *JsonObject, UObject* WorldContextObject);
|
||||||
|
|
||||||
|
/** Create new Json Binary value */
|
||||||
|
UFUNCTION(BlueprintPure, meta = (DisplayName = "Construct Json Binary Value", HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"), Category = "SIOJ|Json")
|
||||||
|
static USIOJsonValue* ConstructJsonValueBinary(UObject* WorldContextObject, TArray<uint8> ByteArray);
|
||||||
|
|
||||||
|
/** Create new Json value from FJsonValue (to be used from USIOJsonObject) */
|
||||||
|
static USIOJsonValue* ConstructJsonValue(UObject* WorldContextObject, const TSharedPtr<FJsonValue>& InValue);
|
||||||
|
|
||||||
|
/** Create new Json value from JSON encoded string*/
|
||||||
|
UFUNCTION(BlueprintPure, meta = (DisplayName = "Value From Json String", HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"), Category = "SIOJ|Json")
|
||||||
|
static USIOJsonValue* ValueFromJsonString(UObject* WorldContextObject, const FString& StringValue);
|
||||||
|
|
||||||
|
/** Get the root Json value */
|
||||||
|
TSharedPtr<FJsonValue>& GetRootValue();
|
||||||
|
|
||||||
|
/** Set the root Json value */
|
||||||
|
void SetRootValue(TSharedPtr<FJsonValue>& JsonValue);
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// FJsonValue API
|
||||||
|
|
||||||
|
/** Get type of Json value (Enum) */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
ESIOJson::Type GetType() const;
|
||||||
|
|
||||||
|
/** Get type of Json value (String) */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
FString GetTypeString() const;
|
||||||
|
|
||||||
|
/** Returns true if this value is a 'null' */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
bool IsNull() const;
|
||||||
|
|
||||||
|
/** Returns this value as a double, throwing an error if this is not an Json Number
|
||||||
|
* Attn.!! float used instead of double to make the function blueprintable! */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
float AsNumber() const;
|
||||||
|
|
||||||
|
/** Returns this value as a string, throwing an error if this is not an Json String */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
FString AsString() const;
|
||||||
|
|
||||||
|
/** Returns this value as a boolean, throwing an error if this is not an Json Bool */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
bool AsBool() const;
|
||||||
|
|
||||||
|
/** Returns this value as an array, throwing an error if this is not an Json Array */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
TArray<USIOJsonValue*> AsArray() const;
|
||||||
|
|
||||||
|
/** Returns this value as an object, throwing an error if this is not an Json Object */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
USIOJsonObject* AsObject();
|
||||||
|
|
||||||
|
//Convert message to binary data
|
||||||
|
UFUNCTION(BlueprintPure, Category = "SIOJ|Json")
|
||||||
|
TArray<uint8> AsBinary();
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SIOJ|Json")
|
||||||
|
FString EncodeJson() const;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Data
|
||||||
|
|
||||||
|
private:
|
||||||
|
/** Internal JSON data */
|
||||||
|
TSharedPtr<FJsonValue> JsonVal;
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Helpers
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** Simple error logger */
|
||||||
|
void ErrorMessage(const FString& InType) const;
|
||||||
|
|
||||||
|
};
|
@ -0,0 +1,36 @@
|
|||||||
|
// Modifications Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
|
||||||
|
// Copyright 2014 Vladimir Alyamkin. All Rights Reserved.
|
||||||
|
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace UnrealBuildTool.Rules
|
||||||
|
{
|
||||||
|
public class SIOJson : ModuleRules
|
||||||
|
{
|
||||||
|
public SIOJson(ReadOnlyTargetRules Target) : base(Target)
|
||||||
|
{
|
||||||
|
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||||
|
|
||||||
|
PrivateIncludePaths.AddRange(
|
||||||
|
new string[] {
|
||||||
|
"SIOJson/Private",
|
||||||
|
// ... add other private include paths required here ...
|
||||||
|
});
|
||||||
|
|
||||||
|
PublicDependencyModuleNames.AddRange(
|
||||||
|
new string[]
|
||||||
|
{
|
||||||
|
"Core",
|
||||||
|
"CoreUObject",
|
||||||
|
"Engine",
|
||||||
|
"HTTP",
|
||||||
|
"Json",
|
||||||
|
"JsonUtilities",
|
||||||
|
"CoreUtility"
|
||||||
|
// ... add other public dependencies that you statically link with here ...
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,218 @@
|
|||||||
|
// Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
|
||||||
|
#include "SIOMessageConvert.h"
|
||||||
|
#include "Runtime/Json/Public/Serialization/JsonWriter.h"
|
||||||
|
#include "Runtime/Json/Public/Policies/CondensedJsonPrintPolicy.h"
|
||||||
|
#include "SIOJsonValue.h"
|
||||||
|
|
||||||
|
DEFINE_LOG_CATEGORY(SocketIO);
|
||||||
|
|
||||||
|
typedef TJsonWriterFactory< TCHAR, TCondensedJsonPrintPolicy<TCHAR> > FCondensedJsonStringWriterFactory;
|
||||||
|
typedef TJsonWriter< TCHAR, TCondensedJsonPrintPolicy<TCHAR> > FCondensedJsonStringWriter;
|
||||||
|
|
||||||
|
TSharedPtr<FJsonValue> USIOMessageConvert::ToJsonValue(const sio::message::ptr& Message)
|
||||||
|
{
|
||||||
|
if (Message == nullptr)
|
||||||
|
{
|
||||||
|
return MakeShareable(new FJsonValueNull());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto flag = Message->get_flag();
|
||||||
|
|
||||||
|
if (flag == sio::message::flag_integer)
|
||||||
|
{
|
||||||
|
return MakeShareable(new FJsonValueNumber(Message->get_int()));
|
||||||
|
}
|
||||||
|
else if (flag == sio::message::flag_double)
|
||||||
|
{
|
||||||
|
return MakeShareable(new FJsonValueNumber(Message->get_double()));
|
||||||
|
}
|
||||||
|
else if (flag == sio::message::flag_string)
|
||||||
|
{
|
||||||
|
return MakeShareable(new FJsonValueString(FStringFromStd(Message->get_string())));
|
||||||
|
}
|
||||||
|
else if (flag == sio::message::flag_binary)
|
||||||
|
{
|
||||||
|
//FString WarningString = FString::Printf(TEXT("<binary (size %d bytes) not supported in FJsonValue, use raw sio::message methods>"), Binary->length());
|
||||||
|
|
||||||
|
//convert sio buffer ptr into the array
|
||||||
|
TArray<uint8> Buffer;
|
||||||
|
Buffer.Append((uint8*)(Message->get_binary()->data()), Message->get_binary()->size());
|
||||||
|
//todo: investigate if binary optimization is possible? Do we copy?
|
||||||
|
|
||||||
|
return MakeShareable(new FJsonValueBinary(Buffer));
|
||||||
|
}
|
||||||
|
else if (flag == sio::message::flag_array)
|
||||||
|
{
|
||||||
|
auto MessageVector = Message->get_vector();
|
||||||
|
TArray< TSharedPtr<FJsonValue> > InArray;
|
||||||
|
|
||||||
|
InArray.Reset(MessageVector.size());
|
||||||
|
|
||||||
|
for (auto ItemMessage : MessageVector)
|
||||||
|
{
|
||||||
|
InArray.Add(ToJsonValue(ItemMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
return MakeShareable(new FJsonValueArray(InArray));
|
||||||
|
}
|
||||||
|
else if (flag == sio::message::flag_object)
|
||||||
|
{
|
||||||
|
auto MessageMap = Message->get_map();
|
||||||
|
TSharedPtr<FJsonObject> InObject = MakeShareable(new FJsonObject());
|
||||||
|
|
||||||
|
for (auto MapPair : MessageMap)
|
||||||
|
{
|
||||||
|
InObject->SetField(FStringFromStd(MapPair.first), ToJsonValue(MapPair.second));
|
||||||
|
}
|
||||||
|
|
||||||
|
return MakeShareable(new FJsonValueObject(InObject));
|
||||||
|
}
|
||||||
|
else if (flag == sio::message::flag_boolean)
|
||||||
|
{
|
||||||
|
return MakeShareable(new FJsonValueBoolean(Message->get_bool()));
|
||||||
|
}
|
||||||
|
else if (flag == sio::message::flag_null)
|
||||||
|
{
|
||||||
|
return MakeShareable(new FJsonValueNull());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return MakeShareable(new FJsonValueNull());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sio::message::ptr USIOMessageConvert::ToSIOMessage(const TSharedPtr<FJsonValue>& JsonValue)
|
||||||
|
{
|
||||||
|
if (!JsonValue.IsValid())
|
||||||
|
{
|
||||||
|
return sio::null_message::create();
|
||||||
|
}
|
||||||
|
else if (JsonValue->Type == EJson::None)
|
||||||
|
{
|
||||||
|
return sio::null_message::create();
|
||||||
|
}
|
||||||
|
else if (JsonValue->Type == EJson::Null)
|
||||||
|
{
|
||||||
|
return sio::null_message::create();
|
||||||
|
}
|
||||||
|
else if (JsonValue->Type == EJson::String)
|
||||||
|
{
|
||||||
|
if (FJsonValueBinary::IsBinary(JsonValue))
|
||||||
|
{
|
||||||
|
auto BinaryArray = FJsonValueBinary::AsBinary(JsonValue);
|
||||||
|
return sio::binary_message::create(std::make_shared<std::string>((char*)BinaryArray.GetData(), BinaryArray.Num()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return sio::string_message::create(StdString(JsonValue->AsString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (JsonValue->Type == EJson::Number)
|
||||||
|
{
|
||||||
|
return sio::double_message::create(JsonValue->AsNumber());
|
||||||
|
}
|
||||||
|
else if (JsonValue->Type == EJson::Boolean)
|
||||||
|
{
|
||||||
|
return sio::bool_message::create(JsonValue->AsBool());
|
||||||
|
}
|
||||||
|
else if (JsonValue->Type == EJson::Array)
|
||||||
|
{
|
||||||
|
auto ValueArray = JsonValue->AsArray();
|
||||||
|
auto ArrayMessage = sio::array_message::create();
|
||||||
|
|
||||||
|
for (auto ItemValue : ValueArray)
|
||||||
|
{
|
||||||
|
//must use get_vector() for each
|
||||||
|
ArrayMessage->get_vector().push_back(ToSIOMessage(ItemValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ArrayMessage;
|
||||||
|
}
|
||||||
|
else if (JsonValue->Type == EJson::Object)
|
||||||
|
{
|
||||||
|
auto ValueTmap = JsonValue->AsObject()->Values;
|
||||||
|
|
||||||
|
auto ObjectMessage = sio::object_message::create();
|
||||||
|
|
||||||
|
for (auto ItemPair : ValueTmap)
|
||||||
|
{
|
||||||
|
//important to use get_map() directly to insert the key in the correct map and not a pointer copy
|
||||||
|
ObjectMessage->get_map()[StdString(ItemPair.Key)] = ToSIOMessage(ItemPair.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ObjectMessage;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return sio::null_message::create();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//We assume utf8 in transport
|
||||||
|
std::string USIOMessageConvert::StdString(FString UEString)
|
||||||
|
{
|
||||||
|
return std::string(TCHAR_TO_UTF8(*UEString));
|
||||||
|
}
|
||||||
|
|
||||||
|
FString USIOMessageConvert::FStringFromStd(std::string StdString)
|
||||||
|
{
|
||||||
|
return FString(UTF8_TO_TCHAR(StdString.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, std::string> USIOMessageConvert::JsonObjectToStdStringMap(TSharedPtr<FJsonObject> InObject)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> ParamMap;
|
||||||
|
|
||||||
|
if (InObject.IsValid())
|
||||||
|
{
|
||||||
|
for (auto Pair : InObject->Values)
|
||||||
|
{
|
||||||
|
TSharedPtr<FJsonValue> Value = Pair.Value;
|
||||||
|
|
||||||
|
//If it's a string value, add it to the std map
|
||||||
|
if (Value->Type == EJson::String)
|
||||||
|
{
|
||||||
|
ParamMap[USIOMessageConvert::StdString(Pair.Key)] = USIOMessageConvert::StdString(Value->AsString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ParamMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
TMap<FString, FString> USIOMessageConvert::JsonObjectToFStringMap(TSharedPtr<FJsonObject> InObject)
|
||||||
|
{
|
||||||
|
TMap<FString, FString> ParamMap;
|
||||||
|
|
||||||
|
if (InObject.IsValid())
|
||||||
|
{
|
||||||
|
for (auto Pair : InObject->Values)
|
||||||
|
{
|
||||||
|
TSharedPtr<FJsonValue> Value = Pair.Value;
|
||||||
|
|
||||||
|
//If it's a string value, add it to the std map
|
||||||
|
if (Value->Type == EJson::String)
|
||||||
|
{
|
||||||
|
ParamMap.Add(Pair.Key, Value->AsString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ParamMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, std::string> USIOMessageConvert::FStringMapToStdStringMap(const TMap<FString, FString>& InMap)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> ParamMap;
|
||||||
|
|
||||||
|
for (auto Pair : InMap)
|
||||||
|
{
|
||||||
|
ParamMap[USIOMessageConvert::StdString(Pair.Key)] = USIOMessageConvert::StdString(Pair.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ParamMap;
|
||||||
|
}
|
@ -0,0 +1,151 @@
|
|||||||
|
// Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
|
||||||
|
#include "SocketIOClient.h"
|
||||||
|
#include "SocketIONative.h"
|
||||||
|
#include "SIOMessageConvert.h"
|
||||||
|
#include "CULambdaRunnable.h"
|
||||||
|
#include "Runtime/Core/Public/HAL/ThreadSafeBool.h"
|
||||||
|
|
||||||
|
#define LOCTEXT_NAMESPACE "FSocketIOClientModule"
|
||||||
|
|
||||||
|
//struct
|
||||||
|
|
||||||
|
class FSocketIOClientModule : public ISocketIOClientModule
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//virtual TSharedPtr<FSocketIONative> NewValidNativePointer() override;
|
||||||
|
virtual TSharedPtr<FSocketIONative> NewValidNativePointer(const bool bShouldUseTlsLibraries, const bool bShouldVerifyTLSCertificate) override;
|
||||||
|
virtual TSharedPtr<FSocketIONative> ValidSharedNativePointer(FString SharedId, const bool bShouldUseTlsLibraries, const bool bShouldVerifyTLSCertificate) override;
|
||||||
|
void ReleaseNativePointer(TSharedPtr<FSocketIONative> PointerToRelease) override;
|
||||||
|
|
||||||
|
/** IModuleInterface implementation */
|
||||||
|
virtual void StartupModule() override;
|
||||||
|
virtual void ShutdownModule() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
FCriticalSection DeleteSection;
|
||||||
|
|
||||||
|
//All native pointers manages by the plugin
|
||||||
|
TArray<TSharedPtr<FSocketIONative>> PluginNativePointers;
|
||||||
|
|
||||||
|
//Shared pointers, these will typically be alive past game world lifecycles
|
||||||
|
TMap<FString, TSharedPtr<FSocketIONative>> SharedNativePointers;
|
||||||
|
TSet<TSharedPtr<FSocketIONative>> AllSharedPtrs; //reverse lookup
|
||||||
|
|
||||||
|
FThreadSafeBool bHasActiveNativePointers;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void FSocketIOClientModule::StartupModule()
|
||||||
|
{
|
||||||
|
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
|
||||||
|
|
||||||
|
PluginNativePointers.Empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIOClientModule::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.
|
||||||
|
|
||||||
|
/*
|
||||||
|
Ensure we call release pointers, this will catch all the plugin scoped
|
||||||
|
connections pointers which don't get auto-released between game worlds.
|
||||||
|
*/
|
||||||
|
auto AllActivePointers = PluginNativePointers;
|
||||||
|
for (auto& Pointer : AllActivePointers)
|
||||||
|
{
|
||||||
|
ReleaseNativePointer(Pointer);
|
||||||
|
}
|
||||||
|
AllActivePointers.Empty();
|
||||||
|
|
||||||
|
//Wait for all pointers to release
|
||||||
|
float Elapsed = 0.f;
|
||||||
|
while (bHasActiveNativePointers)
|
||||||
|
{
|
||||||
|
FPlatformProcess::Sleep(0.01f);
|
||||||
|
Elapsed += 0.01f;
|
||||||
|
|
||||||
|
//if it takes more than 5 seconds, just quit
|
||||||
|
if (Elapsed > 5.f)
|
||||||
|
{
|
||||||
|
UE_LOG(SocketIO, Warning, TEXT("FSocketIOClientModule::ShutdownModule force quit due to long wait to quit."));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Native pointers will be automatically released by uninitialize components
|
||||||
|
PluginNativePointers.Empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedPtr<FSocketIONative> FSocketIOClientModule::NewValidNativePointer(const bool bShouldUseTlsLibraries, const bool bShouldVerifyTLSCertificate)
|
||||||
|
{
|
||||||
|
TSharedPtr<FSocketIONative> NewPointer = MakeShareable(new FSocketIONative(bShouldUseTlsLibraries, bShouldVerifyTLSCertificate));
|
||||||
|
|
||||||
|
PluginNativePointers.Add(NewPointer);
|
||||||
|
|
||||||
|
bHasActiveNativePointers = true;
|
||||||
|
|
||||||
|
return NewPointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedPtr<FSocketIONative> FSocketIOClientModule::ValidSharedNativePointer(FString SharedId, const bool bShouldUseTlsLibraries, const bool bShouldVerifyTLSCertificate)
|
||||||
|
{
|
||||||
|
//Found key? return it
|
||||||
|
if (SharedNativePointers.Contains(SharedId))
|
||||||
|
{
|
||||||
|
return SharedNativePointers[SharedId];
|
||||||
|
}
|
||||||
|
//Otherwise request a new id and return it
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TSharedPtr<FSocketIONative> NewNativePtr = NewValidNativePointer(bShouldUseTlsLibraries, bShouldVerifyTLSCertificate);
|
||||||
|
SharedNativePointers.Add(SharedId, NewNativePtr);
|
||||||
|
AllSharedPtrs.Add(NewNativePtr);
|
||||||
|
return NewNativePtr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIOClientModule::ReleaseNativePointer(TSharedPtr<FSocketIONative> PointerToRelease)
|
||||||
|
{
|
||||||
|
//Remove shared ptr references if any
|
||||||
|
if (AllSharedPtrs.Contains(PointerToRelease))
|
||||||
|
{
|
||||||
|
AllSharedPtrs.Remove(PointerToRelease);
|
||||||
|
for (auto& Pair : SharedNativePointers)
|
||||||
|
{
|
||||||
|
if (Pair.Value == PointerToRelease)
|
||||||
|
{
|
||||||
|
SharedNativePointers.Remove(Pair.Key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Release the pointer on the background thread pool, this can take ~ 1 sec per connection
|
||||||
|
FCULambdaRunnable::RunLambdaOnBackGroundThread([PointerToRelease, this]
|
||||||
|
{
|
||||||
|
if (PointerToRelease.IsValid())
|
||||||
|
{
|
||||||
|
//Ensure only one thread at a time removes from array
|
||||||
|
{
|
||||||
|
FScopeLock Lock(&DeleteSection);
|
||||||
|
PluginNativePointers.Remove(PointerToRelease);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Disconnect, this can happen simultaneously
|
||||||
|
if (PointerToRelease->bIsConnected)
|
||||||
|
{
|
||||||
|
PointerToRelease->SyncDisconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Update our active status
|
||||||
|
bHasActiveNativePointers = PluginNativePointers.Num() > 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef LOCTEXT_NAMESPACE
|
||||||
|
|
||||||
|
IMPLEMENT_MODULE(FSocketIOClientModule, SocketIOClient)
|
@ -0,0 +1,676 @@
|
|||||||
|
// Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
|
||||||
|
#include "SocketIOClientComponent.h"
|
||||||
|
#include "SIOJConvert.h"
|
||||||
|
#include "SIOMessageConvert.h"
|
||||||
|
#include "SIOJRequestJSON.h"
|
||||||
|
#include "SocketIOClient.h"
|
||||||
|
#include "Engine/Engine.h"
|
||||||
|
|
||||||
|
|
||||||
|
USocketIOClientComponent::USocketIOClientComponent(const FObjectInitializer &init) : UActorComponent(init)
|
||||||
|
{
|
||||||
|
bWantsInitializeComponent = true;
|
||||||
|
bAutoActivate = true;
|
||||||
|
|
||||||
|
bForceTLS = false;
|
||||||
|
bUnbindEventsOnDisconnect = false;
|
||||||
|
bShouldVerifyTLSCertificate = false; //Until verification feature is implemented, this should default to false
|
||||||
|
bShouldAutoConnect = true;
|
||||||
|
NativeClient = nullptr;
|
||||||
|
bLimitConnectionToGameWorld = true;
|
||||||
|
SessionId = TEXT("Invalid");
|
||||||
|
|
||||||
|
//Plugin scoped utilities
|
||||||
|
bPluginScopedConnection = false;
|
||||||
|
PluginScopedId = TEXT("Default");
|
||||||
|
bVerboseConnectionLog = true;
|
||||||
|
ReconnectionTimeout = 0.f;
|
||||||
|
MaxReconnectionAttempts = -1.f;
|
||||||
|
ReconnectionDelayInMs = 5000;
|
||||||
|
|
||||||
|
bStaticallyInitialized = false;
|
||||||
|
|
||||||
|
ClearCallbacks();
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::StaticInitialization(UObject* WorldContextObject, bool bValidOwnerWorld /*= false*/)
|
||||||
|
{
|
||||||
|
bStaticallyInitialized = true;
|
||||||
|
|
||||||
|
if (!bValidOwnerWorld)
|
||||||
|
{
|
||||||
|
//Need to allow connections to non-owner worlds.
|
||||||
|
bLimitConnectionToGameWorld = false;
|
||||||
|
|
||||||
|
//The auto-connect will never happen in this case, so disable for clarity
|
||||||
|
bShouldAutoConnect = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//We statically initialize for all cases
|
||||||
|
InitializeNative();
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::InitializeComponent()
|
||||||
|
{
|
||||||
|
Super::InitializeComponent();
|
||||||
|
|
||||||
|
if (!bStaticallyInitialized)
|
||||||
|
{
|
||||||
|
InitializeNative();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::InitializeNative()
|
||||||
|
{
|
||||||
|
if (bPluginScopedConnection)
|
||||||
|
{
|
||||||
|
NativeClient = ISocketIOClientModule::Get().ValidSharedNativePointer(PluginScopedId, bForceTLS, bShouldVerifyTLSCertificate);
|
||||||
|
|
||||||
|
//Enforcement: This is the default FSocketIONative option value, but this component depends on it being true.
|
||||||
|
NativeClient->bCallbackOnGameThread = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NativeClient = ISocketIOClientModule::Get().NewValidNativePointer(bForceTLS, bShouldVerifyTLSCertificate);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetupCallbacks();
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::BeginPlay()
|
||||||
|
{
|
||||||
|
Super::BeginPlay();
|
||||||
|
|
||||||
|
//Auto-connect to default address if supported and not already connected
|
||||||
|
if (bShouldAutoConnect && !bIsConnected)
|
||||||
|
{
|
||||||
|
ConnectWithParams(URLParams);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
USocketIOClientComponent::~USocketIOClientComponent()
|
||||||
|
{
|
||||||
|
ClearCallbacks();
|
||||||
|
|
||||||
|
//If we're a regular connection we should close and release when we quit
|
||||||
|
if (!bPluginScopedConnection && NativeClient.IsValid())
|
||||||
|
{
|
||||||
|
ISocketIOClientModule::Get().ReleaseNativePointer(NativeClient);
|
||||||
|
NativeClient = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::UninitializeComponent()
|
||||||
|
{
|
||||||
|
//Because our connections can last longer than game world
|
||||||
|
//end, we let plugin-scoped structures manage our memory.
|
||||||
|
//We must ensure we set our pointer to null however.
|
||||||
|
|
||||||
|
ClearCallbacks();
|
||||||
|
|
||||||
|
//If we're a regular connection we should close and release when we quit
|
||||||
|
if (!bPluginScopedConnection)
|
||||||
|
{
|
||||||
|
ISocketIOClientModule::Get().ReleaseNativePointer(NativeClient);
|
||||||
|
NativeClient = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Super::UninitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::SetupCallbacks()
|
||||||
|
{
|
||||||
|
//Sync current connected state
|
||||||
|
bIsConnected = NativeClient->bIsConnected;
|
||||||
|
if (bIsConnected)
|
||||||
|
{
|
||||||
|
SessionId = NativeClient->SessionId;
|
||||||
|
URLParams = NativeClient->URLParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
NativeClient->OnConnectedCallback = [this](const FString& InSocketId, const FString& InSessionId)
|
||||||
|
{
|
||||||
|
if (NativeClient.IsValid())
|
||||||
|
{
|
||||||
|
bIsConnected = true;
|
||||||
|
SocketId = InSocketId;
|
||||||
|
SessionId = InSessionId;
|
||||||
|
bool bIsReconnection = bIsHavingConnectionProblems;
|
||||||
|
bIsHavingConnectionProblems = false;
|
||||||
|
OnConnected.Broadcast(SocketId, SessionId, bIsReconnection);
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const FSIOCCloseEventSignature OnDisconnectedSafe = OnDisconnected;
|
||||||
|
|
||||||
|
NativeClient->OnDisconnectedCallback = [OnDisconnectedSafe, this](const ESIOConnectionCloseReason Reason)
|
||||||
|
{
|
||||||
|
if (NativeClient.IsValid())
|
||||||
|
{
|
||||||
|
bIsConnected = false;
|
||||||
|
OnDisconnectedSafe.Broadcast(Reason);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
NativeClient->OnNamespaceConnectedCallback = [this](const FString& Namespace)
|
||||||
|
{
|
||||||
|
if (NativeClient.IsValid())
|
||||||
|
{
|
||||||
|
OnSocketNamespaceConnected.Broadcast(Namespace);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const FSIOCSocketEventSignature OnSocketNamespaceDisconnectedSafe = OnSocketNamespaceDisconnected;
|
||||||
|
|
||||||
|
NativeClient->OnNamespaceDisconnectedCallback = [this, OnSocketNamespaceDisconnectedSafe](const FString& Namespace)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (NativeClient.IsValid())
|
||||||
|
{
|
||||||
|
OnSocketNamespaceDisconnectedSafe.Broadcast(Namespace);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
NativeClient->OnReconnectionCallback = [this](const uint32 AttemptCount, const uint32 DelayInMs)
|
||||||
|
{
|
||||||
|
if (NativeClient.IsValid())
|
||||||
|
{
|
||||||
|
//First time we know about this problem?
|
||||||
|
if (!bIsHavingConnectionProblems)
|
||||||
|
{
|
||||||
|
TimeWhenConnectionProblemsStarted = FDateTime::Now();
|
||||||
|
bIsHavingConnectionProblems = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FTimespan Difference = FDateTime::Now() - TimeWhenConnectionProblemsStarted;
|
||||||
|
float ElapsedInSec = Difference.GetTotalSeconds();
|
||||||
|
|
||||||
|
if (ReconnectionTimeout > 0 && ElapsedInSec > ReconnectionTimeout)
|
||||||
|
{
|
||||||
|
//Let's stop trying and disconnect if we're using timeouts
|
||||||
|
Disconnect();
|
||||||
|
}
|
||||||
|
OnConnectionProblems.Broadcast(AttemptCount, DelayInMs, ElapsedInSec);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
NativeClient->OnFailCallback = [this]()
|
||||||
|
{
|
||||||
|
if(NativeClient.IsValid())
|
||||||
|
{
|
||||||
|
OnFail.Broadcast();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::ClearCallbacks()
|
||||||
|
{
|
||||||
|
if (NativeClient.IsValid())
|
||||||
|
{
|
||||||
|
NativeClient->ClearAllCallbacks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool USocketIOClientComponent::CallBPFunctionWithResponse(UObject* Target, const FString& FunctionName, TArray<TSharedPtr<FJsonValue>> Response)
|
||||||
|
{
|
||||||
|
if (!Target->IsValidLowLevel())
|
||||||
|
{
|
||||||
|
UE_LOG(SocketIO, Warning, TEXT("CallFunctionByNameWithArguments: Target not found for '%s'"), *FunctionName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
UWorld* World = GEngine->GetWorldFromContextObject(Target, EGetWorldErrorMode::LogAndReturnNull);
|
||||||
|
if (!World)
|
||||||
|
{
|
||||||
|
UE_LOG(SocketIO, Log, TEXT("World is invalid, %s BP function call ignored."), *FunctionName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (World && World->bIsTearingDown)
|
||||||
|
{
|
||||||
|
UE_LOG(SocketIO, Log, TEXT("World tearing down, %s BP function call ignored."), *FunctionName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UFunction* Function = Target->FindFunction(FName(*FunctionName));
|
||||||
|
if (nullptr == Function)
|
||||||
|
{
|
||||||
|
UE_LOG(SocketIO, Warning, TEXT("CallFunctionByNameWithArguments: Function not found '%s'"), *FunctionName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check function signature
|
||||||
|
TFieldIterator<FProperty> Iterator(Function);
|
||||||
|
|
||||||
|
TArray<FProperty*> Properties;
|
||||||
|
while (Iterator && (Iterator->PropertyFlags & CPF_Parm))
|
||||||
|
{
|
||||||
|
FProperty* Prop = *Iterator;
|
||||||
|
Properties.Add(Prop);
|
||||||
|
++Iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ResponseJsonValue = USIOJConvert::ToSIOJsonValue(Response);
|
||||||
|
|
||||||
|
bool bResponseNumNotZero = Response.Num() > 0;
|
||||||
|
bool bNoFunctionParams = Properties.Num() == 0;
|
||||||
|
bool bNullResponse = ResponseJsonValue->IsNull();
|
||||||
|
|
||||||
|
if (bNullResponse && bNoFunctionParams)
|
||||||
|
{
|
||||||
|
Target->ProcessEvent(Function, nullptr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (bResponseNumNotZero)
|
||||||
|
{
|
||||||
|
//function has too few params
|
||||||
|
if (bNoFunctionParams)
|
||||||
|
{
|
||||||
|
UE_LOG(SocketIO, Warning, TEXT("CallFunctionByNameWithArguments: Function '%s' has too few parameters, callback parameters ignored : <%s>"), *FunctionName, *ResponseJsonValue->EncodeJson());
|
||||||
|
Target->ProcessEvent(Function, nullptr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
struct FDynamicArgs
|
||||||
|
{
|
||||||
|
void* Arg01 = nullptr;
|
||||||
|
USIOJsonValue* Arg02 = nullptr;
|
||||||
|
};
|
||||||
|
//create the container
|
||||||
|
FDynamicArgs Args = FDynamicArgs();
|
||||||
|
|
||||||
|
//add the full response array as second param
|
||||||
|
Args.Arg02 = ResponseJsonValue;
|
||||||
|
const FString& FirstParam = Properties[0]->GetCPPType();
|
||||||
|
auto FirstFJsonValue = Response[0];
|
||||||
|
|
||||||
|
//Is first param...
|
||||||
|
//SIOJsonValue?
|
||||||
|
if (FirstParam.Equals("USIOJsonValue*"))
|
||||||
|
{
|
||||||
|
//convenience wrapper, response is a single object
|
||||||
|
USIOJsonValue* Value = NewObject<USIOJsonValue>();
|
||||||
|
Value->SetRootValue(FirstFJsonValue);
|
||||||
|
Args.Arg01 = Value;
|
||||||
|
Target->ProcessEvent(Function, &Args);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//SIOJsonObject?
|
||||||
|
else if (FirstParam.Equals("USIOJsonObject*"))
|
||||||
|
{
|
||||||
|
//convenience wrapper, response is a single object
|
||||||
|
USIOJsonObject* ObjectValue = NewObject<USIOJsonObject>();
|
||||||
|
ObjectValue->SetRootObject(FirstFJsonValue->AsObject());
|
||||||
|
Args.Arg01 = ObjectValue;
|
||||||
|
Target->ProcessEvent(Function, &Args);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//String?
|
||||||
|
else if (FirstParam.Equals("FString"))
|
||||||
|
{
|
||||||
|
FString StringValue = USIOJConvert::ToJsonString(FirstFJsonValue);
|
||||||
|
|
||||||
|
Target->ProcessEvent(Function, &StringValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//Float?
|
||||||
|
else if (FirstParam.Equals("float"))
|
||||||
|
{
|
||||||
|
float NumberValue = (float)FirstFJsonValue->AsNumber();
|
||||||
|
Target->ProcessEvent(Function, &NumberValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//Int?
|
||||||
|
else if (FirstParam.Equals("int32"))
|
||||||
|
{
|
||||||
|
int NumberValue = (int)FirstFJsonValue->AsNumber();
|
||||||
|
Target->ProcessEvent(Function, &NumberValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//bool?
|
||||||
|
else if (FirstParam.Equals("bool"))
|
||||||
|
{
|
||||||
|
bool BoolValue = FirstFJsonValue->AsBool();
|
||||||
|
Target->ProcessEvent(Function, &BoolValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//array?
|
||||||
|
else if (FirstParam.Equals("TArray"))
|
||||||
|
{
|
||||||
|
FArrayProperty* ArrayProp = CastField<FArrayProperty>(Properties[0]);
|
||||||
|
|
||||||
|
FString Inner;
|
||||||
|
ArrayProp->GetCPPMacroType(Inner);
|
||||||
|
|
||||||
|
//byte array is the only supported version
|
||||||
|
if (Inner.Equals("uint8"))
|
||||||
|
{
|
||||||
|
TArray<uint8> Bytes = ResponseJsonValue->AsArray()[0]->AsBinary();
|
||||||
|
Target->ProcessEvent(Function, &Bytes);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UE_LOG(SocketIO, Warning, TEXT("CallFunctionByNameWithArguments: Function '%s' signature not supported expected <%s>"), *FunctionName, *ResponseJsonValue->EncodeJson());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool USocketIOClientComponent::CallBPFunctionWithMessage(UObject* Target, const FString& FunctionName, TSharedPtr<FJsonValue> Message)
|
||||||
|
{
|
||||||
|
TArray<TSharedPtr<FJsonValue>> Response;
|
||||||
|
Response.Add(Message);
|
||||||
|
|
||||||
|
return CallBPFunctionWithResponse(Target, FunctionName, Response);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if PLATFORM_WINDOWS
|
||||||
|
#pragma region Connect
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void USocketIOClientComponent::Connect(const FString& InAddressAndPort, const FString& InPath, const FString& InAuthToken, USIOJsonObject* Query /*= nullptr*/, USIOJsonObject* Headers /*= nullptr*/)
|
||||||
|
{
|
||||||
|
//Check if we're limiting this component
|
||||||
|
if (bLimitConnectionToGameWorld)
|
||||||
|
{
|
||||||
|
UWorld* World = GEngine->GetWorldFromContextObject(this, EGetWorldErrorMode::LogAndReturnNull);
|
||||||
|
if (World)
|
||||||
|
{
|
||||||
|
bool bIsGameWorld = (World->IsGameWorld() || World->IsPreviewWorld());
|
||||||
|
if (!bIsGameWorld)
|
||||||
|
{
|
||||||
|
UE_LOG(SocketIO, Log, TEXT("USocketIOClientComponent::Connect attempt in non-game world blocked by bLimitConnectionToGameWorld."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(SocketIO, Warning, TEXT("USocketIOClientComponent::Connect attempt while in invalid world."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//NB: Only if the address and port is not empty do we use any of the other parameters
|
||||||
|
if (!InAddressAndPort.IsEmpty())
|
||||||
|
{
|
||||||
|
URLParams.AddressAndPort = InAddressAndPort;
|
||||||
|
|
||||||
|
TSharedPtr<FJsonObject> QueryFJson;
|
||||||
|
TSharedPtr<FJsonObject> HeadersFJson;
|
||||||
|
|
||||||
|
if (Query != nullptr)
|
||||||
|
{
|
||||||
|
QueryFJson = Query->GetRootObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Headers != nullptr)
|
||||||
|
{
|
||||||
|
HeadersFJson = Headers->GetRootObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
URLParams.Query = USIOMessageConvert::JsonObjectToFStringMap(QueryFJson);
|
||||||
|
URLParams.Headers = USIOMessageConvert::JsonObjectToFStringMap(HeadersFJson);
|
||||||
|
URLParams.Path = InPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!InAuthToken.IsEmpty())
|
||||||
|
{
|
||||||
|
URLParams.AuthToken = InAuthToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Sync all params to native client before connecting
|
||||||
|
NativeClient->MaxReconnectionAttempts = MaxReconnectionAttempts;
|
||||||
|
NativeClient->ReconnectionDelay = ReconnectionDelayInMs;
|
||||||
|
NativeClient->VerboseLog = bVerboseConnectionLog;
|
||||||
|
NativeClient->bUnbindEventsOnDisconnect = bUnbindEventsOnDisconnect;
|
||||||
|
NativeClient->bForceTLSUse = bForceTLS;
|
||||||
|
|
||||||
|
ConnectWithParams(URLParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::ConnectWithParams(const FSIOConnectParams& InURLParams)
|
||||||
|
{
|
||||||
|
NativeClient->Connect(InURLParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::ConnectNative(const FString& InAddressAndPort,
|
||||||
|
const FString& InPath,
|
||||||
|
const FString& InAuthToken,
|
||||||
|
const TSharedPtr<FJsonObject>& Query /*= nullptr*/,
|
||||||
|
const TSharedPtr<FJsonObject>& Headers /*= nullptr*/)
|
||||||
|
{
|
||||||
|
FSIOConnectParams Params;
|
||||||
|
Params.AddressAndPort = InAddressAndPort;
|
||||||
|
Params.Path = InPath;
|
||||||
|
Params.AuthToken = InAuthToken;
|
||||||
|
|
||||||
|
Params.Query = USIOMessageConvert::JsonObjectToFStringMap(Query);
|
||||||
|
Params.Headers = USIOMessageConvert::JsonObjectToFStringMap(Headers);
|
||||||
|
|
||||||
|
ConnectWithParams(Params);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::Disconnect()
|
||||||
|
{
|
||||||
|
NativeClient->Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::SyncDisconnect()
|
||||||
|
{
|
||||||
|
NativeClient->SyncDisconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::JoinNamespace(const FString& Namespace)
|
||||||
|
{
|
||||||
|
NativeClient->JoinNamespace(Namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::LeaveNamespace(const FString& Namespace)
|
||||||
|
{
|
||||||
|
NativeClient->LeaveNamespace(Namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if PLATFORM_WINDOWS
|
||||||
|
#pragma endregion Connect
|
||||||
|
#pragma region Emit
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void USocketIOClientComponent::Emit(const FString& EventName, USIOJsonValue* Message, const FString& Namespace /*= FString(TEXT("/"))*/)
|
||||||
|
{
|
||||||
|
//Set the message is not null
|
||||||
|
TSharedPtr<FJsonValue> JsonMessage = nullptr;
|
||||||
|
if (Message != nullptr)
|
||||||
|
{
|
||||||
|
JsonMessage = Message->GetRootValue();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JsonMessage = MakeShareable(new FJsonValueNull);
|
||||||
|
}
|
||||||
|
|
||||||
|
NativeClient->Emit(EventName, JsonMessage, nullptr, Namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::EmitWithCallBack(const FString& EventName, USIOJsonValue* Message /*= nullptr*/, const FString& CallbackFunctionName /*= FString(TEXT(""))*/, UObject* Target /*= nullptr*/, const FString& Namespace /*= FString(TEXT("/"))*/, UObject* WorldContextObject /*= nullptr*/)
|
||||||
|
{
|
||||||
|
if (!CallbackFunctionName.IsEmpty())
|
||||||
|
{
|
||||||
|
if (Target == nullptr)
|
||||||
|
{
|
||||||
|
Target = WorldContextObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Set the message is not null
|
||||||
|
TSharedPtr<FJsonValue> JsonMessage = nullptr;
|
||||||
|
if (Message != nullptr)
|
||||||
|
{
|
||||||
|
JsonMessage = Message->GetRootValue();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JsonMessage = MakeShareable(new FJsonValueNull);
|
||||||
|
}
|
||||||
|
|
||||||
|
EmitNative(EventName, JsonMessage, [&, Target, CallbackFunctionName, this](const TArray<TSharedPtr<FJsonValue>>& Response)
|
||||||
|
{
|
||||||
|
CallBPFunctionWithResponse(Target, CallbackFunctionName, Response);
|
||||||
|
}, Namespace);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EmitNative(EventName, Message->GetRootValue(),nullptr,Namespace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::EmitWithGraphCallBack(const FString& EventName, struct FLatentActionInfo LatentInfo, USIOJsonValue*& Result, USIOJsonValue* Message /*= nullptr*/, const FString& Namespace /*= FString(TEXT("/"))*/)
|
||||||
|
{
|
||||||
|
//Set the message is not null
|
||||||
|
TSharedPtr<FJsonValue> JsonMessage = nullptr;
|
||||||
|
if (Message != nullptr)
|
||||||
|
{
|
||||||
|
JsonMessage = Message->GetRootValue();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JsonMessage = MakeShareable(new FJsonValueNull);
|
||||||
|
}
|
||||||
|
FCULatentAction *LatentAction = FCULatentAction::CreateLatentAction(LatentInfo, this);
|
||||||
|
|
||||||
|
if (LatentAction)
|
||||||
|
{
|
||||||
|
//emit the message and pass the LatentAction, we also pass the result reference through lambda capture
|
||||||
|
NativeClient->Emit(EventName, JsonMessage, [this, LatentAction, &Result](const TArray<TSharedPtr<FJsonValue>>& Response)
|
||||||
|
{
|
||||||
|
// Finish the latent action
|
||||||
|
if (LatentAction)
|
||||||
|
{
|
||||||
|
TSharedPtr<FJsonValue> FirstResponseValue = Response[0];
|
||||||
|
USIOJsonValue* ResultObj = NewObject<USIOJsonValue>();
|
||||||
|
ResultObj->SetRootValue(FirstResponseValue);
|
||||||
|
Result = ResultObj; //update the output value
|
||||||
|
LatentAction->Call(); //resume the latent action
|
||||||
|
}
|
||||||
|
}, Namespace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::EmitNative(const FString& EventName, const TSharedPtr<FJsonValue>& Message /*= nullptr*/, TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction /*= nullptr*/, const FString& Namespace /*= FString(TEXT("/"))*/)
|
||||||
|
{
|
||||||
|
NativeClient->Emit(EventName, Message, CallbackFunction, Namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::EmitNative(const FString& EventName, const TSharedPtr<FJsonObject>& ObjectMessage /*= nullptr*/, TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction /*= nullptr*/, const FString& Namespace /*= FString(TEXT("/"))*/)
|
||||||
|
{
|
||||||
|
EmitNative(EventName, MakeShareable(new FJsonValueObject(ObjectMessage)), CallbackFunction, Namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::EmitNative(const FString& EventName, const FString& StringMessage /*= FString()*/, TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction /*= nullptr*/, const FString& Namespace /*= FString(TEXT("/"))*/)
|
||||||
|
{
|
||||||
|
EmitNative(EventName, MakeShareable(new FJsonValueString(StringMessage)), CallbackFunction, Namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::EmitNative(const FString& EventName, double NumberMessage, TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction /*= nullptr*/, const FString& Namespace /*= FString(TEXT("/"))*/)
|
||||||
|
{
|
||||||
|
EmitNative(EventName, MakeShareable(new FJsonValueNumber(NumberMessage)), CallbackFunction, Namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::EmitNative(const FString& EventName, const TArray<uint8>& BinaryMessage, TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction /*= nullptr*/, const FString& Namespace /*= FString(TEXT("/"))*/)
|
||||||
|
{
|
||||||
|
EmitNative(EventName, MakeShareable(new FJsonValueBinary(BinaryMessage)), CallbackFunction, Namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::EmitNative(const FString& EventName, const TArray<TSharedPtr<FJsonValue>>& ArrayMessage, TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction /*= nullptr*/, const FString& Namespace /*= FString(TEXT("/"))*/)
|
||||||
|
{
|
||||||
|
EmitNative(EventName, MakeShareable(new FJsonValueArray(ArrayMessage)), CallbackFunction, Namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::EmitNative(const FString& EventName, bool BooleanMessage, TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction /*= nullptr*/, const FString& Namespace /*= FString(TEXT("/"))*/)
|
||||||
|
{
|
||||||
|
EmitNative(EventName, MakeShareable(new FJsonValueBoolean(BooleanMessage)), CallbackFunction, Namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::EmitNative(const FString& EventName, UStruct* Struct, const void* StructPtr, TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction /*= nullptr*/, const FString& Namespace /*= FString(TEXT("/"))*/)
|
||||||
|
{
|
||||||
|
EmitNative(EventName, USIOJConvert::ToJsonObject(Struct, (void*)StructPtr), CallbackFunction, Namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::EmitNative(const FString& EventName, const SIO_TEXT_TYPE StringMessage /*= TEXT("")*/, TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction /*= nullptr*/, const FString& Namespace /*= FString(TEXT("/"))*/)
|
||||||
|
{
|
||||||
|
EmitNative(EventName, MakeShareable(new FJsonValueString(FString(StringMessage))), CallbackFunction, Namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if PLATFORM_WINDOWS
|
||||||
|
#pragma endregion Emit
|
||||||
|
#pragma region OnEvents
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void USocketIOClientComponent::BindEventToGenericEvent(const FString& EventName, const FString& Namespace)
|
||||||
|
{
|
||||||
|
NativeClient->OnEvent(EventName, [&](const FString& Event, const TSharedPtr<FJsonValue>& EventValue)
|
||||||
|
{
|
||||||
|
USIOJsonValue* NewValue = NewObject<USIOJsonValue>();
|
||||||
|
TSharedPtr<FJsonValue> NonConstValue = EventValue;
|
||||||
|
NewValue->SetRootValue(NonConstValue);
|
||||||
|
OnGenericEvent.Broadcast(Event, NewValue);
|
||||||
|
}, Namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::BindEventToDelegate(const FString& EventName,
|
||||||
|
const FSIOJsonValueSignature& CallbackDelegate,
|
||||||
|
const FString& Namespace /*= TEXT("/")*/,
|
||||||
|
ESIOThreadOverrideOption ThreadOverride /*= USE_DEFAULT*/)
|
||||||
|
{
|
||||||
|
const FSIOJsonValueSignature SafeCallback = CallbackDelegate; //copy for lambda ref
|
||||||
|
OnNativeEvent(EventName, [&, SafeCallback](const FString& Event, const TSharedPtr<FJsonValue>& Message)
|
||||||
|
{
|
||||||
|
USIOJsonValue* Value = NewObject<USIOJsonValue>();
|
||||||
|
TSharedPtr<FJsonValue> NonConstValue = Message;
|
||||||
|
Value->SetRootValue(NonConstValue);
|
||||||
|
|
||||||
|
SafeCallback.ExecuteIfBound(Value);
|
||||||
|
}, Namespace, ThreadOverride);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void USocketIOClientComponent::BindEventToFunction(const FString& EventName,
|
||||||
|
const FString& FunctionName,
|
||||||
|
UObject* Target,
|
||||||
|
const FString& Namespace /*= FString(TEXT("/"))*/,
|
||||||
|
ESIOThreadOverrideOption ThreadOverride /*= USE_DEFAULT*/,
|
||||||
|
UObject* WorldContextObject /*= nullptr*/)
|
||||||
|
{
|
||||||
|
if (!FunctionName.IsEmpty())
|
||||||
|
{
|
||||||
|
if (Target == nullptr)
|
||||||
|
{
|
||||||
|
Target = WorldContextObject;
|
||||||
|
}
|
||||||
|
OnNativeEvent(EventName, [&, FunctionName, Target](const FString& Event, const TSharedPtr<FJsonValue>& Message)
|
||||||
|
{
|
||||||
|
CallBPFunctionWithMessage(Target, FunctionName, Message);
|
||||||
|
}, Namespace, ThreadOverride);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//if we forgot our function name, fallback to regular bind event
|
||||||
|
BindEventToGenericEvent(EventName, Namespace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::UnbindEvent(const FString& EventName, const FString& Namespace/* = TEXT("/")*/)
|
||||||
|
{
|
||||||
|
NativeClient->UnbindEvent(EventName, Namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USocketIOClientComponent::OnNativeEvent(const FString& EventName,
|
||||||
|
TFunction< void(const FString&, const TSharedPtr<FJsonValue>&)> CallbackFunction,
|
||||||
|
const FString& Namespace /*= FString(TEXT("/"))*/,
|
||||||
|
ESIOThreadOverrideOption ThreadOverride /*= USE_DEFAULT*/)
|
||||||
|
{
|
||||||
|
NativeClient->OnEvent(EventName, CallbackFunction, Namespace, ThreadOverride);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if PLATFORM_WINDOWS
|
||||||
|
#pragma endregion OnEvents
|
||||||
|
#endif
|
@ -0,0 +1,168 @@
|
|||||||
|
// Copyright 2019-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
#include "SocketIOFunctionLibrary.h"
|
||||||
|
#include "CULambdaRunnable.h"
|
||||||
|
#include "Engine/Engine.h"
|
||||||
|
|
||||||
|
USocketIOClientComponent* USocketIOFunctionLibrary::ConstructSocketIOComponent(UObject* WorldContextObject)
|
||||||
|
{
|
||||||
|
USocketIOClientComponent* SpawnedComponent = NewObject<USocketIOClientComponent>(WorldContextObject, TEXT("SocketIOClientComponent"));
|
||||||
|
|
||||||
|
if (SpawnedComponent)
|
||||||
|
{
|
||||||
|
//Check if we got spawned by an object with an owner context (e.g. game mode) or not (e.g. game instance)
|
||||||
|
UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull);
|
||||||
|
AActor* MyOwner = SpawnedComponent->GetOwner();
|
||||||
|
bool bOwnerHasValidWorld = (MyOwner ? MyOwner->GetWorld() : nullptr) != nullptr;
|
||||||
|
|
||||||
|
SpawnedComponent->StaticInitialization(WorldContextObject, bOwnerHasValidWorld);
|
||||||
|
|
||||||
|
//Register component if it was spawned by world owning context object, e.g. game mode
|
||||||
|
if (bOwnerHasValidWorld)
|
||||||
|
{
|
||||||
|
//Delay by 1 tick so that we can adjust bShouldAutoConnect/etc
|
||||||
|
FCULambdaRunnable::RunShortLambdaOnGameThread([SpawnedComponent]()
|
||||||
|
{
|
||||||
|
if (SpawnedComponent->IsValidLowLevel())
|
||||||
|
{
|
||||||
|
SpawnedComponent->RegisterComponent();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//Otherwise we don't need to register our component for the networking logic to work.
|
||||||
|
}
|
||||||
|
return SpawnedComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct FDynamicArgs
|
||||||
|
{
|
||||||
|
void* Arg01 = nullptr;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool USocketIOFunctionLibrary::CallFunctionByName(const FString& FunctionName, UObject* Target, UObject* WorldContextObject, USIOJsonValue* Param)
|
||||||
|
{
|
||||||
|
if (Target == nullptr)
|
||||||
|
{
|
||||||
|
Target = WorldContextObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
UFunction* Function = Target->FindFunction(FName(*FunctionName));
|
||||||
|
if (nullptr == Function)
|
||||||
|
{
|
||||||
|
UE_LOG(SocketIO, Warning, TEXT("CallFunctionByName: Function not found '%s'"), *FunctionName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check function signature
|
||||||
|
TFieldIterator<FProperty> Iterator(Function);
|
||||||
|
|
||||||
|
TArray<FProperty*> Properties;
|
||||||
|
while (Iterator && (Iterator->PropertyFlags & CPF_Parm))
|
||||||
|
{
|
||||||
|
FProperty* Prop = *Iterator;
|
||||||
|
Properties.Add(Prop);
|
||||||
|
++Iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
//bool bTargetParamsNonZero = Properties.Num() > 0;
|
||||||
|
bool bTargetParamsZero = Properties.Num() == 0;
|
||||||
|
bool bNullParamPassed = Param->IsNull();
|
||||||
|
|
||||||
|
//UE_LOG(SocketIO, Warning, TEXT("CallFunctionByName: Target %d, %s"), Target, *FunctionName);
|
||||||
|
|
||||||
|
if (bTargetParamsZero && bNullParamPassed)
|
||||||
|
{
|
||||||
|
Target->ProcessEvent(Function, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
else{
|
||||||
|
|
||||||
|
//function has too few params
|
||||||
|
if (bTargetParamsZero)
|
||||||
|
{
|
||||||
|
UE_LOG(SocketIO, Warning, TEXT("CallFunctionByName: Function '%s' has too few parameters, callback parameter ignored : <%s>"), *FunctionName, *Param->EncodeJson());
|
||||||
|
Target->ProcessEvent(Function, nullptr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//create the container
|
||||||
|
FDynamicArgs Args = FDynamicArgs();
|
||||||
|
|
||||||
|
//add the full response array as second param
|
||||||
|
const FString& FirstParam = Properties[0]->GetCPPType();
|
||||||
|
|
||||||
|
FStructProperty* StructProperty = CastField<FStructProperty>(Properties[0]);
|
||||||
|
if (StructProperty)
|
||||||
|
{
|
||||||
|
//StructProperty->getcon
|
||||||
|
}
|
||||||
|
|
||||||
|
//Is first param...
|
||||||
|
//SIOJsonValue?
|
||||||
|
if (FirstParam.Equals("USIOJsonValue*"))
|
||||||
|
{
|
||||||
|
Args.Arg01 = Param;
|
||||||
|
Target->ProcessEvent(Function, &Args);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//SIOJsonObject?
|
||||||
|
else if (FirstParam.Equals("USIOJsonObject*"))
|
||||||
|
{
|
||||||
|
//convenience wrapper, response is a single object
|
||||||
|
Args.Arg01 = Param->AsObject();
|
||||||
|
Target->ProcessEvent(Function, &Args);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//String?
|
||||||
|
else if (FirstParam.Equals("FString"))
|
||||||
|
{
|
||||||
|
FString StringValue = Param->AsString();
|
||||||
|
|
||||||
|
Target->ProcessEvent(Function, &StringValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//Float?
|
||||||
|
else if (FirstParam.Equals("float"))
|
||||||
|
{
|
||||||
|
float NumberValue = (float)Param->AsNumber();
|
||||||
|
Target->ProcessEvent(Function, &NumberValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//Int?
|
||||||
|
else if (FirstParam.Equals("int32"))
|
||||||
|
{
|
||||||
|
int NumberValue = (int)Param->AsNumber();
|
||||||
|
Target->ProcessEvent(Function, &NumberValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//bool?
|
||||||
|
else if (FirstParam.Equals("bool"))
|
||||||
|
{
|
||||||
|
bool BoolValue = Param->AsBool();
|
||||||
|
Target->ProcessEvent(Function, &BoolValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//array?
|
||||||
|
else if (FirstParam.Equals("TArray"))
|
||||||
|
{
|
||||||
|
FArrayProperty* ArrayProp = CastField<FArrayProperty>(Properties[0]);
|
||||||
|
|
||||||
|
FString Inner;
|
||||||
|
ArrayProp->GetCPPMacroType(Inner);
|
||||||
|
|
||||||
|
//byte array is the only supported version
|
||||||
|
if (Inner.Equals("uint8"))
|
||||||
|
{
|
||||||
|
TArray<uint8> Bytes = Param->AsBinary();
|
||||||
|
Target->ProcessEvent(Function, &Bytes);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
@ -0,0 +1,646 @@
|
|||||||
|
// Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
|
||||||
|
#include "SocketIONative.h"
|
||||||
|
#include "SIOMessageConvert.h"
|
||||||
|
#include "CULambdaRunnable.h"
|
||||||
|
#include "SIOJConvert.h"
|
||||||
|
#include "sio_client.h"
|
||||||
|
#include "sio_message.h"
|
||||||
|
#include "sio_socket.h"
|
||||||
|
|
||||||
|
FSocketIONative::FSocketIONative(const bool bForceTLS, const bool bShouldVerifyTLSCertificate)
|
||||||
|
{
|
||||||
|
PrivateClient = nullptr;
|
||||||
|
SessionId = TEXT("Invalid");
|
||||||
|
LastSessionId = TEXT("None");
|
||||||
|
bIsConnected = false;
|
||||||
|
MaxReconnectionAttempts = -1;
|
||||||
|
ReconnectionDelay = 5000;
|
||||||
|
bCallbackOnGameThread = true;
|
||||||
|
bUnbindEventsOnDisconnect = false;
|
||||||
|
bForceTLSUse = bForceTLS;
|
||||||
|
InitPrivateClient(bForceTLS, bShouldVerifyTLSCertificate);
|
||||||
|
|
||||||
|
ClearAllCallbacks();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FSocketIONative::InitPrivateClient(const bool bShouldUseTlsLibraries /*= false*/, const bool bShouldVerifyTLSCertificate /*= false*/)
|
||||||
|
{
|
||||||
|
bIsSetupForTLS = bShouldUseTlsLibraries;
|
||||||
|
bUsingTLSCertVerification = bShouldVerifyTLSCertificate;
|
||||||
|
PrivateClient = MakeShareable(new sio::client(bShouldUseTlsLibraries, bUsingTLSCertVerification));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::Connect(const FSIOConnectParams& InConnectParams)
|
||||||
|
{
|
||||||
|
//Special case: reconnect on current settings if address is empty
|
||||||
|
if (!InConnectParams.AddressAndPort.IsEmpty())
|
||||||
|
{
|
||||||
|
URLParams = InConnectParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
SyncPrivateClientToTLSMode(URLParams.AddressAndPort);
|
||||||
|
|
||||||
|
//Fill std types before going to background thread.
|
||||||
|
|
||||||
|
std::string StdAddressString = USIOMessageConvert::StdString(URLParams.AddressAndPort);
|
||||||
|
std::string StdPathString = USIOMessageConvert::StdString(URLParams.Path);
|
||||||
|
std::map<std::string, std::string> QueryMap = {};
|
||||||
|
std::map<std::string, std::string> HeadersMap = {};
|
||||||
|
sio::message::ptr AuthMessage = sio::object_message::create();
|
||||||
|
if (!URLParams.AuthToken.IsEmpty())
|
||||||
|
{
|
||||||
|
AuthMessage->get_map()["token"] = sio::string_message::create(USIOMessageConvert::StdString(URLParams.AuthToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryMap = USIOMessageConvert::FStringMapToStdStringMap(URLParams.Query);
|
||||||
|
HeadersMap = USIOMessageConvert::FStringMapToStdStringMap(URLParams.Headers);
|
||||||
|
|
||||||
|
//Connect to the server on a background thread so it never blocks
|
||||||
|
FCULambdaRunnable::RunLambdaOnBackGroundThread([&, StdAddressString, StdPathString, QueryMap, HeadersMap, AuthMessage]
|
||||||
|
{
|
||||||
|
PrivateClient->set_reconnect_attempts(MaxReconnectionAttempts);
|
||||||
|
PrivateClient->set_reconnect_delay(ReconnectionDelay);
|
||||||
|
PrivateClient->set_path(StdPathString);
|
||||||
|
|
||||||
|
//close and reconnect if different url
|
||||||
|
if(PrivateClient->opened())
|
||||||
|
{
|
||||||
|
if (PrivateClient->get_url() != StdAddressString)
|
||||||
|
{
|
||||||
|
//sync close to re-open
|
||||||
|
PrivateClient->sync_close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//we're already connected to the correct endpoint, ignore request
|
||||||
|
UE_LOG(SocketIO, Warning, TEXT("Attempted to connect to %s when we're already connected. Request ignored."), UTF8_TO_TCHAR(StdAddressString.c_str()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PrivateClient->connect(StdAddressString, QueryMap, HeadersMap, AuthMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::Connect(const FString& InAddressAndPort)
|
||||||
|
{
|
||||||
|
if (!InAddressAndPort.IsEmpty())
|
||||||
|
{
|
||||||
|
URLParams.AddressAndPort = InAddressAndPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
Connect(URLParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::JoinNamespace(const FString& Namespace)
|
||||||
|
{
|
||||||
|
//just referencing the namespace will join it
|
||||||
|
PrivateClient->socket(USIOMessageConvert::StdString(Namespace));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::LeaveNamespace(const FString& Namespace)
|
||||||
|
{
|
||||||
|
PrivateClient->socket(USIOMessageConvert::StdString(Namespace))->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::Disconnect()
|
||||||
|
{
|
||||||
|
//Ensure disconnected callback is called first because we
|
||||||
|
//clear all callbacks for stability.
|
||||||
|
if (OnDisconnectedCallback)
|
||||||
|
{
|
||||||
|
OnDisconnectedCallback(ESIOConnectionCloseReason::CLOSE_REASON_NORMAL);
|
||||||
|
}
|
||||||
|
bIsConnected = false;
|
||||||
|
|
||||||
|
if (bUnbindEventsOnDisconnect)
|
||||||
|
{
|
||||||
|
ClearAllCallbacks();
|
||||||
|
PrivateClient->close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//Clear and re-init map
|
||||||
|
ClearInternalCallbacks();
|
||||||
|
PrivateClient->close();
|
||||||
|
RebindCurrentEventMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::SyncDisconnect()
|
||||||
|
{
|
||||||
|
//Ensure disconnected callback is called first because we
|
||||||
|
//clear all callbacks for stability.
|
||||||
|
if (OnDisconnectedCallback)
|
||||||
|
{
|
||||||
|
OnDisconnectedCallback(ESIOConnectionCloseReason::CLOSE_REASON_NORMAL);
|
||||||
|
}
|
||||||
|
bIsConnected = false;
|
||||||
|
|
||||||
|
if (bUnbindEventsOnDisconnect)
|
||||||
|
{
|
||||||
|
ClearAllCallbacks();
|
||||||
|
PrivateClient->sync_close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//Clear and re-init map
|
||||||
|
ClearInternalCallbacks();
|
||||||
|
PrivateClient->sync_close();
|
||||||
|
RebindCurrentEventMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::ClearAllCallbacks()
|
||||||
|
{
|
||||||
|
PrivateClient->clear_socket_listeners();
|
||||||
|
SetupInternalCallbacks(); //if clear socket listeners cleared our internal callbacks. reset them
|
||||||
|
EventFunctionMap.Empty();
|
||||||
|
|
||||||
|
OnConnectedCallback = nullptr;
|
||||||
|
OnDisconnectedCallback = nullptr;
|
||||||
|
OnNamespaceConnectedCallback = nullptr;
|
||||||
|
OnNamespaceDisconnectedCallback = nullptr;
|
||||||
|
OnReconnectionCallback = nullptr;
|
||||||
|
OnFailCallback = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::Emit(const FString& EventName, const TSharedPtr<FJsonValue>& Message /*= nullptr*/, TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction /*= nullptr*/, const FString& Namespace /*= FString(TEXT("/"))*/)
|
||||||
|
{
|
||||||
|
TFunction<void(const sio::message::list&)> RawCallback = nullptr;
|
||||||
|
|
||||||
|
//Only bind the raw callback if we pass in a callback ourselves;
|
||||||
|
if (CallbackFunction)
|
||||||
|
{
|
||||||
|
RawCallback = [&, CallbackFunction](const sio::message::list& MessageList)
|
||||||
|
{
|
||||||
|
TArray<TSharedPtr<FJsonValue>> ValueArray;
|
||||||
|
|
||||||
|
for (uint32 i = 0; i < MessageList.size(); i++)
|
||||||
|
{
|
||||||
|
auto ItemMessagePtr = MessageList[i];
|
||||||
|
ValueArray.Add(USIOMessageConvert::ToJsonValue(ItemMessagePtr));
|
||||||
|
}
|
||||||
|
if (CallbackFunction)
|
||||||
|
{
|
||||||
|
CallbackFunction(ValueArray);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
EmitRaw(
|
||||||
|
EventName,
|
||||||
|
USIOMessageConvert::ToSIOMessage(Message),
|
||||||
|
RawCallback,
|
||||||
|
Namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::Emit(const FString& EventName, const TSharedPtr<FJsonObject>& ObjectMessage /*= nullptr*/, TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction /*= nullptr*/, const FString& Namespace /*= FString(TEXT("/"))*/)
|
||||||
|
{
|
||||||
|
Emit(EventName, MakeShareable(new FJsonValueObject(ObjectMessage)), CallbackFunction, Namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::Emit(const FString& EventName, const FString& StringMessage /*= FString()*/, TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction /*= nullptr*/, const FString& Namespace /*= FString(TEXT("/"))*/)
|
||||||
|
{
|
||||||
|
Emit(EventName, MakeShareable(new FJsonValueString(StringMessage)), CallbackFunction, Namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::Emit(const FString& EventName, double NumberMessage, TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction /*= nullptr*/, const FString& Namespace /*= FString(TEXT("/"))*/)
|
||||||
|
{
|
||||||
|
Emit(EventName, MakeShareable(new FJsonValueNumber(NumberMessage)), CallbackFunction, Namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::Emit(const FString& EventName, bool BooleanMessage, TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction /*= nullptr*/, const FString& Namespace /*= FString(TEXT("/"))*/)
|
||||||
|
{
|
||||||
|
Emit(EventName, MakeShareable(new FJsonValueBoolean(BooleanMessage)), CallbackFunction, Namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::Emit(const FString& EventName, const TArray<uint8>& BinaryMessage, TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction /*= nullptr*/, const FString& Namespace /*= FString(TEXT("/"))*/)
|
||||||
|
{
|
||||||
|
Emit(EventName, MakeShareable(new FJsonValueBinary(BinaryMessage)), CallbackFunction, Namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::Emit(const FString& EventName, const TArray<TSharedPtr<FJsonValue>>& ArrayMessage, TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction /*= nullptr*/, const FString& Namespace /*= FString(TEXT("/"))*/)
|
||||||
|
{
|
||||||
|
Emit(EventName, MakeShareable(new FJsonValueArray(ArrayMessage)), CallbackFunction, Namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::Emit(const FString& EventName, UStruct* Struct, const void* StructPtr, TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction /*= nullptr*/, const FString& Namespace /*= FString(TEXT("/"))*/)
|
||||||
|
{
|
||||||
|
Emit(EventName, USIOJConvert::ToJsonObject(Struct, (void*)StructPtr), CallbackFunction, Namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::Emit(const FString& EventName, TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction /*= nullptr*/, const FString& Namespace /*= TEXT("/")*/)
|
||||||
|
{
|
||||||
|
TSharedPtr<FJsonValue> NoneValue;
|
||||||
|
Emit(EventName, NoneValue, CallbackFunction, Namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::Emit(const FString& EventName, const SIO_TEXT_TYPE StringMessage /*= TEXT("")*/, TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction /*= nullptr*/, const FString& Namespace /*= TEXT("/")*/)
|
||||||
|
{
|
||||||
|
Emit(EventName, MakeShareable(new FJsonValueString(FString(StringMessage))), CallbackFunction, Namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::EmitRaw(const FString& EventName, const sio::message::list& MessageList /*= nullptr*/, TFunction<void(const sio::message::list&)> CallbackFunction /*= nullptr*/, const FString& Namespace /*= FString(TEXT("/"))*/)
|
||||||
|
{
|
||||||
|
std::function<void(sio::message::list const&)> RawCallback = nullptr;
|
||||||
|
|
||||||
|
//Only have non-null raw callback if we pass in a callback function
|
||||||
|
if (CallbackFunction)
|
||||||
|
{
|
||||||
|
RawCallback = [&, CallbackFunction](const sio::message::list& response)
|
||||||
|
{
|
||||||
|
if (CallbackFunction != nullptr)
|
||||||
|
{
|
||||||
|
//Callback on game thread
|
||||||
|
if (bCallbackOnGameThread)
|
||||||
|
{
|
||||||
|
FCULambdaRunnable::RunShortLambdaOnGameThread([&, CallbackFunction, response]
|
||||||
|
{
|
||||||
|
if (CallbackFunction)
|
||||||
|
{
|
||||||
|
CallbackFunction(response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CallbackFunction(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
PrivateClient->socket(USIOMessageConvert::StdString(Namespace))->emit(
|
||||||
|
USIOMessageConvert::StdString(EventName),
|
||||||
|
MessageList,
|
||||||
|
RawCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::EmitRawBinary(const FString& EventName, uint8* Data, int32 DataLength, const FString& Namespace /*= FString(TEXT("/"))*/)
|
||||||
|
{
|
||||||
|
PrivateClient->socket(USIOMessageConvert::StdString(Namespace))->emit(USIOMessageConvert::StdString(EventName), std::make_shared<std::string>((char*)Data, DataLength));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::OnEvent(const FString& EventName,
|
||||||
|
TFunction< void(const FString&, const TSharedPtr<FJsonValue>&)> CallbackFunction,
|
||||||
|
const FString& Namespace /*= FString(TEXT("/"))*/,
|
||||||
|
ESIOThreadOverrideOption CallbackThread /*= USE_DEFAULT*/)
|
||||||
|
{
|
||||||
|
//Keep track of all the bound native JsonValue functions
|
||||||
|
FSIOBoundEvent BoundEvent;
|
||||||
|
BoundEvent.Function = CallbackFunction;
|
||||||
|
BoundEvent.Namespace = Namespace;
|
||||||
|
BoundEvent.ThreadOption = CallbackThread;
|
||||||
|
EventFunctionMap.Add(EventName, BoundEvent);
|
||||||
|
|
||||||
|
OnRawEvent(EventName, [&, CallbackFunction](const FString& Event, const sio::message::ptr& RawMessage) {
|
||||||
|
CallbackFunction(Event, USIOMessageConvert::ToJsonValue(RawMessage));
|
||||||
|
}, Namespace, CallbackThread);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::OnRawEvent(const FString& EventName,
|
||||||
|
TFunction< void(const FString&, const sio::message::ptr&)> CallbackFunction,
|
||||||
|
const FString& Namespace /*= FString(TEXT("/"))*/,
|
||||||
|
ESIOThreadOverrideOption CallbackThread /*= USE_DEFAULT*/)
|
||||||
|
{
|
||||||
|
if (CallbackFunction == nullptr)
|
||||||
|
{
|
||||||
|
PrivateClient->socket(USIOMessageConvert::StdString(Namespace))->off(USIOMessageConvert::StdString(EventName));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//determine thread override option
|
||||||
|
bool bCallbackThisEventOnGameThread = bCallbackOnGameThread;
|
||||||
|
switch (CallbackThread)
|
||||||
|
{
|
||||||
|
case USE_DEFAULT:
|
||||||
|
break;
|
||||||
|
case USE_GAME_THREAD:
|
||||||
|
bCallbackThisEventOnGameThread = true;
|
||||||
|
break;
|
||||||
|
case USE_NETWORK_THREAD:
|
||||||
|
bCallbackThisEventOnGameThread = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TFunction< void(const FString&, const sio::message::ptr&)> SafeFunction = CallbackFunction; //copy the function so it remains in context
|
||||||
|
|
||||||
|
PrivateClient->socket(USIOMessageConvert::StdString(Namespace))->on(
|
||||||
|
USIOMessageConvert::StdString(EventName),
|
||||||
|
sio::socket::event_listener_aux(
|
||||||
|
[&, SafeFunction, bCallbackThisEventOnGameThread](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list &ack_resp)
|
||||||
|
{
|
||||||
|
if (SafeFunction != nullptr)
|
||||||
|
{
|
||||||
|
const FString SafeName = USIOMessageConvert::FStringFromStd(name);
|
||||||
|
|
||||||
|
if (bCallbackThisEventOnGameThread)
|
||||||
|
{
|
||||||
|
FCULambdaRunnable::RunShortLambdaOnGameThread([&, SafeFunction, SafeName, data]
|
||||||
|
{
|
||||||
|
SafeFunction(SafeName, data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SafeFunction(SafeName, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::OnRawBinaryEvent(const FString& EventName, TFunction< void(const FString&, const TArray<uint8>&)> CallbackFunction, const FString& Namespace /*= FString(TEXT("/"))*/)
|
||||||
|
{
|
||||||
|
const TFunction< void(const FString&, const TArray<uint8>&)> SafeFunction = CallbackFunction; //copy the function so it remains in context
|
||||||
|
|
||||||
|
PrivateClient->socket(USIOMessageConvert::StdString(Namespace))->on(
|
||||||
|
USIOMessageConvert::StdString(EventName),
|
||||||
|
sio::socket::event_listener_aux(
|
||||||
|
[&, SafeFunction](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list &ack_resp)
|
||||||
|
{
|
||||||
|
const FString SafeName = USIOMessageConvert::FStringFromStd(name);
|
||||||
|
|
||||||
|
//Construct raw buffer
|
||||||
|
if (data->get_flag() == sio::message::flag_binary)
|
||||||
|
{
|
||||||
|
TArray<uint8> Buffer;
|
||||||
|
int32 BufferSize = data->get_binary()->size();
|
||||||
|
auto MessageBuffer = data->get_binary();
|
||||||
|
Buffer.Append((uint8*)(MessageBuffer->data()), BufferSize);
|
||||||
|
|
||||||
|
if (bCallbackOnGameThread)
|
||||||
|
{
|
||||||
|
FCULambdaRunnable::RunShortLambdaOnGameThread([&, SafeFunction, SafeName, Buffer]
|
||||||
|
{
|
||||||
|
SafeFunction(SafeName, Buffer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SafeFunction(SafeName, Buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(SocketIO, Warning, TEXT("Non-binary message received to binary message lambda, check server message data!"));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::UnbindEvent(const FString& EventName, const FString& Namespace /*= TEXT("/")*/)
|
||||||
|
{
|
||||||
|
OnRawEvent(EventName, nullptr, Namespace);
|
||||||
|
EventFunctionMap.Remove(EventName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::ClearInternalCallbacks()
|
||||||
|
{
|
||||||
|
PrivateClient->clear_socket_listeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::SetupInternalCallbacks()
|
||||||
|
{
|
||||||
|
PrivateClient->set_open_listener(sio::client::con_listener([&]()
|
||||||
|
{
|
||||||
|
//too early to get session id here so we defer the connection event until we connect to a namespace
|
||||||
|
}));
|
||||||
|
|
||||||
|
PrivateClient->set_close_listener(sio::client::close_listener([&](sio::client::close_reason const& reason)
|
||||||
|
{
|
||||||
|
bIsConnected = false;
|
||||||
|
|
||||||
|
ESIOConnectionCloseReason DisconnectReason = (ESIOConnectionCloseReason)reason;
|
||||||
|
FString DisconnectReasonString = UEnum::GetValueAsString<ESIOConnectionCloseReason>(DisconnectReason);
|
||||||
|
if (VerboseLog)
|
||||||
|
{
|
||||||
|
UE_LOG(SocketIO, Log, TEXT("SocketIO Disconnected %s reason: %s"), *SessionId, *DisconnectReasonString);
|
||||||
|
}
|
||||||
|
LastSessionId = SessionId;
|
||||||
|
SessionId = TEXT("Invalid");
|
||||||
|
|
||||||
|
if (OnDisconnectedCallback)
|
||||||
|
{
|
||||||
|
if (bCallbackOnGameThread)
|
||||||
|
{
|
||||||
|
FCULambdaRunnable::RunShortLambdaOnGameThread([&, DisconnectReason]
|
||||||
|
{
|
||||||
|
if (OnDisconnectedCallback)
|
||||||
|
{
|
||||||
|
OnDisconnectedCallback(DisconnectReason);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OnDisconnectedCallback(DisconnectReason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
PrivateClient->set_socket_open_listener(sio::client::socket_listener([&](std::string const& nsp)
|
||||||
|
{
|
||||||
|
//Special case, we have a latent connection after already having been disconnected
|
||||||
|
if (!PrivateClient.IsValid())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!bIsConnected)
|
||||||
|
{
|
||||||
|
bIsConnected = true;
|
||||||
|
SessionId = USIOMessageConvert::FStringFromStd(PrivateClient->get_sessionid());
|
||||||
|
SocketId = USIOMessageConvert::FStringFromStd(PrivateClient->socket(nsp)->get_socket_id());
|
||||||
|
|
||||||
|
if (VerboseLog)
|
||||||
|
{
|
||||||
|
UE_LOG(SocketIO, Log, TEXT("SocketIO Connected with session: %s"), *SessionId);
|
||||||
|
}
|
||||||
|
if (OnConnectedCallback)
|
||||||
|
{
|
||||||
|
if (bCallbackOnGameThread)
|
||||||
|
{
|
||||||
|
FCULambdaRunnable::RunShortLambdaOnGameThread([&]
|
||||||
|
{
|
||||||
|
if (OnConnectedCallback)
|
||||||
|
{
|
||||||
|
OnConnectedCallback(SocketId, SessionId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OnConnectedCallback(SocketId, SessionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const FString Namespace = USIOMessageConvert::FStringFromStd(nsp);
|
||||||
|
|
||||||
|
if (VerboseLog)
|
||||||
|
{
|
||||||
|
UE_LOG(SocketIO, Log, TEXT("SocketIO %s connected to namespace: %s"), *SessionId, *Namespace);
|
||||||
|
}
|
||||||
|
if (OnNamespaceConnectedCallback)
|
||||||
|
{
|
||||||
|
if (bCallbackOnGameThread)
|
||||||
|
{
|
||||||
|
FCULambdaRunnable::RunShortLambdaOnGameThread([&, Namespace]
|
||||||
|
{
|
||||||
|
if (OnNamespaceConnectedCallback)
|
||||||
|
{
|
||||||
|
OnNamespaceConnectedCallback(Namespace);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OnNamespaceConnectedCallback(Namespace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
PrivateClient->set_socket_close_listener(sio::client::socket_listener([&](std::string const& nsp)
|
||||||
|
{
|
||||||
|
const FString Namespace = USIOMessageConvert::FStringFromStd(nsp);
|
||||||
|
FString NamespaceSession = SessionId;
|
||||||
|
if (NamespaceSession.Equals(TEXT("Invalid")))
|
||||||
|
{
|
||||||
|
NamespaceSession = LastSessionId;
|
||||||
|
}
|
||||||
|
if (VerboseLog)
|
||||||
|
{
|
||||||
|
UE_LOG(SocketIO, Log, TEXT("SocketIO %s disconnected from namespace: %s"), *NamespaceSession, *Namespace);
|
||||||
|
}
|
||||||
|
if (OnNamespaceDisconnectedCallback)
|
||||||
|
{
|
||||||
|
if (bCallbackOnGameThread)
|
||||||
|
{
|
||||||
|
FCULambdaRunnable::RunShortLambdaOnGameThread([&, Namespace]
|
||||||
|
{
|
||||||
|
if (OnNamespaceDisconnectedCallback)
|
||||||
|
{
|
||||||
|
OnNamespaceDisconnectedCallback(Namespace);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OnNamespaceDisconnectedCallback(Namespace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
PrivateClient->set_fail_listener(sio::client::con_listener([&]()
|
||||||
|
{
|
||||||
|
if (VerboseLog)
|
||||||
|
{
|
||||||
|
UE_LOG(SocketIO, Log, TEXT("SocketIO failed to connect."));
|
||||||
|
}
|
||||||
|
if (OnFailCallback)
|
||||||
|
{
|
||||||
|
if (bCallbackOnGameThread)
|
||||||
|
{
|
||||||
|
FCULambdaRunnable::RunShortLambdaOnGameThread([&]
|
||||||
|
{
|
||||||
|
if (OnFailCallback)
|
||||||
|
{
|
||||||
|
OnFailCallback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OnFailCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
PrivateClient->set_reconnect_listener(sio::client::reconnect_listener([&](unsigned num, unsigned delay)
|
||||||
|
{
|
||||||
|
bIsConnected = false;
|
||||||
|
|
||||||
|
if (VerboseLog)
|
||||||
|
{
|
||||||
|
UE_LOG(SocketIO, Log, TEXT("SocketIO %s appears to have lost connection, reconnecting attempt %d with delay %d"), *SessionId, num, delay);
|
||||||
|
}
|
||||||
|
if (OnReconnectionCallback)
|
||||||
|
{
|
||||||
|
if (bCallbackOnGameThread)
|
||||||
|
{
|
||||||
|
FCULambdaRunnable::RunShortLambdaOnGameThread([&, num, delay]
|
||||||
|
{
|
||||||
|
if (OnReconnectionCallback)
|
||||||
|
{
|
||||||
|
OnReconnectionCallback(num, delay);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OnReconnectionCallback(num, delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::RebindCurrentEventMap()
|
||||||
|
{
|
||||||
|
ClearInternalCallbacks();
|
||||||
|
|
||||||
|
for (auto& EventPair : EventFunctionMap)
|
||||||
|
{
|
||||||
|
const FString& EventName = EventPair.Key;
|
||||||
|
const FSIOBoundEvent EventBind = EventPair.Value;
|
||||||
|
|
||||||
|
OnRawEvent(EventName, [&, EventBind](const FString& Event, const sio::message::ptr& RawMessage) {
|
||||||
|
EventBind.Function(Event, USIOMessageConvert::ToJsonValue(RawMessage));
|
||||||
|
}, EventBind.Namespace, EventBind.ThreadOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetupInternalCallbacks();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FSocketIONative::IsTLSURL(const FString& URL)
|
||||||
|
{
|
||||||
|
return URL.StartsWith(TEXT("https://")) || URL.StartsWith(TEXT("wss://"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSocketIONative::SyncPrivateClientToTLSMode(const FString& URL)
|
||||||
|
{
|
||||||
|
//Should be in TLS mode?
|
||||||
|
if (IsTLSURL(URL) || bForceTLSUse)
|
||||||
|
{
|
||||||
|
//needs to swap to TLS
|
||||||
|
if (!bIsSetupForTLS)
|
||||||
|
{
|
||||||
|
if (PrivateClient->opened())
|
||||||
|
{
|
||||||
|
PrivateClient->sync_close();
|
||||||
|
}
|
||||||
|
ClearInternalCallbacks();
|
||||||
|
InitPrivateClient(true, bUsingTLSCertVerification);
|
||||||
|
RebindCurrentEventMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Should not be in TLS mode
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//URL is not TLS, but we are setup for it
|
||||||
|
if (bIsSetupForTLS)
|
||||||
|
{
|
||||||
|
if (PrivateClient->opened())
|
||||||
|
{
|
||||||
|
PrivateClient->sync_close();
|
||||||
|
}
|
||||||
|
ClearInternalCallbacks();
|
||||||
|
InitPrivateClient(false, bUsingTLSCertVerification);
|
||||||
|
RebindCurrentEventMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,72 @@
|
|||||||
|
// Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Dom/JsonValue.h"
|
||||||
|
#include "Dom/JsonObject.h"
|
||||||
|
#include "sio_client.h"
|
||||||
|
#include "SIOJsonValue.h"
|
||||||
|
#include "SIOMessageConvert.generated.h"
|
||||||
|
|
||||||
|
DECLARE_LOG_CATEGORY_EXTERN(SocketIO, Log, All);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All params defining a connection URL.
|
||||||
|
*/
|
||||||
|
USTRUCT(BlueprintType)
|
||||||
|
struct SOCKETIOCLIENT_API FSIOConnectParams
|
||||||
|
{
|
||||||
|
GENERATED_USTRUCT_BODY();
|
||||||
|
|
||||||
|
/** Default connection address string in form e.g. http://localhost:80. */
|
||||||
|
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = SocketIOConnectionParams)
|
||||||
|
FString AddressAndPort;
|
||||||
|
|
||||||
|
/** A map of query parameters to be added to connection url*/
|
||||||
|
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = SocketIOConnectionParams)
|
||||||
|
TMap<FString, FString> Query;
|
||||||
|
|
||||||
|
/** A map of headers to be added to connection url*/
|
||||||
|
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = SocketIOConnectionParams)
|
||||||
|
TMap<FString, FString> Headers;
|
||||||
|
|
||||||
|
/** Optional authorization JSON to send to the server upon initial connection*/
|
||||||
|
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = SocketIOConnectionParams)
|
||||||
|
FString AuthToken;
|
||||||
|
|
||||||
|
/** Optional path part of URL string. Default is 'socket.io'*/
|
||||||
|
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = SocketIOConnectionParams)
|
||||||
|
FString Path;
|
||||||
|
|
||||||
|
FSIOConnectParams()
|
||||||
|
{
|
||||||
|
AddressAndPort = TEXT("http://localhost:3000");
|
||||||
|
Path = TEXT("socket.io");
|
||||||
|
AuthToken = TEXT("");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static Conversion Utilities
|
||||||
|
*/
|
||||||
|
UCLASS()
|
||||||
|
class SOCKETIOCLIENT_API USIOMessageConvert : public UObject
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
public:
|
||||||
|
|
||||||
|
//To from:
|
||||||
|
|
||||||
|
//sio::message <-> FJsonValue
|
||||||
|
static TSharedPtr<FJsonValue> ToJsonValue(const sio::message::ptr& Message);
|
||||||
|
static sio::message::ptr ToSIOMessage(const TSharedPtr<FJsonValue>& JsonValue);
|
||||||
|
|
||||||
|
//std::string <-> FString
|
||||||
|
static std::string StdString(FString UEString);
|
||||||
|
static FString FStringFromStd(std::string StdString);
|
||||||
|
|
||||||
|
static std::map<std::string, std::string> JsonObjectToStdStringMap(TSharedPtr<FJsonObject> InObject);
|
||||||
|
static TMap<FString, FString> JsonObjectToFStringMap(TSharedPtr<FJsonObject> InObject);
|
||||||
|
static std::map<std::string, std::string> FStringMapToStdStringMap(const TMap<FString, FString>& InMap);
|
||||||
|
};
|
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Runtime/Core/Public/Modules/ModuleManager.h"
|
||||||
|
class FSocketIONative;
|
||||||
|
|
||||||
|
|
||||||
|
class SOCKETIOCLIENT_API ISocketIOClientModule : 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 ISocketIOClientModule& Get()
|
||||||
|
{
|
||||||
|
return FModuleManager::LoadModuleChecked< ISocketIOClientModule >("SocketIOClient");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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("SocketIOClient");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request a new plugin scoped pointer as a shared ptr.
|
||||||
|
*/
|
||||||
|
virtual TSharedPtr<FSocketIONative> NewValidNativePointer(const bool bShouldUseTlsLibraries = false, const bool bShouldVerifyTLSCertificate = false) { return nullptr; };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request a shared FSocketIONative instance for a given id. May allocate a new pointer.
|
||||||
|
*/
|
||||||
|
virtual TSharedPtr<FSocketIONative> ValidSharedNativePointer(FString SharedId, const bool bShouldUseTlsLibraries, const bool bShouldVerifyTLSCertificate) { return nullptr; };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases the given plugin scoped pointer using a background thread
|
||||||
|
* After calling this function make sure to set your pointer to nullptr.
|
||||||
|
*/
|
||||||
|
virtual void ReleaseNativePointer(TSharedPtr<FSocketIONative> PointerToRelease) {};
|
||||||
|
};
|
@ -0,0 +1,493 @@
|
|||||||
|
// Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Components/ActorComponent.h"
|
||||||
|
#include "SocketIONative.h"
|
||||||
|
#include "Runtime/Engine/Classes/Engine/LatentActionManager.h"
|
||||||
|
#include "SocketIOClientComponent.generated.h"
|
||||||
|
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FSIOCEventSignature);
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSIOCSocketEventSignature, FString, Namespace);
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FSIOCOpenEventSignature, FString, SocketId, FString, SessionId, bool, bIsReconnection);
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSIOCCloseEventSignature, TEnumAsByte<ESIOConnectionCloseReason>, Reason);
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FSIOCEventJsonSignature, FString, EventName, class USIOJsonValue*, EventData);
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FSIOConnectionProblemSignature, int32, Attempts, int32, NextAttemptInMs, float, TimeSinceConnected);
|
||||||
|
|
||||||
|
//For Direct Delegate Event Bind
|
||||||
|
DECLARE_DYNAMIC_DELEGATE_OneParam(FSIOJsonValueSignature, USIOJsonValue*, EventData);
|
||||||
|
|
||||||
|
UCLASS(BlueprintType, ClassGroup = "Networking", meta = (BlueprintSpawnableComponent))
|
||||||
|
class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent
|
||||||
|
{
|
||||||
|
GENERATED_UCLASS_BODY()
|
||||||
|
public:
|
||||||
|
|
||||||
|
//Async events
|
||||||
|
|
||||||
|
/** On generic bound event received. Requires Bind Event to Generic Event to be called before. Will not receive Bind Event To Function events. */
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "SocketIO Events")
|
||||||
|
FSIOCEventJsonSignature OnGenericEvent;
|
||||||
|
|
||||||
|
/** Received on socket.io connection established. */
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "SocketIO Events")
|
||||||
|
FSIOCOpenEventSignature OnConnected;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Received on socket.io connection disconnected. This may never get
|
||||||
|
* called in default settings, see OnConnectionProblems event for details.
|
||||||
|
*/
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "SocketIO Events")
|
||||||
|
FSIOCCloseEventSignature OnDisconnected;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Received when connection problems arise. In default settings the
|
||||||
|
* connection will keep repeating trying to reconnect an infinite
|
||||||
|
* amount of times and you may never get OnDisconnected callback
|
||||||
|
* unless you call it.
|
||||||
|
*/
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "SocketIO Events")
|
||||||
|
FSIOConnectionProblemSignature OnConnectionProblems;
|
||||||
|
|
||||||
|
/** Received on having joined namespace. */
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "SocketIO Events")
|
||||||
|
FSIOCSocketEventSignature OnSocketNamespaceConnected;
|
||||||
|
|
||||||
|
/** Received on having left namespace. */
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "SocketIO Events")
|
||||||
|
FSIOCSocketEventSignature OnSocketNamespaceDisconnected;
|
||||||
|
|
||||||
|
/** Received on connection failure. */
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "SocketIO Events")
|
||||||
|
FSIOCEventSignature OnFail;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default connection params used on e.g. on begin play. Can be updated and re-used on custom connection.
|
||||||
|
*/
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Connection Properties")
|
||||||
|
FSIOConnectParams URLParams;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will force using TLS even if url doesn't have https:// prepend.
|
||||||
|
*/
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Connection Properties")
|
||||||
|
bool bForceTLS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true, all your bound events will unbind on disconnect.
|
||||||
|
* Useful for cleanup if typically binding on connection and there
|
||||||
|
* are no early event binds (before connection).
|
||||||
|
*/
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Connection Properties")
|
||||||
|
bool bUnbindEventsOnDisconnect;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If using a TLS url (or if forced) and setting this to false will not verify
|
||||||
|
* the authenticity of the SSL certificate (i.e. asio::ssl::verify_none).
|
||||||
|
*
|
||||||
|
* NOTE: Certification verification is currently not implemented; setting to true will
|
||||||
|
* always fail verification.
|
||||||
|
*/
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Connection Properties")
|
||||||
|
bool bShouldVerifyTLSCertificate;
|
||||||
|
|
||||||
|
/** If true will auto-connect on begin play to address specified in AddressAndPort. */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Connection Properties")
|
||||||
|
bool bShouldAutoConnect;
|
||||||
|
|
||||||
|
/** Delay between reconnection attempts */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Connection Properties")
|
||||||
|
int32 ReconnectionDelayInMs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of times the connection should try before giving up.
|
||||||
|
* Default: infinity, this means you never truly disconnect, just suffer connection problems
|
||||||
|
*/
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Connection Properties")
|
||||||
|
int32 MaxReconnectionAttempts;
|
||||||
|
|
||||||
|
/** Optional parameter to limit reconnections by elapsed time. Default: infinity. */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Connection Properties")
|
||||||
|
float ReconnectionTimeout;
|
||||||
|
|
||||||
|
FDateTime TimeWhenConnectionProblemsStarted;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Connection Properties")
|
||||||
|
bool bVerboseConnectionLog;
|
||||||
|
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Scope Properties")
|
||||||
|
bool bLimitConnectionToGameWorld;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle which enables plugin scoped connections.
|
||||||
|
* If you enable this the connection will remain until you manually call disconnect
|
||||||
|
* or close the app. The latest connection with the same PluginScopedId will use the same connection
|
||||||
|
* as the previous one and receive the same events.
|
||||||
|
*/
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Scope Properties")
|
||||||
|
bool bPluginScopedConnection;
|
||||||
|
|
||||||
|
/** If you leave this as is all plugin scoped connection components will share same connection*/
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Scope Properties")
|
||||||
|
FString PluginScopedId;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category = "SocketIO Connection Properties")
|
||||||
|
bool bIsConnected;
|
||||||
|
|
||||||
|
/** When connected this session id will be valid and contain a unique Id. */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category = "SocketIO Connection Properties")
|
||||||
|
FString SessionId;
|
||||||
|
|
||||||
|
/** Each new connection is assigned a random 20-characters identifier. This identifier is synced with the value on the client-side. */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category = "SocketIO Connection Properties")
|
||||||
|
FString SocketId;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category = "SocketIO Connection Properties")
|
||||||
|
bool bIsHavingConnectionProblems;
|
||||||
|
|
||||||
|
/** If this component has been statically initialized. Largely exposed for traceability. */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category = "SocketIO Connection Properties")
|
||||||
|
bool bStaticallyInitialized;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to a socket.io server, optional method if auto-connect is set to true.
|
||||||
|
* Query and headers are defined by a {'stringKey':'stringValue'} SIOJson Object
|
||||||
|
*
|
||||||
|
* @param AddressAndPort the address in URL format with port, if left empty it will
|
||||||
|
* use current URLParams for all inputs.
|
||||||
|
* @param Path optional ws:// trailing path for socket.io connection
|
||||||
|
* @param Query http query as a SIOJsonObject with string keys and values
|
||||||
|
* @param Headers http header as a SIOJsonObject with string keys and values
|
||||||
|
* @param Auth socket.io authorization option as a SIOJsonObject with string keys and values
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SocketIO Functions")
|
||||||
|
void Connect( const FString& InAddressAndPort = TEXT(""),
|
||||||
|
const FString& InPath = TEXT("socket.io"),
|
||||||
|
const FString& InAuthToken = TEXT(""),
|
||||||
|
USIOJsonObject* Query = nullptr,
|
||||||
|
USIOJsonObject* Headers = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to a socket.io server, optional method if auto-connect is set to true.
|
||||||
|
*
|
||||||
|
* @param InURLParams - A struct holding address&port, path, headers, query, and auth params
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SocketIO Functions")
|
||||||
|
void ConnectWithParams(const FSIOConnectParams& InURLParams);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnect from current socket.io server. This is an asynchronous action,
|
||||||
|
* subscribe to OnDisconnected to know when you can safely continue from a
|
||||||
|
* disconnected state.
|
||||||
|
*
|
||||||
|
* @param AddressAndPort the address in URL format with port
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SocketIO Functions")
|
||||||
|
void Disconnect();
|
||||||
|
|
||||||
|
void SyncDisconnect();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Join a desired namespace. Keep in mind that emitting to a namespace will auto-join it
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SocketIO Functions")
|
||||||
|
void JoinNamespace(const FString& Namespace);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Leave a specified namespace. Should stop listening to events on given namespace.
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SocketIO Functions")
|
||||||
|
void LeaveNamespace(const FString& Namespace);
|
||||||
|
|
||||||
|
//
|
||||||
|
//Blueprint Functions
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit an event with a JsonValue message
|
||||||
|
*
|
||||||
|
* @param Name Event name
|
||||||
|
* @param Message SIOJJsonValue
|
||||||
|
* @param Namespace Namespace within socket.io
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SocketIO Functions")
|
||||||
|
void Emit(const FString& EventName, USIOJsonValue* Message = nullptr, const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit an event with a JsonValue message with a callback function defined by CallBackFunctionName
|
||||||
|
*
|
||||||
|
* @param Name Event name
|
||||||
|
* @param Message SIOJsonValue
|
||||||
|
* @param CallbackFunctionName Name of the optional callback function with signature (String, SIOJsonValue)
|
||||||
|
* @param Target Optional, defaults to caller self. Change to delegate function callback to another class.
|
||||||
|
* @param Namespace Namespace within socket.io
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SocketIO Functions", meta = (WorldContext = "WorldContextObject"))
|
||||||
|
void EmitWithCallBack( const FString& EventName,
|
||||||
|
USIOJsonValue* Message = nullptr,
|
||||||
|
const FString& CallbackFunctionName = FString(""),
|
||||||
|
UObject* Target = nullptr,
|
||||||
|
const FString& Namespace = TEXT("/"),
|
||||||
|
UObject* WorldContextObject = nullptr);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit an event with a JsonValue message with a result callback directly into the event graph. This cannot be called from within blueprint functions.
|
||||||
|
*
|
||||||
|
* @param Name Event name
|
||||||
|
* @param LatentInfo Graph callback reference
|
||||||
|
* @param Result Graph callback result SIOJsonValue
|
||||||
|
* @param Message SIOJsonValue
|
||||||
|
* @param Namespace Namespace within socket.io
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, meta = (Latent, LatentInfo = "LatentInfo"), Category = "SocketIO Functions")
|
||||||
|
void EmitWithGraphCallBack( const FString& EventName,
|
||||||
|
struct FLatentActionInfo LatentInfo,
|
||||||
|
USIOJsonValue*& Result,
|
||||||
|
USIOJsonValue* Message = nullptr,
|
||||||
|
const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind an event directly to a matching delegate. Drag off from red box or
|
||||||
|
* use create event option.
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param CallbackDelegate Delegate that needs to be bound
|
||||||
|
* @param Namespace Optional namespace, defaults to default namespace
|
||||||
|
* @param ThreadOverride Optional override to receive event on specified thread. Note NETWORK thread is lower latency but unsafe for a lot of blueprint use. Use with CAUTION.
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SocketIO Functions")
|
||||||
|
void BindEventToDelegate( const FString& EventName,
|
||||||
|
const FSIOJsonValueSignature& CallbackDelegate,
|
||||||
|
const FString& Namespace = TEXT("/"),
|
||||||
|
ESIOThreadOverrideOption ThreadOverride = USE_DEFAULT);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind an event, then respond to it with 'OnGenericEvent' multi-cast delegate.
|
||||||
|
* If you want functions or custom events to receive the event, use Bind Event To Function.
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param Namespace Optional namespace, defaults to default namespace
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SocketIO Functions")
|
||||||
|
void BindEventToGenericEvent(const FString& EventName, const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind an event to a function with the given name.
|
||||||
|
* Expects a String message signature which can be decoded from JSON into SIOJsonObject
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param FunctionName The function that gets called when the event is received
|
||||||
|
* @param Target Optional, defaults to caller self. Change to delegate to another class.
|
||||||
|
* @param Namespace Optional namespace, defaults to default namespace
|
||||||
|
* @param ThreadOverride Optional override to receive event on specified thread. Note NETWORK thread is lower latency but unsafe for a lot of blueprint use. Use with CAUTION.
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SocketIO Functions", meta = (WorldContext = "WorldContextObject"))
|
||||||
|
void BindEventToFunction( const FString& EventName,
|
||||||
|
const FString& FunctionName,
|
||||||
|
UObject* Target,
|
||||||
|
const FString& Namespace = TEXT("/"),
|
||||||
|
ESIOThreadOverrideOption ThreadOverride = USE_DEFAULT,
|
||||||
|
UObject* WorldContextObject = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unbind an event from whatever it was bound to (safe to call if not already bound)
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param Namespace Optional namespace, defaults to default namespace
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SocketIO Functions")
|
||||||
|
void UnbindEvent(const FString& EventName, const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
//C++ functions
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to a socket.io server, optional method if auto-connect is set to true.
|
||||||
|
* Query and headers are defined by a {'stringKey':'stringValue'} FJsonObjects
|
||||||
|
*
|
||||||
|
* @param AddressAndPort the address in URL format with port
|
||||||
|
* @param Path the path of the socketio server (ex: "socket.io"
|
||||||
|
* @param Query http query as a FJsonObject with string keys and values
|
||||||
|
* @param Headers http header as a FJsonObject with string keys and values
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void ConnectNative( const FString& InAddressAndPort,
|
||||||
|
const FString& InPath = TEXT("socket.io"),
|
||||||
|
const FString& InAuthToken = TEXT(""),
|
||||||
|
const TSharedPtr<FJsonObject>& Query = nullptr,
|
||||||
|
const TSharedPtr<FJsonObject>& Headers = nullptr);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit an event with a JsonValue message
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param Message FJsonValue
|
||||||
|
* @param CallbackFunction Optional callback TFunction
|
||||||
|
* @param Namespace Optional Namespace within socket.io
|
||||||
|
*/
|
||||||
|
void EmitNative(const FString& EventName,
|
||||||
|
const TSharedPtr<FJsonValue>& Message = nullptr,
|
||||||
|
TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction = nullptr,
|
||||||
|
const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Overloaded) Emit an event with a Json Object message
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param ObjectMessage FJsonObject
|
||||||
|
* @param CallbackFunction Optional callback TFunction
|
||||||
|
* @param Namespace Optional Namespace within socket.io
|
||||||
|
*/
|
||||||
|
void EmitNative(const FString& EventName,
|
||||||
|
const TSharedPtr<FJsonObject>& ObjectMessage = nullptr,
|
||||||
|
TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction = nullptr,
|
||||||
|
const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Overloaded) Emit an event with a string message
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param StringMessage Message in string format
|
||||||
|
* @param CallbackFunction Optional callback TFunction
|
||||||
|
* @param Namespace Optional Namespace within socket.io
|
||||||
|
*/
|
||||||
|
void EmitNative(const FString& EventName,
|
||||||
|
const FString& StringMessage = FString(),
|
||||||
|
TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction = nullptr,
|
||||||
|
const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Overloaded) Emit an event with a string message
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param StringMessage Message in string format
|
||||||
|
* @param CallbackFunction Optional callback TFunction
|
||||||
|
* @param Namespace Optional Namespace within socket.io
|
||||||
|
*/
|
||||||
|
void EmitNative(const FString& EventName,
|
||||||
|
const SIO_TEXT_TYPE StringMessage = TEXT(""),
|
||||||
|
TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction = nullptr,
|
||||||
|
const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Overloaded) Emit an event with a number (double) message
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param NumberMessage Message in double format
|
||||||
|
* @param CallbackFunction Optional callback TFunction
|
||||||
|
* @param Namespace Optional Namespace within socket.io
|
||||||
|
*/
|
||||||
|
void EmitNative(const FString& EventName,
|
||||||
|
double NumberMessage,
|
||||||
|
TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction = nullptr,
|
||||||
|
const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Overloaded) Emit an event with a bool message
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param BooleanMessage Message in bool format
|
||||||
|
* @param CallbackFunction Optional callback TFunction
|
||||||
|
* @param Namespace Optional Namespace within socket.io
|
||||||
|
*/
|
||||||
|
void EmitNative(const FString& EventName,
|
||||||
|
bool BooleanMessage,
|
||||||
|
TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction = nullptr,
|
||||||
|
const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Overloaded) Emit an event with a binary message
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param BinaryMessage Message in an TArray of uint8
|
||||||
|
* @param CallbackFunction Optional callback TFunction
|
||||||
|
* @param Namespace Optional Namespace within socket.io
|
||||||
|
*/
|
||||||
|
void EmitNative(const FString& EventName,
|
||||||
|
const TArray<uint8>& BinaryMessage,
|
||||||
|
TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction = nullptr,
|
||||||
|
const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Overloaded) Emit an event with an array message
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param ArrayMessage Message in an TArray of FJsonValues
|
||||||
|
* @param CallbackFunction Optional callback TFunction
|
||||||
|
* @param Namespace Optional Namespace within socket.io
|
||||||
|
*/
|
||||||
|
void EmitNative(const FString& EventName,
|
||||||
|
const TArray<TSharedPtr<FJsonValue>>& ArrayMessage,
|
||||||
|
TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction = nullptr,
|
||||||
|
const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Overloaded) Emit an event with an UStruct message
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param Struct UStruct type usually obtained via e.g. FMyStructType::StaticStruct()
|
||||||
|
* @param StructPtr Pointer to the actual struct memory e.g. &MyStruct
|
||||||
|
* @param CallbackFunction Optional callback TFunction
|
||||||
|
* @param Namespace Optional Namespace within socket.io
|
||||||
|
*/
|
||||||
|
void EmitNative(const FString& EventName,
|
||||||
|
UStruct* Struct,
|
||||||
|
const void* StructPtr,
|
||||||
|
TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction = nullptr,
|
||||||
|
const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call function callback on receiving socket event. C++ only.
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param TFunction Lambda callback, JSONValue
|
||||||
|
* @param Namespace Optional namespace, defaults to default namespace
|
||||||
|
* @param ThreadOverride Optional override to receive event on specified thread. Note NETWORK thread is lower latency but unsafe for a lot of blueprint use. Use with CAUTION.
|
||||||
|
*/
|
||||||
|
void OnNativeEvent( const FString& EventName,
|
||||||
|
TFunction< void(const FString&, const TSharedPtr<FJsonValue>&)> CallbackFunction,
|
||||||
|
const FString& Namespace = TEXT("/"),
|
||||||
|
ESIOThreadOverrideOption ThreadOverride = USE_DEFAULT);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call function callback on receiving binary event. C++ only.
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param TFunction Lambda callback, raw flavor
|
||||||
|
* @param Namespace Optional namespace, defaults to default namespace
|
||||||
|
*/
|
||||||
|
void OnBinaryEvent( const FString& EventName,
|
||||||
|
TFunction< void(const FString&, const TArray<uint8>&)> CallbackFunction,
|
||||||
|
const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
|
||||||
|
/** Called by SocketIOFunctionLibrary to initialize statically constructed components. */
|
||||||
|
void StaticInitialization(UObject* WorldContextObject, bool bValidOwnerWorld);
|
||||||
|
|
||||||
|
virtual void InitializeComponent() override;
|
||||||
|
virtual void UninitializeComponent() override;
|
||||||
|
virtual void BeginPlay() override;
|
||||||
|
|
||||||
|
~USocketIOClientComponent();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void SetupCallbacks();
|
||||||
|
void ClearCallbacks();
|
||||||
|
void InitializeNative();
|
||||||
|
|
||||||
|
bool CallBPFunctionWithResponse(UObject* Target, const FString& FunctionName, TArray<TSharedPtr<FJsonValue>> Response);
|
||||||
|
bool CallBPFunctionWithMessage(UObject* Target, const FString& FunctionName, TSharedPtr<FJsonValue> Message);
|
||||||
|
|
||||||
|
FCriticalSection AllocationSection;
|
||||||
|
TSharedPtr<FSocketIONative> NativeClient;
|
||||||
|
};
|
@ -0,0 +1,27 @@
|
|||||||
|
// Copyright 2019-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||||
|
#include "SocketIOClientComponent.h"
|
||||||
|
#include "SocketIOFunctionLibrary.generated.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static spawning support library
|
||||||
|
*/
|
||||||
|
UCLASS()
|
||||||
|
class SOCKETIOCLIENT_API USocketIOFunctionLibrary : public UBlueprintFunctionLibrary
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** Static function to spawn a component that doesn't attach */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SocketIO Client Static", meta = (WorldContext = "WorldContextObject"))
|
||||||
|
static USocketIOClientComponent* ConstructSocketIOComponent(UObject* WorldContextObject);
|
||||||
|
|
||||||
|
/** Call a function by name with SIOJsonValue signature. Utility for RPC in BPs*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "SocketIO Utility", meta = (WorldContext = "WorldContextObject"))
|
||||||
|
static bool CallFunctionByName(const FString& FunctionName, UObject* Target, UObject* WorldContextObject, USIOJsonValue* Param);
|
||||||
|
};
|
@ -0,0 +1,392 @@
|
|||||||
|
// Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sio_client.h"
|
||||||
|
#include "SIOJsonObject.h"
|
||||||
|
#include "SIOJsonValue.h"
|
||||||
|
#include "SIOJConvert.h"
|
||||||
|
#include "SIOMessageConvert.h"
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
|
||||||
|
UENUM(BlueprintType)
|
||||||
|
enum ESIOConnectionCloseReason
|
||||||
|
{
|
||||||
|
CLOSE_REASON_NORMAL,
|
||||||
|
CLOSE_REASON_DROP
|
||||||
|
};
|
||||||
|
|
||||||
|
UENUM(BlueprintType)
|
||||||
|
enum ESIOThreadOverrideOption
|
||||||
|
{
|
||||||
|
USE_DEFAULT,
|
||||||
|
USE_GAME_THREAD,
|
||||||
|
USE_NETWORK_THREAD
|
||||||
|
};
|
||||||
|
|
||||||
|
//Used in early (pre-connection) binds and maintaining map if re-setting connection type
|
||||||
|
struct FSIOBoundEvent
|
||||||
|
{
|
||||||
|
TFunction< void(const FString&, const TSharedPtr<FJsonValue>&)> Function;
|
||||||
|
FString Namespace;
|
||||||
|
ESIOThreadOverrideOption ThreadOption;
|
||||||
|
|
||||||
|
FSIOBoundEvent()
|
||||||
|
{
|
||||||
|
Namespace = TEXT("/");
|
||||||
|
ThreadOption = USE_DEFAULT;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class SOCKETIOCLIENT_API FSocketIONative
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/** By default TLS verification is off. TLS mode will be set by URL on connect.*/
|
||||||
|
FSocketIONative(const bool bForceTLSMode = false, const bool bShouldVerifyTLSCertificate = false);
|
||||||
|
|
||||||
|
//Native Callbacks
|
||||||
|
TFunction<void(const FString& SocketId, const FString& SessionId)> OnConnectedCallback; //TFunction<void(const FString& SessionId)>
|
||||||
|
TFunction<void(const ESIOConnectionCloseReason Reason)> OnDisconnectedCallback; //TFunction<void(const ESIOConnectionCloseReason Reason)>
|
||||||
|
TFunction<void(const FString& Namespace)> OnNamespaceConnectedCallback; //TFunction<void(const FString& Namespace)>
|
||||||
|
TFunction<void(const FString& Namespace)> OnNamespaceDisconnectedCallback; //TFunction<void(const FString& Namespace)>
|
||||||
|
TFunction<void(const uint32 AttemptCount, const uint32 DelayInMs)> OnReconnectionCallback;
|
||||||
|
TFunction<void()> OnFailCallback;
|
||||||
|
|
||||||
|
//Map for all native functions bound to this socket
|
||||||
|
TMap<FString, FSIOBoundEvent> EventFunctionMap;
|
||||||
|
|
||||||
|
/** Address& Port, Path, Query, Headers, & Auth message */
|
||||||
|
FSIOConnectParams URLParams;
|
||||||
|
|
||||||
|
/** The number of attempts before giving up. 0 = infinity. Set before connecting*/
|
||||||
|
uint32 MaxReconnectionAttempts;
|
||||||
|
|
||||||
|
/** in milliseconds, default is 5000 */
|
||||||
|
uint32 ReconnectionDelay;
|
||||||
|
|
||||||
|
/** Whether this instance has a currently live connection to the server. */
|
||||||
|
bool bIsConnected;
|
||||||
|
|
||||||
|
/** When connected this session id will be valid and contain a unique Id. */
|
||||||
|
FString SessionId;
|
||||||
|
|
||||||
|
/** Each new connection is assigned a random 20-characters identifier. This identifier is synced with the value on the client-side. */
|
||||||
|
FString SocketId;
|
||||||
|
|
||||||
|
//This will remain valid even after we disconnect. Replaced on disconnect.
|
||||||
|
FString LastSessionId;
|
||||||
|
|
||||||
|
/** If set to true, each state change callback will log to console*/
|
||||||
|
bool VerboseLog;
|
||||||
|
|
||||||
|
/** If true, all callbacks and events will occur on game thread. Default true. */
|
||||||
|
bool bCallbackOnGameThread;
|
||||||
|
|
||||||
|
/** Set true if connection currently configured for TLS */
|
||||||
|
bool bIsSetupForTLS;
|
||||||
|
|
||||||
|
/** If at initialization forcing is set true, it will use TLS despite URL used */
|
||||||
|
bool bForceTLSUse;
|
||||||
|
|
||||||
|
/** If true will attempt to verify certificate (NB: this currently doesn't work) */
|
||||||
|
bool bUsingTLSCertVerification;
|
||||||
|
|
||||||
|
/** If true all events are unbound on disconnect */
|
||||||
|
bool bUnbindEventsOnDisconnect;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to a socket.io server, optional method if auto-connect is set to true.
|
||||||
|
* Overloaded function where you don't care about query and headers
|
||||||
|
*
|
||||||
|
* @param AddressAndPort the address in URL format with port
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void Connect(const FString& InAddressAndPort = TEXT(""));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to a socket.io server, optional method if auto-connect is set to true.
|
||||||
|
* Query and headers are defined by a {'stringKey':'stringValue'} SIOJson Object
|
||||||
|
*
|
||||||
|
* @param AddressAndPort the address in URL format with port
|
||||||
|
* @param Query http query as a SIOJsonObject with string keys and values
|
||||||
|
* @param Headers http header as a SIOJsonObject with string keys and values
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void Connect(const FSIOConnectParams& ConnectParams);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Join a desired namespace. Keep in mind that emitting to a namespace will auto-join it
|
||||||
|
*/
|
||||||
|
void JoinNamespace(const FString& Namespace);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Leave a specified namespace.
|
||||||
|
*/
|
||||||
|
void LeaveNamespace(const FString& Namespace);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnect from current socket.io server, optional method.
|
||||||
|
*
|
||||||
|
* @param AddressAndPort the address in URL format with port
|
||||||
|
*/
|
||||||
|
void Disconnect();
|
||||||
|
|
||||||
|
void SyncDisconnect();
|
||||||
|
|
||||||
|
void ClearAllCallbacks();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit an event with a JsonValue message
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param Message FJsonValue
|
||||||
|
* @param CallbackFunction Optional callback TFunction
|
||||||
|
* @param Namespace Optional Namespace within socket.io
|
||||||
|
*/
|
||||||
|
void Emit(
|
||||||
|
const FString& EventName,
|
||||||
|
const TSharedPtr<FJsonValue>& Message = nullptr,
|
||||||
|
TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction = nullptr,
|
||||||
|
const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Overloaded) Emit an event with a Json Object message
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param ObjectMessage FJsonObject
|
||||||
|
* @param CallbackFunction Optional callback TFunction
|
||||||
|
* @param Namespace Optional Namespace within socket.io
|
||||||
|
*/
|
||||||
|
void Emit(
|
||||||
|
const FString& EventName,
|
||||||
|
const TSharedPtr<FJsonObject>& ObjectMessage = nullptr,
|
||||||
|
TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction = nullptr,
|
||||||
|
const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Overloaded) Emit an event with a string message
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param StringMessage Message in string format
|
||||||
|
* @param CallbackFunction Optional callback TFunction
|
||||||
|
* @param Namespace Optional Namespace within socket.io
|
||||||
|
*/
|
||||||
|
void Emit(
|
||||||
|
const FString& EventName,
|
||||||
|
const FString& StringMessage = FString(),
|
||||||
|
TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction = nullptr,
|
||||||
|
const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
|
||||||
|
//flexible type allowing TEXT() passed as second param for overloaded emit
|
||||||
|
#if !defined(SIO_TEXT_TYPE)
|
||||||
|
#if PLATFORM_TCHAR_IS_CHAR16
|
||||||
|
#define SIO_TEXT_TYPE char16_t*
|
||||||
|
#else
|
||||||
|
#define SIO_TEXT_TYPE wchar_t*
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Overloaded) Emit an event with a string literal message
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param StringMessage Message in string format
|
||||||
|
* @param CallbackFunction Optional callback TFunction
|
||||||
|
* @param Namespace Optional Namespace within socket.io
|
||||||
|
*/
|
||||||
|
void Emit(
|
||||||
|
const FString& EventName,
|
||||||
|
const SIO_TEXT_TYPE StringMessage = TEXT(""),
|
||||||
|
TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction = nullptr,
|
||||||
|
const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Overloaded) Emit an event with a number (double) message
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param NumberMessage Message in double format
|
||||||
|
* @param CallbackFunction Optional callback TFunction
|
||||||
|
* @param Namespace Optional Namespace within socket.io
|
||||||
|
*/
|
||||||
|
void Emit(
|
||||||
|
const FString& EventName,
|
||||||
|
double NumberMessage,
|
||||||
|
TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction = nullptr,
|
||||||
|
const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Overloaded) Emit an event with a bool message
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param BooleanMessage Message in bool format
|
||||||
|
* @param CallbackFunction Optional callback TFunction
|
||||||
|
* @param Namespace Optional Namespace within socket.io
|
||||||
|
*/
|
||||||
|
void Emit(
|
||||||
|
const FString& EventName,
|
||||||
|
bool BooleanMessage,
|
||||||
|
TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction = nullptr,
|
||||||
|
const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Overloaded) Emit an event with a binary message
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param BinaryMessage Message in an TArray of uint8
|
||||||
|
* @param CallbackFunction Optional callback TFunction
|
||||||
|
* @param Namespace Optional Namespace within socket.io
|
||||||
|
*/
|
||||||
|
void Emit
|
||||||
|
(const FString& EventName,
|
||||||
|
const TArray<uint8>& BinaryMessage,
|
||||||
|
TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction = nullptr,
|
||||||
|
const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Overloaded) Emit an event with an array message
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param ArrayMessage Message in an TArray of FJsonValues
|
||||||
|
* @param CallbackFunction Optional callback TFunction
|
||||||
|
* @param Namespace Optional Namespace within socket.io
|
||||||
|
*/
|
||||||
|
void Emit(
|
||||||
|
const FString& EventName,
|
||||||
|
const TArray<TSharedPtr<FJsonValue>>& ArrayMessage,
|
||||||
|
TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction = nullptr,
|
||||||
|
const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Overloaded) Emit an event with an UStruct message
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param Struct UStruct type usually obtained via e.g. FMyStructType::StaticStruct()
|
||||||
|
* @param StructPtr Pointer to the actual struct memory e.g. &MyStruct
|
||||||
|
* @param CallbackFunction Optional callback TFunction
|
||||||
|
* @param Namespace Optional Namespace within socket.io
|
||||||
|
*/
|
||||||
|
void Emit(
|
||||||
|
const FString& EventName,
|
||||||
|
UStruct* Struct,
|
||||||
|
const void* StructPtr,
|
||||||
|
TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction = nullptr,
|
||||||
|
const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Overloaded) Emit an event without a message
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param Struct UStruct type usually obtained via e.g. FMyStructType::StaticStruct()
|
||||||
|
* @param StructPtr Pointer to the actual struct memory e.g. &MyStruct
|
||||||
|
* @param CallbackFunction Optional callback TFunction
|
||||||
|
* @param Namespace Optional Namespace within socket.io
|
||||||
|
*/
|
||||||
|
void Emit(
|
||||||
|
const FString& EventName,
|
||||||
|
TFunction< void(const TArray<TSharedPtr<FJsonValue>>&)> CallbackFunction = nullptr,
|
||||||
|
const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit a raw sio::message event
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param MessageList Message in sio::message::list format
|
||||||
|
* @param CallbackFunction Optional callback TFunction with raw signature
|
||||||
|
* @param Namespace Optional Namespace within socket.io
|
||||||
|
*/
|
||||||
|
void EmitRaw(
|
||||||
|
const FString& EventName,
|
||||||
|
const sio::message::list& MessageList = nullptr,
|
||||||
|
TFunction<void(const sio::message::list&)> CallbackFunction = nullptr,
|
||||||
|
const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit an optimized binary message
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param Data Buffer Pointer
|
||||||
|
* @param DataLength Buffer size
|
||||||
|
* @param Namespace Optional Namespace within socket.io
|
||||||
|
*/
|
||||||
|
void EmitRawBinary(
|
||||||
|
const FString& EventName,
|
||||||
|
uint8* Data,
|
||||||
|
int32 DataLength,
|
||||||
|
const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call function callback on receiving socket event. C++ only.
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param TFunction Lambda callback, JSONValue
|
||||||
|
* @param Namespace Optional namespace, defaults to default namespace
|
||||||
|
* @param CallbackThread Override default bCallbackOnGameThread option to specified option for this event
|
||||||
|
*/
|
||||||
|
void OnEvent(
|
||||||
|
const FString& EventName,
|
||||||
|
TFunction< void(const FString&, const TSharedPtr<FJsonValue>&)> CallbackFunction,
|
||||||
|
const FString& Namespace = TEXT("/"),
|
||||||
|
ESIOThreadOverrideOption CallbackThread = USE_DEFAULT);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call function callback on receiving raw event. C++ only.
|
||||||
|
* NB: Does not get added to FSocketIONative event map (use OnEvent)!
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param TFunction Lambda callback, raw flavor
|
||||||
|
* @param Namespace Optional namespace, defaults to default namespace
|
||||||
|
* @param CallbackThread Override default bCallbackOnGameThread option to specified option for this event
|
||||||
|
*/
|
||||||
|
void OnRawEvent(
|
||||||
|
const FString& EventName,
|
||||||
|
TFunction< void(const FString&, const sio::message::ptr&)> CallbackFunction,
|
||||||
|
const FString& Namespace = TEXT("/"),
|
||||||
|
ESIOThreadOverrideOption CallbackThread = USE_DEFAULT);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call function callback on receiving binary event. C++ only.
|
||||||
|
* NB: Does not get added to FSocketIONative event map (use OnEvent)!
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
* @param TFunction Lambda callback, raw flavor
|
||||||
|
* @param Namespace Optional namespace, defaults to default namespace
|
||||||
|
*/
|
||||||
|
void OnRawBinaryEvent(
|
||||||
|
const FString& EventName,
|
||||||
|
TFunction< void(const FString&, const TArray<uint8>&)> CallbackFunction,
|
||||||
|
const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unbinds currently bound callback from given event.
|
||||||
|
*
|
||||||
|
* @param EventName Event name
|
||||||
|
*/
|
||||||
|
void UnbindEvent(const FString& EventName, const FString& Namespace = TEXT("/"));
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
/** On disconnect or mode change bound events become invalid */
|
||||||
|
void ClearInternalCallbacks();
|
||||||
|
|
||||||
|
/** Linkup PrivateClient callbacks to FSocketIONative */
|
||||||
|
void SetupInternalCallbacks();
|
||||||
|
|
||||||
|
void RebindCurrentEventMap();
|
||||||
|
|
||||||
|
/** Checks for https prepend */
|
||||||
|
bool IsTLSURL(const FString& URL);
|
||||||
|
|
||||||
|
/** If non-matching mode, this will:
|
||||||
|
- close the connection
|
||||||
|
- clear and re-link internal callbacks
|
||||||
|
- re-construct PrivateClient in the correct mode
|
||||||
|
NB: URL preference overwritten if bForceTLSUse is true*/
|
||||||
|
void SyncPrivateClientToTLSMode(const FString& URL);
|
||||||
|
|
||||||
|
void InitPrivateClient(const bool bShouldUseTlsLibraries = false, const bool bShouldVerifyTLSCertificate = false);
|
||||||
|
|
||||||
|
TSharedPtr<sio::client> PrivateClient;
|
||||||
|
};
|
@ -0,0 +1,110 @@
|
|||||||
|
// Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
|
||||||
|
using System.IO;
|
||||||
|
using UnrealBuildTool;
|
||||||
|
|
||||||
|
namespace UnrealBuildTool.Rules
|
||||||
|
{
|
||||||
|
public class SocketIOClient : ModuleRules
|
||||||
|
{
|
||||||
|
|
||||||
|
private string ThirdPartyPath
|
||||||
|
{
|
||||||
|
get { return Path.GetFullPath(Path.Combine(ModuleDirectory, "../ThirdParty/")); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private string SocketIOThirdParty
|
||||||
|
{
|
||||||
|
get { return Path.GetFullPath(Path.Combine(ThirdPartyPath, "SocketIO")); }
|
||||||
|
}
|
||||||
|
private string BoostThirdParty
|
||||||
|
{
|
||||||
|
get { return Path.GetFullPath(Path.Combine(ThirdPartyPath, "Boost")); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool LoadLib(ReadOnlyTargetRules Target)
|
||||||
|
{
|
||||||
|
bool isLibrarySupported = false;
|
||||||
|
|
||||||
|
if (Target.Platform == UnrealTargetPlatform.Win64)
|
||||||
|
{
|
||||||
|
isLibrarySupported = true;
|
||||||
|
}
|
||||||
|
else if (Target.Platform == UnrealTargetPlatform.Linux)
|
||||||
|
{
|
||||||
|
isLibrarySupported = true;
|
||||||
|
}
|
||||||
|
else if (Target.Platform == UnrealTargetPlatform.IOS)
|
||||||
|
{
|
||||||
|
isLibrarySupported = true;
|
||||||
|
}
|
||||||
|
else if (Target.Platform == UnrealTargetPlatform.Mac)
|
||||||
|
{
|
||||||
|
isLibrarySupported = true;
|
||||||
|
}
|
||||||
|
else if (Target.Platform == UnrealTargetPlatform.Android)
|
||||||
|
{
|
||||||
|
isLibrarySupported = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isLibrarySupported;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SocketIOClient(ReadOnlyTargetRules Target) : base(Target)
|
||||||
|
{
|
||||||
|
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||||
|
|
||||||
|
PublicIncludePaths.AddRange(
|
||||||
|
new string[] {
|
||||||
|
Path.Combine(ModuleDirectory, "Public"),
|
||||||
|
// ... add public include paths required here ...
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
PrivateIncludePaths.AddRange(
|
||||||
|
new string[] {
|
||||||
|
Path.Combine(ModuleDirectory, "Private"),
|
||||||
|
// ... add other private include paths required here ...
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
PublicDependencyModuleNames.AddRange(
|
||||||
|
new string[]
|
||||||
|
{
|
||||||
|
"Core",
|
||||||
|
"Json",
|
||||||
|
"JsonUtilities",
|
||||||
|
"SIOJson",
|
||||||
|
"CoreUtility",
|
||||||
|
"SocketIOLib",
|
||||||
|
// ... add other public dependencies that you statically link with here ...
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
PrivateDependencyModuleNames.AddRange(
|
||||||
|
new string[]
|
||||||
|
{
|
||||||
|
"CoreUObject",
|
||||||
|
"Engine",
|
||||||
|
"Slate",
|
||||||
|
"SlateCore",
|
||||||
|
// ... add private dependencies that you statically link with here ...
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
DynamicallyLoadedModuleNames.AddRange(
|
||||||
|
new string[]
|
||||||
|
{
|
||||||
|
// ... add any modules that your module loads dynamically here ...
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
LoadLib(Target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
<!-- Copyright 2018-current Getnamo. All Rights Reserved -->
|
||||||
|
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--SocketIO Client plugin additions using Android Plugin Language (APL)-->
|
||||||
|
<root xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<!-- init section is always evaluated once per architecture -->
|
||||||
|
<init>
|
||||||
|
<log text="SocketIO Client init"/>
|
||||||
|
</init>
|
||||||
|
|
||||||
|
<!-- optional files or directories to copy to Intermediate/Android/APK -->
|
||||||
|
<resourceCopies>
|
||||||
|
<log text="Copying SocketIO Client runtime files to staging"/>
|
||||||
|
<isArch arch="armeabi-v7a">
|
||||||
|
<copyFile src="$S(PluginDir)/Source/ThirdParty/SocketIO/Lib/Android/armeabi-v7a/libsioclient.a"
|
||||||
|
dst="$S(BuildDir)/libs/armeabi-v7a/libsioclient.a" />
|
||||||
|
<copyFile src="$S(PluginDir)/Source/ThirdParty/Boost/Lib/Android/armeabi-v7a/libboost_date_time.a"
|
||||||
|
dst="$S(BuildDir)/libs/armeabi-v7a/libboost_date_time.a" />
|
||||||
|
<copyFile src="$S(PluginDir)/Source/ThirdParty/Boost/Lib/Android/armeabi-v7a/libboost_random.a"
|
||||||
|
dst="$S(BuildDir)/libs/armeabi-v7a/libboost_random.a" />
|
||||||
|
<copyFile src="$S(PluginDir)/Source/ThirdParty/Boost/Lib/Android/armeabi-v7a/libboost_system.a"
|
||||||
|
dst="$S(BuildDir)/libs/armeabi-v7a/libboost_system.a" />
|
||||||
|
<log text="armeabi-v7a SocketIO Client runtime files copied"/>
|
||||||
|
</isArch>
|
||||||
|
<isArch arch="arm64-v8a">
|
||||||
|
<copyFile src="$S(PluginDir)/Source/ThirdParty/SocketIO/Lib/Android/arm64-v8a/libsioclient.a"
|
||||||
|
dst="$S(BuildDir)/libs/armeabi-v7a/libsioclient.a" />
|
||||||
|
<copyFile src="$S(PluginDir)/Source/ThirdParty/Boost/Lib/Android/arm64-v8a/libboost_date_time.a"
|
||||||
|
dst="$S(BuildDir)/libs/armeabi-v7a/libboost_date_time.a" />
|
||||||
|
<copyFile src="$S(PluginDir)/Source/ThirdParty/Boost/Lib/Android/arm64-v8a/libboost_random.a"
|
||||||
|
dst="$S(BuildDir)/libs/armeabi-v7a/libboost_random.a" />
|
||||||
|
<copyFile src="$S(PluginDir)/Source/ThirdParty/Boost/Lib/Android/arm64-v8a/libboost_system.a"
|
||||||
|
dst="$S(BuildDir)/libs/armeabi-v7a/libboost_system.a" />
|
||||||
|
<log text="arm64-v8a SocketIO Client runtime files copied"/>
|
||||||
|
</isArch>
|
||||||
|
<isArch arch="x86">
|
||||||
|
<copyFile src="$S(PluginDir)/../../ThirdParty/SocketIO/Lib/Android/x86/libsioclient.a"
|
||||||
|
dst="$S(BuildDir)/libs/armeabi-v7a/libsioclient.a" />
|
||||||
|
<copyFile src="$S(PluginDir)/../../ThirdParty/Boost/Lib/Android/x86/libboost_date_time-mt-1_60.a"
|
||||||
|
dst="$S(BuildDir)/libs/armeabi-v7a/libboost_date_time-mt-1_60.a" />
|
||||||
|
<copyFile src="$S(PluginDir)/../../ThirdParty/Boost/Lib/Android/x86/libboost_random-mt-1_60.a"
|
||||||
|
dst="$S(BuildDir)/libs/armeabi-v7a/libboost_random-mt-1_60.a" />
|
||||||
|
<copyFile src="$S(PluginDir)/../../ThirdParty/Boost/Lib/Android/x86/libboost_system-mt-1_60.a"
|
||||||
|
dst="$S(BuildDir)/libs/armeabi-v7a/libboost_system-mt-1_60.a" />
|
||||||
|
<log text="x86 SocketIO Client runtime files copied"/>
|
||||||
|
</isArch>
|
||||||
|
<isArch arch="x86_64">
|
||||||
|
<copyFile src="$S(PluginDir)/../../ThirdParty/SocketIO/Lib/Android/x86_64/libsioclient.a"
|
||||||
|
dst="$S(BuildDir)/libs/armeabi-v7a/libsioclient.a" />
|
||||||
|
<copyFile src="$S(PluginDir)/../../ThirdParty/Boost/Lib/Android/x86_64/libboost_date_time-mt-1_60.a"
|
||||||
|
dst="$S(BuildDir)/libs/armeabi-v7a/libboost_date_time-mt-1_60.a" />
|
||||||
|
<copyFile src="$S(PluginDir)/../../ThirdParty/Boost/Lib/Android/x86_64/libboost_random-mt-1_60.a"
|
||||||
|
dst="$S(BuildDir)/libs/armeabi-v7a/libboost_random-mt-1_60.a" />
|
||||||
|
<copyFile src="$S(PluginDir)/../../ThirdParty/Boost/Lib/Android/x86_64/libboost_system-mt-1_60.a"
|
||||||
|
dst="$S(BuildDir)/libs/armeabi-v7a/libboost_system-mt-1_60.a" />
|
||||||
|
<log text="x86_64 SocketIO Client runtime files copied"/>
|
||||||
|
</isArch>
|
||||||
|
|
||||||
|
|
||||||
|
</resourceCopies>
|
||||||
|
|
||||||
|
<!-- Manifest Updates -->
|
||||||
|
<androidManifestUpdates>
|
||||||
|
</androidManifestUpdates>
|
||||||
|
|
||||||
|
<!-- Files to copy before the build so they can be used in the build -->
|
||||||
|
<prebuildCopies>
|
||||||
|
</prebuildCopies>
|
||||||
|
|
||||||
|
<!-- optional additions to proguard -->
|
||||||
|
<proguardAdditions>
|
||||||
|
</proguardAdditions>
|
||||||
|
|
||||||
|
<gameActivityImportAdditions>
|
||||||
|
<!-- import com.leapmotion.*; -->
|
||||||
|
<insert>
|
||||||
|
</insert>
|
||||||
|
</gameActivityImportAdditions>
|
||||||
|
|
||||||
|
<gameActivityClassAdditions>
|
||||||
|
</gameActivityClassAdditions>
|
||||||
|
|
||||||
|
<gameActivityOnPauseAdditions>
|
||||||
|
</gameActivityOnPauseAdditions>
|
||||||
|
|
||||||
|
<gameActivityOnResumeAdditions>
|
||||||
|
</gameActivityOnResumeAdditions>
|
||||||
|
|
||||||
|
<gameActivityOnCreateAdditions>
|
||||||
|
</gameActivityOnCreateAdditions>
|
||||||
|
|
||||||
|
<gameActivityOnStartAdditions>
|
||||||
|
</gameActivityOnStartAdditions>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- optional libraries to load in GameActivity.java before libUE4.a -->
|
||||||
|
<!-- we're not loading a .so, it should all statically link...
|
||||||
|
<soLoadLibrary>
|
||||||
|
<loadLibrary name="libsioclient" failmsg="LeapMotion library not loaded and required!" />
|
||||||
|
</soLoadLibrary>-->
|
||||||
|
|
||||||
|
</root>
|
@ -0,0 +1,14 @@
|
|||||||
|
// Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
|
||||||
|
#include "SocketIOLib.h"
|
||||||
|
|
||||||
|
|
||||||
|
//struct
|
||||||
|
|
||||||
|
class FSocketIOLibModule : public ISocketIOLibModule
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
IMPLEMENT_MODULE(FSocketIOLibModule, SocketIOLib)
|
@ -0,0 +1,765 @@
|
|||||||
|
// Modifications Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
//
|
||||||
|
// sio_client_impl.cpp
|
||||||
|
// SioChatDemo
|
||||||
|
//
|
||||||
|
// Created by Melo Yao on 4/3/15.
|
||||||
|
// Copyright (c) 2015 Melo Yao. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
/* This disables two things:
|
||||||
|
1) error 4503 where MSVC complains about
|
||||||
|
decorated names being too long. There's no way around
|
||||||
|
this.
|
||||||
|
2) We also disable a security error triggered by
|
||||||
|
websocketpp not using checked iterators.
|
||||||
|
*/
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(disable : 4503)
|
||||||
|
#define _SCL_SECURE_NO_WARNINGS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* For this code, we will use standalone ASIO
|
||||||
|
and websocketpp in C++11 mode only */
|
||||||
|
#define ASIO_STANDALONE
|
||||||
|
#define _WEBSOCKETPP_CPP11_STL_
|
||||||
|
|
||||||
|
#include "sio_client_impl.h"
|
||||||
|
#include <sstream>
|
||||||
|
#include <mutex>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
// Comment this out to disable handshake logging to stdout
|
||||||
|
#define SIO_LIB_DEBUG 0
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#if defined(SIO_LIB_DEBUG) && SIO_LIB_DEBUG
|
||||||
|
#define DEBUG_LOG(CategoryName, Verbosity, Format, ...) UE_LOG(CategoryName, Verbosity, Format, ##__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define DEBUG_LOG(CategoryName, Verbosity, Format, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define LOG(x)
|
||||||
|
|
||||||
|
using std::chrono::milliseconds;
|
||||||
|
|
||||||
|
namespace sio
|
||||||
|
{
|
||||||
|
/*************************public:*************************/
|
||||||
|
template<typename client_type>
|
||||||
|
client_impl<client_type>::client_impl() :
|
||||||
|
m_ping_interval(0),
|
||||||
|
m_ping_timeout(0),
|
||||||
|
m_network_thread(),
|
||||||
|
m_con_state(con_closed),
|
||||||
|
m_reconn_delay(5000),
|
||||||
|
m_reconn_delay_max(25000),
|
||||||
|
m_reconn_attempts(0xFFFFFFFF),
|
||||||
|
m_reconn_made(0),
|
||||||
|
m_path("socket.io")
|
||||||
|
{
|
||||||
|
using websocketpp::log::alevel;
|
||||||
|
#ifndef DEBUG
|
||||||
|
m_client.clear_access_channels(alevel::all);
|
||||||
|
m_client.set_access_channels(alevel::connect | alevel::disconnect | alevel::app);
|
||||||
|
#endif
|
||||||
|
// Initialize the Asio transport policy
|
||||||
|
m_client.init_asio();
|
||||||
|
|
||||||
|
// Bind the clients we are using
|
||||||
|
using std::placeholders::_1;
|
||||||
|
using std::placeholders::_2;
|
||||||
|
|
||||||
|
m_client.set_open_handler(std::bind(&client_impl<client_type>::on_open, this, _1));
|
||||||
|
m_client.set_close_handler(std::bind(&client_impl<client_type>::on_close, this, _1));
|
||||||
|
m_client.set_fail_handler(std::bind(&client_impl<client_type>::on_fail, this, _1));
|
||||||
|
m_client.set_message_handler(std::bind(&client_impl<client_type>::on_message, this, _1, _2));
|
||||||
|
m_packet_mgr.set_decode_callback(std::bind(&client_impl<client_type>::on_decode, this, _1));
|
||||||
|
m_packet_mgr.set_encode_callback(std::bind(&client_impl<client_type>::on_encode, this, _1, _2));
|
||||||
|
template_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
client_impl<client_type>::~client_impl()
|
||||||
|
{
|
||||||
|
this->sockets_invoke_void(socket_on_close());
|
||||||
|
sync_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::connect(const string& uri, const map<string, string>& query, const map<string, string>& headers, const message::ptr& auth, const std::string& path /*= "socket.io"*/)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (m_reconn_timer)
|
||||||
|
{
|
||||||
|
m_reconn_timer->cancel();
|
||||||
|
m_reconn_timer.reset();
|
||||||
|
}
|
||||||
|
if (m_network_thread)
|
||||||
|
{
|
||||||
|
if (m_con_state == con_closing || m_con_state == con_closed)
|
||||||
|
{
|
||||||
|
//if client is closing, join to wait.
|
||||||
|
//if client is closed, still need to join,
|
||||||
|
//but in closed case,join will return immediately.
|
||||||
|
m_network_thread->join();
|
||||||
|
m_network_thread.reset();//defensive
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//if we are connected, do nothing.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_con_state = con_opening;
|
||||||
|
m_reconn_made = 0;
|
||||||
|
|
||||||
|
if (!uri.empty())
|
||||||
|
{
|
||||||
|
m_base_url = uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
string query_str;
|
||||||
|
for (map<string, string>::const_iterator it = query.begin(); it != query.end(); ++it) {
|
||||||
|
query_str.append("&");
|
||||||
|
query_str.append(it->first);
|
||||||
|
query_str.append("=");
|
||||||
|
string query_str_value = encode_query_string(it->second);
|
||||||
|
query_str.append(query_str_value);
|
||||||
|
}
|
||||||
|
m_query_string = move(query_str);
|
||||||
|
|
||||||
|
m_http_headers = headers;
|
||||||
|
m_auth = auth;
|
||||||
|
|
||||||
|
if (path != ""){
|
||||||
|
m_path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->reset_states();
|
||||||
|
m_client.get_io_service().dispatch(std::bind(&client_impl<client_type>::connect_impl, this, m_base_url, m_query_string));
|
||||||
|
m_network_thread.reset(new thread(std::bind(&client_impl<client_type>::run_loop, this)));//uri lifecycle?
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
socket::ptr const& client_impl<client_type>::socket(string const& nsp)
|
||||||
|
{
|
||||||
|
lock_guard<mutex> guard(m_socket_mutex);
|
||||||
|
string aux;
|
||||||
|
if (nsp == "")
|
||||||
|
{
|
||||||
|
aux = "/";
|
||||||
|
}
|
||||||
|
else if (nsp[0] != '/')
|
||||||
|
{
|
||||||
|
aux.append("/", 1);
|
||||||
|
aux.append(nsp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
aux = nsp;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = m_sockets.find(aux);
|
||||||
|
if (it != m_sockets.end())
|
||||||
|
{
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
shared_ptr<sio::socket> newSocket = shared_ptr<sio::socket>(new sio::socket(this, aux, m_auth));
|
||||||
|
pair<const string, socket::ptr> p(aux, newSocket);
|
||||||
|
return (m_sockets.insert(p).first)->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::close()
|
||||||
|
{
|
||||||
|
m_con_state = con_closing;
|
||||||
|
this->sockets_invoke_void(&sio::socket::close);
|
||||||
|
m_client.get_io_service().dispatch(std::bind(&client_impl<client_type>::close_impl, this, close::status::normal, "End by user"));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::sync_close()
|
||||||
|
{
|
||||||
|
m_con_state = con_closing;
|
||||||
|
this->sockets_invoke_void(&sio::socket::close);
|
||||||
|
m_client.get_io_service().dispatch(std::bind(&client_impl<client_type>::close_impl, this, close::status::normal, "End by user"));
|
||||||
|
if (m_network_thread)
|
||||||
|
{
|
||||||
|
m_network_thread->join();
|
||||||
|
m_network_thread.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::set_logs_default()
|
||||||
|
{
|
||||||
|
m_client.clear_access_channels(websocketpp::log::alevel::all);
|
||||||
|
m_client.set_access_channels(websocketpp::log::alevel::connect | websocketpp::log::alevel::disconnect | websocketpp::log::alevel::app);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::set_logs_quiet()
|
||||||
|
{
|
||||||
|
m_client.clear_access_channels(websocketpp::log::alevel::all);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::set_logs_verbose()
|
||||||
|
{
|
||||||
|
m_client.set_access_channels(websocketpp::log::alevel::all);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************protected:*************************/
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::send(packet& p)
|
||||||
|
{
|
||||||
|
m_packet_mgr.encode(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::remove_socket(string const& nsp)
|
||||||
|
{
|
||||||
|
lock_guard<mutex> guard(m_socket_mutex);
|
||||||
|
auto it = m_sockets.find(nsp);
|
||||||
|
if (it != m_sockets.end())
|
||||||
|
{
|
||||||
|
m_sockets.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
asio::io_service& client_impl<client_type>::get_io_service()
|
||||||
|
{
|
||||||
|
return m_client.get_io_service();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::on_socket_closed(string const& nsp)
|
||||||
|
{
|
||||||
|
if (m_socket_close_listener)m_socket_close_listener(nsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::on_socket_opened(string const& nsp)
|
||||||
|
{
|
||||||
|
if (m_socket_open_listener)m_socket_open_listener(nsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************private:*************************/
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::run_loop()
|
||||||
|
{
|
||||||
|
|
||||||
|
m_client.run();
|
||||||
|
m_client.reset();
|
||||||
|
m_client.get_alog().write(websocketpp::log::alevel::devel,
|
||||||
|
"run loop end");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::connect_impl(const string& uri, const string& queryString)
|
||||||
|
{
|
||||||
|
do {
|
||||||
|
websocketpp::uri uo(uri);
|
||||||
|
ostringstream ss;
|
||||||
|
|
||||||
|
if (is_tls(uri))
|
||||||
|
{
|
||||||
|
// This requires SIO_TLS to have been compiled in.
|
||||||
|
ss << "wss://";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ss << "ws://";
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string host(uo.get_host());
|
||||||
|
// As per RFC2732, literal IPv6 address should be enclosed in "[" and "]".
|
||||||
|
if (host.find(':') != std::string::npos) {
|
||||||
|
ss << "[" << uo.get_host() << "]";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ss << uo.get_host();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a resource path was included in the URI, use that, otherwise
|
||||||
|
// use the default /socket.io/.
|
||||||
|
|
||||||
|
std::string path(uo.get_resource() == "/" ? "/socket.io/" : uo.get_resource());
|
||||||
|
|
||||||
|
//override if m_path is set
|
||||||
|
if (m_path != "socket.io")
|
||||||
|
{
|
||||||
|
path = "/" + m_path + "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
ss << ":" << uo.get_port() << path << "?EIO=4&transport=websocket";
|
||||||
|
if (m_sid.size() > 0) {
|
||||||
|
ss << "&sid=" << m_sid;
|
||||||
|
}
|
||||||
|
ss << "&t=" << time(NULL) << queryString;
|
||||||
|
lib::error_code ec;
|
||||||
|
typename client_type::connection_ptr con = m_client.get_connection(ss.str(), ec);
|
||||||
|
if (ec) {
|
||||||
|
m_client.get_alog().write(websocketpp::log::alevel::app,
|
||||||
|
"Get Connection Error: " + ec.message());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto&& header : m_http_headers) {
|
||||||
|
con->replace_header(header.first, header.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_client.connect(con);
|
||||||
|
return;
|
||||||
|
} while (0);
|
||||||
|
if (m_fail_listener)
|
||||||
|
{
|
||||||
|
m_fail_listener();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::close_impl(close::status::value const& code, string const& reason)
|
||||||
|
{
|
||||||
|
DEBUG_LOG(LogTemp, Log, TEXT("Close by reason: %s"), *FString(reason.c_str()));
|
||||||
|
if (m_reconn_timer)
|
||||||
|
{
|
||||||
|
m_reconn_timer->cancel();
|
||||||
|
m_reconn_timer.reset();
|
||||||
|
}
|
||||||
|
if (m_con.expired())
|
||||||
|
{
|
||||||
|
DEBUG_LOG(LogTemp, Warning, TEXT("close_impl::Error: No active session: %s"), *FString(reason.c_str()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lib::error_code ec;
|
||||||
|
m_client.close(m_con, code, reason, ec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::send_impl(shared_ptr<const string> const& payload_ptr, frame::opcode::value opcode)
|
||||||
|
{
|
||||||
|
if (m_con_state == con_opened)
|
||||||
|
{
|
||||||
|
lib::error_code ec;
|
||||||
|
m_client.send(m_con, *payload_ptr, opcode, ec);
|
||||||
|
if (ec)
|
||||||
|
{
|
||||||
|
//DEBUG_LOG(LogTemp, Warning, TEXT("close_impl::Error: Send failed,reason: %s"), *FString(ec.message().c_str()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::ping(const asio::error_code& ec)
|
||||||
|
{
|
||||||
|
if (ec || m_con.expired())
|
||||||
|
{
|
||||||
|
if (ec != asio::error::operation_aborted)
|
||||||
|
//LOG("ping exit,con is expired?" << m_con.expired() << ",ec:" << ec.message() << endl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
packet p(packet::frame_ping);
|
||||||
|
m_packet_mgr.encode(p, [&](bool /*isBin*/, shared_ptr<const string> payload)
|
||||||
|
{
|
||||||
|
lib::error_code ec;
|
||||||
|
this->m_client.send(this->m_con, *payload, frame::opcode::text, ec);
|
||||||
|
});
|
||||||
|
if (!m_ping_timeout_timer)
|
||||||
|
{
|
||||||
|
m_ping_timeout_timer.reset(new asio::steady_timer(m_client.get_io_service()));
|
||||||
|
std::error_code timeout_ec;
|
||||||
|
m_ping_timeout_timer->expires_from_now(milliseconds(m_ping_timeout), timeout_ec);
|
||||||
|
m_ping_timeout_timer->async_wait(std::bind(&client_impl<client_type>::timeout_pong, this, std::placeholders::_1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::timeout_pong(const asio::error_code& ec)
|
||||||
|
{
|
||||||
|
if (ec)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LOG("Pong timeout" << endl);
|
||||||
|
m_client.get_io_service().dispatch(std::bind(&client_impl<client_type>::close_impl, this, close::status::policy_violation, "Pong timeout"));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::timeout_reconnect(asio::error_code const& ec)
|
||||||
|
{
|
||||||
|
if (ec)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_con_state == con_closed)
|
||||||
|
{
|
||||||
|
m_con_state = con_opening;
|
||||||
|
m_reconn_made++;
|
||||||
|
this->reset_states();
|
||||||
|
LOG("Reconnecting..." << endl);
|
||||||
|
if (m_reconnecting_listener) m_reconnecting_listener();
|
||||||
|
m_client.get_io_service().dispatch(std::bind(&client_impl<client_type>::connect_impl, this, m_base_url, m_query_string));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
unsigned client_impl<client_type>::next_delay() const
|
||||||
|
{
|
||||||
|
//no jitter, fixed power root.
|
||||||
|
unsigned reconn_made = min<unsigned>(m_reconn_made, 32);//protect the pow result to be too big.
|
||||||
|
return static_cast<unsigned>(min<double>(m_reconn_delay * pow(1.5, reconn_made), m_reconn_delay_max));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
socket::ptr client_impl<client_type>::get_socket_locked(string const& nsp)
|
||||||
|
{
|
||||||
|
lock_guard<mutex> guard(m_socket_mutex);
|
||||||
|
auto it = m_sockets.find(nsp);
|
||||||
|
if (it != m_sockets.end())
|
||||||
|
{
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return socket::ptr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::sockets_invoke_void(void (sio::socket::* fn)(void))
|
||||||
|
{
|
||||||
|
map<const string, socket::ptr> socks;
|
||||||
|
{
|
||||||
|
lock_guard<mutex> guard(m_socket_mutex);
|
||||||
|
socks.insert(m_sockets.begin(), m_sockets.end());
|
||||||
|
}
|
||||||
|
for (auto it = socks.begin(); it != socks.end(); ++it) {
|
||||||
|
((*(it->second)).*fn)();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::on_fail(connection_hdl)
|
||||||
|
{
|
||||||
|
if (m_con_state == con_closing) {
|
||||||
|
LOG("Connection failed while closing." << endl);
|
||||||
|
this->close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_con.reset();
|
||||||
|
m_con_state = con_closed;
|
||||||
|
this->sockets_invoke_void(socket_on_disconnect());
|
||||||
|
LOG("Connection failed." << endl);
|
||||||
|
if (m_reconn_made < m_reconn_attempts)
|
||||||
|
{
|
||||||
|
LOG("Reconnect for attempt:" << m_reconn_made << endl);
|
||||||
|
unsigned delay = this->next_delay();
|
||||||
|
if (m_reconnect_listener) m_reconnect_listener(m_reconn_made, delay);
|
||||||
|
m_reconn_timer.reset(new asio::steady_timer(m_client.get_io_service()));
|
||||||
|
asio::error_code ec;
|
||||||
|
m_reconn_timer->expires_from_now(milliseconds(delay), ec);
|
||||||
|
m_reconn_timer->async_wait(std::bind(&client_impl<client_type>::timeout_reconnect, this, std::placeholders::_1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (m_fail_listener)m_fail_listener();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::on_open(connection_hdl con)
|
||||||
|
{
|
||||||
|
if (m_con_state == con_closing) {
|
||||||
|
LOG("Connection opened while closing." << endl);
|
||||||
|
this->close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG("Connected." << endl);
|
||||||
|
m_con_state = con_opened;
|
||||||
|
m_con = con;
|
||||||
|
m_reconn_made = 0;
|
||||||
|
this->sockets_invoke_void(socket_on_open());
|
||||||
|
this->socket("");
|
||||||
|
if (m_open_listener)m_open_listener();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::on_close(connection_hdl con)
|
||||||
|
{
|
||||||
|
LOG("Client Disconnected." << endl);
|
||||||
|
con_state m_con_state_was = m_con_state;
|
||||||
|
m_con_state = con_closed;
|
||||||
|
lib::error_code ec;
|
||||||
|
close::status::value code = close::status::normal;
|
||||||
|
typename client_type::connection_ptr conn_ptr = m_client.get_con_from_hdl(con, ec);
|
||||||
|
if (ec) {
|
||||||
|
LOG("OnClose get conn failed" << ec << endl);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
code = conn_ptr->get_local_close_code();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_con.reset();
|
||||||
|
this->clear_timers();
|
||||||
|
client::close_reason reason;
|
||||||
|
|
||||||
|
// If we initiated the close, no matter what the close status was,
|
||||||
|
// we'll consider it a normal close. (When using TLS, we can
|
||||||
|
// sometimes get a TLS Short Read error when closing.)
|
||||||
|
if (code == close::status::normal || m_con_state_was == con_closing)
|
||||||
|
{
|
||||||
|
this->sockets_invoke_void(socket_on_disconnect());
|
||||||
|
reason = client::close_reason_normal;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->sockets_invoke_void(socket_on_disconnect());
|
||||||
|
if (m_reconn_made < m_reconn_attempts)
|
||||||
|
{
|
||||||
|
LOG("Reconnect for attempt:" << m_reconn_made << endl);
|
||||||
|
unsigned delay = this->next_delay();
|
||||||
|
if (m_reconnect_listener) m_reconnect_listener(m_reconn_made, delay);
|
||||||
|
m_reconn_timer.reset(new asio::steady_timer(m_client.get_io_service()));
|
||||||
|
asio::error_code ec2;
|
||||||
|
m_reconn_timer->expires_from_now(milliseconds(delay), ec2);
|
||||||
|
m_reconn_timer->async_wait(std::bind(&client_impl<client_type>::timeout_reconnect, this, std::placeholders::_1));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reason = client::close_reason_drop;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_close_listener)
|
||||||
|
{
|
||||||
|
m_close_listener(reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::on_message(connection_hdl, message_ptr msg)
|
||||||
|
{
|
||||||
|
if (m_ping_timeout_timer) {
|
||||||
|
asio::error_code ec;
|
||||||
|
m_ping_timeout_timer->expires_from_now(milliseconds(m_ping_timeout), ec);
|
||||||
|
m_ping_timeout_timer->async_wait(std::bind(&client_impl<client_type>::timeout_pong, this, std::placeholders::_1));
|
||||||
|
}
|
||||||
|
// Parse the incoming message according to socket.IO rules
|
||||||
|
m_packet_mgr.put_payload(msg->get_payload());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::on_handshake(message::ptr const& message)
|
||||||
|
{
|
||||||
|
if (message && message->get_flag() == message::flag_object)
|
||||||
|
{
|
||||||
|
const object_message* obj_ptr = static_cast<object_message*>(message.get());
|
||||||
|
const map<string, message::ptr>* values = &(obj_ptr->get_map());
|
||||||
|
auto it = values->find("sid");
|
||||||
|
if (it != values->end()) {
|
||||||
|
m_sid = static_pointer_cast<string_message>(it->second)->get_string();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
it = values->find("pingInterval");
|
||||||
|
if (it != values->end() && it->second->get_flag() == message::flag_integer) {
|
||||||
|
m_ping_interval = (unsigned)static_pointer_cast<int_message>(it->second)->get_int();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_ping_interval = 25000;
|
||||||
|
}
|
||||||
|
it = values->find("pingTimeout");
|
||||||
|
|
||||||
|
if (it != values->end() && it->second->get_flag() == message::flag_integer) {
|
||||||
|
m_ping_timeout = (unsigned)static_pointer_cast<int_message>(it->second)->get_int();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_ping_timeout = 60000;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
failed:
|
||||||
|
//just close it.
|
||||||
|
m_client.get_io_service().dispatch(std::bind(&client_impl<client_type>::close_impl, this, close::status::policy_violation, "Handshake error"));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::on_ping()
|
||||||
|
{
|
||||||
|
packet p(packet::frame_pong);
|
||||||
|
m_packet_mgr.encode(p, [&](bool /*isBin*/, shared_ptr<const string> payload)
|
||||||
|
{
|
||||||
|
this->m_client.send(this->m_con, *payload, frame::opcode::text);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (m_ping_timeout_timer)
|
||||||
|
{
|
||||||
|
m_ping_timeout_timer->cancel();
|
||||||
|
m_ping_timeout_timer.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::on_decode(packet const& p)
|
||||||
|
{
|
||||||
|
switch (p.get_frame())
|
||||||
|
{
|
||||||
|
case packet::frame_message:
|
||||||
|
{
|
||||||
|
socket::ptr so_ptr = get_socket_locked(p.get_nsp());
|
||||||
|
if (so_ptr)socket_on_message_packet(so_ptr, p);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case packet::frame_open:
|
||||||
|
this->on_handshake(p.get_message());
|
||||||
|
break;
|
||||||
|
case packet::frame_close:
|
||||||
|
//FIXME how to deal?
|
||||||
|
this->close_impl(close::status::abnormal_close, "End by server");
|
||||||
|
break;
|
||||||
|
case packet::frame_ping:
|
||||||
|
this->on_ping();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::on_encode(bool isBinary, shared_ptr<const string> const& payload)
|
||||||
|
{
|
||||||
|
LOG("encoded payload length:" << payload->length() << endl);
|
||||||
|
m_client.get_io_service().dispatch(std::bind(&client_impl<client_type>::send_impl, this, payload, isBinary ? frame::opcode::binary : frame::opcode::text));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::clear_timers()
|
||||||
|
{
|
||||||
|
LOG("clear timers" << endl);
|
||||||
|
asio::error_code ec;
|
||||||
|
if (m_ping_timeout_timer)
|
||||||
|
{
|
||||||
|
m_ping_timeout_timer->cancel(ec);
|
||||||
|
m_ping_timeout_timer.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::reset_states()
|
||||||
|
{
|
||||||
|
m_client.reset();
|
||||||
|
m_sid.clear();
|
||||||
|
m_packet_mgr.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void client_impl<client_type_no_tls>::template_init()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#if SIO_TLS
|
||||||
|
typedef websocketpp::lib::shared_ptr<asio::ssl::context> context_ptr;
|
||||||
|
static context_ptr on_tls_init(int verify_mode, connection_hdl conn)
|
||||||
|
{
|
||||||
|
context_ptr ctx = context_ptr(new asio::ssl::context(asio::ssl::context::tlsv12));
|
||||||
|
asio::error_code ec;
|
||||||
|
ctx->set_options(asio::ssl::context::default_workarounds |
|
||||||
|
asio::ssl::context::no_sslv2 |
|
||||||
|
asio::ssl::context::single_dh_use, ec);
|
||||||
|
if (ec)
|
||||||
|
{
|
||||||
|
cerr << "Init tls failed,reason:" << ec.message() << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verify_mode >= 0)
|
||||||
|
{
|
||||||
|
ctx->set_verify_mode(verify_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
void client_impl<client_type>::set_verify_mode(int mode)
|
||||||
|
{
|
||||||
|
verify_mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void client_impl<client_type_tls>::template_init()
|
||||||
|
{
|
||||||
|
m_client.set_tls_init_handler(std::bind(&on_tls_init, verify_mode, std::placeholders::_1));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool client_impl_base::is_tls(const string& uri)
|
||||||
|
{
|
||||||
|
websocketpp::uri uo(uri);
|
||||||
|
if (uo.get_scheme() == "http" || uo.get_scheme() == "ws")
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (uo.get_scheme() == "https" || uo.get_scheme() == "wss")
|
||||||
|
{
|
||||||
|
#if SIO_TLS
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unsupported URI scheme");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
socket* client_impl_base::new_socket(const string& nsp,const message::ptr& auth)
|
||||||
|
{
|
||||||
|
return new sio::socket(this, nsp, auth);
|
||||||
|
}
|
||||||
|
|
||||||
|
void client_impl_base::socket_on_message_packet(socket::ptr& s, const packet& p)
|
||||||
|
{
|
||||||
|
s->on_message_packet(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
template class client_impl<client_type_no_tls>;
|
||||||
|
#if SIO_TLS
|
||||||
|
template class client_impl<client_type_tls>;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
std::string client_impl<client_type>::encode_query_string(const std::string& query) {
|
||||||
|
ostringstream ss;
|
||||||
|
ss << std::hex;
|
||||||
|
// Percent-encode (RFC3986) non-alphanumeric characters.
|
||||||
|
for (const char c : query) {
|
||||||
|
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) {
|
||||||
|
ss << c;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ss << '%' << std::uppercase << std::setw(2) << int((unsigned char)c) << std::nouppercase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ss << std::dec;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,331 @@
|
|||||||
|
// Modifications Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
#ifndef SIO_CLIENT_IMPL_H
|
||||||
|
#define SIO_CLIENT_IMPL_H
|
||||||
|
|
||||||
|
#ifndef SIO_TLS
|
||||||
|
#define SIO_TLS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* This disables two things:
|
||||||
|
1) error 4503 where MSVC complains about
|
||||||
|
decorated names being too long. There's no way around
|
||||||
|
this.
|
||||||
|
2) We also disable a security error triggered by
|
||||||
|
websocketpp not using checked iterators.
|
||||||
|
*/
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(disable : 4503)
|
||||||
|
#define _SCL_SECURE_NO_WARNINGS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* For this code, we will use standalone ASIO
|
||||||
|
and websocketpp in C++11 mode only */
|
||||||
|
#define ASIO_STANDALONE
|
||||||
|
#define _WEBSOCKETPP_CPP11_STL_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#define INTIALIZER(__TYPE__)
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
|
||||||
|
#if PLATFORM_WINDOWS
|
||||||
|
//#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include "Windows/WindowsHWrapper.h"
|
||||||
|
#include "Windows/AllowWindowsPlatformAtomics.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <websocketpp/client.hpp>
|
||||||
|
#include <asio/system_timer.hpp>
|
||||||
|
|
||||||
|
#if defined(DEBUG)
|
||||||
|
#if SIO_TLS
|
||||||
|
#define UI UI_ST
|
||||||
|
THIRD_PARTY_INCLUDES_START
|
||||||
|
#include "openssl/hmac.h"
|
||||||
|
#include <websocketpp/config/debug_asio.hpp>
|
||||||
|
typedef websocketpp::config::debug_asio_tls client_config_tls;
|
||||||
|
THIRD_PARTY_INCLUDES_END
|
||||||
|
#undef UI
|
||||||
|
#endif //SIO_TLS
|
||||||
|
#include <websocketpp/config/debug_asio_no_tls.hpp>
|
||||||
|
typedef websocketpp::config::debug_asio client_config;
|
||||||
|
#else
|
||||||
|
#if SIO_TLS
|
||||||
|
#define UI UI_ST
|
||||||
|
THIRD_PARTY_INCLUDES_START
|
||||||
|
#include "openssl/hmac.h"
|
||||||
|
#include <websocketpp/config/asio_client.hpp>
|
||||||
|
typedef websocketpp::config::asio_tls_client client_config_tls;
|
||||||
|
THIRD_PARTY_INCLUDES_END
|
||||||
|
#undef UI
|
||||||
|
#endif //SIO_TLS
|
||||||
|
#include <websocketpp/config/asio_no_tls_client.hpp>
|
||||||
|
typedef websocketpp::config::asio_client client_config;
|
||||||
|
#endif //DEBUG
|
||||||
|
#include <asio/deadline_timer.hpp>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <map>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "sio_client.h"
|
||||||
|
#include "sio_packet.h"
|
||||||
|
|
||||||
|
#if PLATFORM_WINDOWS
|
||||||
|
#include "Windows/HideWindowsPlatformAtomics.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace sio
|
||||||
|
{
|
||||||
|
using namespace websocketpp;
|
||||||
|
|
||||||
|
typedef websocketpp::client<client_config> client_type_no_tls;
|
||||||
|
#if SIO_TLS
|
||||||
|
typedef websocketpp::client<client_config_tls> client_type_tls;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class client_impl_base {
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum con_state
|
||||||
|
{
|
||||||
|
con_opening,
|
||||||
|
con_opened,
|
||||||
|
con_closing,
|
||||||
|
con_closed
|
||||||
|
};
|
||||||
|
|
||||||
|
client_impl_base() {}
|
||||||
|
virtual void template_init() {};
|
||||||
|
|
||||||
|
virtual ~client_impl_base() {}
|
||||||
|
|
||||||
|
// listeners and event bindings. (see SYNTHESIS_SETTER below)
|
||||||
|
virtual void set_open_listener(client::con_listener const&) {};
|
||||||
|
virtual void set_fail_listener(client::con_listener const&) {};
|
||||||
|
virtual void set_reconnect_listener(client::reconnect_listener const&) {};
|
||||||
|
virtual void set_reconnecting_listener(client::con_listener const&) {};
|
||||||
|
virtual void set_close_listener(client::close_listener const&) {};
|
||||||
|
virtual void set_socket_open_listener(client::socket_listener const&) {};
|
||||||
|
virtual void set_socket_close_listener(client::socket_listener const&) {};
|
||||||
|
|
||||||
|
// used by sio::client
|
||||||
|
virtual void clear_con_listeners() {};
|
||||||
|
virtual void clear_socket_listeners() {};
|
||||||
|
virtual void connect(const string& uri, const map<string, string>& query, const map<string, string>& headers, const message::ptr& auth, const std::string& path = "socket.io") {};
|
||||||
|
|
||||||
|
virtual sio::socket::ptr const& socket(const std::string& nsp) = 0;
|
||||||
|
virtual void close() {};
|
||||||
|
virtual void sync_close() {};
|
||||||
|
virtual bool opened() const { return false; };
|
||||||
|
virtual std::string const& get_sessionid() const = 0;
|
||||||
|
virtual void set_reconnect_attempts(unsigned attempts) {};
|
||||||
|
virtual void set_reconnect_delay(unsigned millis) {};
|
||||||
|
virtual void set_reconnect_delay_max(unsigned millis) {};
|
||||||
|
|
||||||
|
// used by sio::socket
|
||||||
|
virtual void send(packet& p) {};
|
||||||
|
virtual void remove_socket(std::string const& nsp) {};
|
||||||
|
virtual asio::io_service& get_io_service() = 0;
|
||||||
|
virtual void on_socket_closed(std::string const& nsp) {};
|
||||||
|
virtual void on_socket_opened(std::string const& nsp) {};
|
||||||
|
|
||||||
|
virtual void set_logs_default() {};
|
||||||
|
virtual void set_logs_quiet() {};
|
||||||
|
virtual void set_logs_verbose() {};
|
||||||
|
|
||||||
|
virtual std::string const& get_current_url() const = 0;
|
||||||
|
|
||||||
|
// used for selecting whether or not to use TLS
|
||||||
|
static bool is_tls(const std::string& uri);
|
||||||
|
|
||||||
|
#if SIO_TLS
|
||||||
|
virtual void set_verify_mode(int mode) {};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Wrap protected member functions of sio::socket because only client_impl_base is friended.
|
||||||
|
sio::socket* new_socket(std::string const&, message::ptr const&);
|
||||||
|
void socket_on_message_packet(sio::socket::ptr&, packet const&);
|
||||||
|
typedef void (sio::socket::* socket_void_fn)(void);
|
||||||
|
inline socket_void_fn socket_on_close() { return &sio::socket::on_close; }
|
||||||
|
inline socket_void_fn socket_on_disconnect() { return &sio::socket::on_disconnect; }
|
||||||
|
inline socket_void_fn socket_on_open() { return &sio::socket::on_open; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename client_type>
|
||||||
|
class client_impl : public client_impl_base {
|
||||||
|
public:
|
||||||
|
typedef typename client_type::message_ptr message_ptr;
|
||||||
|
|
||||||
|
client_impl();
|
||||||
|
void template_init() override; // template-specific initialization
|
||||||
|
|
||||||
|
~client_impl();
|
||||||
|
|
||||||
|
//set listeners and event bindings.
|
||||||
|
void connect(const std::string& uri, const std::map<std::string, std::string>& queryString,
|
||||||
|
const std::map<std::string, std::string>& httpExtraHeaders, const message::ptr& auth, const std::string& path = "socket.io");
|
||||||
|
|
||||||
|
sio::socket::ptr const& socket(const std::string& nsp);
|
||||||
|
|
||||||
|
// Closes the connection
|
||||||
|
void close();
|
||||||
|
|
||||||
|
void sync_close();
|
||||||
|
|
||||||
|
bool opened() const { return m_con_state == con_opened; }
|
||||||
|
|
||||||
|
std::string const& get_sessionid() const { return m_sid; }
|
||||||
|
|
||||||
|
std::string const& get_current_url() const { return m_base_url; }
|
||||||
|
|
||||||
|
void set_reconnect_attempts(unsigned attempts) { m_reconn_attempts = attempts; }
|
||||||
|
|
||||||
|
void set_reconnect_delay(unsigned millis) { m_reconn_delay = millis; if (m_reconn_delay_max < millis) m_reconn_delay_max = millis; }
|
||||||
|
|
||||||
|
void set_reconnect_delay_max(unsigned millis) { m_reconn_delay_max = millis; if (m_reconn_delay > millis) m_reconn_delay = millis; }
|
||||||
|
|
||||||
|
void set_logs_default();
|
||||||
|
|
||||||
|
void set_logs_quiet();
|
||||||
|
|
||||||
|
void set_logs_verbose();
|
||||||
|
|
||||||
|
#define SYNTHESIS_SETTER(__TYPE__,__FIELD__) \
|
||||||
|
void set_##__FIELD__(__TYPE__ const& l) \
|
||||||
|
{ m_##__FIELD__ = l;}
|
||||||
|
|
||||||
|
SYNTHESIS_SETTER(client::con_listener, open_listener)
|
||||||
|
|
||||||
|
SYNTHESIS_SETTER(client::con_listener, fail_listener)
|
||||||
|
|
||||||
|
SYNTHESIS_SETTER(client::reconnect_listener, reconnect_listener)
|
||||||
|
|
||||||
|
SYNTHESIS_SETTER(client::con_listener, reconnecting_listener)
|
||||||
|
|
||||||
|
SYNTHESIS_SETTER(client::close_listener, close_listener)
|
||||||
|
|
||||||
|
SYNTHESIS_SETTER(client::socket_listener, socket_open_listener)
|
||||||
|
|
||||||
|
SYNTHESIS_SETTER(client::socket_listener, socket_close_listener)
|
||||||
|
#undef SYNTHESIS_SETTER
|
||||||
|
|
||||||
|
#if SIO_TLS
|
||||||
|
void set_verify_mode(int mode) override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
void send(packet& p);
|
||||||
|
|
||||||
|
void remove_socket(std::string const& nsp);
|
||||||
|
|
||||||
|
asio::io_service& get_io_service();
|
||||||
|
|
||||||
|
void on_socket_closed(std::string const& nsp);
|
||||||
|
|
||||||
|
void on_socket_opened(std::string const& nsp);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void run_loop();
|
||||||
|
|
||||||
|
void connect_impl(const std::string& uri, const std::string& query);
|
||||||
|
|
||||||
|
void close_impl(close::status::value const& code, std::string const& reason);
|
||||||
|
|
||||||
|
void send_impl(std::shared_ptr<const std::string> const& payload_ptr, frame::opcode::value opcode);
|
||||||
|
|
||||||
|
void ping(const asio::error_code& ec);
|
||||||
|
|
||||||
|
void timeout_pong(const asio::error_code& ec);
|
||||||
|
|
||||||
|
void timeout_reconnect(asio::error_code const& ec);
|
||||||
|
|
||||||
|
unsigned next_delay() const;
|
||||||
|
|
||||||
|
socket::ptr get_socket_locked(std::string const& nsp);
|
||||||
|
|
||||||
|
void sockets_invoke_void(void (sio::socket::* fn)(void));
|
||||||
|
|
||||||
|
void on_decode(packet const& pack);
|
||||||
|
void on_encode(bool isBinary, shared_ptr<const string> const& payload);
|
||||||
|
|
||||||
|
//websocket callbacks
|
||||||
|
void on_fail(connection_hdl con);
|
||||||
|
|
||||||
|
void on_open(connection_hdl con);
|
||||||
|
|
||||||
|
void on_close(connection_hdl con);
|
||||||
|
|
||||||
|
void on_message(connection_hdl con, message_ptr msg);
|
||||||
|
|
||||||
|
//socketio callbacks
|
||||||
|
void on_handshake(message::ptr const& message);
|
||||||
|
|
||||||
|
void on_ping();
|
||||||
|
|
||||||
|
void reset_states();
|
||||||
|
|
||||||
|
void clear_timers();
|
||||||
|
|
||||||
|
// Percent encode query string
|
||||||
|
std::string encode_query_string(const std::string& query);
|
||||||
|
|
||||||
|
// Connection pointer for client functions.
|
||||||
|
connection_hdl m_con;
|
||||||
|
client_type m_client;
|
||||||
|
|
||||||
|
// Socket.IO server settings
|
||||||
|
std::string m_sid;
|
||||||
|
std::string m_base_url;
|
||||||
|
std::string m_query_string;
|
||||||
|
std::map<std::string, std::string> m_http_headers;
|
||||||
|
message::ptr m_auth;
|
||||||
|
|
||||||
|
unsigned int m_ping_interval;
|
||||||
|
unsigned int m_ping_timeout;
|
||||||
|
|
||||||
|
std::unique_ptr<std::thread> m_network_thread;
|
||||||
|
|
||||||
|
packet_manager m_packet_mgr;
|
||||||
|
|
||||||
|
std::unique_ptr<asio::steady_timer> m_ping_timeout_timer;
|
||||||
|
|
||||||
|
std::unique_ptr<asio::steady_timer> m_reconn_timer;
|
||||||
|
|
||||||
|
con_state m_con_state;
|
||||||
|
|
||||||
|
client::con_listener m_open_listener;
|
||||||
|
client::con_listener m_fail_listener;
|
||||||
|
client::con_listener m_reconnecting_listener;
|
||||||
|
client::reconnect_listener m_reconnect_listener;
|
||||||
|
client::close_listener m_close_listener;
|
||||||
|
|
||||||
|
client::socket_listener m_socket_open_listener;
|
||||||
|
client::socket_listener m_socket_close_listener;
|
||||||
|
|
||||||
|
std::map<const std::string, socket::ptr> m_sockets;
|
||||||
|
|
||||||
|
std::mutex m_socket_mutex;
|
||||||
|
|
||||||
|
unsigned m_reconn_delay;
|
||||||
|
|
||||||
|
unsigned m_reconn_delay_max;
|
||||||
|
|
||||||
|
unsigned m_reconn_attempts;
|
||||||
|
|
||||||
|
unsigned m_reconn_made;
|
||||||
|
|
||||||
|
//passthrough path of plugin
|
||||||
|
std::string m_path;
|
||||||
|
|
||||||
|
#if SIO_TLS
|
||||||
|
int verify_mode = -1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
friend class sio::client;
|
||||||
|
friend class sio::socket;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif // SIO_CLIENT_IMPL_H
|
@ -0,0 +1,531 @@
|
|||||||
|
// Modifications Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
//
|
||||||
|
// sio_packet.cpp
|
||||||
|
//
|
||||||
|
// Created by Melo Yao on 3/22/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
/* This disables two things:
|
||||||
|
1) error 4503 where MSVC complains about
|
||||||
|
decorated names being too long. There's no way around
|
||||||
|
this.
|
||||||
|
2) We also disable a security error triggered by
|
||||||
|
websocketpp not using checked iterators.
|
||||||
|
*/
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(disable : 4503)
|
||||||
|
#define _SCL_SECURE_NO_WARNINGS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* For this code, we will use standalone ASIO
|
||||||
|
and websocketpp in C++11 mode only */
|
||||||
|
#define ASIO_STANDALONE
|
||||||
|
#define _WEBSOCKETPP_CPP11_STL_
|
||||||
|
|
||||||
|
#include "sio_packet.h"
|
||||||
|
#include <rapidjson/document.h>
|
||||||
|
#include <rapidjson/encodedstream.h>
|
||||||
|
#include <rapidjson/writer.h>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#define kBIN_PLACE_HOLDER "_placeholder"
|
||||||
|
|
||||||
|
#include "Runtime/Core/Public/Misc/CString.h"
|
||||||
|
#include "Runtime/Core/Public/Containers/UnrealString.h"
|
||||||
|
|
||||||
|
namespace sio
|
||||||
|
{
|
||||||
|
using namespace rapidjsonsocketio;
|
||||||
|
using namespace std;
|
||||||
|
void accept_message(message const& msg, Value& val, Document& doc, vector<shared_ptr<const string> >& buffers);
|
||||||
|
|
||||||
|
void accept_bool_message(bool_message const& msg, Value& val)
|
||||||
|
{
|
||||||
|
val.SetBool(msg.get_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
void accept_null_message(Value& val)
|
||||||
|
{
|
||||||
|
val.SetNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
void accept_int_message(int_message const& msg, Value& val)
|
||||||
|
{
|
||||||
|
val.SetInt64(msg.get_int());
|
||||||
|
}
|
||||||
|
|
||||||
|
void accept_double_message(double_message const& msg, Value& val)
|
||||||
|
{
|
||||||
|
val.SetDouble(msg.get_double());
|
||||||
|
}
|
||||||
|
|
||||||
|
void accept_string_message(string_message const& msg, Value& val)
|
||||||
|
{
|
||||||
|
val.SetString(msg.get_string().data(), (SizeType)msg.get_string().length());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void accept_binary_message(binary_message const& msg, Value& val, Document& doc, vector<shared_ptr<const string> >& buffers)
|
||||||
|
{
|
||||||
|
val.SetObject();
|
||||||
|
Value boolVal;
|
||||||
|
boolVal.SetBool(true);
|
||||||
|
val.AddMember(kBIN_PLACE_HOLDER, boolVal, doc.GetAllocator());
|
||||||
|
Value numVal;
|
||||||
|
numVal.SetInt((int)buffers.size());
|
||||||
|
val.AddMember("num", numVal, doc.GetAllocator());
|
||||||
|
buffers.push_back(msg.get_binary());
|
||||||
|
}
|
||||||
|
|
||||||
|
void accept_array_message(array_message const& msg, Value& val, Document& doc, vector<shared_ptr<const string> >& buffers)
|
||||||
|
{
|
||||||
|
val.SetArray();
|
||||||
|
for (vector<message::ptr>::const_iterator it = msg.get_vector().begin(); it != msg.get_vector().end(); ++it) {
|
||||||
|
Value child;
|
||||||
|
accept_message(*(*it), child, doc, buffers);
|
||||||
|
val.PushBack(child, doc.GetAllocator());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void accept_object_message(object_message const& msg, Value& val, Document& doc, vector<shared_ptr<const string> >& buffers)
|
||||||
|
{
|
||||||
|
val.SetObject();
|
||||||
|
for (map<string, message::ptr>::const_iterator it = msg.get_map().begin(); it != msg.get_map().end(); ++it) {
|
||||||
|
Value nameVal;
|
||||||
|
nameVal.SetString(it->first.data(), (SizeType)it->first.length(), doc.GetAllocator());
|
||||||
|
Value valueVal;
|
||||||
|
accept_message(*(it->second), valueVal, doc, buffers);
|
||||||
|
val.AddMember(nameVal, valueVal, doc.GetAllocator());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void accept_message(message const& msg, Value& val, Document& doc, vector<shared_ptr<const string> >& buffers)
|
||||||
|
{
|
||||||
|
const message* msg_ptr = &msg;
|
||||||
|
switch (msg.get_flag())
|
||||||
|
{
|
||||||
|
case message::flag_integer:
|
||||||
|
{
|
||||||
|
accept_int_message(*(static_cast<const int_message*>(msg_ptr)), val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case message::flag_double:
|
||||||
|
{
|
||||||
|
accept_double_message(*(static_cast<const double_message*>(msg_ptr)), val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case message::flag_string:
|
||||||
|
{
|
||||||
|
accept_string_message(*(static_cast<const string_message*>(msg_ptr)), val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case message::flag_boolean:
|
||||||
|
{
|
||||||
|
accept_bool_message(*(static_cast<const bool_message*>(msg_ptr)), val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case message::flag_null:
|
||||||
|
{
|
||||||
|
accept_null_message(val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case message::flag_binary:
|
||||||
|
{
|
||||||
|
accept_binary_message(*(static_cast<const binary_message*>(msg_ptr)), val, doc, buffers);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case message::flag_array:
|
||||||
|
{
|
||||||
|
accept_array_message(*(static_cast<const array_message*>(msg_ptr)), val, doc, buffers);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case message::flag_object:
|
||||||
|
{
|
||||||
|
accept_object_message(*(static_cast<const object_message*>(msg_ptr)), val, doc, buffers);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message::ptr from_json(Value const& value, vector<shared_ptr<const string> > const& buffers)
|
||||||
|
{
|
||||||
|
if (value.IsInt64())
|
||||||
|
{
|
||||||
|
return int_message::create(value.GetInt64());
|
||||||
|
}
|
||||||
|
else if (value.IsDouble())
|
||||||
|
{
|
||||||
|
return double_message::create(value.GetDouble());
|
||||||
|
}
|
||||||
|
else if (value.IsString())
|
||||||
|
{
|
||||||
|
string str(value.GetString(), value.GetStringLength());
|
||||||
|
return string_message::create(str);
|
||||||
|
}
|
||||||
|
else if (value.IsArray())
|
||||||
|
{
|
||||||
|
message::ptr ptr = array_message::create();
|
||||||
|
for (SizeType i = 0; i < value.Size(); ++i) {
|
||||||
|
static_cast<array_message*>(ptr.get())->get_vector().push_back(from_json(value[i], buffers));
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
else if (value.IsObject())
|
||||||
|
{
|
||||||
|
//binary placeholder
|
||||||
|
auto mem_it = value.FindMember(kBIN_PLACE_HOLDER);
|
||||||
|
if (mem_it != value.MemberEnd() && mem_it->value.GetBool()) {
|
||||||
|
|
||||||
|
int num = value["num"].GetInt();
|
||||||
|
if (num >= 0 && num < static_cast<int>(buffers.size()))
|
||||||
|
{
|
||||||
|
return binary_message::create(buffers[num]);
|
||||||
|
}
|
||||||
|
return message::ptr();
|
||||||
|
}
|
||||||
|
//real object message.
|
||||||
|
message::ptr ptr = object_message::create();
|
||||||
|
for (auto it = value.MemberBegin(); it != value.MemberEnd(); ++it)
|
||||||
|
{
|
||||||
|
if (it->name.IsString())
|
||||||
|
{
|
||||||
|
string key(it->name.GetString(), it->name.GetStringLength());
|
||||||
|
static_cast<object_message*>(ptr.get())->get_map()[key] = from_json(it->value, buffers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
else if (value.IsBool())
|
||||||
|
{
|
||||||
|
return bool_message::create(value.GetBool());
|
||||||
|
}
|
||||||
|
else if (value.IsNull())
|
||||||
|
{
|
||||||
|
return null_message::create();
|
||||||
|
}
|
||||||
|
return message::ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
packet::packet(string const& nsp, message::ptr const& msg, int pack_id, bool isAck) :
|
||||||
|
_frame(frame_message),
|
||||||
|
_type((isAck ? type_ack : type_event) | type_undetermined),
|
||||||
|
_nsp(nsp),
|
||||||
|
_pack_id(pack_id),
|
||||||
|
_message(msg),
|
||||||
|
_pending_buffers(0)
|
||||||
|
{
|
||||||
|
assert((!isAck
|
||||||
|
|| (isAck && pack_id >= 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
packet::packet(type type, string const& nsp, message::ptr const& msg) :
|
||||||
|
_frame(frame_message),
|
||||||
|
_type(type),
|
||||||
|
_nsp(nsp),
|
||||||
|
_pack_id(-1),
|
||||||
|
_message(msg),
|
||||||
|
_pending_buffers(0)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
packet::packet(packet::frame_type frame) :
|
||||||
|
_frame(frame),
|
||||||
|
_type(type_undetermined),
|
||||||
|
_pack_id(-1),
|
||||||
|
_pending_buffers(0)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
packet::packet() :
|
||||||
|
_type(type_undetermined),
|
||||||
|
_pack_id(-1),
|
||||||
|
_pending_buffers(0)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool packet::is_binary_message(string const& payload_ptr)
|
||||||
|
{
|
||||||
|
return payload_ptr.size() > 0 && payload_ptr[0] == frame_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool packet::is_text_message(string const& payload_ptr)
|
||||||
|
{
|
||||||
|
return payload_ptr.size() > 0 && payload_ptr[0] == (frame_message + '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
bool packet::is_message(string const& payload_ptr)
|
||||||
|
{
|
||||||
|
return is_binary_message(payload_ptr) || is_text_message(payload_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool packet::parse_buffer(const string& buf_payload)
|
||||||
|
{
|
||||||
|
if (_pending_buffers > 0) {
|
||||||
|
//assert(is_binary_message(buf_payload));//this is ensured by outside.
|
||||||
|
_buffers.push_back(std::make_shared<string>(buf_payload.data(), buf_payload.size()));
|
||||||
|
_pending_buffers--;
|
||||||
|
if (_pending_buffers == 0) {
|
||||||
|
|
||||||
|
Document doc;
|
||||||
|
doc.Parse<0>(_buffers.front()->data());
|
||||||
|
_buffers.erase(_buffers.begin());
|
||||||
|
_message = from_json(doc, _buffers);
|
||||||
|
_buffers.clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool packet::parse(const string& payload_ptr)
|
||||||
|
{
|
||||||
|
assert(!is_binary_message(payload_ptr)); //this is ensured by outside
|
||||||
|
_frame = (packet::frame_type)(payload_ptr[0] - '0');
|
||||||
|
_message.reset();
|
||||||
|
_pack_id = -1;
|
||||||
|
_buffers.clear();
|
||||||
|
_pending_buffers = 0;
|
||||||
|
size_t pos = 1;
|
||||||
|
if (_frame == frame_message) {
|
||||||
|
_type = (packet::type)(payload_ptr[pos] - '0');
|
||||||
|
if (_type < type_min || _type > type_max)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
if (_type == type_binary_event || _type == type_binary_ack) {
|
||||||
|
size_t score_pos = payload_ptr.find('-');
|
||||||
|
_pending_buffers = FCString::Atoi64(*FString(payload_ptr.substr(pos, score_pos - pos).c_str()));
|
||||||
|
pos = score_pos + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t nsp_json_pos = payload_ptr.find_first_of("{[\"/", pos, 4);
|
||||||
|
if (nsp_json_pos == string::npos)//no namespace and no message,the end.
|
||||||
|
{
|
||||||
|
_nsp = "/";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_t json_pos = nsp_json_pos;
|
||||||
|
if (payload_ptr[nsp_json_pos] == '/')//nsp_json_pos is start of nsp
|
||||||
|
{
|
||||||
|
size_t comma_pos = payload_ptr.find_first_of(",");//end of nsp
|
||||||
|
if (comma_pos == string::npos)//packet end with nsp
|
||||||
|
{
|
||||||
|
_nsp = payload_ptr.substr(nsp_json_pos);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else//we have a message, maybe the message have an id.
|
||||||
|
{
|
||||||
|
_nsp = payload_ptr.substr(nsp_json_pos, comma_pos - nsp_json_pos);
|
||||||
|
pos = comma_pos + 1;//start of the message
|
||||||
|
json_pos = payload_ptr.find_first_of("\"[{", pos, 3);//start of the json part of message
|
||||||
|
if (json_pos == string::npos)
|
||||||
|
{
|
||||||
|
//no message,the end
|
||||||
|
//assume if there's no message, there's no message id.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_nsp = "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos < json_pos)//we've got pack id.
|
||||||
|
{
|
||||||
|
_pack_id = FCString::Atoi(*FString(payload_ptr.substr(pos, json_pos - pos).c_str()));
|
||||||
|
}
|
||||||
|
if (_frame == frame_message && (_type == type_binary_event || _type == type_binary_ack)) {
|
||||||
|
//parse later when all buffers are arrived.
|
||||||
|
_buffers.push_back(make_shared<string>(payload_ptr.data() + json_pos, payload_ptr.length() - json_pos));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Document doc;
|
||||||
|
doc.Parse<0>(payload_ptr.data() + json_pos);
|
||||||
|
_message = from_json(doc, vector<shared_ptr<const string> >());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool packet::accept(string& payload_ptr, vector<shared_ptr<const string> >& buffers)
|
||||||
|
{
|
||||||
|
char frame_char = _frame + '0';
|
||||||
|
payload_ptr.append(&frame_char, 1);
|
||||||
|
if (_frame != frame_message) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool hasMessage = false;
|
||||||
|
Document doc;
|
||||||
|
if (_message) {
|
||||||
|
accept_message(*_message, doc, doc, buffers);
|
||||||
|
hasMessage = true;
|
||||||
|
}
|
||||||
|
bool hasBinary = buffers.size() > 0;
|
||||||
|
_type = _type & (~type_undetermined);
|
||||||
|
if (_type == type_event)
|
||||||
|
{
|
||||||
|
_type = hasBinary ? type_binary_event : type_event;
|
||||||
|
}
|
||||||
|
else if (_type == type_ack)
|
||||||
|
{
|
||||||
|
_type = hasBinary ? type_binary_ack : type_ack;
|
||||||
|
}
|
||||||
|
ostringstream ss;
|
||||||
|
ss.precision(8);
|
||||||
|
ss << _type;
|
||||||
|
if (hasBinary) {
|
||||||
|
ss << buffers.size() << "-";
|
||||||
|
}
|
||||||
|
if (_nsp.size() > 0 && _nsp != "/")
|
||||||
|
{
|
||||||
|
ss << _nsp;
|
||||||
|
if (hasMessage || _pack_id >= 0) {
|
||||||
|
ss << ",";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_pack_id >= 0)
|
||||||
|
{
|
||||||
|
ss << _pack_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
payload_ptr.append(ss.str());
|
||||||
|
if (hasMessage)
|
||||||
|
{
|
||||||
|
StringBuffer buffer;
|
||||||
|
Writer<StringBuffer> writer(buffer);
|
||||||
|
doc.Accept(writer);
|
||||||
|
payload_ptr.append(buffer.GetString(), buffer.GetSize());
|
||||||
|
}
|
||||||
|
return hasBinary;
|
||||||
|
}
|
||||||
|
|
||||||
|
packet::frame_type packet::get_frame() const
|
||||||
|
{
|
||||||
|
return _frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
packet::type packet::get_type() const
|
||||||
|
{
|
||||||
|
assert((_type & type_undetermined) == 0);
|
||||||
|
return (type)_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
string const& packet::get_nsp() const
|
||||||
|
{
|
||||||
|
return _nsp;
|
||||||
|
}
|
||||||
|
|
||||||
|
message::ptr const& packet::get_message() const
|
||||||
|
{
|
||||||
|
return _message;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned packet::get_pack_id() const
|
||||||
|
{
|
||||||
|
return _pack_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void packet_manager::set_decode_callback(function<void(packet const&)> const& decode_callback)
|
||||||
|
{
|
||||||
|
m_decode_callback = decode_callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void packet_manager::set_encode_callback(function<void(bool, shared_ptr<const string> const&)> const& encode_callback)
|
||||||
|
{
|
||||||
|
m_encode_callback = encode_callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void packet_manager::reset()
|
||||||
|
{
|
||||||
|
m_partial_packet.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void packet_manager::encode(packet& pack, encode_callback_function const& override_encode_callback) const
|
||||||
|
{
|
||||||
|
shared_ptr<string> ptr = make_shared<string>();
|
||||||
|
vector<shared_ptr<const string> > buffers;
|
||||||
|
const encode_callback_function* cb_ptr = &m_encode_callback;
|
||||||
|
if (override_encode_callback)
|
||||||
|
{
|
||||||
|
cb_ptr = &override_encode_callback;
|
||||||
|
}
|
||||||
|
if (pack.accept(*ptr, buffers))
|
||||||
|
{
|
||||||
|
if ((*cb_ptr))
|
||||||
|
{
|
||||||
|
(*cb_ptr)(false, ptr);
|
||||||
|
}
|
||||||
|
for (auto it = buffers.begin(); it != buffers.end(); ++it)
|
||||||
|
{
|
||||||
|
if ((*cb_ptr))
|
||||||
|
{
|
||||||
|
(*cb_ptr)(true, *it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((*cb_ptr))
|
||||||
|
{
|
||||||
|
(*cb_ptr)(false, ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void packet_manager::put_payload(string const& payload)
|
||||||
|
{
|
||||||
|
unique_ptr<packet> p;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (packet::is_text_message(payload))
|
||||||
|
{
|
||||||
|
p.reset(new packet());
|
||||||
|
if (p->parse(payload))
|
||||||
|
{
|
||||||
|
m_partial_packet = std::move(p);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//In current socket.io version, it doesn't appear binary payloads come with a frame message.
|
||||||
|
else if (m_partial_packet && payload.size() > 0)
|
||||||
|
{
|
||||||
|
if (!m_partial_packet->parse_buffer(payload))
|
||||||
|
{
|
||||||
|
p = std::move(m_partial_packet);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p.reset(new packet());
|
||||||
|
p->parse(payload);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
if (m_decode_callback)
|
||||||
|
{
|
||||||
|
m_decode_callback(*p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,107 @@
|
|||||||
|
// Modifications Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
//
|
||||||
|
// sio_packet.h
|
||||||
|
//
|
||||||
|
// Created by Melo Yao on 3/19/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef SIO_PACKET_H
|
||||||
|
#define SIO_PACKET_H
|
||||||
|
#include <sstream>
|
||||||
|
#include "sio_message.h"
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace sio
|
||||||
|
{
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
class packet
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum frame_type
|
||||||
|
{
|
||||||
|
frame_open = 0,
|
||||||
|
frame_close = 1,
|
||||||
|
frame_ping = 2,
|
||||||
|
frame_pong = 3,
|
||||||
|
frame_message = 4,
|
||||||
|
frame_upgrade = 5,
|
||||||
|
frame_noop = 6
|
||||||
|
};
|
||||||
|
|
||||||
|
enum type
|
||||||
|
{
|
||||||
|
type_min = 0,
|
||||||
|
type_connect = 0,
|
||||||
|
type_disconnect = 1,
|
||||||
|
type_event = 2,
|
||||||
|
type_ack = 3,
|
||||||
|
type_error = 4,
|
||||||
|
type_binary_event = 5,
|
||||||
|
type_binary_ack = 6,
|
||||||
|
type_max = 6,
|
||||||
|
type_undetermined = 0x10 //undetermined mask bit
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
frame_type _frame;
|
||||||
|
int _type;
|
||||||
|
string _nsp;
|
||||||
|
int _pack_id;
|
||||||
|
message::ptr _message;
|
||||||
|
unsigned _pending_buffers;
|
||||||
|
vector<shared_ptr<const string> > _buffers;
|
||||||
|
public:
|
||||||
|
packet(string const& nsp, message::ptr const& msg, int pack_id = -1, bool isAck = false);//message type constructor.
|
||||||
|
|
||||||
|
packet(frame_type frame);
|
||||||
|
|
||||||
|
packet(type type, string const& nsp = string(), message::ptr const& msg = message::ptr());//other message types constructor.
|
||||||
|
//empty constructor for parse.
|
||||||
|
packet();
|
||||||
|
|
||||||
|
frame_type get_frame() const;
|
||||||
|
|
||||||
|
type get_type() const;
|
||||||
|
|
||||||
|
bool parse(string const& payload_ptr);//return true if need to parse buffer.
|
||||||
|
|
||||||
|
bool parse_buffer(string const& buf_payload);
|
||||||
|
|
||||||
|
bool accept(string& payload_ptr, vector<shared_ptr<const string> >& buffers); //return true if has binary buffers.
|
||||||
|
|
||||||
|
string const& get_nsp() const;
|
||||||
|
|
||||||
|
message::ptr const& get_message() const;
|
||||||
|
|
||||||
|
unsigned get_pack_id() const;
|
||||||
|
|
||||||
|
static bool is_message(string const& payload_ptr);
|
||||||
|
static bool is_text_message(string const& payload_ptr);
|
||||||
|
static bool is_binary_message(string const& payload_ptr);
|
||||||
|
};
|
||||||
|
|
||||||
|
class packet_manager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef function<void(bool, shared_ptr<const string> const&)> encode_callback_function;
|
||||||
|
typedef function<void(packet const&)> decode_callback_function;
|
||||||
|
|
||||||
|
void set_decode_callback(decode_callback_function const& decode_callback);
|
||||||
|
|
||||||
|
void set_encode_callback(encode_callback_function const& encode_callback);
|
||||||
|
|
||||||
|
void encode(packet& pack, encode_callback_function const& override_encode_callback = encode_callback_function()) const;
|
||||||
|
|
||||||
|
void put_payload(string const& payload);
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
private:
|
||||||
|
decode_callback_function m_decode_callback;
|
||||||
|
|
||||||
|
encode_callback_function m_encode_callback;
|
||||||
|
|
||||||
|
std::unique_ptr<packet> m_partial_packet;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
@ -0,0 +1,210 @@
|
|||||||
|
// Modifications Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
//
|
||||||
|
// sio_client.h
|
||||||
|
//
|
||||||
|
// Created by Melo Yao on 3/25/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
/* This disables two things:
|
||||||
|
1) error 4503 where MSVC complains about
|
||||||
|
decorated names being too long. There's no way around
|
||||||
|
this.
|
||||||
|
2) We also disable a security error triggered by
|
||||||
|
websocketpp not using checked iterators.
|
||||||
|
*/
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(disable : 4503)
|
||||||
|
#define _SCL_SECURE_NO_WARNINGS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* For this code, we will use standalone ASIO
|
||||||
|
and websocketpp in C++11 mode only */
|
||||||
|
|
||||||
|
#define ASIO_STANDALONE
|
||||||
|
#define _WEBSOCKETPP_CPP11_STL_
|
||||||
|
|
||||||
|
#include "sio_client.h"
|
||||||
|
#include "internal/sio_client_impl.h"
|
||||||
|
|
||||||
|
using namespace websocketpp;
|
||||||
|
using std::stringstream;
|
||||||
|
|
||||||
|
namespace sio
|
||||||
|
{
|
||||||
|
client::client():
|
||||||
|
m_impl(new client_impl<client_type_no_tls>())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
client::client(const bool bShouldUseTlsLibraries, const bool bShouldVerifyTLSCertificate)
|
||||||
|
{
|
||||||
|
if (bShouldUseTlsLibraries)
|
||||||
|
{
|
||||||
|
#if SIO_TLS
|
||||||
|
m_impl = new client_impl<client_type_tls>();
|
||||||
|
|
||||||
|
if (bShouldVerifyTLSCertificate)
|
||||||
|
{
|
||||||
|
m_impl->set_verify_mode(asio::ssl::verify_peer);
|
||||||
|
// TODO: add verify CA chain file
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_impl->set_verify_mode(asio::ssl::verify_none);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_impl->template_init(); // reinitialize based on the new mode
|
||||||
|
#else
|
||||||
|
m_impl = new client_impl<client_type_no_tls>();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_impl = new client_impl<client_type_no_tls>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client::~client()
|
||||||
|
{
|
||||||
|
delete m_impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void client::set_open_listener(con_listener const& l)
|
||||||
|
{
|
||||||
|
m_impl->set_open_listener(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
void client::set_fail_listener(con_listener const& l)
|
||||||
|
{
|
||||||
|
m_impl->set_fail_listener(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
void client::set_close_listener(close_listener const& l)
|
||||||
|
{
|
||||||
|
m_impl->set_close_listener(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
void client::set_socket_open_listener(socket_listener const& l)
|
||||||
|
{
|
||||||
|
m_impl->set_socket_open_listener(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
void client::set_reconnect_listener(reconnect_listener const& l)
|
||||||
|
{
|
||||||
|
m_impl->set_reconnect_listener(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
void client::set_reconnecting_listener(con_listener const& l)
|
||||||
|
{
|
||||||
|
m_impl->set_reconnecting_listener(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
void client::set_socket_close_listener(socket_listener const& l)
|
||||||
|
{
|
||||||
|
m_impl->set_socket_close_listener(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
void client::clear_con_listeners()
|
||||||
|
{
|
||||||
|
m_impl->clear_con_listeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void client::clear_socket_listeners()
|
||||||
|
{
|
||||||
|
m_impl->clear_socket_listeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void client::connect()
|
||||||
|
{
|
||||||
|
m_impl->connect(std::string(), {}, {}, {}, m_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void client::connect(const std::string& uri)
|
||||||
|
{
|
||||||
|
m_impl->connect(uri, {}, {}, {}, m_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void client::connect(const std::string& uri, const std::map<string,string>& query)
|
||||||
|
{
|
||||||
|
m_impl->connect(uri, query, {}, {}, m_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void client::connect(const std::string& uri, const std::map<std::string,std::string>& query,
|
||||||
|
const std::map<std::string,std::string>& http_extra_headers,
|
||||||
|
const message::ptr& auth)
|
||||||
|
{
|
||||||
|
m_impl->connect(uri, query, http_extra_headers, auth, m_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
socket::ptr const& client::socket(const std::string& nsp)
|
||||||
|
{
|
||||||
|
return m_impl->socket(nsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Closes the connection
|
||||||
|
void client::close()
|
||||||
|
{
|
||||||
|
m_impl->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void client::sync_close()
|
||||||
|
{
|
||||||
|
m_impl->sync_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool client::opened() const
|
||||||
|
{
|
||||||
|
return m_impl->opened();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string const& client::get_url() const
|
||||||
|
{
|
||||||
|
return m_impl->get_current_url();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string const& client::get_sessionid() const
|
||||||
|
{
|
||||||
|
return m_impl->get_sessionid();
|
||||||
|
}
|
||||||
|
|
||||||
|
void client::set_reconnect_attempts(int attempts)
|
||||||
|
{
|
||||||
|
m_impl->set_reconnect_attempts(attempts);
|
||||||
|
}
|
||||||
|
|
||||||
|
void client::set_reconnect_delay(unsigned millis)
|
||||||
|
{
|
||||||
|
m_impl->set_reconnect_delay(millis);
|
||||||
|
}
|
||||||
|
|
||||||
|
void client::set_reconnect_delay_max(unsigned millis)
|
||||||
|
{
|
||||||
|
m_impl->set_reconnect_delay_max(millis);
|
||||||
|
}
|
||||||
|
|
||||||
|
void client::set_path(const std::string& path)
|
||||||
|
{
|
||||||
|
m_path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void client::stop()
|
||||||
|
{
|
||||||
|
//m_impl->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void client::set_logs_default()
|
||||||
|
{
|
||||||
|
m_impl->set_logs_default();
|
||||||
|
}
|
||||||
|
|
||||||
|
void client::set_logs_quiet()
|
||||||
|
{
|
||||||
|
m_impl->set_logs_quiet();
|
||||||
|
}
|
||||||
|
|
||||||
|
void client::set_logs_verbose()
|
||||||
|
{
|
||||||
|
m_impl->set_logs_verbose();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,653 @@
|
|||||||
|
// Modifications Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
/* This disables two things:
|
||||||
|
1) error 4503 where MSVC complains about
|
||||||
|
decorated names being too long. There's no way around
|
||||||
|
this.
|
||||||
|
2) We also disable a security error triggered by
|
||||||
|
websocketpp not using checked iterators.
|
||||||
|
*/
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(disable : 4503)
|
||||||
|
#define _SCL_SECURE_NO_WARNINGS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(LOG)
|
||||||
|
#define LOG(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* For this code, we will use standalone ASIO
|
||||||
|
and websocketpp in C++11 mode only */
|
||||||
|
#define ASIO_STANDALONE
|
||||||
|
#define _WEBSOCKETPP_CPP11_STL_
|
||||||
|
|
||||||
|
#include "sio_socket.h"
|
||||||
|
#include "internal/sio_packet.h"
|
||||||
|
#include "internal/sio_client_impl.h"
|
||||||
|
#include <asio/steady_timer.hpp>
|
||||||
|
#include <asio/error_code.hpp>
|
||||||
|
#include <queue>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#if defined(DEBUG) && DEBUG
|
||||||
|
#define LOG(x) std::cout << x
|
||||||
|
#else
|
||||||
|
#define LOG(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NULL_GUARD(_x_) \
|
||||||
|
if(_x_ == NULL) return
|
||||||
|
|
||||||
|
namespace sio
|
||||||
|
{
|
||||||
|
class event_adapter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void adapt_func(socket::event_listener_aux const& func, event& event)
|
||||||
|
{
|
||||||
|
func(event.get_name(),event.get_message(),event.need_ack(),event.get_ack_message_impl());
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline socket::event_listener do_adapt(socket::event_listener_aux const& func)
|
||||||
|
{
|
||||||
|
return std::bind(&event_adapter::adapt_func, func,std::placeholders::_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline event create_event(std::string const& nsp,std::string const& name,message::list&& message,bool need_ack)
|
||||||
|
{
|
||||||
|
return event(nsp,name,message,need_ack);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::string& event::get_nsp() const
|
||||||
|
{
|
||||||
|
return m_nsp;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& event::get_name() const
|
||||||
|
{
|
||||||
|
return m_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
const message::ptr& event::get_message() const
|
||||||
|
{
|
||||||
|
if(m_messages.size()>0)
|
||||||
|
return m_messages[0];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
static message::ptr null_ptr;
|
||||||
|
return null_ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const message::list& event::get_messages() const
|
||||||
|
{
|
||||||
|
return m_messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool event::need_ack() const
|
||||||
|
{
|
||||||
|
return m_need_ack;
|
||||||
|
}
|
||||||
|
|
||||||
|
void event::put_ack_message(message::list const& ack_message)
|
||||||
|
{
|
||||||
|
if(m_need_ack)
|
||||||
|
m_ack_message = std::move(ack_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
event::event(std::string const& nsp,std::string const& name,message::list&& messages,bool need_ack):
|
||||||
|
m_nsp(nsp),
|
||||||
|
m_name(name),
|
||||||
|
m_messages(std::move(messages)),
|
||||||
|
m_need_ack(need_ack)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
event::event(std::string const& nsp,std::string const& name,message::list const& messages,bool need_ack):
|
||||||
|
m_nsp(nsp),
|
||||||
|
m_name(name),
|
||||||
|
m_messages(messages),
|
||||||
|
m_need_ack(need_ack)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
message::list const& event::get_ack_message() const
|
||||||
|
{
|
||||||
|
return m_ack_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
message::list& event::get_ack_message_impl()
|
||||||
|
{
|
||||||
|
return m_ack_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
class socket::impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
impl(client_impl_base *,std::string const&, message::ptr const&);
|
||||||
|
~impl();
|
||||||
|
|
||||||
|
void on(std::string const& event_name,event_listener_aux const& func);
|
||||||
|
|
||||||
|
void on(std::string const& event_name,event_listener const& func);
|
||||||
|
|
||||||
|
void off(std::string const& event_name);
|
||||||
|
|
||||||
|
void off_all();
|
||||||
|
|
||||||
|
#define SYNTHESIS_SETTER(__TYPE__,__FIELD__) \
|
||||||
|
void set_##__FIELD__(__TYPE__ const& l) \
|
||||||
|
{ m_##__FIELD__ = l;}
|
||||||
|
|
||||||
|
SYNTHESIS_SETTER(error_listener, error_listener) //socket io errors
|
||||||
|
|
||||||
|
#undef SYNTHESIS_SETTER
|
||||||
|
|
||||||
|
void on_error(error_listener const& l);
|
||||||
|
|
||||||
|
void off_error();
|
||||||
|
|
||||||
|
void close();
|
||||||
|
|
||||||
|
void emit(std::string const& name, message::list const& msglist, std::function<void (message::list const&)> const& ack);
|
||||||
|
|
||||||
|
std::string const& get_namespace() const {return m_nsp;}
|
||||||
|
|
||||||
|
std::string const& get_socket_id() const { return m_socket_id; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void on_connected();
|
||||||
|
|
||||||
|
void on_close();
|
||||||
|
|
||||||
|
void on_open();
|
||||||
|
|
||||||
|
void on_message_packet(packet const& packet);
|
||||||
|
|
||||||
|
void on_disconnect();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// Message Parsing callbacks.
|
||||||
|
void on_socketio_event(const std::string& nsp, int msgId,const std::string& name, message::list&& message);
|
||||||
|
void on_socketio_ack(int msgId, message::list const& message);
|
||||||
|
void on_socketio_error(message::ptr const& err_message);
|
||||||
|
|
||||||
|
event_listener get_bind_listener_locked(string const& event);
|
||||||
|
|
||||||
|
void ack(int msgId, string const& name, message::list const& ack_message);
|
||||||
|
|
||||||
|
void timeout_connection(const lib::error_code &ec);
|
||||||
|
|
||||||
|
void send_connect();
|
||||||
|
|
||||||
|
void send_packet(packet& p);
|
||||||
|
|
||||||
|
static event_listener s_null_event_listener;
|
||||||
|
|
||||||
|
static unsigned int s_global_event_id;
|
||||||
|
|
||||||
|
sio::client_impl_base *m_client;
|
||||||
|
|
||||||
|
bool m_connected;
|
||||||
|
std::string m_nsp;
|
||||||
|
message::ptr m_auth;
|
||||||
|
|
||||||
|
std::string m_socket_id;
|
||||||
|
|
||||||
|
std::map<unsigned int, std::function<void (message::list const&)> > m_acks;
|
||||||
|
|
||||||
|
std::map<std::string, event_listener> m_event_binding;
|
||||||
|
|
||||||
|
error_listener m_error_listener;
|
||||||
|
|
||||||
|
std::unique_ptr<asio::system_timer> m_connection_timer;
|
||||||
|
|
||||||
|
std::queue<packet> m_packet_queue;
|
||||||
|
|
||||||
|
std::mutex m_event_mutex;
|
||||||
|
|
||||||
|
std::mutex m_packet_mutex;
|
||||||
|
|
||||||
|
friend class socket;
|
||||||
|
};
|
||||||
|
|
||||||
|
void socket::impl::on(std::string const& event_name,event_listener_aux const& func)
|
||||||
|
{
|
||||||
|
this->on(event_name,event_adapter::do_adapt(func));
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::impl::on(std::string const& event_name,event_listener const& func)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(m_event_mutex);
|
||||||
|
m_event_binding[event_name] = func;
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::impl::off(std::string const& event_name)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(m_event_mutex);
|
||||||
|
auto it = m_event_binding.find(event_name);
|
||||||
|
if(it!=m_event_binding.end())
|
||||||
|
{
|
||||||
|
m_event_binding.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::impl::off_all()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(m_event_mutex);
|
||||||
|
m_event_binding.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::impl::on_error(error_listener const& l)
|
||||||
|
{
|
||||||
|
m_error_listener = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::impl::off_error()
|
||||||
|
{
|
||||||
|
m_error_listener = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
socket::impl::impl(client_impl_base *client,std::string const& nsp, message::ptr const& auth):
|
||||||
|
m_client(client),
|
||||||
|
m_connected(false),
|
||||||
|
m_nsp(nsp),
|
||||||
|
m_auth(auth)
|
||||||
|
{
|
||||||
|
NULL_GUARD(client);
|
||||||
|
if(m_client->opened())
|
||||||
|
{
|
||||||
|
send_connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
socket::impl::~impl()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int socket::impl::s_global_event_id = 1;
|
||||||
|
|
||||||
|
void socket::impl::emit(std::string const& name, message::list const& msglist, std::function<void (message::list const&)> const& ack)
|
||||||
|
{
|
||||||
|
NULL_GUARD(m_client);
|
||||||
|
message::ptr msg_ptr = msglist.to_array_message(name);
|
||||||
|
int pack_id;
|
||||||
|
if(ack)
|
||||||
|
{
|
||||||
|
pack_id = s_global_event_id++;
|
||||||
|
std::lock_guard<std::mutex> guard(m_event_mutex);
|
||||||
|
m_acks[pack_id] = ack;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pack_id = -1;
|
||||||
|
}
|
||||||
|
packet p(m_nsp, msg_ptr,pack_id);
|
||||||
|
send_packet(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::impl::send_connect()
|
||||||
|
{
|
||||||
|
NULL_GUARD(m_client);
|
||||||
|
packet p(packet::type_connect, m_nsp, m_auth);
|
||||||
|
m_client->send(p);
|
||||||
|
m_connection_timer.reset(new asio::system_timer(m_client->get_io_service()));
|
||||||
|
lib::error_code ec;
|
||||||
|
m_connection_timer->expires_from_now(std::chrono::milliseconds(20000), ec);
|
||||||
|
m_connection_timer->async_wait(std::bind(&socket::impl::timeout_connection,this, std::placeholders::_1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::impl::close()
|
||||||
|
{
|
||||||
|
NULL_GUARD(m_client);
|
||||||
|
if(m_connected)
|
||||||
|
{
|
||||||
|
packet p(packet::type_disconnect, m_nsp);
|
||||||
|
send_packet(p);
|
||||||
|
|
||||||
|
if(!m_connection_timer)
|
||||||
|
{
|
||||||
|
m_connection_timer.reset(new asio::system_timer(m_client->get_io_service()));
|
||||||
|
}
|
||||||
|
lib::error_code ec;
|
||||||
|
m_connection_timer->expires_from_now(std::chrono::milliseconds(3000), ec);
|
||||||
|
m_connection_timer->async_wait(std::bind(&socket::impl::on_close, this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::impl::on_connected()
|
||||||
|
{
|
||||||
|
if(m_connection_timer)
|
||||||
|
{
|
||||||
|
m_connection_timer->cancel();
|
||||||
|
m_connection_timer.reset();
|
||||||
|
}
|
||||||
|
if(!m_connected)
|
||||||
|
{
|
||||||
|
m_connected = true;
|
||||||
|
m_client->on_socket_opened(m_nsp);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
m_packet_mutex.lock();
|
||||||
|
if(m_packet_queue.empty())
|
||||||
|
{
|
||||||
|
m_packet_mutex.unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sio::packet front_pack = std::move(m_packet_queue.front());
|
||||||
|
m_packet_queue.pop();
|
||||||
|
m_packet_mutex.unlock();
|
||||||
|
m_client->send(front_pack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::impl::on_close()
|
||||||
|
{
|
||||||
|
NULL_GUARD(m_client);
|
||||||
|
sio::client_impl_base *client = m_client;
|
||||||
|
m_client = NULL;
|
||||||
|
|
||||||
|
if(m_connection_timer)
|
||||||
|
{
|
||||||
|
m_connection_timer->cancel();
|
||||||
|
m_connection_timer.reset();
|
||||||
|
}
|
||||||
|
m_connected = false;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(m_packet_mutex);
|
||||||
|
while (!m_packet_queue.empty()) {
|
||||||
|
m_packet_queue.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
client->on_socket_closed(m_nsp);
|
||||||
|
client->remove_socket(m_nsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::impl::on_open()
|
||||||
|
{
|
||||||
|
send_connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::impl::on_disconnect()
|
||||||
|
{
|
||||||
|
NULL_GUARD(m_client);
|
||||||
|
if(m_connected)
|
||||||
|
{
|
||||||
|
m_connected = false;
|
||||||
|
std::lock_guard<std::mutex> guard(m_packet_mutex);
|
||||||
|
while (!m_packet_queue.empty()) {
|
||||||
|
m_packet_queue.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::impl::on_message_packet(packet const& p)
|
||||||
|
{
|
||||||
|
NULL_GUARD(m_client);
|
||||||
|
if(p.get_nsp() == m_nsp)
|
||||||
|
{
|
||||||
|
switch (p.get_type())
|
||||||
|
{
|
||||||
|
// Connect open
|
||||||
|
case packet::type_connect:
|
||||||
|
{
|
||||||
|
LOG("Received Message type (Connect)"<<std::endl);
|
||||||
|
|
||||||
|
const object_message* obj_ptr = static_cast<const object_message*>(p.get_message().get());
|
||||||
|
if(obj_ptr)
|
||||||
|
{
|
||||||
|
const map<string, message::ptr>* values = &(obj_ptr->get_map());
|
||||||
|
auto it = values->find("sid");
|
||||||
|
if (it != values->end()) {
|
||||||
|
m_socket_id = static_pointer_cast<string_message>(it->second)->get_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->on_connected();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case packet::type_disconnect:
|
||||||
|
{
|
||||||
|
LOG("Received Message type (Disconnect)"<<std::endl);
|
||||||
|
this->on_close();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case packet::type_event:
|
||||||
|
case packet::type_binary_event:
|
||||||
|
{
|
||||||
|
LOG("Received Message type (Event)"<<std::endl);
|
||||||
|
const message::ptr ptr = p.get_message();
|
||||||
|
if(ptr->get_flag() == message::flag_array)
|
||||||
|
{
|
||||||
|
const array_message* array_ptr = static_cast<const array_message*>(ptr.get());
|
||||||
|
if(array_ptr->get_vector().size() >= 1&&array_ptr->get_vector()[0]->get_flag() == message::flag_string)
|
||||||
|
{
|
||||||
|
const string_message* name_ptr = static_cast<const string_message*>(array_ptr->get_vector()[0].get());
|
||||||
|
message::list mlist;
|
||||||
|
for(size_t i = 1;i<array_ptr->get_vector().size();++i)
|
||||||
|
{
|
||||||
|
mlist.push(array_ptr->get_vector()[i]);
|
||||||
|
}
|
||||||
|
this->on_socketio_event(p.get_nsp(), p.get_pack_id(),name_ptr->get_string(), std::move(mlist));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Ack
|
||||||
|
case packet::type_ack:
|
||||||
|
case packet::type_binary_ack:
|
||||||
|
{
|
||||||
|
LOG("Received Message type (ACK)"<<std::endl);
|
||||||
|
const message::ptr ptr = p.get_message();
|
||||||
|
if(ptr->get_flag() == message::flag_array)
|
||||||
|
{
|
||||||
|
message::list msglist(ptr->get_vector());
|
||||||
|
this->on_socketio_ack(p.get_pack_id(),msglist);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->on_socketio_ack(p.get_pack_id(),message::list(ptr));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Error
|
||||||
|
case packet::type_error:
|
||||||
|
{
|
||||||
|
LOG("Received Message type (ERROR)"<<std::endl);
|
||||||
|
this->on_socketio_error(p.get_message());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::impl::on_socketio_event(const std::string& nsp,int msgId,const std::string& name, message::list && message)
|
||||||
|
{
|
||||||
|
bool needAck = msgId >= 0;
|
||||||
|
event ev = event_adapter::create_event(nsp,name, std::move(message),needAck);
|
||||||
|
event_listener func = this->get_bind_listener_locked(name);
|
||||||
|
if(func)func(ev);
|
||||||
|
if(needAck)
|
||||||
|
{
|
||||||
|
this->ack(msgId, name, ev.get_ack_message());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::impl::ack(int msgId, const string &, const message::list &ack_message)
|
||||||
|
{
|
||||||
|
packet p(m_nsp, ack_message.to_array_message(),msgId,true);
|
||||||
|
send_packet(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::impl::on_socketio_ack(int msgId, message::list const& message)
|
||||||
|
{
|
||||||
|
std::function<void (message::list const&)> l;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(m_event_mutex);
|
||||||
|
auto it = m_acks.find(msgId);
|
||||||
|
if(it!=m_acks.end())
|
||||||
|
{
|
||||||
|
l = it->second;
|
||||||
|
m_acks.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(l)l(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::impl::on_socketio_error(message::ptr const& err_message)
|
||||||
|
{
|
||||||
|
if(m_error_listener)m_error_listener(err_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::impl::timeout_connection(const lib::error_code &ec)
|
||||||
|
{
|
||||||
|
NULL_GUARD(m_client);
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_connection_timer.reset();
|
||||||
|
LOG("Connection timeout,close socket."<<std::endl);
|
||||||
|
//Should close socket if no connected message arrive.Otherwise we'll never ask for open again.
|
||||||
|
this->on_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::impl::send_packet(sio::packet &p)
|
||||||
|
{
|
||||||
|
NULL_GUARD(m_client);
|
||||||
|
if(m_connected)
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
m_packet_mutex.lock();
|
||||||
|
if(m_packet_queue.empty())
|
||||||
|
{
|
||||||
|
m_packet_mutex.unlock();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sio::packet front_pack = std::move(m_packet_queue.front());
|
||||||
|
m_packet_queue.pop();
|
||||||
|
m_packet_mutex.unlock();
|
||||||
|
m_client->send(front_pack);
|
||||||
|
}
|
||||||
|
m_client->send(p);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(m_packet_mutex);
|
||||||
|
m_packet_queue.push(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
socket::event_listener socket::impl::get_bind_listener_locked(const string &event)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(m_event_mutex);
|
||||||
|
auto it = m_event_binding.find(event);
|
||||||
|
if(it!=m_event_binding.end())
|
||||||
|
{
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
return socket::event_listener();
|
||||||
|
}
|
||||||
|
|
||||||
|
socket::socket(client_impl_base* client,std::string const& nsp,message::ptr const& auth):
|
||||||
|
m_impl(new impl(client,nsp,auth))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
socket::~socket()
|
||||||
|
{
|
||||||
|
delete m_impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::on(std::string const& event_name,event_listener const& func)
|
||||||
|
{
|
||||||
|
m_impl->on(event_name, func);
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::on(std::string const& event_name,event_listener_aux const& func)
|
||||||
|
{
|
||||||
|
m_impl->on(event_name, func);
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::off(std::string const& event_name)
|
||||||
|
{
|
||||||
|
m_impl->off(event_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::off_all()
|
||||||
|
{
|
||||||
|
m_impl->off_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::close()
|
||||||
|
{
|
||||||
|
m_impl->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::on_error(error_listener const& l)
|
||||||
|
{
|
||||||
|
m_impl->on_error(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::off_error()
|
||||||
|
{
|
||||||
|
m_impl->off_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::emit(std::string const& name, message::list const& msglist, std::function<void (message::list const&)> const& ack)
|
||||||
|
{
|
||||||
|
m_impl->emit(name, msglist,ack);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string const& socket::get_namespace() const
|
||||||
|
{
|
||||||
|
return m_impl->get_namespace();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string const& socket::get_socket_id() const
|
||||||
|
{
|
||||||
|
return m_impl->get_socket_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::on_connected()
|
||||||
|
{
|
||||||
|
m_impl->on_connected();
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::on_close()
|
||||||
|
{
|
||||||
|
m_impl->on_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::on_open()
|
||||||
|
{
|
||||||
|
m_impl->on_open();
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::on_message_packet(packet const& p)
|
||||||
|
{
|
||||||
|
m_impl->on_message_packet(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket::on_disconnect()
|
||||||
|
{
|
||||||
|
m_impl->on_disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
|||||||
|
// Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Runtime/Core/Public/Modules/ModuleManager.h"
|
||||||
|
|
||||||
|
|
||||||
|
class SOCKETIOLIB_API ISocketIOLibModule : 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 ISocketIOLibModule& Get()
|
||||||
|
{
|
||||||
|
return FModuleManager::LoadModuleChecked< ISocketIOLibModule >("SocketIOLib");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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("SocketIOLib");
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,114 @@
|
|||||||
|
// Modifications Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
//
|
||||||
|
// sio_client.h
|
||||||
|
//
|
||||||
|
// Created by Melo Yao on 3/25/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef SIO_CLIENT_H
|
||||||
|
#define SIO_CLIENT_H
|
||||||
|
#include <string>
|
||||||
|
#include <functional>
|
||||||
|
#include "sio_message.h"
|
||||||
|
#include "sio_socket.h"
|
||||||
|
|
||||||
|
namespace sio
|
||||||
|
{
|
||||||
|
class client_impl_base;
|
||||||
|
|
||||||
|
class SOCKETIOLIB_API client {
|
||||||
|
public:
|
||||||
|
enum close_reason
|
||||||
|
{
|
||||||
|
close_reason_normal,
|
||||||
|
close_reason_drop
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::function<void(void)> con_listener;
|
||||||
|
|
||||||
|
typedef std::function<void(close_reason const& reason)> close_listener;
|
||||||
|
|
||||||
|
typedef std::function<void(unsigned, unsigned)> reconnect_listener;
|
||||||
|
|
||||||
|
typedef std::function<void(std::string const& nsp)> socket_listener;
|
||||||
|
|
||||||
|
client();
|
||||||
|
|
||||||
|
client(const bool bShouldUseTlsLibraries, const bool bShouldVerifyTLSCertificate);
|
||||||
|
|
||||||
|
~client();
|
||||||
|
|
||||||
|
//set listeners and event bindings.
|
||||||
|
void set_open_listener(con_listener const& l);
|
||||||
|
|
||||||
|
void set_fail_listener(con_listener const& l);
|
||||||
|
|
||||||
|
void set_reconnecting_listener(con_listener const& l);
|
||||||
|
|
||||||
|
void set_reconnect_listener(reconnect_listener const& l);
|
||||||
|
|
||||||
|
void set_close_listener(close_listener const& l);
|
||||||
|
|
||||||
|
void set_socket_open_listener(socket_listener const& l);
|
||||||
|
|
||||||
|
void set_socket_close_listener(socket_listener const& l);
|
||||||
|
|
||||||
|
void clear_con_listeners();
|
||||||
|
|
||||||
|
void clear_socket_listeners();
|
||||||
|
|
||||||
|
// Client Functions - such as send, etc.
|
||||||
|
|
||||||
|
void connect();
|
||||||
|
|
||||||
|
void connect(const std::string& uri);
|
||||||
|
|
||||||
|
void connect(const std::string& uri, const std::map<std::string,std::string>& query);
|
||||||
|
|
||||||
|
void connect(const std::string& uri, const std::map<std::string,std::string>& query,
|
||||||
|
const std::map<std::string,std::string>& http_extra_headers, const message::ptr& auth);
|
||||||
|
|
||||||
|
void set_reconnect_attempts(int attempts);
|
||||||
|
|
||||||
|
void set_reconnect_delay(unsigned millis);
|
||||||
|
|
||||||
|
void set_reconnect_delay_max(unsigned millis);
|
||||||
|
|
||||||
|
void set_path(const std::string& path);
|
||||||
|
|
||||||
|
void set_logs_default();
|
||||||
|
|
||||||
|
void set_logs_quiet();
|
||||||
|
|
||||||
|
void set_logs_verbose();
|
||||||
|
|
||||||
|
sio::socket::ptr const& socket(const std::string& nsp = "");
|
||||||
|
|
||||||
|
// Closes the connection
|
||||||
|
void close();
|
||||||
|
|
||||||
|
void sync_close();
|
||||||
|
|
||||||
|
// stop io_service
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
bool opened() const;
|
||||||
|
|
||||||
|
std::string const& get_sessionid() const;
|
||||||
|
|
||||||
|
std::string const& get_url() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
//disable copy constructor and assign operator.
|
||||||
|
client(client const&){}
|
||||||
|
void operator=(client const&){}
|
||||||
|
|
||||||
|
client_impl_base* m_impl;
|
||||||
|
|
||||||
|
std::string m_path;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif // __SIO_CLIENT__H__
|
@ -0,0 +1,570 @@
|
|||||||
|
// Modifications Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
//
|
||||||
|
// sio_message.h
|
||||||
|
//
|
||||||
|
// Created by Melo Yao on 3/25/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __SIO_MESSAGE_H__
|
||||||
|
#define __SIO_MESSAGE_H__
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <cassert>
|
||||||
|
#include <type_traits>
|
||||||
|
namespace sio
|
||||||
|
{
|
||||||
|
class message
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum flag
|
||||||
|
{
|
||||||
|
flag_integer,
|
||||||
|
flag_double,
|
||||||
|
flag_string,
|
||||||
|
flag_binary,
|
||||||
|
flag_array,
|
||||||
|
flag_object,
|
||||||
|
flag_boolean,
|
||||||
|
flag_null
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual ~message(){};
|
||||||
|
|
||||||
|
class list;
|
||||||
|
|
||||||
|
flag get_flag() const
|
||||||
|
{
|
||||||
|
return _flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef std::shared_ptr<message> ptr;
|
||||||
|
|
||||||
|
virtual bool get_bool() const
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int64_t get_int() const
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual double get_double() const
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual std::string const& get_string() const
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
static std::string s_empty_string;
|
||||||
|
s_empty_string.clear();
|
||||||
|
return s_empty_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual std::shared_ptr<const std::string> const& get_binary() const
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
static std::shared_ptr<const std::string> s_empty_binary;
|
||||||
|
s_empty_binary = nullptr;
|
||||||
|
return s_empty_binary;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual const std::vector<ptr>& get_vector() const
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
static std::vector<ptr> s_empty_vector;
|
||||||
|
s_empty_vector.clear();
|
||||||
|
return s_empty_vector;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual std::vector<ptr>& get_vector()
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
static std::vector<ptr> s_empty_vector;
|
||||||
|
s_empty_vector.clear();
|
||||||
|
return s_empty_vector;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual const std::map<std::string,message::ptr>& get_map() const
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
static std::map<std::string,message::ptr> s_empty_map;
|
||||||
|
s_empty_map.clear();
|
||||||
|
return s_empty_map;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual std::map<std::string,message::ptr>& get_map()
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
static std::map<std::string,message::ptr> s_empty_map;
|
||||||
|
s_empty_map.clear();
|
||||||
|
return s_empty_map;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
flag _flag;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
message(flag f):_flag(f){}
|
||||||
|
};
|
||||||
|
|
||||||
|
class null_message : public message
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
null_message()
|
||||||
|
:message(flag_null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static message::ptr create()
|
||||||
|
{
|
||||||
|
return ptr(new null_message());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class bool_message : public message
|
||||||
|
{
|
||||||
|
bool _v;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool_message(bool v)
|
||||||
|
:message(flag_boolean),_v(v)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static message::ptr create(bool v)
|
||||||
|
{
|
||||||
|
return ptr(new bool_message(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get_bool() const
|
||||||
|
{
|
||||||
|
return _v;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class int_message : public message
|
||||||
|
{
|
||||||
|
int64_t _v;
|
||||||
|
protected:
|
||||||
|
int_message(int64_t v)
|
||||||
|
:message(flag_integer),_v(v)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static message::ptr create(int64_t v)
|
||||||
|
{
|
||||||
|
return ptr(new int_message(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t get_int() const
|
||||||
|
{
|
||||||
|
return _v;
|
||||||
|
}
|
||||||
|
|
||||||
|
double get_double() const//add double accessor for integer.
|
||||||
|
{
|
||||||
|
return static_cast<double>(_v);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class double_message : public message
|
||||||
|
{
|
||||||
|
double _v;
|
||||||
|
double_message(double v)
|
||||||
|
:message(flag_double),_v(v)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static message::ptr create(double v)
|
||||||
|
{
|
||||||
|
return ptr(new double_message(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
double get_double() const
|
||||||
|
{
|
||||||
|
return _v;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class string_message : public message
|
||||||
|
{
|
||||||
|
std::string _v;
|
||||||
|
string_message(std::string const& v)
|
||||||
|
:message(flag_string),_v(v)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
string_message(std::string&& v)
|
||||||
|
:message(flag_string),_v(move(v))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
static message::ptr create(std::string const& v)
|
||||||
|
{
|
||||||
|
return ptr(new string_message(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
static message::ptr create(std::string&& v)
|
||||||
|
{
|
||||||
|
return ptr(new string_message(move(v)));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string const& get_string() const
|
||||||
|
{
|
||||||
|
return _v;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class binary_message : public message
|
||||||
|
{
|
||||||
|
std::shared_ptr<const std::string> _v;
|
||||||
|
binary_message(std::shared_ptr<const std::string> const& v)
|
||||||
|
:message(flag_binary),_v(v)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
static message::ptr create(std::shared_ptr<const std::string> const& v)
|
||||||
|
{
|
||||||
|
return ptr(new binary_message(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<const std::string> const& get_binary() const
|
||||||
|
{
|
||||||
|
return _v;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class array_message : public message
|
||||||
|
{
|
||||||
|
std::vector<message::ptr> _v;
|
||||||
|
array_message():message(flag_array)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static message::ptr create()
|
||||||
|
{
|
||||||
|
return ptr(new array_message());
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(message::ptr const& message)
|
||||||
|
{
|
||||||
|
if(message)
|
||||||
|
_v.push_back(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(const std::string& text)
|
||||||
|
{
|
||||||
|
_v.push_back(string_message::create(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(std::string&& text)
|
||||||
|
{
|
||||||
|
_v.push_back(string_message::create(move(text)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(std::shared_ptr<std::string> const& binary)
|
||||||
|
{
|
||||||
|
if(binary)
|
||||||
|
_v.push_back(binary_message::create(binary));
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(std::shared_ptr<const std::string> const& binary)
|
||||||
|
{
|
||||||
|
if(binary)
|
||||||
|
_v.push_back(binary_message::create(binary));
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(size_t pos,message::ptr const& message)
|
||||||
|
{
|
||||||
|
_v.insert(_v.begin()+pos, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(size_t pos,const std::string& text)
|
||||||
|
{
|
||||||
|
_v.insert(_v.begin()+pos, string_message::create(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(size_t pos,std::string&& text)
|
||||||
|
{
|
||||||
|
_v.insert(_v.begin()+pos, string_message::create(move(text)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(size_t pos,std::shared_ptr<std::string> const& binary)
|
||||||
|
{
|
||||||
|
if(binary)
|
||||||
|
_v.insert(_v.begin()+pos, binary_message::create(binary));
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(size_t pos,std::shared_ptr<const std::string> const& binary)
|
||||||
|
{
|
||||||
|
if(binary)
|
||||||
|
_v.insert(_v.begin()+pos, binary_message::create(binary));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size() const
|
||||||
|
{
|
||||||
|
return _v.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
const message::ptr& at(size_t i) const
|
||||||
|
{
|
||||||
|
return _v[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
const message::ptr& operator[] (size_t i) const
|
||||||
|
{
|
||||||
|
return _v[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ptr>& get_vector()
|
||||||
|
{
|
||||||
|
return _v;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<ptr>& get_vector() const
|
||||||
|
{
|
||||||
|
return _v;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class object_message : public message
|
||||||
|
{
|
||||||
|
std::map<std::string,message::ptr> _v;
|
||||||
|
object_message() : message(flag_object)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
static message::ptr create()
|
||||||
|
{
|
||||||
|
return ptr(new object_message());
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(const std::string & key,message::ptr const& message)
|
||||||
|
{
|
||||||
|
_v[key] = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(const std::string & key,const std::string& text)
|
||||||
|
{
|
||||||
|
_v[key] = string_message::create(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(const std::string & key,std::string&& text)
|
||||||
|
{
|
||||||
|
_v[key] = string_message::create(move(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(const std::string & key,std::shared_ptr<std::string> const& binary)
|
||||||
|
{
|
||||||
|
if(binary)
|
||||||
|
_v[key] = binary_message::create(binary);
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(const std::string & key,std::shared_ptr<const std::string> const& binary)
|
||||||
|
{
|
||||||
|
if(binary)
|
||||||
|
_v[key] = binary_message::create(binary);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has(const std::string & key)
|
||||||
|
{
|
||||||
|
return _v.find(key) != _v.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
const message::ptr& at(const std::string & key) const
|
||||||
|
{
|
||||||
|
static std::shared_ptr<message> not_found;
|
||||||
|
|
||||||
|
std::map<std::string,message::ptr>::const_iterator it = _v.find(key);
|
||||||
|
if (it != _v.cend()) return it->second;
|
||||||
|
return not_found;
|
||||||
|
}
|
||||||
|
|
||||||
|
const message::ptr& operator[] (const std::string & key) const
|
||||||
|
{
|
||||||
|
return at(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has(const std::string & key) const
|
||||||
|
{
|
||||||
|
return _v.find(key) != _v.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string,message::ptr>& get_map()
|
||||||
|
{
|
||||||
|
return _v;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::map<std::string,message::ptr>& get_map() const
|
||||||
|
{
|
||||||
|
return _v;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class message::list
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
list()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
list(std::nullptr_t)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
list(message::list&& rhs):
|
||||||
|
m_vector(std::move(rhs.m_vector))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
list & operator= (const message::list && rhs)
|
||||||
|
{
|
||||||
|
m_vector = std::move(rhs.m_vector);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
list(T&& content,
|
||||||
|
typename std::enable_if<std::is_same<std::vector<message::ptr>,typename std::remove_reference<T>::type>::value>::type* = 0):
|
||||||
|
m_vector(std::forward<T>(content))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
list(message::list const& rhs):
|
||||||
|
m_vector(rhs.m_vector)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
list(message::ptr const& message)
|
||||||
|
{
|
||||||
|
if(message)
|
||||||
|
m_vector.push_back(message);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
list(const std::string& text)
|
||||||
|
{
|
||||||
|
m_vector.push_back(string_message::create(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
list(std::string&& text)
|
||||||
|
{
|
||||||
|
m_vector.push_back(string_message::create(move(text)));
|
||||||
|
}
|
||||||
|
|
||||||
|
list(std::shared_ptr<std::string> const& binary)
|
||||||
|
{
|
||||||
|
if(binary)
|
||||||
|
m_vector.push_back(binary_message::create(binary));
|
||||||
|
}
|
||||||
|
|
||||||
|
list(std::shared_ptr<const std::string> const& binary)
|
||||||
|
{
|
||||||
|
if(binary)
|
||||||
|
m_vector.push_back(binary_message::create(binary));
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(message::ptr const& message)
|
||||||
|
{
|
||||||
|
if(message)
|
||||||
|
m_vector.push_back(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(const std::string& text)
|
||||||
|
{
|
||||||
|
m_vector.push_back(string_message::create(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(std::string&& text)
|
||||||
|
{
|
||||||
|
m_vector.push_back(string_message::create(move(text)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(std::shared_ptr<std::string> const& binary)
|
||||||
|
{
|
||||||
|
if(binary)
|
||||||
|
m_vector.push_back(binary_message::create(binary));
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(std::shared_ptr<const std::string> const& binary)
|
||||||
|
{
|
||||||
|
if(binary)
|
||||||
|
m_vector.push_back(binary_message::create(binary));
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(size_t pos,message::ptr const& message)
|
||||||
|
{
|
||||||
|
m_vector.insert(m_vector.begin()+pos, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(size_t pos,const std::string& text)
|
||||||
|
{
|
||||||
|
m_vector.insert(m_vector.begin()+pos, string_message::create(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(size_t pos,std::string&& text)
|
||||||
|
{
|
||||||
|
m_vector.insert(m_vector.begin()+pos, string_message::create(move(text)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(size_t pos,std::shared_ptr<std::string> const& binary)
|
||||||
|
{
|
||||||
|
if(binary)
|
||||||
|
m_vector.insert(m_vector.begin()+pos, binary_message::create(binary));
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(size_t pos,std::shared_ptr<const std::string> const& binary)
|
||||||
|
{
|
||||||
|
if(binary)
|
||||||
|
m_vector.insert(m_vector.begin()+pos, binary_message::create(binary));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size() const
|
||||||
|
{
|
||||||
|
return m_vector.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
const message::ptr& at(size_t i) const
|
||||||
|
{
|
||||||
|
return m_vector[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
const message::ptr& operator[] (size_t i) const
|
||||||
|
{
|
||||||
|
return m_vector[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
message::ptr to_array_message(std::string const& event_name) const
|
||||||
|
{
|
||||||
|
message::ptr arr = array_message::create();
|
||||||
|
arr->get_vector().push_back(string_message::create(event_name));
|
||||||
|
arr->get_vector().insert(arr->get_vector().end(),m_vector.begin(),m_vector.end());
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
message::ptr to_array_message() const
|
||||||
|
{
|
||||||
|
message::ptr arr = array_message::create();
|
||||||
|
arr->get_vector().insert(arr->get_vector().end(),m_vector.begin(),m_vector.end());
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<message::ptr> m_vector;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,106 @@
|
|||||||
|
// Modifications Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
#ifndef SIO_SOCKET_H
|
||||||
|
#define SIO_SOCKET_H
|
||||||
|
#include "sio_message.h"
|
||||||
|
#include <functional>
|
||||||
|
namespace sio
|
||||||
|
{
|
||||||
|
class event_adapter;
|
||||||
|
|
||||||
|
class event
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const std::string& get_nsp() const;
|
||||||
|
|
||||||
|
const std::string& get_name() const;
|
||||||
|
|
||||||
|
const message::ptr& get_message() const;
|
||||||
|
|
||||||
|
const message::list& get_messages() const;
|
||||||
|
|
||||||
|
bool need_ack() const;
|
||||||
|
|
||||||
|
void put_ack_message(message::list const& ack_message);
|
||||||
|
|
||||||
|
message::list const& get_ack_message() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
event(std::string const& nsp,std::string const& name,message::list const& messages,bool need_ack);
|
||||||
|
event(std::string const& nsp,std::string const& name,message::list&& messages,bool need_ack);
|
||||||
|
|
||||||
|
message::list& get_ack_message_impl();
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::string m_nsp;
|
||||||
|
const std::string m_name;
|
||||||
|
const message::list m_messages;
|
||||||
|
const bool m_need_ack;
|
||||||
|
message::list m_ack_message;
|
||||||
|
|
||||||
|
friend class event_adapter;
|
||||||
|
};
|
||||||
|
|
||||||
|
class client_impl_base;
|
||||||
|
class packet;
|
||||||
|
|
||||||
|
//The name 'socket' is taken from concept of official socket.io.
|
||||||
|
class SOCKETIOLIB_API socket
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef std::function<void(const std::string& name,message::ptr const& message,bool need_ack, message::list& ack_message)> event_listener_aux;
|
||||||
|
|
||||||
|
typedef std::function<void(event& event)> event_listener;
|
||||||
|
|
||||||
|
typedef std::function<void(message::ptr const& message)> error_listener;
|
||||||
|
|
||||||
|
typedef std::shared_ptr<socket> ptr;
|
||||||
|
|
||||||
|
~socket();
|
||||||
|
|
||||||
|
void on(std::string const& event_name,event_listener const& func);
|
||||||
|
|
||||||
|
void on(std::string const& event_name,event_listener_aux const& func);
|
||||||
|
|
||||||
|
void off(std::string const& event_name);
|
||||||
|
|
||||||
|
void off_all();
|
||||||
|
|
||||||
|
void close();
|
||||||
|
|
||||||
|
void on_error(error_listener const& l);
|
||||||
|
|
||||||
|
void off_error();
|
||||||
|
|
||||||
|
void emit(std::string const& name, message::list const& msglist = nullptr, std::function<void (message::list const&)> const& ack = nullptr);
|
||||||
|
|
||||||
|
std::string const& get_namespace() const;
|
||||||
|
|
||||||
|
std::string const& get_socket_id() const;
|
||||||
|
|
||||||
|
socket(client_impl_base*,std::string const&,message::ptr const&);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
void on_connected();
|
||||||
|
|
||||||
|
void on_close();
|
||||||
|
|
||||||
|
void on_open();
|
||||||
|
|
||||||
|
void on_disconnect();
|
||||||
|
|
||||||
|
void on_message_packet(packet const& p);
|
||||||
|
|
||||||
|
friend class client_impl_base;
|
||||||
|
|
||||||
|
private:
|
||||||
|
//disable copy constructor and assign operator.
|
||||||
|
socket(socket const&){}
|
||||||
|
void operator=(socket const&){}
|
||||||
|
|
||||||
|
class impl;
|
||||||
|
impl *m_impl;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif // SIO_SOCKET_H
|
@ -0,0 +1,77 @@
|
|||||||
|
// Copyright 2018-current Getnamo. All Rights Reserved
|
||||||
|
|
||||||
|
|
||||||
|
using System.IO;
|
||||||
|
using UnrealBuildTool;
|
||||||
|
|
||||||
|
namespace UnrealBuildTool.Rules
|
||||||
|
{
|
||||||
|
public class SocketIOLib : ModuleRules
|
||||||
|
{
|
||||||
|
private string ThirdPartyPath
|
||||||
|
{
|
||||||
|
get { return Path.GetFullPath(Path.Combine(ModuleDirectory, "../ThirdParty/")); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public SocketIOLib(ReadOnlyTargetRules Target) : base(Target)
|
||||||
|
{
|
||||||
|
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||||
|
bUseRTTI = true;
|
||||||
|
bEnableExceptions = true;
|
||||||
|
|
||||||
|
PublicIncludePaths.AddRange(
|
||||||
|
new string[] {
|
||||||
|
Path.Combine(ModuleDirectory, "Public"),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
PrivateIncludePaths.AddRange(
|
||||||
|
new string[] {
|
||||||
|
Path.Combine(ModuleDirectory, "Private"),
|
||||||
|
Path.Combine(ModuleDirectory, "Private/internal"),
|
||||||
|
Path.Combine(ThirdPartyPath, "websocketpp"),
|
||||||
|
Path.Combine(ThirdPartyPath, "asio/asio/include"),
|
||||||
|
Path.Combine(ThirdPartyPath, "rapidjson/include"),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
PublicDependencyModuleNames.AddRange(
|
||||||
|
new string[]
|
||||||
|
{
|
||||||
|
"Core",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
PrivateDependencyModuleNames.AddRange(
|
||||||
|
new string[]
|
||||||
|
{
|
||||||
|
"CoreUObject",
|
||||||
|
"Engine",
|
||||||
|
"OpenSSL"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
DynamicallyLoadedModuleNames.AddRange(
|
||||||
|
new string[]
|
||||||
|
{
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
//Setup TLS support | Maybe other platforms work as well (untested)
|
||||||
|
if (
|
||||||
|
Target.Platform == UnrealTargetPlatform.Win64 ||
|
||||||
|
Target.Platform == UnrealTargetPlatform.Mac ||
|
||||||
|
Target.Platform == UnrealTargetPlatform.Linux ||
|
||||||
|
Target.Platform == UnrealTargetPlatform.IOS||
|
||||||
|
Target.Platform == UnrealTargetPlatform.Android
|
||||||
|
)
|
||||||
|
{
|
||||||
|
PublicDefinitions.Add("SIO_TLS=1");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/LICENSE
vendored
Normal file
23
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/LICENSE
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
Boost Software License - Version 1.0 - August 17th, 2003
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
obtaining a copy of the software and accompanying documentation covered by
|
||||||
|
this license (the "Software") to use, reproduce, display, distribute,
|
||||||
|
execute, and transmit the Software, and to prepare derivative works of the
|
||||||
|
Software, and to permit third-parties to whom the Software is furnished to
|
||||||
|
do so, all subject to the following:
|
||||||
|
|
||||||
|
The copyright notices in the Software and this entire statement, including
|
||||||
|
the above license grant, this restriction and the following disclaimer,
|
||||||
|
must be included in all copies of the Software, in whole or in part, and
|
||||||
|
all derivative works of the Software, unless such copies or derivative
|
||||||
|
works are solely in the form of machine-executable object code generated by
|
||||||
|
a source language processor.
|
||||||
|
|
||||||
|
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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
2
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/.gitignore
vendored
Normal file
2
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Makefile
|
||||||
|
Makefile.in
|
488
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/Makefile.am
vendored
Normal file
488
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/Makefile.am
vendored
Normal file
@ -0,0 +1,488 @@
|
|||||||
|
# find . -name "*.*pp" | sed -e 's/^\.\///' | sed -e 's/^.*$/ & \\/' | sort
|
||||||
|
nobase_include_HEADERS = \
|
||||||
|
asio/associated_allocator.hpp \
|
||||||
|
asio/associated_executor.hpp \
|
||||||
|
asio/async_result.hpp \
|
||||||
|
asio/basic_datagram_socket.hpp \
|
||||||
|
asio/basic_deadline_timer.hpp \
|
||||||
|
asio/basic_io_object.hpp \
|
||||||
|
asio/basic_raw_socket.hpp \
|
||||||
|
asio/basic_seq_packet_socket.hpp \
|
||||||
|
asio/basic_serial_port.hpp \
|
||||||
|
asio/basic_signal_set.hpp \
|
||||||
|
asio/basic_socket_acceptor.hpp \
|
||||||
|
asio/basic_socket.hpp \
|
||||||
|
asio/basic_socket_iostream.hpp \
|
||||||
|
asio/basic_socket_streambuf.hpp \
|
||||||
|
asio/basic_streambuf_fwd.hpp \
|
||||||
|
asio/basic_streambuf.hpp \
|
||||||
|
asio/basic_stream_socket.hpp \
|
||||||
|
asio/basic_waitable_timer.hpp \
|
||||||
|
asio/bind_executor.hpp \
|
||||||
|
asio/buffered_read_stream_fwd.hpp \
|
||||||
|
asio/buffered_read_stream.hpp \
|
||||||
|
asio/buffered_stream_fwd.hpp \
|
||||||
|
asio/buffered_stream.hpp \
|
||||||
|
asio/buffered_write_stream_fwd.hpp \
|
||||||
|
asio/buffered_write_stream.hpp \
|
||||||
|
asio/buffer.hpp \
|
||||||
|
asio/buffers_iterator.hpp \
|
||||||
|
asio/completion_condition.hpp \
|
||||||
|
asio/connect.hpp \
|
||||||
|
asio/coroutine.hpp \
|
||||||
|
asio/datagram_socket_service.hpp \
|
||||||
|
asio/deadline_timer.hpp \
|
||||||
|
asio/deadline_timer_service.hpp \
|
||||||
|
asio/defer.hpp \
|
||||||
|
asio/detail/array_fwd.hpp \
|
||||||
|
asio/detail/array.hpp \
|
||||||
|
asio/detail/assert.hpp \
|
||||||
|
asio/detail/atomic_count.hpp \
|
||||||
|
asio/detail/base_from_completion_cond.hpp \
|
||||||
|
asio/detail/bind_handler.hpp \
|
||||||
|
asio/detail/buffered_stream_storage.hpp \
|
||||||
|
asio/detail/buffer_resize_guard.hpp \
|
||||||
|
asio/detail/buffer_sequence_adapter.hpp \
|
||||||
|
asio/detail/call_stack.hpp \
|
||||||
|
asio/detail/chrono.hpp \
|
||||||
|
asio/detail/chrono_time_traits.hpp \
|
||||||
|
asio/detail/completion_handler.hpp \
|
||||||
|
asio/detail/concurrency_hint.hpp \
|
||||||
|
asio/detail/conditionally_enabled_event.hpp \
|
||||||
|
asio/detail/conditionally_enabled_mutex.hpp \
|
||||||
|
asio/detail/config.hpp \
|
||||||
|
asio/detail/consuming_buffers.hpp \
|
||||||
|
asio/detail/cstddef.hpp \
|
||||||
|
asio/detail/cstdint.hpp \
|
||||||
|
asio/detail/date_time_fwd.hpp \
|
||||||
|
asio/detail/deadline_timer_service.hpp \
|
||||||
|
asio/detail/dependent_type.hpp \
|
||||||
|
asio/detail/descriptor_ops.hpp \
|
||||||
|
asio/detail/descriptor_read_op.hpp \
|
||||||
|
asio/detail/descriptor_write_op.hpp \
|
||||||
|
asio/detail/dev_poll_reactor.hpp \
|
||||||
|
asio/detail/epoll_reactor.hpp \
|
||||||
|
asio/detail/eventfd_select_interrupter.hpp \
|
||||||
|
asio/detail/event.hpp \
|
||||||
|
asio/detail/executor_function.hpp \
|
||||||
|
asio/detail/executor_op.hpp \
|
||||||
|
asio/detail/fd_set_adapter.hpp \
|
||||||
|
asio/detail/fenced_block.hpp \
|
||||||
|
asio/detail/functional.hpp \
|
||||||
|
asio/detail/future.hpp \
|
||||||
|
asio/detail/gcc_arm_fenced_block.hpp \
|
||||||
|
asio/detail/gcc_hppa_fenced_block.hpp \
|
||||||
|
asio/detail/gcc_sync_fenced_block.hpp \
|
||||||
|
asio/detail/gcc_x86_fenced_block.hpp \
|
||||||
|
asio/detail/global.hpp \
|
||||||
|
asio/detail/handler_alloc_helpers.hpp \
|
||||||
|
asio/detail/handler_cont_helpers.hpp \
|
||||||
|
asio/detail/handler_invoke_helpers.hpp \
|
||||||
|
asio/detail/handler_tracking.hpp \
|
||||||
|
asio/detail/handler_type_requirements.hpp \
|
||||||
|
asio/detail/handler_work.hpp \
|
||||||
|
asio/detail/hash_map.hpp \
|
||||||
|
asio/detail/impl/buffer_sequence_adapter.ipp \
|
||||||
|
asio/detail/impl/descriptor_ops.ipp \
|
||||||
|
asio/detail/impl/dev_poll_reactor.hpp \
|
||||||
|
asio/detail/impl/dev_poll_reactor.ipp \
|
||||||
|
asio/detail/impl/epoll_reactor.hpp \
|
||||||
|
asio/detail/impl/epoll_reactor.ipp \
|
||||||
|
asio/detail/impl/eventfd_select_interrupter.ipp \
|
||||||
|
asio/detail/impl/handler_tracking.ipp \
|
||||||
|
asio/detail/impl/kqueue_reactor.hpp \
|
||||||
|
asio/detail/impl/kqueue_reactor.ipp \
|
||||||
|
asio/detail/impl/null_event.ipp \
|
||||||
|
asio/detail/impl/pipe_select_interrupter.ipp \
|
||||||
|
asio/detail/impl/posix_event.ipp \
|
||||||
|
asio/detail/impl/posix_mutex.ipp \
|
||||||
|
asio/detail/impl/posix_thread.ipp \
|
||||||
|
asio/detail/impl/posix_tss_ptr.ipp \
|
||||||
|
asio/detail/impl/reactive_descriptor_service.ipp \
|
||||||
|
asio/detail/impl/reactive_serial_port_service.ipp \
|
||||||
|
asio/detail/impl/reactive_socket_service_base.ipp \
|
||||||
|
asio/detail/impl/resolver_service_base.ipp \
|
||||||
|
asio/detail/impl/scheduler.ipp \
|
||||||
|
asio/detail/impl/select_reactor.hpp \
|
||||||
|
asio/detail/impl/select_reactor.ipp \
|
||||||
|
asio/detail/impl/service_registry.hpp \
|
||||||
|
asio/detail/impl/service_registry.ipp \
|
||||||
|
asio/detail/impl/signal_set_service.ipp \
|
||||||
|
asio/detail/impl/socket_ops.ipp \
|
||||||
|
asio/detail/impl/socket_select_interrupter.ipp \
|
||||||
|
asio/detail/impl/strand_executor_service.hpp \
|
||||||
|
asio/detail/impl/strand_executor_service.ipp \
|
||||||
|
asio/detail/impl/strand_service.hpp \
|
||||||
|
asio/detail/impl/strand_service.ipp \
|
||||||
|
asio/detail/impl/throw_error.ipp \
|
||||||
|
asio/detail/impl/timer_queue_ptime.ipp \
|
||||||
|
asio/detail/impl/timer_queue_set.ipp \
|
||||||
|
asio/detail/impl/win_event.ipp \
|
||||||
|
asio/detail/impl/win_iocp_handle_service.ipp \
|
||||||
|
asio/detail/impl/win_iocp_io_context.hpp \
|
||||||
|
asio/detail/impl/win_iocp_io_context.ipp \
|
||||||
|
asio/detail/impl/win_iocp_serial_port_service.ipp \
|
||||||
|
asio/detail/impl/win_iocp_socket_service_base.ipp \
|
||||||
|
asio/detail/impl/win_mutex.ipp \
|
||||||
|
asio/detail/impl/win_object_handle_service.ipp \
|
||||||
|
asio/detail/impl/winrt_ssocket_service_base.ipp \
|
||||||
|
asio/detail/impl/winrt_timer_scheduler.hpp \
|
||||||
|
asio/detail/impl/winrt_timer_scheduler.ipp \
|
||||||
|
asio/detail/impl/winsock_init.ipp \
|
||||||
|
asio/detail/impl/win_static_mutex.ipp \
|
||||||
|
asio/detail/impl/win_thread.ipp \
|
||||||
|
asio/detail/impl/win_tss_ptr.ipp \
|
||||||
|
asio/detail/io_control.hpp \
|
||||||
|
asio/detail/is_buffer_sequence.hpp \
|
||||||
|
asio/detail/is_executor.hpp \
|
||||||
|
asio/detail/keyword_tss_ptr.hpp \
|
||||||
|
asio/detail/kqueue_reactor.hpp \
|
||||||
|
asio/detail/limits.hpp \
|
||||||
|
asio/detail/local_free_on_block_exit.hpp \
|
||||||
|
asio/detail/macos_fenced_block.hpp \
|
||||||
|
asio/detail/memory.hpp \
|
||||||
|
asio/detail/mutex.hpp \
|
||||||
|
asio/detail/noncopyable.hpp \
|
||||||
|
asio/detail/null_event.hpp \
|
||||||
|
asio/detail/null_fenced_block.hpp \
|
||||||
|
asio/detail/null_global.hpp \
|
||||||
|
asio/detail/null_mutex.hpp \
|
||||||
|
asio/detail/null_reactor.hpp \
|
||||||
|
asio/detail/null_signal_blocker.hpp \
|
||||||
|
asio/detail/null_socket_service.hpp \
|
||||||
|
asio/detail/null_static_mutex.hpp \
|
||||||
|
asio/detail/null_thread.hpp \
|
||||||
|
asio/detail/null_tss_ptr.hpp \
|
||||||
|
asio/detail/object_pool.hpp \
|
||||||
|
asio/detail/old_win_sdk_compat.hpp \
|
||||||
|
asio/detail/operation.hpp \
|
||||||
|
asio/detail/op_queue.hpp \
|
||||||
|
asio/detail/pipe_select_interrupter.hpp \
|
||||||
|
asio/detail/pop_options.hpp \
|
||||||
|
asio/detail/posix_event.hpp \
|
||||||
|
asio/detail/posix_fd_set_adapter.hpp \
|
||||||
|
asio/detail/posix_global.hpp \
|
||||||
|
asio/detail/posix_mutex.hpp \
|
||||||
|
asio/detail/posix_signal_blocker.hpp \
|
||||||
|
asio/detail/posix_static_mutex.hpp \
|
||||||
|
asio/detail/posix_thread.hpp \
|
||||||
|
asio/detail/posix_tss_ptr.hpp \
|
||||||
|
asio/detail/push_options.hpp \
|
||||||
|
asio/detail/reactive_descriptor_service.hpp \
|
||||||
|
asio/detail/reactive_null_buffers_op.hpp \
|
||||||
|
asio/detail/reactive_serial_port_service.hpp \
|
||||||
|
asio/detail/reactive_socket_accept_op.hpp \
|
||||||
|
asio/detail/reactive_socket_connect_op.hpp \
|
||||||
|
asio/detail/reactive_socket_recvfrom_op.hpp \
|
||||||
|
asio/detail/reactive_socket_recvmsg_op.hpp \
|
||||||
|
asio/detail/reactive_socket_recv_op.hpp \
|
||||||
|
asio/detail/reactive_socket_send_op.hpp \
|
||||||
|
asio/detail/reactive_socket_sendto_op.hpp \
|
||||||
|
asio/detail/reactive_socket_service_base.hpp \
|
||||||
|
asio/detail/reactive_socket_service.hpp \
|
||||||
|
asio/detail/reactive_wait_op.hpp \
|
||||||
|
asio/detail/reactor_fwd.hpp \
|
||||||
|
asio/detail/reactor.hpp \
|
||||||
|
asio/detail/reactor_op.hpp \
|
||||||
|
asio/detail/reactor_op_queue.hpp \
|
||||||
|
asio/detail/recycling_allocator.hpp \
|
||||||
|
asio/detail/regex_fwd.hpp \
|
||||||
|
asio/detail/resolve_endpoint_op.hpp \
|
||||||
|
asio/detail/resolve_op.hpp \
|
||||||
|
asio/detail/resolve_query_op.hpp \
|
||||||
|
asio/detail/resolver_service_base.hpp \
|
||||||
|
asio/detail/resolver_service.hpp \
|
||||||
|
asio/detail/scheduler.hpp \
|
||||||
|
asio/detail/scheduler_operation.hpp \
|
||||||
|
asio/detail/scheduler_thread_info.hpp \
|
||||||
|
asio/detail/scoped_lock.hpp \
|
||||||
|
asio/detail/scoped_ptr.hpp \
|
||||||
|
asio/detail/select_interrupter.hpp \
|
||||||
|
asio/detail/select_reactor.hpp \
|
||||||
|
asio/detail/service_registry.hpp \
|
||||||
|
asio/detail/signal_blocker.hpp \
|
||||||
|
asio/detail/signal_handler.hpp \
|
||||||
|
asio/detail/signal_init.hpp \
|
||||||
|
asio/detail/signal_op.hpp \
|
||||||
|
asio/detail/signal_set_service.hpp \
|
||||||
|
asio/detail/socket_holder.hpp \
|
||||||
|
asio/detail/socket_ops.hpp \
|
||||||
|
asio/detail/socket_option.hpp \
|
||||||
|
asio/detail/socket_select_interrupter.hpp \
|
||||||
|
asio/detail/socket_types.hpp \
|
||||||
|
asio/detail/solaris_fenced_block.hpp \
|
||||||
|
asio/detail/static_mutex.hpp \
|
||||||
|
asio/detail/std_event.hpp \
|
||||||
|
asio/detail/std_fenced_block.hpp \
|
||||||
|
asio/detail/std_global.hpp \
|
||||||
|
asio/detail/std_mutex.hpp \
|
||||||
|
asio/detail/std_static_mutex.hpp \
|
||||||
|
asio/detail/std_thread.hpp \
|
||||||
|
asio/detail/strand_executor_service.hpp \
|
||||||
|
asio/detail/strand_service.hpp \
|
||||||
|
asio/detail/string_view.hpp \
|
||||||
|
asio/detail/thread_context.hpp \
|
||||||
|
asio/detail/thread_group.hpp \
|
||||||
|
asio/detail/thread.hpp \
|
||||||
|
asio/detail/thread_info_base.hpp \
|
||||||
|
asio/detail/throw_error.hpp \
|
||||||
|
asio/detail/throw_exception.hpp \
|
||||||
|
asio/detail/timer_queue_base.hpp \
|
||||||
|
asio/detail/timer_queue.hpp \
|
||||||
|
asio/detail/timer_queue_ptime.hpp \
|
||||||
|
asio/detail/timer_queue_set.hpp \
|
||||||
|
asio/detail/timer_scheduler_fwd.hpp \
|
||||||
|
asio/detail/timer_scheduler.hpp \
|
||||||
|
asio/detail/tss_ptr.hpp \
|
||||||
|
asio/detail/type_traits.hpp \
|
||||||
|
asio/detail/variadic_templates.hpp \
|
||||||
|
asio/detail/wait_handler.hpp \
|
||||||
|
asio/detail/wait_op.hpp \
|
||||||
|
asio/detail/winapp_thread.hpp \
|
||||||
|
asio/detail/wince_thread.hpp \
|
||||||
|
asio/detail/win_event.hpp \
|
||||||
|
asio/detail/win_fd_set_adapter.hpp \
|
||||||
|
asio/detail/win_fenced_block.hpp \
|
||||||
|
asio/detail/win_global.hpp \
|
||||||
|
asio/detail/win_iocp_handle_read_op.hpp \
|
||||||
|
asio/detail/win_iocp_handle_service.hpp \
|
||||||
|
asio/detail/win_iocp_handle_write_op.hpp \
|
||||||
|
asio/detail/win_iocp_io_context.hpp \
|
||||||
|
asio/detail/win_iocp_null_buffers_op.hpp \
|
||||||
|
asio/detail/win_iocp_operation.hpp \
|
||||||
|
asio/detail/win_iocp_overlapped_op.hpp \
|
||||||
|
asio/detail/win_iocp_overlapped_ptr.hpp \
|
||||||
|
asio/detail/win_iocp_serial_port_service.hpp \
|
||||||
|
asio/detail/win_iocp_socket_accept_op.hpp \
|
||||||
|
asio/detail/win_iocp_socket_connect_op.hpp \
|
||||||
|
asio/detail/win_iocp_socket_recvfrom_op.hpp \
|
||||||
|
asio/detail/win_iocp_socket_recvmsg_op.hpp \
|
||||||
|
asio/detail/win_iocp_socket_recv_op.hpp \
|
||||||
|
asio/detail/win_iocp_socket_send_op.hpp \
|
||||||
|
asio/detail/win_iocp_socket_service_base.hpp \
|
||||||
|
asio/detail/win_iocp_socket_service.hpp \
|
||||||
|
asio/detail/win_iocp_thread_info.hpp \
|
||||||
|
asio/detail/win_iocp_wait_op.hpp \
|
||||||
|
asio/detail/win_mutex.hpp \
|
||||||
|
asio/detail/win_object_handle_service.hpp \
|
||||||
|
asio/detail/winrt_async_manager.hpp \
|
||||||
|
asio/detail/winrt_async_op.hpp \
|
||||||
|
asio/detail/winrt_resolve_op.hpp \
|
||||||
|
asio/detail/winrt_resolver_service.hpp \
|
||||||
|
asio/detail/winrt_socket_connect_op.hpp \
|
||||||
|
asio/detail/winrt_socket_recv_op.hpp \
|
||||||
|
asio/detail/winrt_socket_send_op.hpp \
|
||||||
|
asio/detail/winrt_ssocket_service_base.hpp \
|
||||||
|
asio/detail/winrt_ssocket_service.hpp \
|
||||||
|
asio/detail/winrt_timer_scheduler.hpp \
|
||||||
|
asio/detail/winrt_utils.hpp \
|
||||||
|
asio/detail/winsock_init.hpp \
|
||||||
|
asio/detail/win_static_mutex.hpp \
|
||||||
|
asio/detail/win_thread.hpp \
|
||||||
|
asio/detail/win_tss_ptr.hpp \
|
||||||
|
asio/detail/work_dispatcher.hpp \
|
||||||
|
asio/detail/wrapped_handler.hpp \
|
||||||
|
asio/dispatch.hpp \
|
||||||
|
asio/error_code.hpp \
|
||||||
|
asio/error.hpp \
|
||||||
|
asio/execution_context.hpp \
|
||||||
|
asio/executor.hpp \
|
||||||
|
asio/executor_work_guard.hpp \
|
||||||
|
asio/experimental/co_spawn.hpp \
|
||||||
|
asio/experimental/detached.hpp \
|
||||||
|
asio/experimental.hpp \
|
||||||
|
asio/experimental/impl/co_spawn.hpp \
|
||||||
|
asio/experimental/impl/detached.hpp \
|
||||||
|
asio/experimental/impl/redirect_error.hpp \
|
||||||
|
asio/experimental/redirect_error.hpp \
|
||||||
|
asio/generic/basic_endpoint.hpp \
|
||||||
|
asio/generic/datagram_protocol.hpp \
|
||||||
|
asio/generic/detail/endpoint.hpp \
|
||||||
|
asio/generic/detail/impl/endpoint.ipp \
|
||||||
|
asio/generic/raw_protocol.hpp \
|
||||||
|
asio/generic/seq_packet_protocol.hpp \
|
||||||
|
asio/generic/stream_protocol.hpp \
|
||||||
|
asio/handler_alloc_hook.hpp \
|
||||||
|
asio/handler_continuation_hook.hpp \
|
||||||
|
asio/handler_invoke_hook.hpp \
|
||||||
|
asio/handler_type.hpp \
|
||||||
|
asio/high_resolution_timer.hpp \
|
||||||
|
asio.hpp \
|
||||||
|
asio/impl/buffered_read_stream.hpp \
|
||||||
|
asio/impl/buffered_write_stream.hpp \
|
||||||
|
asio/impl/connect.hpp \
|
||||||
|
asio/impl/defer.hpp \
|
||||||
|
asio/impl/dispatch.hpp \
|
||||||
|
asio/impl/error_code.ipp \
|
||||||
|
asio/impl/error.ipp \
|
||||||
|
asio/impl/execution_context.hpp \
|
||||||
|
asio/impl/execution_context.ipp \
|
||||||
|
asio/impl/executor.hpp \
|
||||||
|
asio/impl/executor.ipp \
|
||||||
|
asio/impl/handler_alloc_hook.ipp \
|
||||||
|
asio/impl/io_context.hpp \
|
||||||
|
asio/impl/io_context.ipp \
|
||||||
|
asio/impl/post.hpp \
|
||||||
|
asio/impl/read_at.hpp \
|
||||||
|
asio/impl/read.hpp \
|
||||||
|
asio/impl/read_until.hpp \
|
||||||
|
asio/impl/serial_port_base.hpp \
|
||||||
|
asio/impl/serial_port_base.ipp \
|
||||||
|
asio/impl/spawn.hpp \
|
||||||
|
asio/impl/src.cpp \
|
||||||
|
asio/impl/src.hpp \
|
||||||
|
asio/impl/system_context.hpp \
|
||||||
|
asio/impl/system_context.ipp \
|
||||||
|
asio/impl/system_executor.hpp \
|
||||||
|
asio/impl/thread_pool.hpp \
|
||||||
|
asio/impl/thread_pool.ipp \
|
||||||
|
asio/impl/use_future.hpp \
|
||||||
|
asio/impl/write_at.hpp \
|
||||||
|
asio/impl/write.hpp \
|
||||||
|
asio/io_context.hpp \
|
||||||
|
asio/io_context_strand.hpp \
|
||||||
|
asio/io_service.hpp \
|
||||||
|
asio/io_service_strand.hpp \
|
||||||
|
asio/ip/address.hpp \
|
||||||
|
asio/ip/address_v4.hpp \
|
||||||
|
asio/ip/address_v4_iterator.hpp \
|
||||||
|
asio/ip/address_v4_range.hpp \
|
||||||
|
asio/ip/address_v6.hpp \
|
||||||
|
asio/ip/address_v6_iterator.hpp \
|
||||||
|
asio/ip/address_v6_range.hpp \
|
||||||
|
asio/ip/bad_address_cast.hpp \
|
||||||
|
asio/ip/basic_endpoint.hpp \
|
||||||
|
asio/ip/basic_resolver_entry.hpp \
|
||||||
|
asio/ip/basic_resolver.hpp \
|
||||||
|
asio/ip/basic_resolver_iterator.hpp \
|
||||||
|
asio/ip/basic_resolver_query.hpp \
|
||||||
|
asio/ip/basic_resolver_results.hpp \
|
||||||
|
asio/ip/detail/endpoint.hpp \
|
||||||
|
asio/ip/detail/impl/endpoint.ipp \
|
||||||
|
asio/ip/detail/socket_option.hpp \
|
||||||
|
asio/ip/host_name.hpp \
|
||||||
|
asio/ip/icmp.hpp \
|
||||||
|
asio/ip/impl/address.hpp \
|
||||||
|
asio/ip/impl/address.ipp \
|
||||||
|
asio/ip/impl/address_v4.hpp \
|
||||||
|
asio/ip/impl/address_v4.ipp \
|
||||||
|
asio/ip/impl/address_v6.hpp \
|
||||||
|
asio/ip/impl/address_v6.ipp \
|
||||||
|
asio/ip/impl/basic_endpoint.hpp \
|
||||||
|
asio/ip/impl/host_name.ipp \
|
||||||
|
asio/ip/impl/network_v4.hpp \
|
||||||
|
asio/ip/impl/network_v4.ipp \
|
||||||
|
asio/ip/impl/network_v6.hpp \
|
||||||
|
asio/ip/impl/network_v6.ipp \
|
||||||
|
asio/ip/multicast.hpp \
|
||||||
|
asio/ip/network_v4.hpp \
|
||||||
|
asio/ip/network_v6.hpp \
|
||||||
|
asio/ip/resolver_base.hpp \
|
||||||
|
asio/ip/resolver_query_base.hpp \
|
||||||
|
asio/ip/resolver_service.hpp \
|
||||||
|
asio/ip/tcp.hpp \
|
||||||
|
asio/ip/udp.hpp \
|
||||||
|
asio/ip/unicast.hpp \
|
||||||
|
asio/ip/v6_only.hpp \
|
||||||
|
asio/is_executor.hpp \
|
||||||
|
asio/is_read_buffered.hpp \
|
||||||
|
asio/is_write_buffered.hpp \
|
||||||
|
asio/local/basic_endpoint.hpp \
|
||||||
|
asio/local/connect_pair.hpp \
|
||||||
|
asio/local/datagram_protocol.hpp \
|
||||||
|
asio/local/detail/endpoint.hpp \
|
||||||
|
asio/local/detail/impl/endpoint.ipp \
|
||||||
|
asio/local/stream_protocol.hpp \
|
||||||
|
asio/packaged_task.hpp \
|
||||||
|
asio/placeholders.hpp \
|
||||||
|
asio/posix/basic_descriptor.hpp \
|
||||||
|
asio/posix/basic_stream_descriptor.hpp \
|
||||||
|
asio/posix/descriptor_base.hpp \
|
||||||
|
asio/posix/descriptor.hpp \
|
||||||
|
asio/posix/stream_descriptor.hpp \
|
||||||
|
asio/posix/stream_descriptor_service.hpp \
|
||||||
|
asio/post.hpp \
|
||||||
|
asio/raw_socket_service.hpp \
|
||||||
|
asio/read_at.hpp \
|
||||||
|
asio/read.hpp \
|
||||||
|
asio/read_until.hpp \
|
||||||
|
asio/seq_packet_socket_service.hpp \
|
||||||
|
asio/serial_port_base.hpp \
|
||||||
|
asio/serial_port.hpp \
|
||||||
|
asio/serial_port_service.hpp \
|
||||||
|
asio/signal_set.hpp \
|
||||||
|
asio/signal_set_service.hpp \
|
||||||
|
asio/socket_acceptor_service.hpp \
|
||||||
|
asio/socket_base.hpp \
|
||||||
|
asio/spawn.hpp \
|
||||||
|
asio/ssl/context_base.hpp \
|
||||||
|
asio/ssl/context.hpp \
|
||||||
|
asio/ssl/detail/buffered_handshake_op.hpp \
|
||||||
|
asio/ssl/detail/engine.hpp \
|
||||||
|
asio/ssl/detail/handshake_op.hpp \
|
||||||
|
asio/ssl/detail/impl/engine.ipp \
|
||||||
|
asio/ssl/detail/impl/openssl_init.ipp \
|
||||||
|
asio/ssl/detail/io.hpp \
|
||||||
|
asio/ssl/detail/openssl_init.hpp \
|
||||||
|
asio/ssl/detail/openssl_types.hpp \
|
||||||
|
asio/ssl/detail/password_callback.hpp \
|
||||||
|
asio/ssl/detail/read_op.hpp \
|
||||||
|
asio/ssl/detail/shutdown_op.hpp \
|
||||||
|
asio/ssl/detail/stream_core.hpp \
|
||||||
|
asio/ssl/detail/verify_callback.hpp \
|
||||||
|
asio/ssl/detail/write_op.hpp \
|
||||||
|
asio/ssl/error.hpp \
|
||||||
|
asio/ssl.hpp \
|
||||||
|
asio/ssl/impl/context.hpp \
|
||||||
|
asio/ssl/impl/context.ipp \
|
||||||
|
asio/ssl/impl/error.ipp \
|
||||||
|
asio/ssl/impl/rfc2818_verification.ipp \
|
||||||
|
asio/ssl/impl/src.hpp \
|
||||||
|
asio/ssl/rfc2818_verification.hpp \
|
||||||
|
asio/ssl/stream_base.hpp \
|
||||||
|
asio/ssl/stream.hpp \
|
||||||
|
asio/ssl/verify_context.hpp \
|
||||||
|
asio/ssl/verify_mode.hpp \
|
||||||
|
asio/steady_timer.hpp \
|
||||||
|
asio/strand.hpp \
|
||||||
|
asio/streambuf.hpp \
|
||||||
|
asio/stream_socket_service.hpp \
|
||||||
|
asio/system_context.hpp \
|
||||||
|
asio/system_error.hpp \
|
||||||
|
asio/system_executor.hpp \
|
||||||
|
asio/system_timer.hpp \
|
||||||
|
asio/thread.hpp \
|
||||||
|
asio/thread_pool.hpp \
|
||||||
|
asio/time_traits.hpp \
|
||||||
|
asio/ts/buffer.hpp \
|
||||||
|
asio/ts/executor.hpp \
|
||||||
|
asio/ts/internet.hpp \
|
||||||
|
asio/ts/io_context.hpp \
|
||||||
|
asio/ts/netfwd.hpp \
|
||||||
|
asio/ts/net.hpp \
|
||||||
|
asio/ts/socket.hpp \
|
||||||
|
asio/ts/timer.hpp \
|
||||||
|
asio/unyield.hpp \
|
||||||
|
asio/use_future.hpp \
|
||||||
|
asio/uses_executor.hpp \
|
||||||
|
asio/version.hpp \
|
||||||
|
asio/waitable_timer_service.hpp \
|
||||||
|
asio/wait_traits.hpp \
|
||||||
|
asio/windows/basic_handle.hpp \
|
||||||
|
asio/windows/basic_object_handle.hpp \
|
||||||
|
asio/windows/basic_random_access_handle.hpp \
|
||||||
|
asio/windows/basic_stream_handle.hpp \
|
||||||
|
asio/windows/object_handle.hpp \
|
||||||
|
asio/windows/object_handle_service.hpp \
|
||||||
|
asio/windows/overlapped_handle.hpp \
|
||||||
|
asio/windows/overlapped_ptr.hpp \
|
||||||
|
asio/windows/random_access_handle.hpp \
|
||||||
|
asio/windows/random_access_handle_service.hpp \
|
||||||
|
asio/windows/stream_handle.hpp \
|
||||||
|
asio/windows/stream_handle_service.hpp \
|
||||||
|
asio/write_at.hpp \
|
||||||
|
asio/write.hpp \
|
||||||
|
asio/yield.hpp
|
||||||
|
|
||||||
|
MAINTAINERCLEANFILES = \
|
||||||
|
$(srcdir)/Makefile.in
|
152
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/asio.hpp
vendored
Normal file
152
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/asio.hpp
vendored
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
//
|
||||||
|
// asio.hpp
|
||||||
|
// ~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ASIO_HPP
|
||||||
|
#define ASIO_HPP
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
# pragma once
|
||||||
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
|
||||||
|
#include "asio/associated_allocator.hpp"
|
||||||
|
#include "asio/associated_executor.hpp"
|
||||||
|
#include "asio/async_result.hpp"
|
||||||
|
#include "asio/basic_datagram_socket.hpp"
|
||||||
|
#include "asio/basic_deadline_timer.hpp"
|
||||||
|
#include "asio/basic_io_object.hpp"
|
||||||
|
#include "asio/basic_raw_socket.hpp"
|
||||||
|
#include "asio/basic_seq_packet_socket.hpp"
|
||||||
|
#include "asio/basic_serial_port.hpp"
|
||||||
|
#include "asio/basic_signal_set.hpp"
|
||||||
|
#include "asio/basic_socket_acceptor.hpp"
|
||||||
|
#include "asio/basic_socket_iostream.hpp"
|
||||||
|
#include "asio/basic_socket_streambuf.hpp"
|
||||||
|
#include "asio/basic_stream_socket.hpp"
|
||||||
|
#include "asio/basic_streambuf.hpp"
|
||||||
|
#include "asio/basic_waitable_timer.hpp"
|
||||||
|
#include "asio/bind_executor.hpp"
|
||||||
|
#include "asio/buffer.hpp"
|
||||||
|
#include "asio/buffered_read_stream_fwd.hpp"
|
||||||
|
#include "asio/buffered_read_stream.hpp"
|
||||||
|
#include "asio/buffered_stream_fwd.hpp"
|
||||||
|
#include "asio/buffered_stream.hpp"
|
||||||
|
#include "asio/buffered_write_stream_fwd.hpp"
|
||||||
|
#include "asio/buffered_write_stream.hpp"
|
||||||
|
#include "asio/buffers_iterator.hpp"
|
||||||
|
#include "asio/completion_condition.hpp"
|
||||||
|
#include "asio/connect.hpp"
|
||||||
|
#include "asio/coroutine.hpp"
|
||||||
|
#include "asio/datagram_socket_service.hpp"
|
||||||
|
#include "asio/deadline_timer_service.hpp"
|
||||||
|
#include "asio/deadline_timer.hpp"
|
||||||
|
#include "asio/defer.hpp"
|
||||||
|
#include "asio/dispatch.hpp"
|
||||||
|
#include "asio/error.hpp"
|
||||||
|
#include "asio/error_code.hpp"
|
||||||
|
#include "asio/execution_context.hpp"
|
||||||
|
#include "asio/executor.hpp"
|
||||||
|
#include "asio/executor_work_guard.hpp"
|
||||||
|
#include "asio/generic/basic_endpoint.hpp"
|
||||||
|
#include "asio/generic/datagram_protocol.hpp"
|
||||||
|
#include "asio/generic/raw_protocol.hpp"
|
||||||
|
#include "asio/generic/seq_packet_protocol.hpp"
|
||||||
|
#include "asio/generic/stream_protocol.hpp"
|
||||||
|
#include "asio/handler_alloc_hook.hpp"
|
||||||
|
#include "asio/handler_continuation_hook.hpp"
|
||||||
|
#include "asio/handler_invoke_hook.hpp"
|
||||||
|
#include "asio/handler_type.hpp"
|
||||||
|
#include "asio/high_resolution_timer.hpp"
|
||||||
|
#include "asio/io_context.hpp"
|
||||||
|
#include "asio/io_context_strand.hpp"
|
||||||
|
#include "asio/io_service.hpp"
|
||||||
|
#include "asio/io_service_strand.hpp"
|
||||||
|
#include "asio/ip/address.hpp"
|
||||||
|
#include "asio/ip/address_v4.hpp"
|
||||||
|
#include "asio/ip/address_v4_iterator.hpp"
|
||||||
|
#include "asio/ip/address_v4_range.hpp"
|
||||||
|
#include "asio/ip/address_v6.hpp"
|
||||||
|
#include "asio/ip/address_v6_iterator.hpp"
|
||||||
|
#include "asio/ip/address_v6_range.hpp"
|
||||||
|
#include "asio/ip/bad_address_cast.hpp"
|
||||||
|
#include "asio/ip/basic_endpoint.hpp"
|
||||||
|
#include "asio/ip/basic_resolver.hpp"
|
||||||
|
#include "asio/ip/basic_resolver_entry.hpp"
|
||||||
|
#include "asio/ip/basic_resolver_iterator.hpp"
|
||||||
|
#include "asio/ip/basic_resolver_query.hpp"
|
||||||
|
#include "asio/ip/host_name.hpp"
|
||||||
|
#include "asio/ip/icmp.hpp"
|
||||||
|
#include "asio/ip/multicast.hpp"
|
||||||
|
#include "asio/ip/resolver_base.hpp"
|
||||||
|
#include "asio/ip/resolver_query_base.hpp"
|
||||||
|
#include "asio/ip/resolver_service.hpp"
|
||||||
|
#include "asio/ip/tcp.hpp"
|
||||||
|
#include "asio/ip/udp.hpp"
|
||||||
|
#include "asio/ip/unicast.hpp"
|
||||||
|
#include "asio/ip/v6_only.hpp"
|
||||||
|
#include "asio/is_executor.hpp"
|
||||||
|
#include "asio/is_read_buffered.hpp"
|
||||||
|
#include "asio/is_write_buffered.hpp"
|
||||||
|
#include "asio/local/basic_endpoint.hpp"
|
||||||
|
#include "asio/local/connect_pair.hpp"
|
||||||
|
#include "asio/local/datagram_protocol.hpp"
|
||||||
|
#include "asio/local/stream_protocol.hpp"
|
||||||
|
#include "asio/packaged_task.hpp"
|
||||||
|
#include "asio/placeholders.hpp"
|
||||||
|
#include "asio/posix/basic_descriptor.hpp"
|
||||||
|
#include "asio/posix/basic_stream_descriptor.hpp"
|
||||||
|
#include "asio/posix/descriptor.hpp"
|
||||||
|
#include "asio/posix/descriptor_base.hpp"
|
||||||
|
#include "asio/posix/stream_descriptor.hpp"
|
||||||
|
#include "asio/posix/stream_descriptor_service.hpp"
|
||||||
|
#include "asio/post.hpp"
|
||||||
|
#include "asio/raw_socket_service.hpp"
|
||||||
|
#include "asio/read.hpp"
|
||||||
|
#include "asio/read_at.hpp"
|
||||||
|
#include "asio/read_until.hpp"
|
||||||
|
#include "asio/seq_packet_socket_service.hpp"
|
||||||
|
#include "asio/serial_port.hpp"
|
||||||
|
#include "asio/serial_port_base.hpp"
|
||||||
|
#include "asio/serial_port_service.hpp"
|
||||||
|
#include "asio/signal_set.hpp"
|
||||||
|
#include "asio/signal_set_service.hpp"
|
||||||
|
#include "asio/socket_acceptor_service.hpp"
|
||||||
|
#include "asio/socket_base.hpp"
|
||||||
|
#include "asio/steady_timer.hpp"
|
||||||
|
#include "asio/strand.hpp"
|
||||||
|
#include "asio/stream_socket_service.hpp"
|
||||||
|
#include "asio/streambuf.hpp"
|
||||||
|
#include "asio/system_context.hpp"
|
||||||
|
#include "asio/system_error.hpp"
|
||||||
|
#include "asio/system_executor.hpp"
|
||||||
|
#include "asio/system_timer.hpp"
|
||||||
|
#include "asio/thread.hpp"
|
||||||
|
#include "asio/thread_pool.hpp"
|
||||||
|
#include "asio/time_traits.hpp"
|
||||||
|
#include "asio/use_future.hpp"
|
||||||
|
#include "asio/uses_executor.hpp"
|
||||||
|
#include "asio/version.hpp"
|
||||||
|
#include "asio/wait_traits.hpp"
|
||||||
|
#include "asio/waitable_timer_service.hpp"
|
||||||
|
#include "asio/windows/basic_handle.hpp"
|
||||||
|
#include "asio/windows/basic_object_handle.hpp"
|
||||||
|
#include "asio/windows/basic_random_access_handle.hpp"
|
||||||
|
#include "asio/windows/basic_stream_handle.hpp"
|
||||||
|
#include "asio/windows/object_handle.hpp"
|
||||||
|
#include "asio/windows/object_handle_service.hpp"
|
||||||
|
#include "asio/windows/overlapped_handle.hpp"
|
||||||
|
#include "asio/windows/overlapped_ptr.hpp"
|
||||||
|
#include "asio/windows/random_access_handle.hpp"
|
||||||
|
#include "asio/windows/random_access_handle_service.hpp"
|
||||||
|
#include "asio/windows/stream_handle.hpp"
|
||||||
|
#include "asio/windows/stream_handle_service.hpp"
|
||||||
|
#include "asio/write.hpp"
|
||||||
|
#include "asio/write_at.hpp"
|
||||||
|
|
||||||
|
#endif // ASIO_HPP
|
@ -0,0 +1,131 @@
|
|||||||
|
//
|
||||||
|
// associated_allocator.hpp
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ASIO_ASSOCIATED_ALLOCATOR_HPP
|
||||||
|
#define ASIO_ASSOCIATED_ALLOCATOR_HPP
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
# pragma once
|
||||||
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
|
||||||
|
#include "asio/detail/config.hpp"
|
||||||
|
#include <memory>
|
||||||
|
#include "asio/detail/type_traits.hpp"
|
||||||
|
|
||||||
|
#include "asio/detail/push_options.hpp"
|
||||||
|
|
||||||
|
namespace asio {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename>
|
||||||
|
struct associated_allocator_check
|
||||||
|
{
|
||||||
|
typedef void type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename E, typename = void>
|
||||||
|
struct associated_allocator_impl
|
||||||
|
{
|
||||||
|
typedef E type;
|
||||||
|
|
||||||
|
static type get(const T&, const E& e) ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename E>
|
||||||
|
struct associated_allocator_impl<T, E,
|
||||||
|
typename associated_allocator_check<typename T::allocator_type>::type>
|
||||||
|
{
|
||||||
|
typedef typename T::allocator_type type;
|
||||||
|
|
||||||
|
static type get(const T& t, const E&) ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return t.get_allocator();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
/// Traits type used to obtain the allocator associated with an object.
|
||||||
|
/**
|
||||||
|
* A program may specialise this traits type if the @c T template parameter in
|
||||||
|
* the specialisation is a user-defined type. The template parameter @c
|
||||||
|
* Allocator shall be a type meeting the Allocator requirements.
|
||||||
|
*
|
||||||
|
* Specialisations shall meet the following requirements, where @c t is a const
|
||||||
|
* reference to an object of type @c T, and @c a is an object of type @c
|
||||||
|
* Allocator.
|
||||||
|
*
|
||||||
|
* @li Provide a nested typedef @c type that identifies a type meeting the
|
||||||
|
* Allocator requirements.
|
||||||
|
*
|
||||||
|
* @li Provide a noexcept static member function named @c get, callable as @c
|
||||||
|
* get(t) and with return type @c type.
|
||||||
|
*
|
||||||
|
* @li Provide a noexcept static member function named @c get, callable as @c
|
||||||
|
* get(t,a) and with return type @c type.
|
||||||
|
*/
|
||||||
|
template <typename T, typename Allocator = std::allocator<void> >
|
||||||
|
struct associated_allocator
|
||||||
|
{
|
||||||
|
/// If @c T has a nested type @c allocator_type, <tt>T::allocator_type</tt>.
|
||||||
|
/// Otherwise @c Allocator.
|
||||||
|
#if defined(GENERATING_DOCUMENTATION)
|
||||||
|
typedef see_below type;
|
||||||
|
#else // defined(GENERATING_DOCUMENTATION)
|
||||||
|
typedef typename detail::associated_allocator_impl<T, Allocator>::type type;
|
||||||
|
#endif // defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
/// If @c T has a nested type @c allocator_type, returns
|
||||||
|
/// <tt>t.get_allocator()</tt>. Otherwise returns @c a.
|
||||||
|
static type get(const T& t,
|
||||||
|
const Allocator& a = Allocator()) ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return detail::associated_allocator_impl<T, Allocator>::get(t, a);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Helper function to obtain an object's associated allocator.
|
||||||
|
/**
|
||||||
|
* @returns <tt>associated_allocator<T>::get(t)</tt>
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
inline typename associated_allocator<T>::type
|
||||||
|
get_associated_allocator(const T& t) ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return associated_allocator<T>::get(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function to obtain an object's associated allocator.
|
||||||
|
/**
|
||||||
|
* @returns <tt>associated_allocator<T, Allocator>::get(t, a)</tt>
|
||||||
|
*/
|
||||||
|
template <typename T, typename Allocator>
|
||||||
|
inline typename associated_allocator<T, Allocator>::type
|
||||||
|
get_associated_allocator(const T& t, const Allocator& a) ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return associated_allocator<T, Allocator>::get(t, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(ASIO_HAS_ALIAS_TEMPLATES)
|
||||||
|
|
||||||
|
template <typename T, typename Allocator = std::allocator<void> >
|
||||||
|
using associated_allocator_t
|
||||||
|
= typename associated_allocator<T, Allocator>::type;
|
||||||
|
|
||||||
|
#endif // defined(ASIO_HAS_ALIAS_TEMPLATES)
|
||||||
|
|
||||||
|
} // namespace asio
|
||||||
|
|
||||||
|
#include "asio/detail/pop_options.hpp"
|
||||||
|
|
||||||
|
#endif // ASIO_ASSOCIATED_ALLOCATOR_HPP
|
@ -0,0 +1,149 @@
|
|||||||
|
//
|
||||||
|
// associated_executor.hpp
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ASIO_ASSOCIATED_EXECUTOR_HPP
|
||||||
|
#define ASIO_ASSOCIATED_EXECUTOR_HPP
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
# pragma once
|
||||||
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
|
||||||
|
#include "asio/detail/config.hpp"
|
||||||
|
#include "asio/detail/type_traits.hpp"
|
||||||
|
#include "asio/is_executor.hpp"
|
||||||
|
#include "asio/system_executor.hpp"
|
||||||
|
|
||||||
|
#include "asio/detail/push_options.hpp"
|
||||||
|
|
||||||
|
namespace asio {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename>
|
||||||
|
struct associated_executor_check
|
||||||
|
{
|
||||||
|
typedef void type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename E, typename = void>
|
||||||
|
struct associated_executor_impl
|
||||||
|
{
|
||||||
|
typedef E type;
|
||||||
|
|
||||||
|
static type get(const T&, const E& e) ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename E>
|
||||||
|
struct associated_executor_impl<T, E,
|
||||||
|
typename associated_executor_check<typename T::executor_type>::type>
|
||||||
|
{
|
||||||
|
typedef typename T::executor_type type;
|
||||||
|
|
||||||
|
static type get(const T& t, const E&) ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return t.get_executor();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
/// Traits type used to obtain the executor associated with an object.
|
||||||
|
/**
|
||||||
|
* A program may specialise this traits type if the @c T template parameter in
|
||||||
|
* the specialisation is a user-defined type. The template parameter @c
|
||||||
|
* Executor shall be a type meeting the Executor requirements.
|
||||||
|
*
|
||||||
|
* Specialisations shall meet the following requirements, where @c t is a const
|
||||||
|
* reference to an object of type @c T, and @c e is an object of type @c
|
||||||
|
* Executor.
|
||||||
|
*
|
||||||
|
* @li Provide a nested typedef @c type that identifies a type meeting the
|
||||||
|
* Executor requirements.
|
||||||
|
*
|
||||||
|
* @li Provide a noexcept static member function named @c get, callable as @c
|
||||||
|
* get(t) and with return type @c type.
|
||||||
|
*
|
||||||
|
* @li Provide a noexcept static member function named @c get, callable as @c
|
||||||
|
* get(t,e) and with return type @c type.
|
||||||
|
*/
|
||||||
|
template <typename T, typename Executor = system_executor>
|
||||||
|
struct associated_executor
|
||||||
|
{
|
||||||
|
/// If @c T has a nested type @c executor_type, <tt>T::executor_type</tt>.
|
||||||
|
/// Otherwise @c Executor.
|
||||||
|
#if defined(GENERATING_DOCUMENTATION)
|
||||||
|
typedef see_below type;
|
||||||
|
#else // defined(GENERATING_DOCUMENTATION)
|
||||||
|
typedef typename detail::associated_executor_impl<T, Executor>::type type;
|
||||||
|
#endif // defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
/// If @c T has a nested type @c executor_type, returns
|
||||||
|
/// <tt>t.get_executor()</tt>. Otherwise returns @c ex.
|
||||||
|
static type get(const T& t,
|
||||||
|
const Executor& ex = Executor()) ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return detail::associated_executor_impl<T, Executor>::get(t, ex);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Helper function to obtain an object's associated executor.
|
||||||
|
/**
|
||||||
|
* @returns <tt>associated_executor<T>::get(t)</tt>
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
inline typename associated_executor<T>::type
|
||||||
|
get_associated_executor(const T& t) ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return associated_executor<T>::get(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function to obtain an object's associated executor.
|
||||||
|
/**
|
||||||
|
* @returns <tt>associated_executor<T, Executor>::get(t, ex)</tt>
|
||||||
|
*/
|
||||||
|
template <typename T, typename Executor>
|
||||||
|
inline typename associated_executor<T, Executor>::type
|
||||||
|
get_associated_executor(const T& t, const Executor& ex,
|
||||||
|
typename enable_if<is_executor<
|
||||||
|
Executor>::value>::type* = 0) ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return associated_executor<T, Executor>::get(t, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function to obtain an object's associated executor.
|
||||||
|
/**
|
||||||
|
* @returns <tt>associated_executor<T, typename
|
||||||
|
* ExecutionContext::executor_type>::get(t, ctx.get_executor())</tt>
|
||||||
|
*/
|
||||||
|
template <typename T, typename ExecutionContext>
|
||||||
|
inline typename associated_executor<T,
|
||||||
|
typename ExecutionContext::executor_type>::type
|
||||||
|
get_associated_executor(const T& t, ExecutionContext& ctx,
|
||||||
|
typename enable_if<is_convertible<ExecutionContext&,
|
||||||
|
execution_context&>::value>::type* = 0) ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return associated_executor<T,
|
||||||
|
typename ExecutionContext::executor_type>::get(t, ctx.get_executor());
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(ASIO_HAS_ALIAS_TEMPLATES)
|
||||||
|
|
||||||
|
template <typename T, typename Executor = system_executor>
|
||||||
|
using associated_executor_t = typename associated_executor<T, Executor>::type;
|
||||||
|
|
||||||
|
#endif // defined(ASIO_HAS_ALIAS_TEMPLATES)
|
||||||
|
|
||||||
|
} // namespace asio
|
||||||
|
|
||||||
|
#include "asio/detail/pop_options.hpp"
|
||||||
|
|
||||||
|
#endif // ASIO_ASSOCIATED_EXECUTOR_HPP
|
221
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/asio/async_result.hpp
vendored
Normal file
221
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/asio/async_result.hpp
vendored
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
//
|
||||||
|
// async_result.hpp
|
||||||
|
// ~~~~~~~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ASIO_ASYNC_RESULT_HPP
|
||||||
|
#define ASIO_ASYNC_RESULT_HPP
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
# pragma once
|
||||||
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
|
||||||
|
#include "asio/detail/config.hpp"
|
||||||
|
#include "asio/detail/type_traits.hpp"
|
||||||
|
#include "asio/handler_type.hpp"
|
||||||
|
|
||||||
|
#include "asio/detail/push_options.hpp"
|
||||||
|
|
||||||
|
namespace asio {
|
||||||
|
|
||||||
|
/// An interface for customising the behaviour of an initiating function.
|
||||||
|
/**
|
||||||
|
* The async_result traits class is used for determining:
|
||||||
|
*
|
||||||
|
* @li the concrete completion handler type to be called at the end of the
|
||||||
|
* asynchronous operation;
|
||||||
|
*
|
||||||
|
* @li the initiating function return type; and
|
||||||
|
*
|
||||||
|
* @li how the return value of the initiating function is obtained.
|
||||||
|
*
|
||||||
|
* The trait allows the handler and return types to be determined at the point
|
||||||
|
* where the specific completion handler signature is known.
|
||||||
|
*
|
||||||
|
* This template may be specialised for user-defined completion token types.
|
||||||
|
* The primary template assumes that the CompletionToken is the completion
|
||||||
|
* handler.
|
||||||
|
*/
|
||||||
|
#if defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
template <typename CompletionToken, typename Signature>
|
||||||
|
#else // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
template <typename CompletionToken, typename Signature = void>
|
||||||
|
#endif // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
class async_result
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
#if defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
/// The concrete completion handler type for the specific signature.
|
||||||
|
typedef CompletionToken completion_handler_type;
|
||||||
|
|
||||||
|
/// The return type of the initiating function.
|
||||||
|
typedef void return_type;
|
||||||
|
#else // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
// For backward compatibility, determine the concrete completion handler type
|
||||||
|
// by using the legacy handler_type trait.
|
||||||
|
typedef typename handler_type<CompletionToken, Signature>::type
|
||||||
|
completion_handler_type;
|
||||||
|
|
||||||
|
// For backward compatibility, determine the initiating function return type
|
||||||
|
// using the legacy single-parameter version of async_result.
|
||||||
|
typedef typename async_result<completion_handler_type>::type return_type;
|
||||||
|
#endif // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
/// Construct an async result from a given handler.
|
||||||
|
/**
|
||||||
|
* When using a specalised async_result, the constructor has an opportunity
|
||||||
|
* to initialise some state associated with the completion handler, which is
|
||||||
|
* then returned from the initiating function.
|
||||||
|
*/
|
||||||
|
explicit async_result(completion_handler_type& h)
|
||||||
|
#if defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
// No data members to initialise.
|
||||||
|
#else // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
: legacy_result_(h)
|
||||||
|
#endif // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
{
|
||||||
|
(void)h;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtain the value to be returned from the initiating function.
|
||||||
|
return_type get()
|
||||||
|
{
|
||||||
|
#if defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
// Nothing to do.
|
||||||
|
#else // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
return legacy_result_.get();
|
||||||
|
#endif // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
async_result(const async_result&) ASIO_DELETED;
|
||||||
|
async_result& operator=(const async_result&) ASIO_DELETED;
|
||||||
|
|
||||||
|
#if defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
// No data members.
|
||||||
|
#else // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
async_result<completion_handler_type> legacy_result_;
|
||||||
|
#endif // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
};
|
||||||
|
|
||||||
|
#if !defined(ASIO_NO_DEPRECATED)
|
||||||
|
|
||||||
|
/// (Deprecated: Use two-parameter version of async_result.) An interface for
|
||||||
|
/// customising the behaviour of an initiating function.
|
||||||
|
/**
|
||||||
|
* This template may be specialised for user-defined handler types.
|
||||||
|
*/
|
||||||
|
template <typename Handler>
|
||||||
|
class async_result<Handler>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// The return type of the initiating function.
|
||||||
|
typedef void type;
|
||||||
|
|
||||||
|
/// Construct an async result from a given handler.
|
||||||
|
/**
|
||||||
|
* When using a specalised async_result, the constructor has an opportunity
|
||||||
|
* to initialise some state associated with the handler, which is then
|
||||||
|
* returned from the initiating function.
|
||||||
|
*/
|
||||||
|
explicit async_result(Handler&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtain the value to be returned from the initiating function.
|
||||||
|
type get()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // !defined(ASIO_NO_DEPRECATED)
|
||||||
|
|
||||||
|
/// Helper template to deduce the handler type from a CompletionToken, capture
|
||||||
|
/// a local copy of the handler, and then create an async_result for the
|
||||||
|
/// handler.
|
||||||
|
template <typename CompletionToken, typename Signature>
|
||||||
|
struct async_completion
|
||||||
|
{
|
||||||
|
/// The real handler type to be used for the asynchronous operation.
|
||||||
|
typedef typename asio::async_result<
|
||||||
|
typename decay<CompletionToken>::type,
|
||||||
|
Signature>::completion_handler_type completion_handler_type;
|
||||||
|
|
||||||
|
#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
/// Constructor.
|
||||||
|
/**
|
||||||
|
* The constructor creates the concrete completion handler and makes the link
|
||||||
|
* between the handler and the asynchronous result.
|
||||||
|
*/
|
||||||
|
explicit async_completion(CompletionToken& token)
|
||||||
|
: completion_handler(static_cast<typename conditional<
|
||||||
|
is_same<CompletionToken, completion_handler_type>::value,
|
||||||
|
completion_handler_type&, CompletionToken&&>::type>(token)),
|
||||||
|
result(completion_handler)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#else // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
explicit async_completion(typename decay<CompletionToken>::type& token)
|
||||||
|
: completion_handler(token),
|
||||||
|
result(completion_handler)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit async_completion(const typename decay<CompletionToken>::type& token)
|
||||||
|
: completion_handler(token),
|
||||||
|
result(completion_handler)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
/// A copy of, or reference to, a real handler object.
|
||||||
|
#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
typename conditional<
|
||||||
|
is_same<CompletionToken, completion_handler_type>::value,
|
||||||
|
completion_handler_type&, completion_handler_type>::type completion_handler;
|
||||||
|
#else // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
completion_handler_type completion_handler;
|
||||||
|
#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
/// The result of the asynchronous operation's initiating function.
|
||||||
|
async_result<typename decay<CompletionToken>::type, Signature> result;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename CompletionToken, typename Signature>
|
||||||
|
struct async_result_helper
|
||||||
|
: async_result<typename decay<CompletionToken>::type, Signature>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace asio
|
||||||
|
|
||||||
|
#include "asio/detail/pop_options.hpp"
|
||||||
|
|
||||||
|
#if defined(GENERATING_DOCUMENTATION)
|
||||||
|
# define ASIO_INITFN_RESULT_TYPE(ct, sig) \
|
||||||
|
void_or_deduced
|
||||||
|
#elif defined(_MSC_VER) && (_MSC_VER < 1500)
|
||||||
|
# define ASIO_INITFN_RESULT_TYPE(ct, sig) \
|
||||||
|
typename ::asio::detail::async_result_helper< \
|
||||||
|
ct, sig>::return_type
|
||||||
|
#define ASIO_HANDLER_TYPE(ct, sig) \
|
||||||
|
typename ::asio::detail::async_result_helper< \
|
||||||
|
ct, sig>::completion_handler_type
|
||||||
|
#else
|
||||||
|
# define ASIO_INITFN_RESULT_TYPE(ct, sig) \
|
||||||
|
typename ::asio::async_result< \
|
||||||
|
typename ::asio::decay<ct>::type, sig>::return_type
|
||||||
|
#define ASIO_HANDLER_TYPE(ct, sig) \
|
||||||
|
typename ::asio::async_result< \
|
||||||
|
typename ::asio::decay<ct>::type, sig>::completion_handler_type
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // ASIO_ASYNC_RESULT_HPP
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,628 @@
|
|||||||
|
//
|
||||||
|
// basic_deadline_timer.hpp
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ASIO_BASIC_DEADLINE_TIMER_HPP
|
||||||
|
#define ASIO_BASIC_DEADLINE_TIMER_HPP
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
# pragma once
|
||||||
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
|
||||||
|
#include "asio/detail/config.hpp"
|
||||||
|
|
||||||
|
#if defined(ASIO_HAS_BOOST_DATE_TIME) \
|
||||||
|
|| defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include "asio/basic_io_object.hpp"
|
||||||
|
#include "asio/detail/handler_type_requirements.hpp"
|
||||||
|
#include "asio/detail/throw_error.hpp"
|
||||||
|
#include "asio/error.hpp"
|
||||||
|
#include "asio/time_traits.hpp"
|
||||||
|
|
||||||
|
#if defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
# include "asio/deadline_timer_service.hpp"
|
||||||
|
#else // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
# include "asio/detail/deadline_timer_service.hpp"
|
||||||
|
# define ASIO_SVC_T detail::deadline_timer_service<TimeTraits>
|
||||||
|
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
|
||||||
|
#include "asio/detail/push_options.hpp"
|
||||||
|
|
||||||
|
namespace asio {
|
||||||
|
|
||||||
|
/// Provides waitable timer functionality.
|
||||||
|
/**
|
||||||
|
* The basic_deadline_timer class template provides the ability to perform a
|
||||||
|
* blocking or asynchronous wait for a timer to expire.
|
||||||
|
*
|
||||||
|
* A deadline timer is always in one of two states: "expired" or "not expired".
|
||||||
|
* If the wait() or async_wait() function is called on an expired timer, the
|
||||||
|
* wait operation will complete immediately.
|
||||||
|
*
|
||||||
|
* Most applications will use the asio::deadline_timer typedef.
|
||||||
|
*
|
||||||
|
* @par Thread Safety
|
||||||
|
* @e Distinct @e objects: Safe.@n
|
||||||
|
* @e Shared @e objects: Unsafe.
|
||||||
|
*
|
||||||
|
* @par Examples
|
||||||
|
* Performing a blocking wait:
|
||||||
|
* @code
|
||||||
|
* // Construct a timer without setting an expiry time.
|
||||||
|
* asio::deadline_timer timer(io_context);
|
||||||
|
*
|
||||||
|
* // Set an expiry time relative to now.
|
||||||
|
* timer.expires_from_now(boost::posix_time::seconds(5));
|
||||||
|
*
|
||||||
|
* // Wait for the timer to expire.
|
||||||
|
* timer.wait();
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @par
|
||||||
|
* Performing an asynchronous wait:
|
||||||
|
* @code
|
||||||
|
* void handler(const asio::error_code& error)
|
||||||
|
* {
|
||||||
|
* if (!error)
|
||||||
|
* {
|
||||||
|
* // Timer expired.
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* // Construct a timer with an absolute expiry time.
|
||||||
|
* asio::deadline_timer timer(io_context,
|
||||||
|
* boost::posix_time::time_from_string("2005-12-07 23:59:59.000"));
|
||||||
|
*
|
||||||
|
* // Start an asynchronous wait.
|
||||||
|
* timer.async_wait(handler);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @par Changing an active deadline_timer's expiry time
|
||||||
|
*
|
||||||
|
* Changing the expiry time of a timer while there are pending asynchronous
|
||||||
|
* waits causes those wait operations to be cancelled. To ensure that the action
|
||||||
|
* associated with the timer is performed only once, use something like this:
|
||||||
|
* used:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* void on_some_event()
|
||||||
|
* {
|
||||||
|
* if (my_timer.expires_from_now(seconds(5)) > 0)
|
||||||
|
* {
|
||||||
|
* // We managed to cancel the timer. Start new asynchronous wait.
|
||||||
|
* my_timer.async_wait(on_timeout);
|
||||||
|
* }
|
||||||
|
* else
|
||||||
|
* {
|
||||||
|
* // Too late, timer has already expired!
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* void on_timeout(const asio::error_code& e)
|
||||||
|
* {
|
||||||
|
* if (e != asio::error::operation_aborted)
|
||||||
|
* {
|
||||||
|
* // Timer was not cancelled, take necessary action.
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @li The asio::basic_deadline_timer::expires_from_now() function
|
||||||
|
* cancels any pending asynchronous waits, and returns the number of
|
||||||
|
* asynchronous waits that were cancelled. If it returns 0 then you were too
|
||||||
|
* late and the wait handler has already been executed, or will soon be
|
||||||
|
* executed. If it returns 1 then the wait handler was successfully cancelled.
|
||||||
|
*
|
||||||
|
* @li If a wait handler is cancelled, the asio::error_code passed to
|
||||||
|
* it contains the value asio::error::operation_aborted.
|
||||||
|
*/
|
||||||
|
template <typename Time,
|
||||||
|
typename TimeTraits = asio::time_traits<Time>
|
||||||
|
ASIO_SVC_TPARAM_DEF2(= deadline_timer_service<Time, TimeTraits>)>
|
||||||
|
class basic_deadline_timer
|
||||||
|
: ASIO_SVC_ACCESS basic_io_object<ASIO_SVC_T>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// The type of the executor associated with the object.
|
||||||
|
typedef io_context::executor_type executor_type;
|
||||||
|
|
||||||
|
/// The time traits type.
|
||||||
|
typedef TimeTraits traits_type;
|
||||||
|
|
||||||
|
/// The time type.
|
||||||
|
typedef typename traits_type::time_type time_type;
|
||||||
|
|
||||||
|
/// The duration type.
|
||||||
|
typedef typename traits_type::duration_type duration_type;
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
/**
|
||||||
|
* This constructor creates a timer without setting an expiry time. The
|
||||||
|
* expires_at() or expires_from_now() functions must be called to set an
|
||||||
|
* expiry time before the timer can be waited on.
|
||||||
|
*
|
||||||
|
* @param io_context The io_context object that the timer will use to dispatch
|
||||||
|
* handlers for any asynchronous operations performed on the timer.
|
||||||
|
*/
|
||||||
|
explicit basic_deadline_timer(asio::io_context& io_context)
|
||||||
|
: basic_io_object<ASIO_SVC_T>(io_context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructor to set a particular expiry time as an absolute time.
|
||||||
|
/**
|
||||||
|
* This constructor creates a timer and sets the expiry time.
|
||||||
|
*
|
||||||
|
* @param io_context The io_context object that the timer will use to dispatch
|
||||||
|
* handlers for any asynchronous operations performed on the timer.
|
||||||
|
*
|
||||||
|
* @param expiry_time The expiry time to be used for the timer, expressed
|
||||||
|
* as an absolute time.
|
||||||
|
*/
|
||||||
|
basic_deadline_timer(asio::io_context& io_context,
|
||||||
|
const time_type& expiry_time)
|
||||||
|
: basic_io_object<ASIO_SVC_T>(io_context)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
this->get_service().expires_at(this->get_implementation(), expiry_time, ec);
|
||||||
|
asio::detail::throw_error(ec, "expires_at");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructor to set a particular expiry time relative to now.
|
||||||
|
/**
|
||||||
|
* This constructor creates a timer and sets the expiry time.
|
||||||
|
*
|
||||||
|
* @param io_context The io_context object that the timer will use to dispatch
|
||||||
|
* handlers for any asynchronous operations performed on the timer.
|
||||||
|
*
|
||||||
|
* @param expiry_time The expiry time to be used for the timer, relative to
|
||||||
|
* now.
|
||||||
|
*/
|
||||||
|
basic_deadline_timer(asio::io_context& io_context,
|
||||||
|
const duration_type& expiry_time)
|
||||||
|
: basic_io_object<ASIO_SVC_T>(io_context)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
this->get_service().expires_from_now(
|
||||||
|
this->get_implementation(), expiry_time, ec);
|
||||||
|
asio::detail::throw_error(ec, "expires_from_now");
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
/// Move-construct a basic_deadline_timer from another.
|
||||||
|
/**
|
||||||
|
* This constructor moves a timer from one object to another.
|
||||||
|
*
|
||||||
|
* @param other The other basic_deadline_timer object from which the move will
|
||||||
|
* occur.
|
||||||
|
*
|
||||||
|
* @note Following the move, the moved-from object is in the same state as if
|
||||||
|
* constructed using the @c basic_deadline_timer(io_context&) constructor.
|
||||||
|
*/
|
||||||
|
basic_deadline_timer(basic_deadline_timer&& other)
|
||||||
|
: basic_io_object<ASIO_SVC_T>(std::move(other))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move-assign a basic_deadline_timer from another.
|
||||||
|
/**
|
||||||
|
* This assignment operator moves a timer from one object to another. Cancels
|
||||||
|
* any outstanding asynchronous operations associated with the target object.
|
||||||
|
*
|
||||||
|
* @param other The other basic_deadline_timer object from which the move will
|
||||||
|
* occur.
|
||||||
|
*
|
||||||
|
* @note Following the move, the moved-from object is in the same state as if
|
||||||
|
* constructed using the @c basic_deadline_timer(io_context&) constructor.
|
||||||
|
*/
|
||||||
|
basic_deadline_timer& operator=(basic_deadline_timer&& other)
|
||||||
|
{
|
||||||
|
basic_io_object<ASIO_SVC_T>::operator=(std::move(other));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
/// Destroys the timer.
|
||||||
|
/**
|
||||||
|
* This function destroys the timer, cancelling any outstanding asynchronous
|
||||||
|
* wait operations associated with the timer as if by calling @c cancel.
|
||||||
|
*/
|
||||||
|
~basic_deadline_timer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
// These functions are provided by basic_io_object<>.
|
||||||
|
#else // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
#if !defined(ASIO_NO_DEPRECATED)
|
||||||
|
/// (Deprecated: Use get_executor().) Get the io_context associated with the
|
||||||
|
/// object.
|
||||||
|
/**
|
||||||
|
* This function may be used to obtain the io_context object that the I/O
|
||||||
|
* object uses to dispatch handlers for asynchronous operations.
|
||||||
|
*
|
||||||
|
* @return A reference to the io_context object that the I/O object will use
|
||||||
|
* to dispatch handlers. Ownership is not transferred to the caller.
|
||||||
|
*/
|
||||||
|
asio::io_context& get_io_context()
|
||||||
|
{
|
||||||
|
return basic_io_object<ASIO_SVC_T>::get_io_context();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// (Deprecated: Use get_executor().) Get the io_context associated with the
|
||||||
|
/// object.
|
||||||
|
/**
|
||||||
|
* This function may be used to obtain the io_context object that the I/O
|
||||||
|
* object uses to dispatch handlers for asynchronous operations.
|
||||||
|
*
|
||||||
|
* @return A reference to the io_context object that the I/O object will use
|
||||||
|
* to dispatch handlers. Ownership is not transferred to the caller.
|
||||||
|
*/
|
||||||
|
asio::io_context& get_io_service()
|
||||||
|
{
|
||||||
|
return basic_io_object<ASIO_SVC_T>::get_io_service();
|
||||||
|
}
|
||||||
|
#endif // !defined(ASIO_NO_DEPRECATED)
|
||||||
|
|
||||||
|
/// Get the executor associated with the object.
|
||||||
|
executor_type get_executor() ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return basic_io_object<ASIO_SVC_T>::get_executor();
|
||||||
|
}
|
||||||
|
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
|
||||||
|
/// Cancel any asynchronous operations that are waiting on the timer.
|
||||||
|
/**
|
||||||
|
* This function forces the completion of any pending asynchronous wait
|
||||||
|
* operations against the timer. The handler for each cancelled operation will
|
||||||
|
* be invoked with the asio::error::operation_aborted error code.
|
||||||
|
*
|
||||||
|
* Cancelling the timer does not change the expiry time.
|
||||||
|
*
|
||||||
|
* @return The number of asynchronous operations that were cancelled.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*
|
||||||
|
* @note If the timer has already expired when cancel() is called, then the
|
||||||
|
* handlers for asynchronous wait operations will:
|
||||||
|
*
|
||||||
|
* @li have already been invoked; or
|
||||||
|
*
|
||||||
|
* @li have been queued for invocation in the near future.
|
||||||
|
*
|
||||||
|
* These handlers can no longer be cancelled, and therefore are passed an
|
||||||
|
* error code that indicates the successful completion of the wait operation.
|
||||||
|
*/
|
||||||
|
std::size_t cancel()
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
std::size_t s = this->get_service().cancel(this->get_implementation(), ec);
|
||||||
|
asio::detail::throw_error(ec, "cancel");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cancel any asynchronous operations that are waiting on the timer.
|
||||||
|
/**
|
||||||
|
* This function forces the completion of any pending asynchronous wait
|
||||||
|
* operations against the timer. The handler for each cancelled operation will
|
||||||
|
* be invoked with the asio::error::operation_aborted error code.
|
||||||
|
*
|
||||||
|
* Cancelling the timer does not change the expiry time.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*
|
||||||
|
* @return The number of asynchronous operations that were cancelled.
|
||||||
|
*
|
||||||
|
* @note If the timer has already expired when cancel() is called, then the
|
||||||
|
* handlers for asynchronous wait operations will:
|
||||||
|
*
|
||||||
|
* @li have already been invoked; or
|
||||||
|
*
|
||||||
|
* @li have been queued for invocation in the near future.
|
||||||
|
*
|
||||||
|
* These handlers can no longer be cancelled, and therefore are passed an
|
||||||
|
* error code that indicates the successful completion of the wait operation.
|
||||||
|
*/
|
||||||
|
std::size_t cancel(asio::error_code& ec)
|
||||||
|
{
|
||||||
|
return this->get_service().cancel(this->get_implementation(), ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cancels one asynchronous operation that is waiting on the timer.
|
||||||
|
/**
|
||||||
|
* This function forces the completion of one pending asynchronous wait
|
||||||
|
* operation against the timer. Handlers are cancelled in FIFO order. The
|
||||||
|
* handler for the cancelled operation will be invoked with the
|
||||||
|
* asio::error::operation_aborted error code.
|
||||||
|
*
|
||||||
|
* Cancelling the timer does not change the expiry time.
|
||||||
|
*
|
||||||
|
* @return The number of asynchronous operations that were cancelled. That is,
|
||||||
|
* either 0 or 1.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*
|
||||||
|
* @note If the timer has already expired when cancel_one() is called, then
|
||||||
|
* the handlers for asynchronous wait operations will:
|
||||||
|
*
|
||||||
|
* @li have already been invoked; or
|
||||||
|
*
|
||||||
|
* @li have been queued for invocation in the near future.
|
||||||
|
*
|
||||||
|
* These handlers can no longer be cancelled, and therefore are passed an
|
||||||
|
* error code that indicates the successful completion of the wait operation.
|
||||||
|
*/
|
||||||
|
std::size_t cancel_one()
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
std::size_t s = this->get_service().cancel_one(
|
||||||
|
this->get_implementation(), ec);
|
||||||
|
asio::detail::throw_error(ec, "cancel_one");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cancels one asynchronous operation that is waiting on the timer.
|
||||||
|
/**
|
||||||
|
* This function forces the completion of one pending asynchronous wait
|
||||||
|
* operation against the timer. Handlers are cancelled in FIFO order. The
|
||||||
|
* handler for the cancelled operation will be invoked with the
|
||||||
|
* asio::error::operation_aborted error code.
|
||||||
|
*
|
||||||
|
* Cancelling the timer does not change the expiry time.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*
|
||||||
|
* @return The number of asynchronous operations that were cancelled. That is,
|
||||||
|
* either 0 or 1.
|
||||||
|
*
|
||||||
|
* @note If the timer has already expired when cancel_one() is called, then
|
||||||
|
* the handlers for asynchronous wait operations will:
|
||||||
|
*
|
||||||
|
* @li have already been invoked; or
|
||||||
|
*
|
||||||
|
* @li have been queued for invocation in the near future.
|
||||||
|
*
|
||||||
|
* These handlers can no longer be cancelled, and therefore are passed an
|
||||||
|
* error code that indicates the successful completion of the wait operation.
|
||||||
|
*/
|
||||||
|
std::size_t cancel_one(asio::error_code& ec)
|
||||||
|
{
|
||||||
|
return this->get_service().cancel_one(this->get_implementation(), ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the timer's expiry time as an absolute time.
|
||||||
|
/**
|
||||||
|
* This function may be used to obtain the timer's current expiry time.
|
||||||
|
* Whether the timer has expired or not does not affect this value.
|
||||||
|
*/
|
||||||
|
time_type expires_at() const
|
||||||
|
{
|
||||||
|
return this->get_service().expires_at(this->get_implementation());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the timer's expiry time as an absolute time.
|
||||||
|
/**
|
||||||
|
* This function sets the expiry time. Any pending asynchronous wait
|
||||||
|
* operations will be cancelled. The handler for each cancelled operation will
|
||||||
|
* be invoked with the asio::error::operation_aborted error code.
|
||||||
|
*
|
||||||
|
* @param expiry_time The expiry time to be used for the timer.
|
||||||
|
*
|
||||||
|
* @return The number of asynchronous operations that were cancelled.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*
|
||||||
|
* @note If the timer has already expired when expires_at() is called, then
|
||||||
|
* the handlers for asynchronous wait operations will:
|
||||||
|
*
|
||||||
|
* @li have already been invoked; or
|
||||||
|
*
|
||||||
|
* @li have been queued for invocation in the near future.
|
||||||
|
*
|
||||||
|
* These handlers can no longer be cancelled, and therefore are passed an
|
||||||
|
* error code that indicates the successful completion of the wait operation.
|
||||||
|
*/
|
||||||
|
std::size_t expires_at(const time_type& expiry_time)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
std::size_t s = this->get_service().expires_at(
|
||||||
|
this->get_implementation(), expiry_time, ec);
|
||||||
|
asio::detail::throw_error(ec, "expires_at");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the timer's expiry time as an absolute time.
|
||||||
|
/**
|
||||||
|
* This function sets the expiry time. Any pending asynchronous wait
|
||||||
|
* operations will be cancelled. The handler for each cancelled operation will
|
||||||
|
* be invoked with the asio::error::operation_aborted error code.
|
||||||
|
*
|
||||||
|
* @param expiry_time The expiry time to be used for the timer.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*
|
||||||
|
* @return The number of asynchronous operations that were cancelled.
|
||||||
|
*
|
||||||
|
* @note If the timer has already expired when expires_at() is called, then
|
||||||
|
* the handlers for asynchronous wait operations will:
|
||||||
|
*
|
||||||
|
* @li have already been invoked; or
|
||||||
|
*
|
||||||
|
* @li have been queued for invocation in the near future.
|
||||||
|
*
|
||||||
|
* These handlers can no longer be cancelled, and therefore are passed an
|
||||||
|
* error code that indicates the successful completion of the wait operation.
|
||||||
|
*/
|
||||||
|
std::size_t expires_at(const time_type& expiry_time,
|
||||||
|
asio::error_code& ec)
|
||||||
|
{
|
||||||
|
return this->get_service().expires_at(
|
||||||
|
this->get_implementation(), expiry_time, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the timer's expiry time relative to now.
|
||||||
|
/**
|
||||||
|
* This function may be used to obtain the timer's current expiry time.
|
||||||
|
* Whether the timer has expired or not does not affect this value.
|
||||||
|
*/
|
||||||
|
duration_type expires_from_now() const
|
||||||
|
{
|
||||||
|
return this->get_service().expires_from_now(this->get_implementation());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the timer's expiry time relative to now.
|
||||||
|
/**
|
||||||
|
* This function sets the expiry time. Any pending asynchronous wait
|
||||||
|
* operations will be cancelled. The handler for each cancelled operation will
|
||||||
|
* be invoked with the asio::error::operation_aborted error code.
|
||||||
|
*
|
||||||
|
* @param expiry_time The expiry time to be used for the timer.
|
||||||
|
*
|
||||||
|
* @return The number of asynchronous operations that were cancelled.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*
|
||||||
|
* @note If the timer has already expired when expires_from_now() is called,
|
||||||
|
* then the handlers for asynchronous wait operations will:
|
||||||
|
*
|
||||||
|
* @li have already been invoked; or
|
||||||
|
*
|
||||||
|
* @li have been queued for invocation in the near future.
|
||||||
|
*
|
||||||
|
* These handlers can no longer be cancelled, and therefore are passed an
|
||||||
|
* error code that indicates the successful completion of the wait operation.
|
||||||
|
*/
|
||||||
|
std::size_t expires_from_now(const duration_type& expiry_time)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
std::size_t s = this->get_service().expires_from_now(
|
||||||
|
this->get_implementation(), expiry_time, ec);
|
||||||
|
asio::detail::throw_error(ec, "expires_from_now");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the timer's expiry time relative to now.
|
||||||
|
/**
|
||||||
|
* This function sets the expiry time. Any pending asynchronous wait
|
||||||
|
* operations will be cancelled. The handler for each cancelled operation will
|
||||||
|
* be invoked with the asio::error::operation_aborted error code.
|
||||||
|
*
|
||||||
|
* @param expiry_time The expiry time to be used for the timer.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*
|
||||||
|
* @return The number of asynchronous operations that were cancelled.
|
||||||
|
*
|
||||||
|
* @note If the timer has already expired when expires_from_now() is called,
|
||||||
|
* then the handlers for asynchronous wait operations will:
|
||||||
|
*
|
||||||
|
* @li have already been invoked; or
|
||||||
|
*
|
||||||
|
* @li have been queued for invocation in the near future.
|
||||||
|
*
|
||||||
|
* These handlers can no longer be cancelled, and therefore are passed an
|
||||||
|
* error code that indicates the successful completion of the wait operation.
|
||||||
|
*/
|
||||||
|
std::size_t expires_from_now(const duration_type& expiry_time,
|
||||||
|
asio::error_code& ec)
|
||||||
|
{
|
||||||
|
return this->get_service().expires_from_now(
|
||||||
|
this->get_implementation(), expiry_time, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform a blocking wait on the timer.
|
||||||
|
/**
|
||||||
|
* This function is used to wait for the timer to expire. This function
|
||||||
|
* blocks and does not return until the timer has expired.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*/
|
||||||
|
void wait()
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
this->get_service().wait(this->get_implementation(), ec);
|
||||||
|
asio::detail::throw_error(ec, "wait");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform a blocking wait on the timer.
|
||||||
|
/**
|
||||||
|
* This function is used to wait for the timer to expire. This function
|
||||||
|
* blocks and does not return until the timer has expired.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*/
|
||||||
|
void wait(asio::error_code& ec)
|
||||||
|
{
|
||||||
|
this->get_service().wait(this->get_implementation(), ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start an asynchronous wait on the timer.
|
||||||
|
/**
|
||||||
|
* This function may be used to initiate an asynchronous wait against the
|
||||||
|
* timer. It always returns immediately.
|
||||||
|
*
|
||||||
|
* For each call to async_wait(), the supplied handler will be called exactly
|
||||||
|
* once. The handler will be called when:
|
||||||
|
*
|
||||||
|
* @li The timer has expired.
|
||||||
|
*
|
||||||
|
* @li The timer was cancelled, in which case the handler is passed the error
|
||||||
|
* code asio::error::operation_aborted.
|
||||||
|
*
|
||||||
|
* @param handler The handler to be called when the timer expires. Copies
|
||||||
|
* will be made of the handler as required. The function signature of the
|
||||||
|
* handler must be:
|
||||||
|
* @code void handler(
|
||||||
|
* const asio::error_code& error // Result of operation.
|
||||||
|
* ); @endcode
|
||||||
|
* Regardless of whether the asynchronous operation completes immediately or
|
||||||
|
* not, the handler will not be invoked from within this function. Invocation
|
||||||
|
* of the handler will be performed in a manner equivalent to using
|
||||||
|
* asio::io_context::post().
|
||||||
|
*/
|
||||||
|
template <typename WaitHandler>
|
||||||
|
ASIO_INITFN_RESULT_TYPE(WaitHandler,
|
||||||
|
void (asio::error_code))
|
||||||
|
async_wait(ASIO_MOVE_ARG(WaitHandler) handler)
|
||||||
|
{
|
||||||
|
// If you get an error on the following line it means that your handler does
|
||||||
|
// not meet the documented type requirements for a WaitHandler.
|
||||||
|
ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check;
|
||||||
|
|
||||||
|
#if defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
return this->get_service().async_wait(this->get_implementation(),
|
||||||
|
ASIO_MOVE_CAST(WaitHandler)(handler));
|
||||||
|
#else // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
async_completion<WaitHandler,
|
||||||
|
void (asio::error_code)> init(handler);
|
||||||
|
|
||||||
|
this->get_service().async_wait(this->get_implementation(),
|
||||||
|
init.completion_handler);
|
||||||
|
|
||||||
|
return init.result.get();
|
||||||
|
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace asio
|
||||||
|
|
||||||
|
#include "asio/detail/pop_options.hpp"
|
||||||
|
|
||||||
|
#if !defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
# undef ASIO_SVC_T
|
||||||
|
#endif // !defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
|
||||||
|
#endif // defined(ASIO_HAS_BOOST_DATE_TIME)
|
||||||
|
// || defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
#endif // ASIO_BASIC_DEADLINE_TIMER_HPP
|
290
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/asio/basic_io_object.hpp
vendored
Normal file
290
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/asio/basic_io_object.hpp
vendored
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
//
|
||||||
|
// basic_io_object.hpp
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ASIO_BASIC_IO_OBJECT_HPP
|
||||||
|
#define ASIO_BASIC_IO_OBJECT_HPP
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
# pragma once
|
||||||
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
|
||||||
|
#include "asio/detail/config.hpp"
|
||||||
|
#include "asio/io_context.hpp"
|
||||||
|
|
||||||
|
#include "asio/detail/push_options.hpp"
|
||||||
|
|
||||||
|
namespace asio {
|
||||||
|
|
||||||
|
#if defined(ASIO_HAS_MOVE)
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
// Type trait used to determine whether a service supports move.
|
||||||
|
template <typename IoObjectService>
|
||||||
|
class service_has_move
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
typedef IoObjectService service_type;
|
||||||
|
typedef typename service_type::implementation_type implementation_type;
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
static auto asio_service_has_move_eval(T* t, U* u)
|
||||||
|
-> decltype(t->move_construct(*u, *u), char());
|
||||||
|
static char (&asio_service_has_move_eval(...))[2];
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const bool value =
|
||||||
|
sizeof(asio_service_has_move_eval(
|
||||||
|
static_cast<service_type*>(0),
|
||||||
|
static_cast<implementation_type*>(0))) == 1;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif // defined(ASIO_HAS_MOVE)
|
||||||
|
|
||||||
|
/// Base class for all I/O objects.
|
||||||
|
/**
|
||||||
|
* @note All I/O objects are non-copyable. However, when using C++0x, certain
|
||||||
|
* I/O objects do support move construction and move assignment.
|
||||||
|
*/
|
||||||
|
#if !defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
template <typename IoObjectService>
|
||||||
|
#else
|
||||||
|
template <typename IoObjectService,
|
||||||
|
bool Movable = detail::service_has_move<IoObjectService>::value>
|
||||||
|
#endif
|
||||||
|
class basic_io_object
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// The type of the service that will be used to provide I/O operations.
|
||||||
|
typedef IoObjectService service_type;
|
||||||
|
|
||||||
|
/// The underlying implementation type of I/O object.
|
||||||
|
typedef typename service_type::implementation_type implementation_type;
|
||||||
|
|
||||||
|
#if !defined(ASIO_NO_DEPRECATED)
|
||||||
|
/// (Deprecated: Use get_executor().) Get the io_context associated with the
|
||||||
|
/// object.
|
||||||
|
/**
|
||||||
|
* This function may be used to obtain the io_context object that the I/O
|
||||||
|
* object uses to dispatch handlers for asynchronous operations.
|
||||||
|
*
|
||||||
|
* @return A reference to the io_context object that the I/O object will use
|
||||||
|
* to dispatch handlers. Ownership is not transferred to the caller.
|
||||||
|
*/
|
||||||
|
asio::io_context& get_io_context()
|
||||||
|
{
|
||||||
|
return service_.get_io_context();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// (Deprecated: Use get_executor().) Get the io_context associated with the
|
||||||
|
/// object.
|
||||||
|
/**
|
||||||
|
* This function may be used to obtain the io_context object that the I/O
|
||||||
|
* object uses to dispatch handlers for asynchronous operations.
|
||||||
|
*
|
||||||
|
* @return A reference to the io_context object that the I/O object will use
|
||||||
|
* to dispatch handlers. Ownership is not transferred to the caller.
|
||||||
|
*/
|
||||||
|
asio::io_context& get_io_service()
|
||||||
|
{
|
||||||
|
return service_.get_io_context();
|
||||||
|
}
|
||||||
|
#endif // !defined(ASIO_NO_DEPRECATED)
|
||||||
|
|
||||||
|
/// The type of the executor associated with the object.
|
||||||
|
typedef asio::io_context::executor_type executor_type;
|
||||||
|
|
||||||
|
/// Get the executor associated with the object.
|
||||||
|
executor_type get_executor() ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return service_.get_io_context().get_executor();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// Construct a basic_io_object.
|
||||||
|
/**
|
||||||
|
* Performs:
|
||||||
|
* @code get_service().construct(get_implementation()); @endcode
|
||||||
|
*/
|
||||||
|
explicit basic_io_object(asio::io_context& io_context)
|
||||||
|
: service_(asio::use_service<IoObjectService>(io_context))
|
||||||
|
{
|
||||||
|
service_.construct(implementation_);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(GENERATING_DOCUMENTATION)
|
||||||
|
/// Move-construct a basic_io_object.
|
||||||
|
/**
|
||||||
|
* Performs:
|
||||||
|
* @code get_service().move_construct(
|
||||||
|
* get_implementation(), other.get_implementation()); @endcode
|
||||||
|
*
|
||||||
|
* @note Available only for services that support movability,
|
||||||
|
*/
|
||||||
|
basic_io_object(basic_io_object&& other);
|
||||||
|
|
||||||
|
/// Move-assign a basic_io_object.
|
||||||
|
/**
|
||||||
|
* Performs:
|
||||||
|
* @code get_service().move_assign(get_implementation(),
|
||||||
|
* other.get_service(), other.get_implementation()); @endcode
|
||||||
|
*
|
||||||
|
* @note Available only for services that support movability,
|
||||||
|
*/
|
||||||
|
basic_io_object& operator=(basic_io_object&& other);
|
||||||
|
|
||||||
|
/// Perform a converting move-construction of a basic_io_object.
|
||||||
|
template <typename IoObjectService1>
|
||||||
|
basic_io_object(IoObjectService1& other_service,
|
||||||
|
typename IoObjectService1::implementation_type& other_implementation);
|
||||||
|
#endif // defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
/// Protected destructor to prevent deletion through this type.
|
||||||
|
/**
|
||||||
|
* Performs:
|
||||||
|
* @code get_service().destroy(get_implementation()); @endcode
|
||||||
|
*/
|
||||||
|
~basic_io_object()
|
||||||
|
{
|
||||||
|
service_.destroy(implementation_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the service associated with the I/O object.
|
||||||
|
service_type& get_service()
|
||||||
|
{
|
||||||
|
return service_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the service associated with the I/O object.
|
||||||
|
const service_type& get_service() const
|
||||||
|
{
|
||||||
|
return service_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the underlying implementation of the I/O object.
|
||||||
|
implementation_type& get_implementation()
|
||||||
|
{
|
||||||
|
return implementation_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the underlying implementation of the I/O object.
|
||||||
|
const implementation_type& get_implementation() const
|
||||||
|
{
|
||||||
|
return implementation_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
basic_io_object(const basic_io_object&);
|
||||||
|
basic_io_object& operator=(const basic_io_object&);
|
||||||
|
|
||||||
|
// The service associated with the I/O object.
|
||||||
|
service_type& service_;
|
||||||
|
|
||||||
|
/// The underlying implementation of the I/O object.
|
||||||
|
implementation_type implementation_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(ASIO_HAS_MOVE)
|
||||||
|
// Specialisation for movable objects.
|
||||||
|
template <typename IoObjectService>
|
||||||
|
class basic_io_object<IoObjectService, true>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef IoObjectService service_type;
|
||||||
|
typedef typename service_type::implementation_type implementation_type;
|
||||||
|
|
||||||
|
#if !defined(ASIO_NO_DEPRECATED)
|
||||||
|
asio::io_context& get_io_context()
|
||||||
|
{
|
||||||
|
return service_->get_io_context();
|
||||||
|
}
|
||||||
|
|
||||||
|
asio::io_context& get_io_service()
|
||||||
|
{
|
||||||
|
return service_->get_io_context();
|
||||||
|
}
|
||||||
|
#endif // !defined(ASIO_NO_DEPRECATED)
|
||||||
|
|
||||||
|
typedef asio::io_context::executor_type executor_type;
|
||||||
|
|
||||||
|
executor_type get_executor() ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return service_->get_io_context().get_executor();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
explicit basic_io_object(asio::io_context& io_context)
|
||||||
|
: service_(&asio::use_service<IoObjectService>(io_context))
|
||||||
|
{
|
||||||
|
service_->construct(implementation_);
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_io_object(basic_io_object&& other)
|
||||||
|
: service_(&other.get_service())
|
||||||
|
{
|
||||||
|
service_->move_construct(implementation_, other.implementation_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IoObjectService1>
|
||||||
|
basic_io_object(IoObjectService1& other_service,
|
||||||
|
typename IoObjectService1::implementation_type& other_implementation)
|
||||||
|
: service_(&asio::use_service<IoObjectService>(
|
||||||
|
other_service.get_io_context()))
|
||||||
|
{
|
||||||
|
service_->converting_move_construct(implementation_,
|
||||||
|
other_service, other_implementation);
|
||||||
|
}
|
||||||
|
|
||||||
|
~basic_io_object()
|
||||||
|
{
|
||||||
|
service_->destroy(implementation_);
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_io_object& operator=(basic_io_object&& other)
|
||||||
|
{
|
||||||
|
service_->move_assign(implementation_,
|
||||||
|
*other.service_, other.implementation_);
|
||||||
|
service_ = other.service_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
service_type& get_service()
|
||||||
|
{
|
||||||
|
return *service_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const service_type& get_service() const
|
||||||
|
{
|
||||||
|
return *service_;
|
||||||
|
}
|
||||||
|
|
||||||
|
implementation_type& get_implementation()
|
||||||
|
{
|
||||||
|
return implementation_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const implementation_type& get_implementation() const
|
||||||
|
{
|
||||||
|
return implementation_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
basic_io_object(const basic_io_object&);
|
||||||
|
void operator=(const basic_io_object&);
|
||||||
|
|
||||||
|
IoObjectService* service_;
|
||||||
|
implementation_type implementation_;
|
||||||
|
};
|
||||||
|
#endif // defined(ASIO_HAS_MOVE)
|
||||||
|
|
||||||
|
} // namespace asio
|
||||||
|
|
||||||
|
#include "asio/detail/pop_options.hpp"
|
||||||
|
|
||||||
|
#endif // ASIO_BASIC_IO_OBJECT_HPP
|
1030
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/asio/basic_raw_socket.hpp
vendored
Normal file
1030
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/asio/basic_raw_socket.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,618 @@
|
|||||||
|
//
|
||||||
|
// basic_seq_packet_socket.hpp
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ASIO_BASIC_SEQ_PACKET_SOCKET_HPP
|
||||||
|
#define ASIO_BASIC_SEQ_PACKET_SOCKET_HPP
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
# pragma once
|
||||||
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
|
||||||
|
#include "asio/detail/config.hpp"
|
||||||
|
#include <cstddef>
|
||||||
|
#include "asio/basic_socket.hpp"
|
||||||
|
#include "asio/detail/handler_type_requirements.hpp"
|
||||||
|
#include "asio/detail/throw_error.hpp"
|
||||||
|
#include "asio/error.hpp"
|
||||||
|
|
||||||
|
#if defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
# include "asio/seq_packet_socket_service.hpp"
|
||||||
|
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
|
||||||
|
#include "asio/detail/push_options.hpp"
|
||||||
|
|
||||||
|
namespace asio {
|
||||||
|
|
||||||
|
/// Provides sequenced packet socket functionality.
|
||||||
|
/**
|
||||||
|
* The basic_seq_packet_socket class template provides asynchronous and blocking
|
||||||
|
* sequenced packet socket functionality.
|
||||||
|
*
|
||||||
|
* @par Thread Safety
|
||||||
|
* @e Distinct @e objects: Safe.@n
|
||||||
|
* @e Shared @e objects: Unsafe.
|
||||||
|
*/
|
||||||
|
template <typename Protocol
|
||||||
|
ASIO_SVC_TPARAM_DEF1(= seq_packet_socket_service<Protocol>)>
|
||||||
|
class basic_seq_packet_socket
|
||||||
|
: public basic_socket<Protocol ASIO_SVC_TARG>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// The native representation of a socket.
|
||||||
|
#if defined(GENERATING_DOCUMENTATION)
|
||||||
|
typedef implementation_defined native_handle_type;
|
||||||
|
#else
|
||||||
|
typedef typename basic_socket<
|
||||||
|
Protocol ASIO_SVC_TARG>::native_handle_type native_handle_type;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// The protocol type.
|
||||||
|
typedef Protocol protocol_type;
|
||||||
|
|
||||||
|
/// The endpoint type.
|
||||||
|
typedef typename Protocol::endpoint endpoint_type;
|
||||||
|
|
||||||
|
/// Construct a basic_seq_packet_socket without opening it.
|
||||||
|
/**
|
||||||
|
* This constructor creates a sequenced packet socket without opening it. The
|
||||||
|
* socket needs to be opened and then connected or accepted before data can
|
||||||
|
* be sent or received on it.
|
||||||
|
*
|
||||||
|
* @param io_context The io_context object that the sequenced packet socket
|
||||||
|
* will use to dispatch handlers for any asynchronous operations performed on
|
||||||
|
* the socket.
|
||||||
|
*/
|
||||||
|
explicit basic_seq_packet_socket(asio::io_context& io_context)
|
||||||
|
: basic_socket<Protocol ASIO_SVC_TARG>(io_context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct and open a basic_seq_packet_socket.
|
||||||
|
/**
|
||||||
|
* This constructor creates and opens a sequenced_packet socket. The socket
|
||||||
|
* needs to be connected or accepted before data can be sent or received on
|
||||||
|
* it.
|
||||||
|
*
|
||||||
|
* @param io_context The io_context object that the sequenced packet socket
|
||||||
|
* will use to dispatch handlers for any asynchronous operations performed on
|
||||||
|
* the socket.
|
||||||
|
*
|
||||||
|
* @param protocol An object specifying protocol parameters to be used.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*/
|
||||||
|
basic_seq_packet_socket(asio::io_context& io_context,
|
||||||
|
const protocol_type& protocol)
|
||||||
|
: basic_socket<Protocol ASIO_SVC_TARG>(io_context, protocol)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a basic_seq_packet_socket, opening it and binding it to the
|
||||||
|
/// given local endpoint.
|
||||||
|
/**
|
||||||
|
* This constructor creates a sequenced packet socket and automatically opens
|
||||||
|
* it bound to the specified endpoint on the local machine. The protocol used
|
||||||
|
* is the protocol associated with the given endpoint.
|
||||||
|
*
|
||||||
|
* @param io_context The io_context object that the sequenced packet socket
|
||||||
|
* will use to dispatch handlers for any asynchronous operations performed on
|
||||||
|
* the socket.
|
||||||
|
*
|
||||||
|
* @param endpoint An endpoint on the local machine to which the sequenced
|
||||||
|
* packet socket will be bound.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*/
|
||||||
|
basic_seq_packet_socket(asio::io_context& io_context,
|
||||||
|
const endpoint_type& endpoint)
|
||||||
|
: basic_socket<Protocol ASIO_SVC_TARG>(io_context, endpoint)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a basic_seq_packet_socket on an existing native socket.
|
||||||
|
/**
|
||||||
|
* This constructor creates a sequenced packet socket object to hold an
|
||||||
|
* existing native socket.
|
||||||
|
*
|
||||||
|
* @param io_context The io_context object that the sequenced packet socket
|
||||||
|
* will use to dispatch handlers for any asynchronous operations performed on
|
||||||
|
* the socket.
|
||||||
|
*
|
||||||
|
* @param protocol An object specifying protocol parameters to be used.
|
||||||
|
*
|
||||||
|
* @param native_socket The new underlying socket implementation.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*/
|
||||||
|
basic_seq_packet_socket(asio::io_context& io_context,
|
||||||
|
const protocol_type& protocol, const native_handle_type& native_socket)
|
||||||
|
: basic_socket<Protocol ASIO_SVC_TARG>(
|
||||||
|
io_context, protocol, native_socket)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
/// Move-construct a basic_seq_packet_socket from another.
|
||||||
|
/**
|
||||||
|
* This constructor moves a sequenced packet socket from one object to
|
||||||
|
* another.
|
||||||
|
*
|
||||||
|
* @param other The other basic_seq_packet_socket object from which the move
|
||||||
|
* will occur.
|
||||||
|
*
|
||||||
|
* @note Following the move, the moved-from object is in the same state as if
|
||||||
|
* constructed using the @c basic_seq_packet_socket(io_context&) constructor.
|
||||||
|
*/
|
||||||
|
basic_seq_packet_socket(basic_seq_packet_socket&& other)
|
||||||
|
: basic_socket<Protocol ASIO_SVC_TARG>(std::move(other))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move-assign a basic_seq_packet_socket from another.
|
||||||
|
/**
|
||||||
|
* This assignment operator moves a sequenced packet socket from one object to
|
||||||
|
* another.
|
||||||
|
*
|
||||||
|
* @param other The other basic_seq_packet_socket object from which the move
|
||||||
|
* will occur.
|
||||||
|
*
|
||||||
|
* @note Following the move, the moved-from object is in the same state as if
|
||||||
|
* constructed using the @c basic_seq_packet_socket(io_context&) constructor.
|
||||||
|
*/
|
||||||
|
basic_seq_packet_socket& operator=(basic_seq_packet_socket&& other)
|
||||||
|
{
|
||||||
|
basic_socket<Protocol ASIO_SVC_TARG>::operator=(std::move(other));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move-construct a basic_seq_packet_socket from a socket of another protocol
|
||||||
|
/// type.
|
||||||
|
/**
|
||||||
|
* This constructor moves a sequenced packet socket from one object to
|
||||||
|
* another.
|
||||||
|
*
|
||||||
|
* @param other The other basic_seq_packet_socket object from which the move
|
||||||
|
* will occur.
|
||||||
|
*
|
||||||
|
* @note Following the move, the moved-from object is in the same state as if
|
||||||
|
* constructed using the @c basic_seq_packet_socket(io_context&) constructor.
|
||||||
|
*/
|
||||||
|
template <typename Protocol1 ASIO_SVC_TPARAM1>
|
||||||
|
basic_seq_packet_socket(
|
||||||
|
basic_seq_packet_socket<Protocol1 ASIO_SVC_TARG1>&& other,
|
||||||
|
typename enable_if<is_convertible<Protocol1, Protocol>::value>::type* = 0)
|
||||||
|
: basic_socket<Protocol ASIO_SVC_TARG>(std::move(other))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move-assign a basic_seq_packet_socket from a socket of another protocol
|
||||||
|
/// type.
|
||||||
|
/**
|
||||||
|
* This assignment operator moves a sequenced packet socket from one object to
|
||||||
|
* another.
|
||||||
|
*
|
||||||
|
* @param other The other basic_seq_packet_socket object from which the move
|
||||||
|
* will occur.
|
||||||
|
*
|
||||||
|
* @note Following the move, the moved-from object is in the same state as if
|
||||||
|
* constructed using the @c basic_seq_packet_socket(io_context&) constructor.
|
||||||
|
*/
|
||||||
|
template <typename Protocol1 ASIO_SVC_TPARAM1>
|
||||||
|
typename enable_if<is_convertible<Protocol1, Protocol>::value,
|
||||||
|
basic_seq_packet_socket>::type& operator=(
|
||||||
|
basic_seq_packet_socket<Protocol1 ASIO_SVC_TARG1>&& other)
|
||||||
|
{
|
||||||
|
basic_socket<Protocol ASIO_SVC_TARG>::operator=(std::move(other));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
/// Destroys the socket.
|
||||||
|
/**
|
||||||
|
* This function destroys the socket, cancelling any outstanding asynchronous
|
||||||
|
* operations associated with the socket as if by calling @c cancel.
|
||||||
|
*/
|
||||||
|
~basic_seq_packet_socket()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send some data on the socket.
|
||||||
|
/**
|
||||||
|
* This function is used to send data on the sequenced packet socket. The
|
||||||
|
* function call will block until the data has been sent successfully, or an
|
||||||
|
* until error occurs.
|
||||||
|
*
|
||||||
|
* @param buffers One or more data buffers to be sent on the socket.
|
||||||
|
*
|
||||||
|
* @param flags Flags specifying how the send call is to be made.
|
||||||
|
*
|
||||||
|
* @returns The number of bytes sent.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* To send a single data buffer use the @ref buffer function as follows:
|
||||||
|
* @code
|
||||||
|
* socket.send(asio::buffer(data, size), 0);
|
||||||
|
* @endcode
|
||||||
|
* See the @ref buffer documentation for information on sending multiple
|
||||||
|
* buffers in one go, and how to use it with arrays, boost::array or
|
||||||
|
* std::vector.
|
||||||
|
*/
|
||||||
|
template <typename ConstBufferSequence>
|
||||||
|
std::size_t send(const ConstBufferSequence& buffers,
|
||||||
|
socket_base::message_flags flags)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
std::size_t s = this->get_service().send(
|
||||||
|
this->get_implementation(), buffers, flags, ec);
|
||||||
|
asio::detail::throw_error(ec, "send");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send some data on the socket.
|
||||||
|
/**
|
||||||
|
* This function is used to send data on the sequenced packet socket. The
|
||||||
|
* function call will block the data has been sent successfully, or an until
|
||||||
|
* error occurs.
|
||||||
|
*
|
||||||
|
* @param buffers One or more data buffers to be sent on the socket.
|
||||||
|
*
|
||||||
|
* @param flags Flags specifying how the send call is to be made.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*
|
||||||
|
* @returns The number of bytes sent. Returns 0 if an error occurred.
|
||||||
|
*
|
||||||
|
* @note The send operation may not transmit all of the data to the peer.
|
||||||
|
* Consider using the @ref write function if you need to ensure that all data
|
||||||
|
* is written before the blocking operation completes.
|
||||||
|
*/
|
||||||
|
template <typename ConstBufferSequence>
|
||||||
|
std::size_t send(const ConstBufferSequence& buffers,
|
||||||
|
socket_base::message_flags flags, asio::error_code& ec)
|
||||||
|
{
|
||||||
|
return this->get_service().send(
|
||||||
|
this->get_implementation(), buffers, flags, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start an asynchronous send.
|
||||||
|
/**
|
||||||
|
* This function is used to asynchronously send data on the sequenced packet
|
||||||
|
* socket. The function call always returns immediately.
|
||||||
|
*
|
||||||
|
* @param buffers One or more data buffers to be sent on the socket. Although
|
||||||
|
* the buffers object may be copied as necessary, ownership of the underlying
|
||||||
|
* memory blocks is retained by the caller, which must guarantee that they
|
||||||
|
* remain valid until the handler is called.
|
||||||
|
*
|
||||||
|
* @param flags Flags specifying how the send call is to be made.
|
||||||
|
*
|
||||||
|
* @param handler The handler to be called when the send operation completes.
|
||||||
|
* Copies will be made of the handler as required. The function signature of
|
||||||
|
* the handler must be:
|
||||||
|
* @code void handler(
|
||||||
|
* const asio::error_code& error, // Result of operation.
|
||||||
|
* std::size_t bytes_transferred // Number of bytes sent.
|
||||||
|
* ); @endcode
|
||||||
|
* Regardless of whether the asynchronous operation completes immediately or
|
||||||
|
* not, the handler will not be invoked from within this function. Invocation
|
||||||
|
* of the handler will be performed in a manner equivalent to using
|
||||||
|
* asio::io_context::post().
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* To send a single data buffer use the @ref buffer function as follows:
|
||||||
|
* @code
|
||||||
|
* socket.async_send(asio::buffer(data, size), 0, handler);
|
||||||
|
* @endcode
|
||||||
|
* See the @ref buffer documentation for information on sending multiple
|
||||||
|
* buffers in one go, and how to use it with arrays, boost::array or
|
||||||
|
* std::vector.
|
||||||
|
*/
|
||||||
|
template <typename ConstBufferSequence, typename WriteHandler>
|
||||||
|
ASIO_INITFN_RESULT_TYPE(WriteHandler,
|
||||||
|
void (asio::error_code, std::size_t))
|
||||||
|
async_send(const ConstBufferSequence& buffers,
|
||||||
|
socket_base::message_flags flags,
|
||||||
|
ASIO_MOVE_ARG(WriteHandler) handler)
|
||||||
|
{
|
||||||
|
// If you get an error on the following line it means that your handler does
|
||||||
|
// not meet the documented type requirements for a WriteHandler.
|
||||||
|
ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;
|
||||||
|
|
||||||
|
#if defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
return this->get_service().async_send(this->get_implementation(),
|
||||||
|
buffers, flags, ASIO_MOVE_CAST(WriteHandler)(handler));
|
||||||
|
#else // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
async_completion<WriteHandler,
|
||||||
|
void (asio::error_code, std::size_t)> init(handler);
|
||||||
|
|
||||||
|
this->get_service().async_send(this->get_implementation(),
|
||||||
|
buffers, flags, init.completion_handler);
|
||||||
|
|
||||||
|
return init.result.get();
|
||||||
|
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Receive some data on the socket.
|
||||||
|
/**
|
||||||
|
* This function is used to receive data on the sequenced packet socket. The
|
||||||
|
* function call will block until data has been received successfully, or
|
||||||
|
* until an error occurs.
|
||||||
|
*
|
||||||
|
* @param buffers One or more buffers into which the data will be received.
|
||||||
|
*
|
||||||
|
* @param out_flags After the receive call completes, contains flags
|
||||||
|
* associated with the received data. For example, if the
|
||||||
|
* socket_base::message_end_of_record bit is set then the received data marks
|
||||||
|
* the end of a record.
|
||||||
|
*
|
||||||
|
* @returns The number of bytes received.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure. An error code of
|
||||||
|
* asio::error::eof indicates that the connection was closed by the
|
||||||
|
* peer.
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* To receive into a single data buffer use the @ref buffer function as
|
||||||
|
* follows:
|
||||||
|
* @code
|
||||||
|
* socket.receive(asio::buffer(data, size), out_flags);
|
||||||
|
* @endcode
|
||||||
|
* See the @ref buffer documentation for information on receiving into
|
||||||
|
* multiple buffers in one go, and how to use it with arrays, boost::array or
|
||||||
|
* std::vector.
|
||||||
|
*/
|
||||||
|
template <typename MutableBufferSequence>
|
||||||
|
std::size_t receive(const MutableBufferSequence& buffers,
|
||||||
|
socket_base::message_flags& out_flags)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
#if defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
std::size_t s = this->get_service().receive(
|
||||||
|
this->get_implementation(), buffers, 0, out_flags, ec);
|
||||||
|
#else // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
std::size_t s = this->get_service().receive_with_flags(
|
||||||
|
this->get_implementation(), buffers, 0, out_flags, ec);
|
||||||
|
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
asio::detail::throw_error(ec, "receive");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Receive some data on the socket.
|
||||||
|
/**
|
||||||
|
* This function is used to receive data on the sequenced packet socket. The
|
||||||
|
* function call will block until data has been received successfully, or
|
||||||
|
* until an error occurs.
|
||||||
|
*
|
||||||
|
* @param buffers One or more buffers into which the data will be received.
|
||||||
|
*
|
||||||
|
* @param in_flags Flags specifying how the receive call is to be made.
|
||||||
|
*
|
||||||
|
* @param out_flags After the receive call completes, contains flags
|
||||||
|
* associated with the received data. For example, if the
|
||||||
|
* socket_base::message_end_of_record bit is set then the received data marks
|
||||||
|
* the end of a record.
|
||||||
|
*
|
||||||
|
* @returns The number of bytes received.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure. An error code of
|
||||||
|
* asio::error::eof indicates that the connection was closed by the
|
||||||
|
* peer.
|
||||||
|
*
|
||||||
|
* @note The receive operation may not receive all of the requested number of
|
||||||
|
* bytes. Consider using the @ref read function if you need to ensure that the
|
||||||
|
* requested amount of data is read before the blocking operation completes.
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* To receive into a single data buffer use the @ref buffer function as
|
||||||
|
* follows:
|
||||||
|
* @code
|
||||||
|
* socket.receive(asio::buffer(data, size), 0, out_flags);
|
||||||
|
* @endcode
|
||||||
|
* See the @ref buffer documentation for information on receiving into
|
||||||
|
* multiple buffers in one go, and how to use it with arrays, boost::array or
|
||||||
|
* std::vector.
|
||||||
|
*/
|
||||||
|
template <typename MutableBufferSequence>
|
||||||
|
std::size_t receive(const MutableBufferSequence& buffers,
|
||||||
|
socket_base::message_flags in_flags,
|
||||||
|
socket_base::message_flags& out_flags)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
#if defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
std::size_t s = this->get_service().receive(
|
||||||
|
this->get_implementation(), buffers, in_flags, out_flags, ec);
|
||||||
|
#else // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
std::size_t s = this->get_service().receive_with_flags(
|
||||||
|
this->get_implementation(), buffers, in_flags, out_flags, ec);
|
||||||
|
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
asio::detail::throw_error(ec, "receive");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Receive some data on a connected socket.
|
||||||
|
/**
|
||||||
|
* This function is used to receive data on the sequenced packet socket. The
|
||||||
|
* function call will block until data has been received successfully, or
|
||||||
|
* until an error occurs.
|
||||||
|
*
|
||||||
|
* @param buffers One or more buffers into which the data will be received.
|
||||||
|
*
|
||||||
|
* @param in_flags Flags specifying how the receive call is to be made.
|
||||||
|
*
|
||||||
|
* @param out_flags After the receive call completes, contains flags
|
||||||
|
* associated with the received data. For example, if the
|
||||||
|
* socket_base::message_end_of_record bit is set then the received data marks
|
||||||
|
* the end of a record.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*
|
||||||
|
* @returns The number of bytes received. Returns 0 if an error occurred.
|
||||||
|
*
|
||||||
|
* @note The receive operation may not receive all of the requested number of
|
||||||
|
* bytes. Consider using the @ref read function if you need to ensure that the
|
||||||
|
* requested amount of data is read before the blocking operation completes.
|
||||||
|
*/
|
||||||
|
template <typename MutableBufferSequence>
|
||||||
|
std::size_t receive(const MutableBufferSequence& buffers,
|
||||||
|
socket_base::message_flags in_flags,
|
||||||
|
socket_base::message_flags& out_flags, asio::error_code& ec)
|
||||||
|
{
|
||||||
|
#if defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
return this->get_service().receive(this->get_implementation(),
|
||||||
|
buffers, in_flags, out_flags, ec);
|
||||||
|
#else // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
return this->get_service().receive_with_flags(this->get_implementation(),
|
||||||
|
buffers, in_flags, out_flags, ec);
|
||||||
|
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start an asynchronous receive.
|
||||||
|
/**
|
||||||
|
* This function is used to asynchronously receive data from the sequenced
|
||||||
|
* packet socket. The function call always returns immediately.
|
||||||
|
*
|
||||||
|
* @param buffers One or more buffers into which the data will be received.
|
||||||
|
* Although the buffers object may be copied as necessary, ownership of the
|
||||||
|
* underlying memory blocks is retained by the caller, which must guarantee
|
||||||
|
* that they remain valid until the handler is called.
|
||||||
|
*
|
||||||
|
* @param out_flags Once the asynchronous operation completes, contains flags
|
||||||
|
* associated with the received data. For example, if the
|
||||||
|
* socket_base::message_end_of_record bit is set then the received data marks
|
||||||
|
* the end of a record. The caller must guarantee that the referenced
|
||||||
|
* variable remains valid until the handler is called.
|
||||||
|
*
|
||||||
|
* @param handler The handler to be called when the receive operation
|
||||||
|
* completes. Copies will be made of the handler as required. The function
|
||||||
|
* signature of the handler must be:
|
||||||
|
* @code void handler(
|
||||||
|
* const asio::error_code& error, // Result of operation.
|
||||||
|
* std::size_t bytes_transferred // Number of bytes received.
|
||||||
|
* ); @endcode
|
||||||
|
* Regardless of whether the asynchronous operation completes immediately or
|
||||||
|
* not, the handler will not be invoked from within this function. Invocation
|
||||||
|
* of the handler will be performed in a manner equivalent to using
|
||||||
|
* asio::io_context::post().
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* To receive into a single data buffer use the @ref buffer function as
|
||||||
|
* follows:
|
||||||
|
* @code
|
||||||
|
* socket.async_receive(asio::buffer(data, size), out_flags, handler);
|
||||||
|
* @endcode
|
||||||
|
* See the @ref buffer documentation for information on receiving into
|
||||||
|
* multiple buffers in one go, and how to use it with arrays, boost::array or
|
||||||
|
* std::vector.
|
||||||
|
*/
|
||||||
|
template <typename MutableBufferSequence, typename ReadHandler>
|
||||||
|
ASIO_INITFN_RESULT_TYPE(ReadHandler,
|
||||||
|
void (asio::error_code, std::size_t))
|
||||||
|
async_receive(const MutableBufferSequence& buffers,
|
||||||
|
socket_base::message_flags& out_flags,
|
||||||
|
ASIO_MOVE_ARG(ReadHandler) handler)
|
||||||
|
{
|
||||||
|
// If you get an error on the following line it means that your handler does
|
||||||
|
// not meet the documented type requirements for a ReadHandler.
|
||||||
|
ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
|
||||||
|
|
||||||
|
#if defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
return this->get_service().async_receive(
|
||||||
|
this->get_implementation(), buffers, 0, out_flags,
|
||||||
|
ASIO_MOVE_CAST(ReadHandler)(handler));
|
||||||
|
#else // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
async_completion<ReadHandler,
|
||||||
|
void (asio::error_code, std::size_t)> init(handler);
|
||||||
|
|
||||||
|
this->get_service().async_receive_with_flags(
|
||||||
|
this->get_implementation(), buffers, 0, out_flags,
|
||||||
|
init.completion_handler);
|
||||||
|
|
||||||
|
return init.result.get();
|
||||||
|
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start an asynchronous receive.
|
||||||
|
/**
|
||||||
|
* This function is used to asynchronously receive data from the sequenced
|
||||||
|
* data socket. The function call always returns immediately.
|
||||||
|
*
|
||||||
|
* @param buffers One or more buffers into which the data will be received.
|
||||||
|
* Although the buffers object may be copied as necessary, ownership of the
|
||||||
|
* underlying memory blocks is retained by the caller, which must guarantee
|
||||||
|
* that they remain valid until the handler is called.
|
||||||
|
*
|
||||||
|
* @param in_flags Flags specifying how the receive call is to be made.
|
||||||
|
*
|
||||||
|
* @param out_flags Once the asynchronous operation completes, contains flags
|
||||||
|
* associated with the received data. For example, if the
|
||||||
|
* socket_base::message_end_of_record bit is set then the received data marks
|
||||||
|
* the end of a record. The caller must guarantee that the referenced
|
||||||
|
* variable remains valid until the handler is called.
|
||||||
|
*
|
||||||
|
* @param handler The handler to be called when the receive operation
|
||||||
|
* completes. Copies will be made of the handler as required. The function
|
||||||
|
* signature of the handler must be:
|
||||||
|
* @code void handler(
|
||||||
|
* const asio::error_code& error, // Result of operation.
|
||||||
|
* std::size_t bytes_transferred // Number of bytes received.
|
||||||
|
* ); @endcode
|
||||||
|
* Regardless of whether the asynchronous operation completes immediately or
|
||||||
|
* not, the handler will not be invoked from within this function. Invocation
|
||||||
|
* of the handler will be performed in a manner equivalent to using
|
||||||
|
* asio::io_context::post().
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* To receive into a single data buffer use the @ref buffer function as
|
||||||
|
* follows:
|
||||||
|
* @code
|
||||||
|
* socket.async_receive(
|
||||||
|
* asio::buffer(data, size),
|
||||||
|
* 0, out_flags, handler);
|
||||||
|
* @endcode
|
||||||
|
* See the @ref buffer documentation for information on receiving into
|
||||||
|
* multiple buffers in one go, and how to use it with arrays, boost::array or
|
||||||
|
* std::vector.
|
||||||
|
*/
|
||||||
|
template <typename MutableBufferSequence, typename ReadHandler>
|
||||||
|
ASIO_INITFN_RESULT_TYPE(ReadHandler,
|
||||||
|
void (asio::error_code, std::size_t))
|
||||||
|
async_receive(const MutableBufferSequence& buffers,
|
||||||
|
socket_base::message_flags in_flags,
|
||||||
|
socket_base::message_flags& out_flags,
|
||||||
|
ASIO_MOVE_ARG(ReadHandler) handler)
|
||||||
|
{
|
||||||
|
// If you get an error on the following line it means that your handler does
|
||||||
|
// not meet the documented type requirements for a ReadHandler.
|
||||||
|
ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
|
||||||
|
|
||||||
|
#if defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
return this->get_service().async_receive(
|
||||||
|
this->get_implementation(), buffers, in_flags, out_flags,
|
||||||
|
ASIO_MOVE_CAST(ReadHandler)(handler));
|
||||||
|
#else // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
async_completion<ReadHandler,
|
||||||
|
void (asio::error_code, std::size_t)> init(handler);
|
||||||
|
|
||||||
|
this->get_service().async_receive_with_flags(
|
||||||
|
this->get_implementation(), buffers, in_flags, out_flags,
|
||||||
|
init.completion_handler);
|
||||||
|
|
||||||
|
return init.result.get();
|
||||||
|
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace asio
|
||||||
|
|
||||||
|
#include "asio/detail/pop_options.hpp"
|
||||||
|
|
||||||
|
#endif // ASIO_BASIC_SEQ_PACKET_SOCKET_HPP
|
@ -0,0 +1,688 @@
|
|||||||
|
//
|
||||||
|
// basic_serial_port.hpp
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ASIO_BASIC_SERIAL_PORT_HPP
|
||||||
|
#define ASIO_BASIC_SERIAL_PORT_HPP
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
# pragma once
|
||||||
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
|
||||||
|
#include "asio/detail/config.hpp"
|
||||||
|
|
||||||
|
#if defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
|
||||||
|
#if defined(ASIO_HAS_SERIAL_PORT) \
|
||||||
|
|| defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "asio/basic_io_object.hpp"
|
||||||
|
#include "asio/detail/handler_type_requirements.hpp"
|
||||||
|
#include "asio/detail/throw_error.hpp"
|
||||||
|
#include "asio/error.hpp"
|
||||||
|
#include "asio/serial_port_base.hpp"
|
||||||
|
#include "asio/serial_port_service.hpp"
|
||||||
|
|
||||||
|
#include "asio/detail/push_options.hpp"
|
||||||
|
|
||||||
|
namespace asio {
|
||||||
|
|
||||||
|
/// Provides serial port functionality.
|
||||||
|
/**
|
||||||
|
* The basic_serial_port class template provides functionality that is common
|
||||||
|
* to all serial ports.
|
||||||
|
*
|
||||||
|
* @par Thread Safety
|
||||||
|
* @e Distinct @e objects: Safe.@n
|
||||||
|
* @e Shared @e objects: Unsafe.
|
||||||
|
*/
|
||||||
|
template <typename SerialPortService = serial_port_service>
|
||||||
|
class basic_serial_port
|
||||||
|
: public basic_io_object<SerialPortService>,
|
||||||
|
public serial_port_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// The native representation of a serial port.
|
||||||
|
typedef typename SerialPortService::native_handle_type native_handle_type;
|
||||||
|
|
||||||
|
/// A basic_serial_port is always the lowest layer.
|
||||||
|
typedef basic_serial_port<SerialPortService> lowest_layer_type;
|
||||||
|
|
||||||
|
/// Construct a basic_serial_port without opening it.
|
||||||
|
/**
|
||||||
|
* This constructor creates a serial port without opening it.
|
||||||
|
*
|
||||||
|
* @param io_context The io_context object that the serial port will use to
|
||||||
|
* dispatch handlers for any asynchronous operations performed on the port.
|
||||||
|
*/
|
||||||
|
explicit basic_serial_port(asio::io_context& io_context)
|
||||||
|
: basic_io_object<SerialPortService>(io_context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct and open a basic_serial_port.
|
||||||
|
/**
|
||||||
|
* This constructor creates and opens a serial port for the specified device
|
||||||
|
* name.
|
||||||
|
*
|
||||||
|
* @param io_context The io_context object that the serial port will use to
|
||||||
|
* dispatch handlers for any asynchronous operations performed on the port.
|
||||||
|
*
|
||||||
|
* @param device The platform-specific device name for this serial
|
||||||
|
* port.
|
||||||
|
*/
|
||||||
|
explicit basic_serial_port(asio::io_context& io_context,
|
||||||
|
const char* device)
|
||||||
|
: basic_io_object<SerialPortService>(io_context)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
this->get_service().open(this->get_implementation(), device, ec);
|
||||||
|
asio::detail::throw_error(ec, "open");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct and open a basic_serial_port.
|
||||||
|
/**
|
||||||
|
* This constructor creates and opens a serial port for the specified device
|
||||||
|
* name.
|
||||||
|
*
|
||||||
|
* @param io_context The io_context object that the serial port will use to
|
||||||
|
* dispatch handlers for any asynchronous operations performed on the port.
|
||||||
|
*
|
||||||
|
* @param device The platform-specific device name for this serial
|
||||||
|
* port.
|
||||||
|
*/
|
||||||
|
explicit basic_serial_port(asio::io_context& io_context,
|
||||||
|
const std::string& device)
|
||||||
|
: basic_io_object<SerialPortService>(io_context)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
this->get_service().open(this->get_implementation(), device, ec);
|
||||||
|
asio::detail::throw_error(ec, "open");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a basic_serial_port on an existing native serial port.
|
||||||
|
/**
|
||||||
|
* This constructor creates a serial port object to hold an existing native
|
||||||
|
* serial port.
|
||||||
|
*
|
||||||
|
* @param io_context The io_context object that the serial port will use to
|
||||||
|
* dispatch handlers for any asynchronous operations performed on the port.
|
||||||
|
*
|
||||||
|
* @param native_serial_port A native serial port.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*/
|
||||||
|
basic_serial_port(asio::io_context& io_context,
|
||||||
|
const native_handle_type& native_serial_port)
|
||||||
|
: basic_io_object<SerialPortService>(io_context)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
this->get_service().assign(this->get_implementation(),
|
||||||
|
native_serial_port, ec);
|
||||||
|
asio::detail::throw_error(ec, "assign");
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
/// Move-construct a basic_serial_port from another.
|
||||||
|
/**
|
||||||
|
* This constructor moves a serial port from one object to another.
|
||||||
|
*
|
||||||
|
* @param other The other basic_serial_port object from which the move will
|
||||||
|
* occur.
|
||||||
|
*
|
||||||
|
* @note Following the move, the moved-from object is in the same state as if
|
||||||
|
* constructed using the @c basic_serial_port(io_context&) constructor.
|
||||||
|
*/
|
||||||
|
basic_serial_port(basic_serial_port&& other)
|
||||||
|
: basic_io_object<SerialPortService>(
|
||||||
|
ASIO_MOVE_CAST(basic_serial_port)(other))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move-assign a basic_serial_port from another.
|
||||||
|
/**
|
||||||
|
* This assignment operator moves a serial port from one object to another.
|
||||||
|
*
|
||||||
|
* @param other The other basic_serial_port object from which the move will
|
||||||
|
* occur.
|
||||||
|
*
|
||||||
|
* @note Following the move, the moved-from object is in the same state as if
|
||||||
|
* constructed using the @c basic_serial_port(io_context&) constructor.
|
||||||
|
*/
|
||||||
|
basic_serial_port& operator=(basic_serial_port&& other)
|
||||||
|
{
|
||||||
|
basic_io_object<SerialPortService>::operator=(
|
||||||
|
ASIO_MOVE_CAST(basic_serial_port)(other));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
/// Get a reference to the lowest layer.
|
||||||
|
/**
|
||||||
|
* This function returns a reference to the lowest layer in a stack of
|
||||||
|
* layers. Since a basic_serial_port cannot contain any further layers, it
|
||||||
|
* simply returns a reference to itself.
|
||||||
|
*
|
||||||
|
* @return A reference to the lowest layer in the stack of layers. Ownership
|
||||||
|
* is not transferred to the caller.
|
||||||
|
*/
|
||||||
|
lowest_layer_type& lowest_layer()
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a const reference to the lowest layer.
|
||||||
|
/**
|
||||||
|
* This function returns a const reference to the lowest layer in a stack of
|
||||||
|
* layers. Since a basic_serial_port cannot contain any further layers, it
|
||||||
|
* simply returns a reference to itself.
|
||||||
|
*
|
||||||
|
* @return A const reference to the lowest layer in the stack of layers.
|
||||||
|
* Ownership is not transferred to the caller.
|
||||||
|
*/
|
||||||
|
const lowest_layer_type& lowest_layer() const
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Open the serial port using the specified device name.
|
||||||
|
/**
|
||||||
|
* This function opens the serial port for the specified device name.
|
||||||
|
*
|
||||||
|
* @param device The platform-specific device name.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*/
|
||||||
|
void open(const std::string& device)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
this->get_service().open(this->get_implementation(), device, ec);
|
||||||
|
asio::detail::throw_error(ec, "open");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Open the serial port using the specified device name.
|
||||||
|
/**
|
||||||
|
* This function opens the serial port using the given platform-specific
|
||||||
|
* device name.
|
||||||
|
*
|
||||||
|
* @param device The platform-specific device name.
|
||||||
|
*
|
||||||
|
* @param ec Set the indicate what error occurred, if any.
|
||||||
|
*/
|
||||||
|
ASIO_SYNC_OP_VOID open(const std::string& device,
|
||||||
|
asio::error_code& ec)
|
||||||
|
{
|
||||||
|
this->get_service().open(this->get_implementation(), device, ec);
|
||||||
|
ASIO_SYNC_OP_VOID_RETURN(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assign an existing native serial port to the serial port.
|
||||||
|
/*
|
||||||
|
* This function opens the serial port to hold an existing native serial port.
|
||||||
|
*
|
||||||
|
* @param native_serial_port A native serial port.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*/
|
||||||
|
void assign(const native_handle_type& native_serial_port)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
this->get_service().assign(this->get_implementation(),
|
||||||
|
native_serial_port, ec);
|
||||||
|
asio::detail::throw_error(ec, "assign");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assign an existing native serial port to the serial port.
|
||||||
|
/*
|
||||||
|
* This function opens the serial port to hold an existing native serial port.
|
||||||
|
*
|
||||||
|
* @param native_serial_port A native serial port.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*/
|
||||||
|
ASIO_SYNC_OP_VOID assign(const native_handle_type& native_serial_port,
|
||||||
|
asio::error_code& ec)
|
||||||
|
{
|
||||||
|
this->get_service().assign(this->get_implementation(),
|
||||||
|
native_serial_port, ec);
|
||||||
|
ASIO_SYNC_OP_VOID_RETURN(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determine whether the serial port is open.
|
||||||
|
bool is_open() const
|
||||||
|
{
|
||||||
|
return this->get_service().is_open(this->get_implementation());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Close the serial port.
|
||||||
|
/**
|
||||||
|
* This function is used to close the serial port. Any asynchronous read or
|
||||||
|
* write operations will be cancelled immediately, and will complete with the
|
||||||
|
* asio::error::operation_aborted error.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*/
|
||||||
|
void close()
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
this->get_service().close(this->get_implementation(), ec);
|
||||||
|
asio::detail::throw_error(ec, "close");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Close the serial port.
|
||||||
|
/**
|
||||||
|
* This function is used to close the serial port. Any asynchronous read or
|
||||||
|
* write operations will be cancelled immediately, and will complete with the
|
||||||
|
* asio::error::operation_aborted error.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*/
|
||||||
|
ASIO_SYNC_OP_VOID close(asio::error_code& ec)
|
||||||
|
{
|
||||||
|
this->get_service().close(this->get_implementation(), ec);
|
||||||
|
ASIO_SYNC_OP_VOID_RETURN(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the native serial port representation.
|
||||||
|
/**
|
||||||
|
* This function may be used to obtain the underlying representation of the
|
||||||
|
* serial port. This is intended to allow access to native serial port
|
||||||
|
* functionality that is not otherwise provided.
|
||||||
|
*/
|
||||||
|
native_handle_type native_handle()
|
||||||
|
{
|
||||||
|
return this->get_service().native_handle(this->get_implementation());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cancel all asynchronous operations associated with the serial port.
|
||||||
|
/**
|
||||||
|
* This function causes all outstanding asynchronous read or write operations
|
||||||
|
* to finish immediately, and the handlers for cancelled operations will be
|
||||||
|
* passed the asio::error::operation_aborted error.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*/
|
||||||
|
void cancel()
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
this->get_service().cancel(this->get_implementation(), ec);
|
||||||
|
asio::detail::throw_error(ec, "cancel");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cancel all asynchronous operations associated with the serial port.
|
||||||
|
/**
|
||||||
|
* This function causes all outstanding asynchronous read or write operations
|
||||||
|
* to finish immediately, and the handlers for cancelled operations will be
|
||||||
|
* passed the asio::error::operation_aborted error.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*/
|
||||||
|
ASIO_SYNC_OP_VOID cancel(asio::error_code& ec)
|
||||||
|
{
|
||||||
|
this->get_service().cancel(this->get_implementation(), ec);
|
||||||
|
ASIO_SYNC_OP_VOID_RETURN(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a break sequence to the serial port.
|
||||||
|
/**
|
||||||
|
* This function causes a break sequence of platform-specific duration to be
|
||||||
|
* sent out the serial port.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*/
|
||||||
|
void send_break()
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
this->get_service().send_break(this->get_implementation(), ec);
|
||||||
|
asio::detail::throw_error(ec, "send_break");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a break sequence to the serial port.
|
||||||
|
/**
|
||||||
|
* This function causes a break sequence of platform-specific duration to be
|
||||||
|
* sent out the serial port.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*/
|
||||||
|
ASIO_SYNC_OP_VOID send_break(asio::error_code& ec)
|
||||||
|
{
|
||||||
|
this->get_service().send_break(this->get_implementation(), ec);
|
||||||
|
ASIO_SYNC_OP_VOID_RETURN(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set an option on the serial port.
|
||||||
|
/**
|
||||||
|
* This function is used to set an option on the serial port.
|
||||||
|
*
|
||||||
|
* @param option The option value to be set on the serial port.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*
|
||||||
|
* @sa SettableSerialPortOption @n
|
||||||
|
* asio::serial_port_base::baud_rate @n
|
||||||
|
* asio::serial_port_base::flow_control @n
|
||||||
|
* asio::serial_port_base::parity @n
|
||||||
|
* asio::serial_port_base::stop_bits @n
|
||||||
|
* asio::serial_port_base::character_size
|
||||||
|
*/
|
||||||
|
template <typename SettableSerialPortOption>
|
||||||
|
void set_option(const SettableSerialPortOption& option)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
this->get_service().set_option(this->get_implementation(), option, ec);
|
||||||
|
asio::detail::throw_error(ec, "set_option");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set an option on the serial port.
|
||||||
|
/**
|
||||||
|
* This function is used to set an option on the serial port.
|
||||||
|
*
|
||||||
|
* @param option The option value to be set on the serial port.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*
|
||||||
|
* @sa SettableSerialPortOption @n
|
||||||
|
* asio::serial_port_base::baud_rate @n
|
||||||
|
* asio::serial_port_base::flow_control @n
|
||||||
|
* asio::serial_port_base::parity @n
|
||||||
|
* asio::serial_port_base::stop_bits @n
|
||||||
|
* asio::serial_port_base::character_size
|
||||||
|
*/
|
||||||
|
template <typename SettableSerialPortOption>
|
||||||
|
ASIO_SYNC_OP_VOID set_option(const SettableSerialPortOption& option,
|
||||||
|
asio::error_code& ec)
|
||||||
|
{
|
||||||
|
this->get_service().set_option(this->get_implementation(), option, ec);
|
||||||
|
ASIO_SYNC_OP_VOID_RETURN(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get an option from the serial port.
|
||||||
|
/**
|
||||||
|
* This function is used to get the current value of an option on the serial
|
||||||
|
* port.
|
||||||
|
*
|
||||||
|
* @param option The option value to be obtained from the serial port.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*
|
||||||
|
* @sa GettableSerialPortOption @n
|
||||||
|
* asio::serial_port_base::baud_rate @n
|
||||||
|
* asio::serial_port_base::flow_control @n
|
||||||
|
* asio::serial_port_base::parity @n
|
||||||
|
* asio::serial_port_base::stop_bits @n
|
||||||
|
* asio::serial_port_base::character_size
|
||||||
|
*/
|
||||||
|
template <typename GettableSerialPortOption>
|
||||||
|
void get_option(GettableSerialPortOption& option)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
this->get_service().get_option(this->get_implementation(), option, ec);
|
||||||
|
asio::detail::throw_error(ec, "get_option");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get an option from the serial port.
|
||||||
|
/**
|
||||||
|
* This function is used to get the current value of an option on the serial
|
||||||
|
* port.
|
||||||
|
*
|
||||||
|
* @param option The option value to be obtained from the serial port.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*
|
||||||
|
* @sa GettableSerialPortOption @n
|
||||||
|
* asio::serial_port_base::baud_rate @n
|
||||||
|
* asio::serial_port_base::flow_control @n
|
||||||
|
* asio::serial_port_base::parity @n
|
||||||
|
* asio::serial_port_base::stop_bits @n
|
||||||
|
* asio::serial_port_base::character_size
|
||||||
|
*/
|
||||||
|
template <typename GettableSerialPortOption>
|
||||||
|
ASIO_SYNC_OP_VOID get_option(GettableSerialPortOption& option,
|
||||||
|
asio::error_code& ec)
|
||||||
|
{
|
||||||
|
this->get_service().get_option(this->get_implementation(), option, ec);
|
||||||
|
ASIO_SYNC_OP_VOID_RETURN(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write some data to the serial port.
|
||||||
|
/**
|
||||||
|
* This function is used to write data to the serial port. The function call
|
||||||
|
* will block until one or more bytes of the data has been written
|
||||||
|
* successfully, or until an error occurs.
|
||||||
|
*
|
||||||
|
* @param buffers One or more data buffers to be written to the serial port.
|
||||||
|
*
|
||||||
|
* @returns The number of bytes written.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure. An error code of
|
||||||
|
* asio::error::eof indicates that the connection was closed by the
|
||||||
|
* peer.
|
||||||
|
*
|
||||||
|
* @note The write_some operation may not transmit all of the data to the
|
||||||
|
* peer. Consider using the @ref write function if you need to ensure that
|
||||||
|
* all data is written before the blocking operation completes.
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* To write a single data buffer use the @ref buffer function as follows:
|
||||||
|
* @code
|
||||||
|
* serial_port.write_some(asio::buffer(data, size));
|
||||||
|
* @endcode
|
||||||
|
* See the @ref buffer documentation for information on writing multiple
|
||||||
|
* buffers in one go, and how to use it with arrays, boost::array or
|
||||||
|
* std::vector.
|
||||||
|
*/
|
||||||
|
template <typename ConstBufferSequence>
|
||||||
|
std::size_t write_some(const ConstBufferSequence& buffers)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
std::size_t s = this->get_service().write_some(
|
||||||
|
this->get_implementation(), buffers, ec);
|
||||||
|
asio::detail::throw_error(ec, "write_some");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write some data to the serial port.
|
||||||
|
/**
|
||||||
|
* This function is used to write data to the serial port. The function call
|
||||||
|
* will block until one or more bytes of the data has been written
|
||||||
|
* successfully, or until an error occurs.
|
||||||
|
*
|
||||||
|
* @param buffers One or more data buffers to be written to the serial port.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*
|
||||||
|
* @returns The number of bytes written. Returns 0 if an error occurred.
|
||||||
|
*
|
||||||
|
* @note The write_some operation may not transmit all of the data to the
|
||||||
|
* peer. Consider using the @ref write function if you need to ensure that
|
||||||
|
* all data is written before the blocking operation completes.
|
||||||
|
*/
|
||||||
|
template <typename ConstBufferSequence>
|
||||||
|
std::size_t write_some(const ConstBufferSequence& buffers,
|
||||||
|
asio::error_code& ec)
|
||||||
|
{
|
||||||
|
return this->get_service().write_some(
|
||||||
|
this->get_implementation(), buffers, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start an asynchronous write.
|
||||||
|
/**
|
||||||
|
* This function is used to asynchronously write data to the serial port.
|
||||||
|
* The function call always returns immediately.
|
||||||
|
*
|
||||||
|
* @param buffers One or more data buffers to be written to the serial port.
|
||||||
|
* Although the buffers object may be copied as necessary, ownership of the
|
||||||
|
* underlying memory blocks is retained by the caller, which must guarantee
|
||||||
|
* that they remain valid until the handler is called.
|
||||||
|
*
|
||||||
|
* @param handler The handler to be called when the write operation completes.
|
||||||
|
* Copies will be made of the handler as required. The function signature of
|
||||||
|
* the handler must be:
|
||||||
|
* @code void handler(
|
||||||
|
* const asio::error_code& error, // Result of operation.
|
||||||
|
* std::size_t bytes_transferred // Number of bytes written.
|
||||||
|
* ); @endcode
|
||||||
|
* Regardless of whether the asynchronous operation completes immediately or
|
||||||
|
* not, the handler will not be invoked from within this function. Invocation
|
||||||
|
* of the handler will be performed in a manner equivalent to using
|
||||||
|
* asio::io_context::post().
|
||||||
|
*
|
||||||
|
* @note The write operation may not transmit all of the data to the peer.
|
||||||
|
* Consider using the @ref async_write function if you need to ensure that all
|
||||||
|
* data is written before the asynchronous operation completes.
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* To write a single data buffer use the @ref buffer function as follows:
|
||||||
|
* @code
|
||||||
|
* serial_port.async_write_some(asio::buffer(data, size), handler);
|
||||||
|
* @endcode
|
||||||
|
* See the @ref buffer documentation for information on writing multiple
|
||||||
|
* buffers in one go, and how to use it with arrays, boost::array or
|
||||||
|
* std::vector.
|
||||||
|
*/
|
||||||
|
template <typename ConstBufferSequence, typename WriteHandler>
|
||||||
|
ASIO_INITFN_RESULT_TYPE(WriteHandler,
|
||||||
|
void (asio::error_code, std::size_t))
|
||||||
|
async_write_some(const ConstBufferSequence& buffers,
|
||||||
|
ASIO_MOVE_ARG(WriteHandler) handler)
|
||||||
|
{
|
||||||
|
// If you get an error on the following line it means that your handler does
|
||||||
|
// not meet the documented type requirements for a WriteHandler.
|
||||||
|
ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;
|
||||||
|
|
||||||
|
return this->get_service().async_write_some(this->get_implementation(),
|
||||||
|
buffers, ASIO_MOVE_CAST(WriteHandler)(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read some data from the serial port.
|
||||||
|
/**
|
||||||
|
* This function is used to read data from the serial port. The function
|
||||||
|
* call will block until one or more bytes of data has been read successfully,
|
||||||
|
* or until an error occurs.
|
||||||
|
*
|
||||||
|
* @param buffers One or more buffers into which the data will be read.
|
||||||
|
*
|
||||||
|
* @returns The number of bytes read.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure. An error code of
|
||||||
|
* asio::error::eof indicates that the connection was closed by the
|
||||||
|
* peer.
|
||||||
|
*
|
||||||
|
* @note The read_some operation may not read all of the requested number of
|
||||||
|
* bytes. Consider using the @ref read function if you need to ensure that
|
||||||
|
* the requested amount of data is read before the blocking operation
|
||||||
|
* completes.
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* To read into a single data buffer use the @ref buffer function as follows:
|
||||||
|
* @code
|
||||||
|
* serial_port.read_some(asio::buffer(data, size));
|
||||||
|
* @endcode
|
||||||
|
* See the @ref buffer documentation for information on reading into multiple
|
||||||
|
* buffers in one go, and how to use it with arrays, boost::array or
|
||||||
|
* std::vector.
|
||||||
|
*/
|
||||||
|
template <typename MutableBufferSequence>
|
||||||
|
std::size_t read_some(const MutableBufferSequence& buffers)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
std::size_t s = this->get_service().read_some(
|
||||||
|
this->get_implementation(), buffers, ec);
|
||||||
|
asio::detail::throw_error(ec, "read_some");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read some data from the serial port.
|
||||||
|
/**
|
||||||
|
* This function is used to read data from the serial port. The function
|
||||||
|
* call will block until one or more bytes of data has been read successfully,
|
||||||
|
* or until an error occurs.
|
||||||
|
*
|
||||||
|
* @param buffers One or more buffers into which the data will be read.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*
|
||||||
|
* @returns The number of bytes read. Returns 0 if an error occurred.
|
||||||
|
*
|
||||||
|
* @note The read_some operation may not read all of the requested number of
|
||||||
|
* bytes. Consider using the @ref read function if you need to ensure that
|
||||||
|
* the requested amount of data is read before the blocking operation
|
||||||
|
* completes.
|
||||||
|
*/
|
||||||
|
template <typename MutableBufferSequence>
|
||||||
|
std::size_t read_some(const MutableBufferSequence& buffers,
|
||||||
|
asio::error_code& ec)
|
||||||
|
{
|
||||||
|
return this->get_service().read_some(
|
||||||
|
this->get_implementation(), buffers, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start an asynchronous read.
|
||||||
|
/**
|
||||||
|
* This function is used to asynchronously read data from the serial port.
|
||||||
|
* The function call always returns immediately.
|
||||||
|
*
|
||||||
|
* @param buffers One or more buffers into which the data will be read.
|
||||||
|
* Although the buffers object may be copied as necessary, ownership of the
|
||||||
|
* underlying memory blocks is retained by the caller, which must guarantee
|
||||||
|
* that they remain valid until the handler is called.
|
||||||
|
*
|
||||||
|
* @param handler The handler to be called when the read operation completes.
|
||||||
|
* Copies will be made of the handler as required. The function signature of
|
||||||
|
* the handler must be:
|
||||||
|
* @code void handler(
|
||||||
|
* const asio::error_code& error, // Result of operation.
|
||||||
|
* std::size_t bytes_transferred // Number of bytes read.
|
||||||
|
* ); @endcode
|
||||||
|
* Regardless of whether the asynchronous operation completes immediately or
|
||||||
|
* not, the handler will not be invoked from within this function. Invocation
|
||||||
|
* of the handler will be performed in a manner equivalent to using
|
||||||
|
* asio::io_context::post().
|
||||||
|
*
|
||||||
|
* @note The read operation may not read all of the requested number of bytes.
|
||||||
|
* Consider using the @ref async_read function if you need to ensure that the
|
||||||
|
* requested amount of data is read before the asynchronous operation
|
||||||
|
* completes.
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* To read into a single data buffer use the @ref buffer function as follows:
|
||||||
|
* @code
|
||||||
|
* serial_port.async_read_some(asio::buffer(data, size), handler);
|
||||||
|
* @endcode
|
||||||
|
* See the @ref buffer documentation for information on reading into multiple
|
||||||
|
* buffers in one go, and how to use it with arrays, boost::array or
|
||||||
|
* std::vector.
|
||||||
|
*/
|
||||||
|
template <typename MutableBufferSequence, typename ReadHandler>
|
||||||
|
ASIO_INITFN_RESULT_TYPE(ReadHandler,
|
||||||
|
void (asio::error_code, std::size_t))
|
||||||
|
async_read_some(const MutableBufferSequence& buffers,
|
||||||
|
ASIO_MOVE_ARG(ReadHandler) handler)
|
||||||
|
{
|
||||||
|
// If you get an error on the following line it means that your handler does
|
||||||
|
// not meet the documented type requirements for a ReadHandler.
|
||||||
|
ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
|
||||||
|
|
||||||
|
return this->get_service().async_read_some(this->get_implementation(),
|
||||||
|
buffers, ASIO_MOVE_CAST(ReadHandler)(handler));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace asio
|
||||||
|
|
||||||
|
#include "asio/detail/pop_options.hpp"
|
||||||
|
|
||||||
|
#endif // defined(ASIO_HAS_SERIAL_PORT)
|
||||||
|
// || defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
|
||||||
|
#endif // ASIO_BASIC_SERIAL_PORT_HPP
|
391
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/asio/basic_signal_set.hpp
vendored
Normal file
391
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/asio/basic_signal_set.hpp
vendored
Normal file
@ -0,0 +1,391 @@
|
|||||||
|
//
|
||||||
|
// basic_signal_set.hpp
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ASIO_BASIC_SIGNAL_SET_HPP
|
||||||
|
#define ASIO_BASIC_SIGNAL_SET_HPP
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
# pragma once
|
||||||
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
|
||||||
|
#include "asio/detail/config.hpp"
|
||||||
|
|
||||||
|
#if defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
|
||||||
|
#include "asio/basic_io_object.hpp"
|
||||||
|
#include "asio/detail/handler_type_requirements.hpp"
|
||||||
|
#include "asio/detail/throw_error.hpp"
|
||||||
|
#include "asio/error.hpp"
|
||||||
|
#include "asio/signal_set_service.hpp"
|
||||||
|
|
||||||
|
#include "asio/detail/push_options.hpp"
|
||||||
|
|
||||||
|
namespace asio {
|
||||||
|
|
||||||
|
/// Provides signal functionality.
|
||||||
|
/**
|
||||||
|
* The basic_signal_set class template provides the ability to perform an
|
||||||
|
* asynchronous wait for one or more signals to occur.
|
||||||
|
*
|
||||||
|
* Most applications will use the asio::signal_set typedef.
|
||||||
|
*
|
||||||
|
* @par Thread Safety
|
||||||
|
* @e Distinct @e objects: Safe.@n
|
||||||
|
* @e Shared @e objects: Unsafe.
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* Performing an asynchronous wait:
|
||||||
|
* @code
|
||||||
|
* void handler(
|
||||||
|
* const asio::error_code& error,
|
||||||
|
* int signal_number)
|
||||||
|
* {
|
||||||
|
* if (!error)
|
||||||
|
* {
|
||||||
|
* // A signal occurred.
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* // Construct a signal set registered for process termination.
|
||||||
|
* asio::signal_set signals(io_context, SIGINT, SIGTERM);
|
||||||
|
*
|
||||||
|
* // Start an asynchronous wait for one of the signals to occur.
|
||||||
|
* signals.async_wait(handler);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @par Queueing of signal notifications
|
||||||
|
*
|
||||||
|
* If a signal is registered with a signal_set, and the signal occurs when
|
||||||
|
* there are no waiting handlers, then the signal notification is queued. The
|
||||||
|
* next async_wait operation on that signal_set will dequeue the notification.
|
||||||
|
* If multiple notifications are queued, subsequent async_wait operations
|
||||||
|
* dequeue them one at a time. Signal notifications are dequeued in order of
|
||||||
|
* ascending signal number.
|
||||||
|
*
|
||||||
|
* If a signal number is removed from a signal_set (using the @c remove or @c
|
||||||
|
* erase member functions) then any queued notifications for that signal are
|
||||||
|
* discarded.
|
||||||
|
*
|
||||||
|
* @par Multiple registration of signals
|
||||||
|
*
|
||||||
|
* The same signal number may be registered with different signal_set objects.
|
||||||
|
* When the signal occurs, one handler is called for each signal_set object.
|
||||||
|
*
|
||||||
|
* Note that multiple registration only works for signals that are registered
|
||||||
|
* using Asio. The application must not also register a signal handler using
|
||||||
|
* functions such as @c signal() or @c sigaction().
|
||||||
|
*
|
||||||
|
* @par Signal masking on POSIX platforms
|
||||||
|
*
|
||||||
|
* POSIX allows signals to be blocked using functions such as @c sigprocmask()
|
||||||
|
* and @c pthread_sigmask(). For signals to be delivered, programs must ensure
|
||||||
|
* that any signals registered using signal_set objects are unblocked in at
|
||||||
|
* least one thread.
|
||||||
|
*/
|
||||||
|
template <typename SignalSetService = signal_set_service>
|
||||||
|
class basic_signal_set
|
||||||
|
: public basic_io_object<SignalSetService>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Construct a signal set without adding any signals.
|
||||||
|
/**
|
||||||
|
* This constructor creates a signal set without registering for any signals.
|
||||||
|
*
|
||||||
|
* @param io_context The io_context object that the signal set will use to
|
||||||
|
* dispatch handlers for any asynchronous operations performed on the set.
|
||||||
|
*/
|
||||||
|
explicit basic_signal_set(asio::io_context& io_context)
|
||||||
|
: basic_io_object<SignalSetService>(io_context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a signal set and add one signal.
|
||||||
|
/**
|
||||||
|
* This constructor creates a signal set and registers for one signal.
|
||||||
|
*
|
||||||
|
* @param io_context The io_context object that the signal set will use to
|
||||||
|
* dispatch handlers for any asynchronous operations performed on the set.
|
||||||
|
*
|
||||||
|
* @param signal_number_1 The signal number to be added.
|
||||||
|
*
|
||||||
|
* @note This constructor is equivalent to performing:
|
||||||
|
* @code asio::signal_set signals(io_context);
|
||||||
|
* signals.add(signal_number_1); @endcode
|
||||||
|
*/
|
||||||
|
basic_signal_set(asio::io_context& io_context, int signal_number_1)
|
||||||
|
: basic_io_object<SignalSetService>(io_context)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
this->get_service().add(this->get_implementation(), signal_number_1, ec);
|
||||||
|
asio::detail::throw_error(ec, "add");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a signal set and add two signals.
|
||||||
|
/**
|
||||||
|
* This constructor creates a signal set and registers for two signals.
|
||||||
|
*
|
||||||
|
* @param io_context The io_context object that the signal set will use to
|
||||||
|
* dispatch handlers for any asynchronous operations performed on the set.
|
||||||
|
*
|
||||||
|
* @param signal_number_1 The first signal number to be added.
|
||||||
|
*
|
||||||
|
* @param signal_number_2 The second signal number to be added.
|
||||||
|
*
|
||||||
|
* @note This constructor is equivalent to performing:
|
||||||
|
* @code asio::signal_set signals(io_context);
|
||||||
|
* signals.add(signal_number_1);
|
||||||
|
* signals.add(signal_number_2); @endcode
|
||||||
|
*/
|
||||||
|
basic_signal_set(asio::io_context& io_context, int signal_number_1,
|
||||||
|
int signal_number_2)
|
||||||
|
: basic_io_object<SignalSetService>(io_context)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
this->get_service().add(this->get_implementation(), signal_number_1, ec);
|
||||||
|
asio::detail::throw_error(ec, "add");
|
||||||
|
this->get_service().add(this->get_implementation(), signal_number_2, ec);
|
||||||
|
asio::detail::throw_error(ec, "add");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a signal set and add three signals.
|
||||||
|
/**
|
||||||
|
* This constructor creates a signal set and registers for three signals.
|
||||||
|
*
|
||||||
|
* @param io_context The io_context object that the signal set will use to
|
||||||
|
* dispatch handlers for any asynchronous operations performed on the set.
|
||||||
|
*
|
||||||
|
* @param signal_number_1 The first signal number to be added.
|
||||||
|
*
|
||||||
|
* @param signal_number_2 The second signal number to be added.
|
||||||
|
*
|
||||||
|
* @param signal_number_3 The third signal number to be added.
|
||||||
|
*
|
||||||
|
* @note This constructor is equivalent to performing:
|
||||||
|
* @code asio::signal_set signals(io_context);
|
||||||
|
* signals.add(signal_number_1);
|
||||||
|
* signals.add(signal_number_2);
|
||||||
|
* signals.add(signal_number_3); @endcode
|
||||||
|
*/
|
||||||
|
basic_signal_set(asio::io_context& io_context, int signal_number_1,
|
||||||
|
int signal_number_2, int signal_number_3)
|
||||||
|
: basic_io_object<SignalSetService>(io_context)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
this->get_service().add(this->get_implementation(), signal_number_1, ec);
|
||||||
|
asio::detail::throw_error(ec, "add");
|
||||||
|
this->get_service().add(this->get_implementation(), signal_number_2, ec);
|
||||||
|
asio::detail::throw_error(ec, "add");
|
||||||
|
this->get_service().add(this->get_implementation(), signal_number_3, ec);
|
||||||
|
asio::detail::throw_error(ec, "add");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a signal to a signal_set.
|
||||||
|
/**
|
||||||
|
* This function adds the specified signal to the set. It has no effect if the
|
||||||
|
* signal is already in the set.
|
||||||
|
*
|
||||||
|
* @param signal_number The signal to be added to the set.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*/
|
||||||
|
void add(int signal_number)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
this->get_service().add(this->get_implementation(), signal_number, ec);
|
||||||
|
asio::detail::throw_error(ec, "add");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a signal to a signal_set.
|
||||||
|
/**
|
||||||
|
* This function adds the specified signal to the set. It has no effect if the
|
||||||
|
* signal is already in the set.
|
||||||
|
*
|
||||||
|
* @param signal_number The signal to be added to the set.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*/
|
||||||
|
ASIO_SYNC_OP_VOID add(int signal_number, asio::error_code& ec)
|
||||||
|
{
|
||||||
|
this->get_service().add(this->get_implementation(), signal_number, ec);
|
||||||
|
ASIO_SYNC_OP_VOID_RETURN(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a signal from a signal_set.
|
||||||
|
/**
|
||||||
|
* This function removes the specified signal from the set. It has no effect
|
||||||
|
* if the signal is not in the set.
|
||||||
|
*
|
||||||
|
* @param signal_number The signal to be removed from the set.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*
|
||||||
|
* @note Removes any notifications that have been queued for the specified
|
||||||
|
* signal number.
|
||||||
|
*/
|
||||||
|
void remove(int signal_number)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
this->get_service().remove(this->get_implementation(), signal_number, ec);
|
||||||
|
asio::detail::throw_error(ec, "remove");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a signal from a signal_set.
|
||||||
|
/**
|
||||||
|
* This function removes the specified signal from the set. It has no effect
|
||||||
|
* if the signal is not in the set.
|
||||||
|
*
|
||||||
|
* @param signal_number The signal to be removed from the set.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*
|
||||||
|
* @note Removes any notifications that have been queued for the specified
|
||||||
|
* signal number.
|
||||||
|
*/
|
||||||
|
ASIO_SYNC_OP_VOID remove(int signal_number,
|
||||||
|
asio::error_code& ec)
|
||||||
|
{
|
||||||
|
this->get_service().remove(this->get_implementation(), signal_number, ec);
|
||||||
|
ASIO_SYNC_OP_VOID_RETURN(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove all signals from a signal_set.
|
||||||
|
/**
|
||||||
|
* This function removes all signals from the set. It has no effect if the set
|
||||||
|
* is already empty.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*
|
||||||
|
* @note Removes all queued notifications.
|
||||||
|
*/
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
this->get_service().clear(this->get_implementation(), ec);
|
||||||
|
asio::detail::throw_error(ec, "clear");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove all signals from a signal_set.
|
||||||
|
/**
|
||||||
|
* This function removes all signals from the set. It has no effect if the set
|
||||||
|
* is already empty.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*
|
||||||
|
* @note Removes all queued notifications.
|
||||||
|
*/
|
||||||
|
ASIO_SYNC_OP_VOID clear(asio::error_code& ec)
|
||||||
|
{
|
||||||
|
this->get_service().clear(this->get_implementation(), ec);
|
||||||
|
ASIO_SYNC_OP_VOID_RETURN(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cancel all operations associated with the signal set.
|
||||||
|
/**
|
||||||
|
* This function forces the completion of any pending asynchronous wait
|
||||||
|
* operations against the signal set. The handler for each cancelled
|
||||||
|
* operation will be invoked with the asio::error::operation_aborted
|
||||||
|
* error code.
|
||||||
|
*
|
||||||
|
* Cancellation does not alter the set of registered signals.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*
|
||||||
|
* @note If a registered signal occurred before cancel() is called, then the
|
||||||
|
* handlers for asynchronous wait operations will:
|
||||||
|
*
|
||||||
|
* @li have already been invoked; or
|
||||||
|
*
|
||||||
|
* @li have been queued for invocation in the near future.
|
||||||
|
*
|
||||||
|
* These handlers can no longer be cancelled, and therefore are passed an
|
||||||
|
* error code that indicates the successful completion of the wait operation.
|
||||||
|
*/
|
||||||
|
void cancel()
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
this->get_service().cancel(this->get_implementation(), ec);
|
||||||
|
asio::detail::throw_error(ec, "cancel");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cancel all operations associated with the signal set.
|
||||||
|
/**
|
||||||
|
* This function forces the completion of any pending asynchronous wait
|
||||||
|
* operations against the signal set. The handler for each cancelled
|
||||||
|
* operation will be invoked with the asio::error::operation_aborted
|
||||||
|
* error code.
|
||||||
|
*
|
||||||
|
* Cancellation does not alter the set of registered signals.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*
|
||||||
|
* @note If a registered signal occurred before cancel() is called, then the
|
||||||
|
* handlers for asynchronous wait operations will:
|
||||||
|
*
|
||||||
|
* @li have already been invoked; or
|
||||||
|
*
|
||||||
|
* @li have been queued for invocation in the near future.
|
||||||
|
*
|
||||||
|
* These handlers can no longer be cancelled, and therefore are passed an
|
||||||
|
* error code that indicates the successful completion of the wait operation.
|
||||||
|
*/
|
||||||
|
ASIO_SYNC_OP_VOID cancel(asio::error_code& ec)
|
||||||
|
{
|
||||||
|
this->get_service().cancel(this->get_implementation(), ec);
|
||||||
|
ASIO_SYNC_OP_VOID_RETURN(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start an asynchronous operation to wait for a signal to be delivered.
|
||||||
|
/**
|
||||||
|
* This function may be used to initiate an asynchronous wait against the
|
||||||
|
* signal set. It always returns immediately.
|
||||||
|
*
|
||||||
|
* For each call to async_wait(), the supplied handler will be called exactly
|
||||||
|
* once. The handler will be called when:
|
||||||
|
*
|
||||||
|
* @li One of the registered signals in the signal set occurs; or
|
||||||
|
*
|
||||||
|
* @li The signal set was cancelled, in which case the handler is passed the
|
||||||
|
* error code asio::error::operation_aborted.
|
||||||
|
*
|
||||||
|
* @param handler The handler to be called when the signal occurs. Copies
|
||||||
|
* will be made of the handler as required. The function signature of the
|
||||||
|
* handler must be:
|
||||||
|
* @code void handler(
|
||||||
|
* const asio::error_code& error, // Result of operation.
|
||||||
|
* int signal_number // Indicates which signal occurred.
|
||||||
|
* ); @endcode
|
||||||
|
* Regardless of whether the asynchronous operation completes immediately or
|
||||||
|
* not, the handler will not be invoked from within this function. Invocation
|
||||||
|
* of the handler will be performed in a manner equivalent to using
|
||||||
|
* asio::io_context::post().
|
||||||
|
*/
|
||||||
|
template <typename SignalHandler>
|
||||||
|
ASIO_INITFN_RESULT_TYPE(SignalHandler,
|
||||||
|
void (asio::error_code, int))
|
||||||
|
async_wait(ASIO_MOVE_ARG(SignalHandler) handler)
|
||||||
|
{
|
||||||
|
// If you get an error on the following line it means that your handler does
|
||||||
|
// not meet the documented type requirements for a SignalHandler.
|
||||||
|
ASIO_SIGNAL_HANDLER_CHECK(SignalHandler, handler) type_check;
|
||||||
|
|
||||||
|
return this->get_service().async_wait(this->get_implementation(),
|
||||||
|
ASIO_MOVE_CAST(SignalHandler)(handler));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace asio
|
||||||
|
|
||||||
|
#include "asio/detail/pop_options.hpp"
|
||||||
|
|
||||||
|
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
|
||||||
|
#endif // ASIO_BASIC_SIGNAL_SET_HPP
|
1757
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/asio/basic_socket.hpp
vendored
Normal file
1757
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/asio/basic_socket.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,430 @@
|
|||||||
|
//
|
||||||
|
// basic_socket_iostream.hpp
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ASIO_BASIC_SOCKET_IOSTREAM_HPP
|
||||||
|
#define ASIO_BASIC_SOCKET_IOSTREAM_HPP
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
# pragma once
|
||||||
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
|
||||||
|
#include "asio/detail/config.hpp"
|
||||||
|
|
||||||
|
#if !defined(ASIO_NO_IOSTREAM)
|
||||||
|
|
||||||
|
#include <istream>
|
||||||
|
#include <ostream>
|
||||||
|
#include "asio/basic_socket_streambuf.hpp"
|
||||||
|
|
||||||
|
#if defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
# include "asio/stream_socket_service.hpp"
|
||||||
|
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
|
||||||
|
#if !defined(ASIO_HAS_VARIADIC_TEMPLATES)
|
||||||
|
|
||||||
|
# include "asio/detail/variadic_templates.hpp"
|
||||||
|
|
||||||
|
// A macro that should expand to:
|
||||||
|
// template <typename T1, ..., typename Tn>
|
||||||
|
// explicit basic_socket_iostream(T1 x1, ..., Tn xn)
|
||||||
|
// : std::basic_iostream<char>(
|
||||||
|
// &this->detail::socket_iostream_base<
|
||||||
|
// Protocol ASIO_SVC_TARG, Clock,
|
||||||
|
// WaitTraits ASIO_SVC_TARG1>::streambuf_)
|
||||||
|
// {
|
||||||
|
// if (rdbuf()->connect(x1, ..., xn) == 0)
|
||||||
|
// this->setstate(std::ios_base::failbit);
|
||||||
|
// }
|
||||||
|
// This macro should only persist within this file.
|
||||||
|
|
||||||
|
# define ASIO_PRIVATE_CTR_DEF(n) \
|
||||||
|
template <ASIO_VARIADIC_TPARAMS(n)> \
|
||||||
|
explicit basic_socket_iostream(ASIO_VARIADIC_BYVAL_PARAMS(n)) \
|
||||||
|
: std::basic_iostream<char>( \
|
||||||
|
&this->detail::socket_iostream_base< \
|
||||||
|
Protocol ASIO_SVC_TARG, Clock, \
|
||||||
|
WaitTraits ASIO_SVC_TARG1>::streambuf_) \
|
||||||
|
{ \
|
||||||
|
this->setf(std::ios_base::unitbuf); \
|
||||||
|
if (rdbuf()->connect(ASIO_VARIADIC_BYVAL_ARGS(n)) == 0) \
|
||||||
|
this->setstate(std::ios_base::failbit); \
|
||||||
|
} \
|
||||||
|
/**/
|
||||||
|
|
||||||
|
// A macro that should expand to:
|
||||||
|
// template <typename T1, ..., typename Tn>
|
||||||
|
// void connect(T1 x1, ..., Tn xn)
|
||||||
|
// {
|
||||||
|
// if (rdbuf()->connect(x1, ..., xn) == 0)
|
||||||
|
// this->setstate(std::ios_base::failbit);
|
||||||
|
// }
|
||||||
|
// This macro should only persist within this file.
|
||||||
|
|
||||||
|
# define ASIO_PRIVATE_CONNECT_DEF(n) \
|
||||||
|
template <ASIO_VARIADIC_TPARAMS(n)> \
|
||||||
|
void connect(ASIO_VARIADIC_BYVAL_PARAMS(n)) \
|
||||||
|
{ \
|
||||||
|
if (rdbuf()->connect(ASIO_VARIADIC_BYVAL_ARGS(n)) == 0) \
|
||||||
|
this->setstate(std::ios_base::failbit); \
|
||||||
|
} \
|
||||||
|
/**/
|
||||||
|
|
||||||
|
#endif // !defined(ASIO_HAS_VARIADIC_TEMPLATES)
|
||||||
|
|
||||||
|
#include "asio/detail/push_options.hpp"
|
||||||
|
|
||||||
|
namespace asio {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
// A separate base class is used to ensure that the streambuf is initialised
|
||||||
|
// prior to the basic_socket_iostream's basic_iostream base class.
|
||||||
|
template <typename Protocol ASIO_SVC_TPARAM,
|
||||||
|
typename Clock, typename WaitTraits ASIO_SVC_TPARAM1>
|
||||||
|
class socket_iostream_base
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
socket_iostream_base()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(ASIO_HAS_MOVE)
|
||||||
|
socket_iostream_base(socket_iostream_base&& other)
|
||||||
|
: streambuf_(std::move(other.streambuf_))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
socket_iostream_base(basic_stream_socket<Protocol> s)
|
||||||
|
: streambuf_(std::move(s))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
socket_iostream_base& operator=(socket_iostream_base&& other)
|
||||||
|
{
|
||||||
|
streambuf_ = std::move(other.streambuf_);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
#endif // defined(ASIO_HAS_MOVE)
|
||||||
|
|
||||||
|
basic_socket_streambuf<Protocol ASIO_SVC_TARG,
|
||||||
|
Clock, WaitTraits ASIO_SVC_TARG1> streambuf_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
#if !defined(ASIO_BASIC_SOCKET_IOSTREAM_FWD_DECL)
|
||||||
|
#define ASIO_BASIC_SOCKET_IOSTREAM_FWD_DECL
|
||||||
|
|
||||||
|
// Forward declaration with defaulted arguments.
|
||||||
|
template <typename Protocol
|
||||||
|
ASIO_SVC_TPARAM_DEF1(= stream_socket_service<Protocol>),
|
||||||
|
#if defined(ASIO_HAS_BOOST_DATE_TIME) \
|
||||||
|
&& defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
|
||||||
|
typename Clock = boost::posix_time::ptime,
|
||||||
|
typename WaitTraits = time_traits<Clock>
|
||||||
|
ASIO_SVC_TPARAM1_DEF2(= deadline_timer_service<Clock, WaitTraits>)>
|
||||||
|
#else // defined(ASIO_HAS_BOOST_DATE_TIME)
|
||||||
|
// && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
|
||||||
|
typename Clock = chrono::steady_clock,
|
||||||
|
typename WaitTraits = wait_traits<Clock>
|
||||||
|
ASIO_SVC_TPARAM1_DEF1(= steady_timer::service_type)>
|
||||||
|
#endif // defined(ASIO_HAS_BOOST_DATE_TIME)
|
||||||
|
// && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
|
||||||
|
class basic_socket_iostream;
|
||||||
|
|
||||||
|
#endif // !defined(ASIO_BASIC_SOCKET_IOSTREAM_FWD_DECL)
|
||||||
|
|
||||||
|
/// Iostream interface for a socket.
|
||||||
|
#if defined(GENERATING_DOCUMENTATION)
|
||||||
|
template <typename Protocol,
|
||||||
|
typename Clock = chrono::steady_clock,
|
||||||
|
typename WaitTraits = wait_traits<Clock> >
|
||||||
|
#else // defined(GENERATING_DOCUMENTATION)
|
||||||
|
template <typename Protocol ASIO_SVC_TPARAM,
|
||||||
|
typename Clock, typename WaitTraits ASIO_SVC_TPARAM1>
|
||||||
|
#endif // defined(GENERATING_DOCUMENTATION)
|
||||||
|
class basic_socket_iostream
|
||||||
|
: private detail::socket_iostream_base<Protocol
|
||||||
|
ASIO_SVC_TARG, Clock, WaitTraits ASIO_SVC_TARG1>,
|
||||||
|
public std::basic_iostream<char>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// These typedefs are intended keep this class's implementation independent
|
||||||
|
// of whether it's using Boost.DateClock, Boost.Chrono or std::chrono.
|
||||||
|
#if defined(ASIO_HAS_BOOST_DATE_TIME) \
|
||||||
|
&& defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
|
||||||
|
typedef WaitTraits traits_helper;
|
||||||
|
#else // defined(ASIO_HAS_BOOST_DATE_TIME)
|
||||||
|
// && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
|
||||||
|
typedef detail::chrono_time_traits<Clock, WaitTraits> traits_helper;
|
||||||
|
#endif // defined(ASIO_HAS_BOOST_DATE_TIME)
|
||||||
|
// && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// The protocol type.
|
||||||
|
typedef Protocol protocol_type;
|
||||||
|
|
||||||
|
/// The endpoint type.
|
||||||
|
typedef typename Protocol::endpoint endpoint_type;
|
||||||
|
|
||||||
|
/// The clock type.
|
||||||
|
typedef Clock clock_type;
|
||||||
|
|
||||||
|
#if defined(GENERATING_DOCUMENTATION)
|
||||||
|
/// (Deprecated: Use time_point.) The time type.
|
||||||
|
typedef typename WaitTraits::time_type time_type;
|
||||||
|
|
||||||
|
/// The time type.
|
||||||
|
typedef typename WaitTraits::time_point time_point;
|
||||||
|
|
||||||
|
/// (Deprecated: Use duration.) The duration type.
|
||||||
|
typedef typename WaitTraits::duration_type duration_type;
|
||||||
|
|
||||||
|
/// The duration type.
|
||||||
|
typedef typename WaitTraits::duration duration;
|
||||||
|
#else
|
||||||
|
# if !defined(ASIO_NO_DEPRECATED)
|
||||||
|
typedef typename traits_helper::time_type time_type;
|
||||||
|
typedef typename traits_helper::duration_type duration_type;
|
||||||
|
# endif // !defined(ASIO_NO_DEPRECATED)
|
||||||
|
typedef typename traits_helper::time_type time_point;
|
||||||
|
typedef typename traits_helper::duration_type duration;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Construct a basic_socket_iostream without establishing a connection.
|
||||||
|
basic_socket_iostream()
|
||||||
|
: std::basic_iostream<char>(
|
||||||
|
&this->detail::socket_iostream_base<
|
||||||
|
Protocol ASIO_SVC_TARG, Clock,
|
||||||
|
WaitTraits ASIO_SVC_TARG1>::streambuf_)
|
||||||
|
{
|
||||||
|
this->setf(std::ios_base::unitbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
/// Construct a basic_socket_iostream from the supplied socket.
|
||||||
|
explicit basic_socket_iostream(basic_stream_socket<protocol_type> s)
|
||||||
|
: detail::socket_iostream_base<
|
||||||
|
Protocol ASIO_SVC_TARG, Clock,
|
||||||
|
WaitTraits ASIO_SVC_TARG1>(std::move(s)),
|
||||||
|
std::basic_iostream<char>(
|
||||||
|
&this->detail::socket_iostream_base<
|
||||||
|
Protocol ASIO_SVC_TARG, Clock,
|
||||||
|
WaitTraits ASIO_SVC_TARG1>::streambuf_)
|
||||||
|
{
|
||||||
|
this->setf(std::ios_base::unitbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(ASIO_HAS_STD_IOSTREAM_MOVE) \
|
||||||
|
|| defined(GENERATING_DOCUMENTATION)
|
||||||
|
/// Move-construct a basic_socket_iostream from another.
|
||||||
|
basic_socket_iostream(basic_socket_iostream&& other)
|
||||||
|
: detail::socket_iostream_base<
|
||||||
|
Protocol ASIO_SVC_TARG, Clock,
|
||||||
|
WaitTraits ASIO_SVC_TARG1>(std::move(other)),
|
||||||
|
std::basic_iostream<char>(std::move(other))
|
||||||
|
{
|
||||||
|
this->set_rdbuf(&this->detail::socket_iostream_base<
|
||||||
|
Protocol ASIO_SVC_TARG, Clock,
|
||||||
|
WaitTraits ASIO_SVC_TARG1>::streambuf_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move-assign a basic_socket_iostream from another.
|
||||||
|
basic_socket_iostream& operator=(basic_socket_iostream&& other)
|
||||||
|
{
|
||||||
|
std::basic_iostream<char>::operator=(std::move(other));
|
||||||
|
detail::socket_iostream_base<
|
||||||
|
Protocol ASIO_SVC_TARG, Clock,
|
||||||
|
WaitTraits ASIO_SVC_TARG1>::operator=(std::move(other));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
#endif // defined(ASIO_HAS_STD_IOSTREAM_MOVE)
|
||||||
|
// || defined(GENERATING_DOCUMENTATION)
|
||||||
|
#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
#if defined(GENERATING_DOCUMENTATION)
|
||||||
|
/// Establish a connection to an endpoint corresponding to a resolver query.
|
||||||
|
/**
|
||||||
|
* This constructor automatically establishes a connection based on the
|
||||||
|
* supplied resolver query parameters. The arguments are used to construct
|
||||||
|
* a resolver query object.
|
||||||
|
*/
|
||||||
|
template <typename T1, ..., typename TN>
|
||||||
|
explicit basic_socket_iostream(T1 t1, ..., TN tn);
|
||||||
|
#elif defined(ASIO_HAS_VARIADIC_TEMPLATES)
|
||||||
|
template <typename... T>
|
||||||
|
explicit basic_socket_iostream(T... x)
|
||||||
|
: std::basic_iostream<char>(
|
||||||
|
&this->detail::socket_iostream_base<
|
||||||
|
Protocol ASIO_SVC_TARG, Clock,
|
||||||
|
WaitTraits ASIO_SVC_TARG1>::streambuf_)
|
||||||
|
{
|
||||||
|
this->setf(std::ios_base::unitbuf);
|
||||||
|
if (rdbuf()->connect(x...) == 0)
|
||||||
|
this->setstate(std::ios_base::failbit);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_CTR_DEF)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(GENERATING_DOCUMENTATION)
|
||||||
|
/// Establish a connection to an endpoint corresponding to a resolver query.
|
||||||
|
/**
|
||||||
|
* This function automatically establishes a connection based on the supplied
|
||||||
|
* resolver query parameters. The arguments are used to construct a resolver
|
||||||
|
* query object.
|
||||||
|
*/
|
||||||
|
template <typename T1, ..., typename TN>
|
||||||
|
void connect(T1 t1, ..., TN tn);
|
||||||
|
#elif defined(ASIO_HAS_VARIADIC_TEMPLATES)
|
||||||
|
template <typename... T>
|
||||||
|
void connect(T... x)
|
||||||
|
{
|
||||||
|
if (rdbuf()->connect(x...) == 0)
|
||||||
|
this->setstate(std::ios_base::failbit);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_CONNECT_DEF)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Close the connection.
|
||||||
|
void close()
|
||||||
|
{
|
||||||
|
if (rdbuf()->close() == 0)
|
||||||
|
this->setstate(std::ios_base::failbit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a pointer to the underlying streambuf.
|
||||||
|
basic_socket_streambuf<Protocol ASIO_SVC_TARG,
|
||||||
|
Clock, WaitTraits ASIO_SVC_TARG1>* rdbuf() const
|
||||||
|
{
|
||||||
|
return const_cast<basic_socket_streambuf<Protocol ASIO_SVC_TARG,
|
||||||
|
Clock, WaitTraits ASIO_SVC_TARG1>*>(
|
||||||
|
&this->detail::socket_iostream_base<
|
||||||
|
Protocol ASIO_SVC_TARG, Clock,
|
||||||
|
WaitTraits ASIO_SVC_TARG1>::streambuf_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a reference to the underlying socket.
|
||||||
|
basic_socket<Protocol ASIO_SVC_TARG>& socket()
|
||||||
|
{
|
||||||
|
return rdbuf()->socket();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the last error associated with the stream.
|
||||||
|
/**
|
||||||
|
* @return An \c error_code corresponding to the last error from the stream.
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* To print the error associated with a failure to establish a connection:
|
||||||
|
* @code tcp::iostream s("www.boost.org", "http");
|
||||||
|
* if (!s)
|
||||||
|
* {
|
||||||
|
* std::cout << "Error: " << s.error().message() << std::endl;
|
||||||
|
* } @endcode
|
||||||
|
*/
|
||||||
|
const asio::error_code& error() const
|
||||||
|
{
|
||||||
|
return rdbuf()->error();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(ASIO_NO_DEPRECATED)
|
||||||
|
/// (Deprecated: Use expiry().) Get the stream's expiry time as an absolute
|
||||||
|
/// time.
|
||||||
|
/**
|
||||||
|
* @return An absolute time value representing the stream's expiry time.
|
||||||
|
*/
|
||||||
|
time_point expires_at() const
|
||||||
|
{
|
||||||
|
return rdbuf()->expires_at();
|
||||||
|
}
|
||||||
|
#endif // !defined(ASIO_NO_DEPRECATED)
|
||||||
|
|
||||||
|
/// Get the stream's expiry time as an absolute time.
|
||||||
|
/**
|
||||||
|
* @return An absolute time value representing the stream's expiry time.
|
||||||
|
*/
|
||||||
|
time_point expiry() const
|
||||||
|
{
|
||||||
|
return rdbuf()->expiry();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the stream's expiry time as an absolute time.
|
||||||
|
/**
|
||||||
|
* This function sets the expiry time associated with the stream. Stream
|
||||||
|
* operations performed after this time (where the operations cannot be
|
||||||
|
* completed using the internal buffers) will fail with the error
|
||||||
|
* asio::error::operation_aborted.
|
||||||
|
*
|
||||||
|
* @param expiry_time The expiry time to be used for the stream.
|
||||||
|
*/
|
||||||
|
void expires_at(const time_point& expiry_time)
|
||||||
|
{
|
||||||
|
rdbuf()->expires_at(expiry_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the stream's expiry time relative to now.
|
||||||
|
/**
|
||||||
|
* This function sets the expiry time associated with the stream. Stream
|
||||||
|
* operations performed after this time (where the operations cannot be
|
||||||
|
* completed using the internal buffers) will fail with the error
|
||||||
|
* asio::error::operation_aborted.
|
||||||
|
*
|
||||||
|
* @param expiry_time The expiry time to be used for the timer.
|
||||||
|
*/
|
||||||
|
void expires_after(const duration& expiry_time)
|
||||||
|
{
|
||||||
|
rdbuf()->expires_after(expiry_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(ASIO_NO_DEPRECATED)
|
||||||
|
/// (Deprecated: Use expiry().) Get the stream's expiry time relative to now.
|
||||||
|
/**
|
||||||
|
* @return A relative time value representing the stream's expiry time.
|
||||||
|
*/
|
||||||
|
duration expires_from_now() const
|
||||||
|
{
|
||||||
|
return rdbuf()->expires_from_now();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// (Deprecated: Use expires_after().) Set the stream's expiry time relative
|
||||||
|
/// to now.
|
||||||
|
/**
|
||||||
|
* This function sets the expiry time associated with the stream. Stream
|
||||||
|
* operations performed after this time (where the operations cannot be
|
||||||
|
* completed using the internal buffers) will fail with the error
|
||||||
|
* asio::error::operation_aborted.
|
||||||
|
*
|
||||||
|
* @param expiry_time The expiry time to be used for the timer.
|
||||||
|
*/
|
||||||
|
void expires_from_now(const duration& expiry_time)
|
||||||
|
{
|
||||||
|
rdbuf()->expires_from_now(expiry_time);
|
||||||
|
}
|
||||||
|
#endif // !defined(ASIO_NO_DEPRECATED)
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Disallow copying and assignment.
|
||||||
|
basic_socket_iostream(const basic_socket_iostream&) ASIO_DELETED;
|
||||||
|
basic_socket_iostream& operator=(
|
||||||
|
const basic_socket_iostream&) ASIO_DELETED;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace asio
|
||||||
|
|
||||||
|
#include "asio/detail/pop_options.hpp"
|
||||||
|
|
||||||
|
#if !defined(ASIO_HAS_VARIADIC_TEMPLATES)
|
||||||
|
# undef ASIO_PRIVATE_CTR_DEF
|
||||||
|
# undef ASIO_PRIVATE_CONNECT_DEF
|
||||||
|
#endif // !defined(ASIO_HAS_VARIADIC_TEMPLATES)
|
||||||
|
|
||||||
|
#endif // !defined(ASIO_NO_IOSTREAM)
|
||||||
|
|
||||||
|
#endif // ASIO_BASIC_SOCKET_IOSTREAM_HPP
|
@ -0,0 +1,707 @@
|
|||||||
|
//
|
||||||
|
// basic_socket_streambuf.hpp
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ASIO_BASIC_SOCKET_STREAMBUF_HPP
|
||||||
|
#define ASIO_BASIC_SOCKET_STREAMBUF_HPP
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
# pragma once
|
||||||
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
|
||||||
|
#include "asio/detail/config.hpp"
|
||||||
|
|
||||||
|
#if !defined(ASIO_NO_IOSTREAM)
|
||||||
|
|
||||||
|
#include <streambuf>
|
||||||
|
#include <vector>
|
||||||
|
#include "asio/basic_socket.hpp"
|
||||||
|
#include "asio/basic_stream_socket.hpp"
|
||||||
|
#include "asio/detail/buffer_sequence_adapter.hpp"
|
||||||
|
#include "asio/detail/memory.hpp"
|
||||||
|
#include "asio/detail/throw_error.hpp"
|
||||||
|
#include "asio/io_context.hpp"
|
||||||
|
|
||||||
|
#if defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
# include "asio/stream_socket_service.hpp"
|
||||||
|
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
|
||||||
|
#if defined(ASIO_HAS_BOOST_DATE_TIME) \
|
||||||
|
&& defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
|
||||||
|
# if defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
# include "asio/deadline_timer_service.hpp"
|
||||||
|
# else // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
# include "asio/detail/deadline_timer_service.hpp"
|
||||||
|
# endif // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
#else // defined(ASIO_HAS_BOOST_DATE_TIME)
|
||||||
|
// && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
|
||||||
|
# include "asio/steady_timer.hpp"
|
||||||
|
#endif // defined(ASIO_HAS_BOOST_DATE_TIME)
|
||||||
|
// && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
|
||||||
|
|
||||||
|
#if !defined(ASIO_HAS_VARIADIC_TEMPLATES)
|
||||||
|
|
||||||
|
# include "asio/detail/variadic_templates.hpp"
|
||||||
|
|
||||||
|
// A macro that should expand to:
|
||||||
|
// template <typename T1, ..., typename Tn>
|
||||||
|
// basic_socket_streambuf* connect(T1 x1, ..., Tn xn)
|
||||||
|
// {
|
||||||
|
// init_buffers();
|
||||||
|
// typedef typename Protocol::resolver resolver_type;
|
||||||
|
// resolver_type resolver(socket().get_executor().context());
|
||||||
|
// connect_to_endpoints(
|
||||||
|
// resolver.resolve(x1, ..., xn, ec_));
|
||||||
|
// return !ec_ ? this : 0;
|
||||||
|
// }
|
||||||
|
// This macro should only persist within this file.
|
||||||
|
|
||||||
|
# define ASIO_PRIVATE_CONNECT_DEF(n) \
|
||||||
|
template <ASIO_VARIADIC_TPARAMS(n)> \
|
||||||
|
basic_socket_streambuf* connect(ASIO_VARIADIC_BYVAL_PARAMS(n)) \
|
||||||
|
{ \
|
||||||
|
init_buffers(); \
|
||||||
|
typedef typename Protocol::resolver resolver_type; \
|
||||||
|
resolver_type resolver(socket().get_executor().context()); \
|
||||||
|
connect_to_endpoints( \
|
||||||
|
resolver.resolve(ASIO_VARIADIC_BYVAL_ARGS(n), ec_)); \
|
||||||
|
return !ec_ ? this : 0; \
|
||||||
|
} \
|
||||||
|
/**/
|
||||||
|
|
||||||
|
#endif // !defined(ASIO_HAS_VARIADIC_TEMPLATES)
|
||||||
|
|
||||||
|
#if !defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
# define ASIO_SVC_T1 detail::deadline_timer_service<traits_helper>
|
||||||
|
#endif // !defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
|
||||||
|
#include "asio/detail/push_options.hpp"
|
||||||
|
|
||||||
|
namespace asio {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
// A separate base class is used to ensure that the io_context member is
|
||||||
|
// initialised prior to the basic_socket_streambuf's basic_socket base class.
|
||||||
|
class socket_streambuf_io_context
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
socket_streambuf_io_context(io_context* ctx)
|
||||||
|
: default_io_context_(ctx)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
shared_ptr<io_context> default_io_context_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A separate base class is used to ensure that the dynamically allocated
|
||||||
|
// buffers are constructed prior to the basic_socket_streambuf's basic_socket
|
||||||
|
// base class. This makes moving the socket is the last potentially throwing
|
||||||
|
// step in the streambuf's move constructor, giving the constructor a strong
|
||||||
|
// exception safety guarantee.
|
||||||
|
class socket_streambuf_buffers
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
socket_streambuf_buffers()
|
||||||
|
: get_buffer_(buffer_size),
|
||||||
|
put_buffer_(buffer_size)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
enum { buffer_size = 512 };
|
||||||
|
std::vector<char> get_buffer_;
|
||||||
|
std::vector<char> put_buffer_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
#if !defined(ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL)
|
||||||
|
#define ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL
|
||||||
|
|
||||||
|
// Forward declaration with defaulted arguments.
|
||||||
|
template <typename Protocol
|
||||||
|
ASIO_SVC_TPARAM_DEF1(= stream_socket_service<Protocol>),
|
||||||
|
#if defined(ASIO_HAS_BOOST_DATE_TIME) \
|
||||||
|
&& defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
|
||||||
|
typename Clock = boost::posix_time::ptime,
|
||||||
|
typename WaitTraits = time_traits<Clock>
|
||||||
|
ASIO_SVC_TPARAM1_DEF2(= deadline_timer_service<Clock, WaitTraits>)>
|
||||||
|
#else // defined(ASIO_HAS_BOOST_DATE_TIME)
|
||||||
|
// && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
|
||||||
|
typename Clock = chrono::steady_clock,
|
||||||
|
typename WaitTraits = wait_traits<Clock>
|
||||||
|
ASIO_SVC_TPARAM1_DEF1(= steady_timer::service_type)>
|
||||||
|
#endif // defined(ASIO_HAS_BOOST_DATE_TIME)
|
||||||
|
// && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
|
||||||
|
class basic_socket_streambuf;
|
||||||
|
|
||||||
|
#endif // !defined(ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL)
|
||||||
|
|
||||||
|
/// Iostream streambuf for a socket.
|
||||||
|
#if defined(GENERATING_DOCUMENTATION)
|
||||||
|
template <typename Protocol,
|
||||||
|
typename Clock = chrono::steady_clock,
|
||||||
|
typename WaitTraits = wait_traits<Clock> >
|
||||||
|
#else // defined(GENERATING_DOCUMENTATION)
|
||||||
|
template <typename Protocol ASIO_SVC_TPARAM,
|
||||||
|
typename Clock, typename WaitTraits ASIO_SVC_TPARAM1>
|
||||||
|
#endif // defined(GENERATING_DOCUMENTATION)
|
||||||
|
class basic_socket_streambuf
|
||||||
|
: public std::streambuf,
|
||||||
|
private detail::socket_streambuf_io_context,
|
||||||
|
private detail::socket_streambuf_buffers,
|
||||||
|
#if defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
private basic_socket<Protocol ASIO_SVC_TARG>
|
||||||
|
#else // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
public basic_socket<Protocol ASIO_SVC_TARG>
|
||||||
|
#endif // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// These typedefs are intended keep this class's implementation independent
|
||||||
|
// of whether it's using Boost.DateClock, Boost.Chrono or std::chrono.
|
||||||
|
#if defined(ASIO_HAS_BOOST_DATE_TIME) \
|
||||||
|
&& defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
|
||||||
|
typedef WaitTraits traits_helper;
|
||||||
|
#else // defined(ASIO_HAS_BOOST_DATE_TIME)
|
||||||
|
// && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
|
||||||
|
typedef detail::chrono_time_traits<Clock, WaitTraits> traits_helper;
|
||||||
|
#endif // defined(ASIO_HAS_BOOST_DATE_TIME)
|
||||||
|
// && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// The protocol type.
|
||||||
|
typedef Protocol protocol_type;
|
||||||
|
|
||||||
|
/// The endpoint type.
|
||||||
|
typedef typename Protocol::endpoint endpoint_type;
|
||||||
|
|
||||||
|
/// The clock type.
|
||||||
|
typedef Clock clock_type;
|
||||||
|
|
||||||
|
#if defined(GENERATING_DOCUMENTATION)
|
||||||
|
/// (Deprecated: Use time_point.) The time type.
|
||||||
|
typedef typename WaitTraits::time_type time_type;
|
||||||
|
|
||||||
|
/// The time type.
|
||||||
|
typedef typename WaitTraits::time_point time_point;
|
||||||
|
|
||||||
|
/// (Deprecated: Use duration.) The duration type.
|
||||||
|
typedef typename WaitTraits::duration_type duration_type;
|
||||||
|
|
||||||
|
/// The duration type.
|
||||||
|
typedef typename WaitTraits::duration duration;
|
||||||
|
#else
|
||||||
|
# if !defined(ASIO_NO_DEPRECATED)
|
||||||
|
typedef typename traits_helper::time_type time_type;
|
||||||
|
typedef typename traits_helper::duration_type duration_type;
|
||||||
|
# endif // !defined(ASIO_NO_DEPRECATED)
|
||||||
|
typedef typename traits_helper::time_type time_point;
|
||||||
|
typedef typename traits_helper::duration_type duration;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Construct a basic_socket_streambuf without establishing a connection.
|
||||||
|
basic_socket_streambuf()
|
||||||
|
: detail::socket_streambuf_io_context(new io_context),
|
||||||
|
basic_socket<Protocol ASIO_SVC_TARG>(*default_io_context_),
|
||||||
|
expiry_time_(max_expiry_time())
|
||||||
|
{
|
||||||
|
init_buffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
/// Construct a basic_socket_streambuf from the supplied socket.
|
||||||
|
explicit basic_socket_streambuf(basic_stream_socket<protocol_type> s)
|
||||||
|
: detail::socket_streambuf_io_context(0),
|
||||||
|
basic_socket<Protocol ASIO_SVC_TARG>(std::move(s)),
|
||||||
|
expiry_time_(max_expiry_time())
|
||||||
|
{
|
||||||
|
init_buffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move-construct a basic_socket_streambuf from another.
|
||||||
|
basic_socket_streambuf(basic_socket_streambuf&& other)
|
||||||
|
: detail::socket_streambuf_io_context(other),
|
||||||
|
basic_socket<Protocol ASIO_SVC_TARG>(std::move(other.socket())),
|
||||||
|
ec_(other.ec_),
|
||||||
|
expiry_time_(other.expiry_time_)
|
||||||
|
{
|
||||||
|
get_buffer_.swap(other.get_buffer_);
|
||||||
|
put_buffer_.swap(other.put_buffer_);
|
||||||
|
setg(other.eback(), other.gptr(), other.egptr());
|
||||||
|
setp(other.pptr(), other.epptr());
|
||||||
|
other.ec_ = asio::error_code();
|
||||||
|
other.expiry_time_ = max_expiry_time();
|
||||||
|
other.init_buffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move-assign a basic_socket_streambuf from another.
|
||||||
|
basic_socket_streambuf& operator=(basic_socket_streambuf&& other)
|
||||||
|
{
|
||||||
|
this->close();
|
||||||
|
socket() = std::move(other.socket());
|
||||||
|
detail::socket_streambuf_io_context::operator=(other);
|
||||||
|
ec_ = other.ec_;
|
||||||
|
expiry_time_ = other.expiry_time_;
|
||||||
|
get_buffer_.swap(other.get_buffer_);
|
||||||
|
put_buffer_.swap(other.put_buffer_);
|
||||||
|
setg(other.eback(), other.gptr(), other.egptr());
|
||||||
|
setp(other.pptr(), other.epptr());
|
||||||
|
other.ec_ = asio::error_code();
|
||||||
|
other.expiry_time_ = max_expiry_time();
|
||||||
|
other.put_buffer_.resize(buffer_size);
|
||||||
|
other.init_buffers();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
/// Destructor flushes buffered data.
|
||||||
|
virtual ~basic_socket_streambuf()
|
||||||
|
{
|
||||||
|
if (pptr() != pbase())
|
||||||
|
overflow(traits_type::eof());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Establish a connection.
|
||||||
|
/**
|
||||||
|
* This function establishes a connection to the specified endpoint.
|
||||||
|
*
|
||||||
|
* @return \c this if a connection was successfully established, a null
|
||||||
|
* pointer otherwise.
|
||||||
|
*/
|
||||||
|
basic_socket_streambuf* connect(const endpoint_type& endpoint)
|
||||||
|
{
|
||||||
|
init_buffers();
|
||||||
|
ec_ = asio::error_code();
|
||||||
|
this->connect_to_endpoints(&endpoint, &endpoint + 1);
|
||||||
|
return !ec_ ? this : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(GENERATING_DOCUMENTATION)
|
||||||
|
/// Establish a connection.
|
||||||
|
/**
|
||||||
|
* This function automatically establishes a connection based on the supplied
|
||||||
|
* resolver query parameters. The arguments are used to construct a resolver
|
||||||
|
* query object.
|
||||||
|
*
|
||||||
|
* @return \c this if a connection was successfully established, a null
|
||||||
|
* pointer otherwise.
|
||||||
|
*/
|
||||||
|
template <typename T1, ..., typename TN>
|
||||||
|
basic_socket_streambuf* connect(T1 t1, ..., TN tn);
|
||||||
|
#elif defined(ASIO_HAS_VARIADIC_TEMPLATES)
|
||||||
|
template <typename... T>
|
||||||
|
basic_socket_streambuf* connect(T... x)
|
||||||
|
{
|
||||||
|
init_buffers();
|
||||||
|
typedef typename Protocol::resolver resolver_type;
|
||||||
|
resolver_type resolver(socket().get_executor().context());
|
||||||
|
connect_to_endpoints(resolver.resolve(x..., ec_));
|
||||||
|
return !ec_ ? this : 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_CONNECT_DEF)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Close the connection.
|
||||||
|
/**
|
||||||
|
* @return \c this if a connection was successfully established, a null
|
||||||
|
* pointer otherwise.
|
||||||
|
*/
|
||||||
|
basic_socket_streambuf* close()
|
||||||
|
{
|
||||||
|
sync();
|
||||||
|
socket().close(ec_);
|
||||||
|
if (!ec_)
|
||||||
|
init_buffers();
|
||||||
|
return !ec_ ? this : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a reference to the underlying socket.
|
||||||
|
basic_socket<Protocol ASIO_SVC_TARG>& socket()
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the last error associated with the stream buffer.
|
||||||
|
/**
|
||||||
|
* @return An \c error_code corresponding to the last error from the stream
|
||||||
|
* buffer.
|
||||||
|
*/
|
||||||
|
const asio::error_code& error() const
|
||||||
|
{
|
||||||
|
return ec_;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(ASIO_NO_DEPRECATED)
|
||||||
|
/// (Deprecated: Use error().) Get the last error associated with the stream
|
||||||
|
/// buffer.
|
||||||
|
/**
|
||||||
|
* @return An \c error_code corresponding to the last error from the stream
|
||||||
|
* buffer.
|
||||||
|
*/
|
||||||
|
const asio::error_code& puberror() const
|
||||||
|
{
|
||||||
|
return error();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// (Deprecated: Use expiry().) Get the stream buffer's expiry time as an
|
||||||
|
/// absolute time.
|
||||||
|
/**
|
||||||
|
* @return An absolute time value representing the stream buffer's expiry
|
||||||
|
* time.
|
||||||
|
*/
|
||||||
|
time_point expires_at() const
|
||||||
|
{
|
||||||
|
return expiry_time_;
|
||||||
|
}
|
||||||
|
#endif // !defined(ASIO_NO_DEPRECATED)
|
||||||
|
|
||||||
|
/// Get the stream buffer's expiry time as an absolute time.
|
||||||
|
/**
|
||||||
|
* @return An absolute time value representing the stream buffer's expiry
|
||||||
|
* time.
|
||||||
|
*/
|
||||||
|
time_point expiry() const
|
||||||
|
{
|
||||||
|
return expiry_time_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the stream buffer's expiry time as an absolute time.
|
||||||
|
/**
|
||||||
|
* This function sets the expiry time associated with the stream. Stream
|
||||||
|
* operations performed after this time (where the operations cannot be
|
||||||
|
* completed using the internal buffers) will fail with the error
|
||||||
|
* asio::error::operation_aborted.
|
||||||
|
*
|
||||||
|
* @param expiry_time The expiry time to be used for the stream.
|
||||||
|
*/
|
||||||
|
void expires_at(const time_point& expiry_time)
|
||||||
|
{
|
||||||
|
expiry_time_ = expiry_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the stream buffer's expiry time relative to now.
|
||||||
|
/**
|
||||||
|
* This function sets the expiry time associated with the stream. Stream
|
||||||
|
* operations performed after this time (where the operations cannot be
|
||||||
|
* completed using the internal buffers) will fail with the error
|
||||||
|
* asio::error::operation_aborted.
|
||||||
|
*
|
||||||
|
* @param expiry_time The expiry time to be used for the timer.
|
||||||
|
*/
|
||||||
|
void expires_after(const duration& expiry_time)
|
||||||
|
{
|
||||||
|
expiry_time_ = traits_helper::add(traits_helper::now(), expiry_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(ASIO_NO_DEPRECATED)
|
||||||
|
/// (Deprecated: Use expiry().) Get the stream buffer's expiry time relative
|
||||||
|
/// to now.
|
||||||
|
/**
|
||||||
|
* @return A relative time value representing the stream buffer's expiry time.
|
||||||
|
*/
|
||||||
|
duration expires_from_now() const
|
||||||
|
{
|
||||||
|
return traits_helper::subtract(expires_at(), traits_helper::now());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// (Deprecated: Use expires_after().) Set the stream buffer's expiry time
|
||||||
|
/// relative to now.
|
||||||
|
/**
|
||||||
|
* This function sets the expiry time associated with the stream. Stream
|
||||||
|
* operations performed after this time (where the operations cannot be
|
||||||
|
* completed using the internal buffers) will fail with the error
|
||||||
|
* asio::error::operation_aborted.
|
||||||
|
*
|
||||||
|
* @param expiry_time The expiry time to be used for the timer.
|
||||||
|
*/
|
||||||
|
void expires_from_now(const duration& expiry_time)
|
||||||
|
{
|
||||||
|
expiry_time_ = traits_helper::add(traits_helper::now(), expiry_time);
|
||||||
|
}
|
||||||
|
#endif // !defined(ASIO_NO_DEPRECATED)
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int_type underflow()
|
||||||
|
{
|
||||||
|
#if defined(ASIO_WINDOWS_RUNTIME)
|
||||||
|
ec_ = asio::error::operation_not_supported;
|
||||||
|
return traits_type::eof();
|
||||||
|
#else // defined(ASIO_WINDOWS_RUNTIME)
|
||||||
|
if (gptr() != egptr())
|
||||||
|
return traits_type::eof();
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
// Check if we are past the expiry time.
|
||||||
|
if (traits_helper::less_than(expiry_time_, traits_helper::now()))
|
||||||
|
{
|
||||||
|
ec_ = asio::error::timed_out;
|
||||||
|
return traits_type::eof();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to complete the operation without blocking.
|
||||||
|
if (!socket().native_non_blocking())
|
||||||
|
socket().native_non_blocking(true, ec_);
|
||||||
|
detail::buffer_sequence_adapter<mutable_buffer, mutable_buffer>
|
||||||
|
bufs(asio::buffer(get_buffer_) + putback_max);
|
||||||
|
detail::signed_size_type bytes = detail::socket_ops::recv(
|
||||||
|
socket().native_handle(), bufs.buffers(), bufs.count(), 0, ec_);
|
||||||
|
|
||||||
|
// Check if operation succeeded.
|
||||||
|
if (bytes > 0)
|
||||||
|
{
|
||||||
|
setg(&get_buffer_[0], &get_buffer_[0] + putback_max,
|
||||||
|
&get_buffer_[0] + putback_max + bytes);
|
||||||
|
return traits_type::to_int_type(*gptr());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for EOF.
|
||||||
|
if (bytes == 0)
|
||||||
|
{
|
||||||
|
ec_ = asio::error::eof;
|
||||||
|
return traits_type::eof();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Operation failed.
|
||||||
|
if (ec_ != asio::error::would_block
|
||||||
|
&& ec_ != asio::error::try_again)
|
||||||
|
return traits_type::eof();
|
||||||
|
|
||||||
|
// Wait for socket to become ready.
|
||||||
|
if (detail::socket_ops::poll_read(
|
||||||
|
socket().native_handle(), 0, timeout(), ec_) < 0)
|
||||||
|
return traits_type::eof();
|
||||||
|
}
|
||||||
|
#endif // defined(ASIO_WINDOWS_RUNTIME)
|
||||||
|
}
|
||||||
|
|
||||||
|
int_type overflow(int_type c)
|
||||||
|
{
|
||||||
|
#if defined(ASIO_WINDOWS_RUNTIME)
|
||||||
|
ec_ = asio::error::operation_not_supported;
|
||||||
|
return traits_type::eof();
|
||||||
|
#else // defined(ASIO_WINDOWS_RUNTIME)
|
||||||
|
char_type ch = traits_type::to_char_type(c);
|
||||||
|
|
||||||
|
// Determine what needs to be sent.
|
||||||
|
const_buffer output_buffer;
|
||||||
|
if (put_buffer_.empty())
|
||||||
|
{
|
||||||
|
if (traits_type::eq_int_type(c, traits_type::eof()))
|
||||||
|
return traits_type::not_eof(c); // Nothing to do.
|
||||||
|
output_buffer = asio::buffer(&ch, sizeof(char_type));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
output_buffer = asio::buffer(pbase(),
|
||||||
|
(pptr() - pbase()) * sizeof(char_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
while (output_buffer.size() > 0)
|
||||||
|
{
|
||||||
|
// Check if we are past the expiry time.
|
||||||
|
if (traits_helper::less_than(expiry_time_, traits_helper::now()))
|
||||||
|
{
|
||||||
|
ec_ = asio::error::timed_out;
|
||||||
|
return traits_type::eof();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to complete the operation without blocking.
|
||||||
|
if (!socket().native_non_blocking())
|
||||||
|
socket().native_non_blocking(true, ec_);
|
||||||
|
detail::buffer_sequence_adapter<
|
||||||
|
const_buffer, const_buffer> bufs(output_buffer);
|
||||||
|
detail::signed_size_type bytes = detail::socket_ops::send(
|
||||||
|
socket().native_handle(), bufs.buffers(), bufs.count(), 0, ec_);
|
||||||
|
|
||||||
|
// Check if operation succeeded.
|
||||||
|
if (bytes > 0)
|
||||||
|
{
|
||||||
|
output_buffer += static_cast<std::size_t>(bytes);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Operation failed.
|
||||||
|
if (ec_ != asio::error::would_block
|
||||||
|
&& ec_ != asio::error::try_again)
|
||||||
|
return traits_type::eof();
|
||||||
|
|
||||||
|
// Wait for socket to become ready.
|
||||||
|
if (detail::socket_ops::poll_write(
|
||||||
|
socket().native_handle(), 0, timeout(), ec_) < 0)
|
||||||
|
return traits_type::eof();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!put_buffer_.empty())
|
||||||
|
{
|
||||||
|
setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size());
|
||||||
|
|
||||||
|
// If the new character is eof then our work here is done.
|
||||||
|
if (traits_type::eq_int_type(c, traits_type::eof()))
|
||||||
|
return traits_type::not_eof(c);
|
||||||
|
|
||||||
|
// Add the new character to the output buffer.
|
||||||
|
*pptr() = ch;
|
||||||
|
pbump(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return c;
|
||||||
|
#endif // defined(ASIO_WINDOWS_RUNTIME)
|
||||||
|
}
|
||||||
|
|
||||||
|
int sync()
|
||||||
|
{
|
||||||
|
return overflow(traits_type::eof());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::streambuf* setbuf(char_type* s, std::streamsize n)
|
||||||
|
{
|
||||||
|
if (pptr() == pbase() && s == 0 && n == 0)
|
||||||
|
{
|
||||||
|
put_buffer_.clear();
|
||||||
|
setp(0, 0);
|
||||||
|
sync();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Disallow copying and assignment.
|
||||||
|
basic_socket_streambuf(const basic_socket_streambuf&) ASIO_DELETED;
|
||||||
|
basic_socket_streambuf& operator=(
|
||||||
|
const basic_socket_streambuf&) ASIO_DELETED;
|
||||||
|
|
||||||
|
void init_buffers()
|
||||||
|
{
|
||||||
|
setg(&get_buffer_[0],
|
||||||
|
&get_buffer_[0] + putback_max,
|
||||||
|
&get_buffer_[0] + putback_max);
|
||||||
|
|
||||||
|
if (put_buffer_.empty())
|
||||||
|
setp(0, 0);
|
||||||
|
else
|
||||||
|
setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
int timeout() const
|
||||||
|
{
|
||||||
|
int64_t msec = traits_helper::to_posix_duration(
|
||||||
|
traits_helper::subtract(expiry_time_,
|
||||||
|
traits_helper::now())).total_milliseconds();
|
||||||
|
if (msec > (std::numeric_limits<int>::max)())
|
||||||
|
msec = (std::numeric_limits<int>::max)();
|
||||||
|
else if (msec < 0)
|
||||||
|
msec = 0;
|
||||||
|
return static_cast<int>(msec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename EndpointSequence>
|
||||||
|
void connect_to_endpoints(const EndpointSequence& endpoints)
|
||||||
|
{
|
||||||
|
this->connect_to_endpoints(endpoints.begin(), endpoints.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename EndpointIterator>
|
||||||
|
void connect_to_endpoints(EndpointIterator begin, EndpointIterator end)
|
||||||
|
{
|
||||||
|
#if defined(ASIO_WINDOWS_RUNTIME)
|
||||||
|
ec_ = asio::error::operation_not_supported;
|
||||||
|
#else // defined(ASIO_WINDOWS_RUNTIME)
|
||||||
|
if (ec_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ec_ = asio::error::not_found;
|
||||||
|
for (EndpointIterator i = begin; i != end; ++i)
|
||||||
|
{
|
||||||
|
// Check if we are past the expiry time.
|
||||||
|
if (traits_helper::less_than(expiry_time_, traits_helper::now()))
|
||||||
|
{
|
||||||
|
ec_ = asio::error::timed_out;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close and reopen the socket.
|
||||||
|
typename Protocol::endpoint ep(*i);
|
||||||
|
socket().close(ec_);
|
||||||
|
socket().open(ep.protocol(), ec_);
|
||||||
|
if (ec_)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Try to complete the operation without blocking.
|
||||||
|
if (!socket().native_non_blocking())
|
||||||
|
socket().native_non_blocking(true, ec_);
|
||||||
|
detail::socket_ops::connect(socket().native_handle(),
|
||||||
|
ep.data(), ep.size(), ec_);
|
||||||
|
|
||||||
|
// Check if operation succeeded.
|
||||||
|
if (!ec_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Operation failed.
|
||||||
|
if (ec_ != asio::error::in_progress
|
||||||
|
&& ec_ != asio::error::would_block)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Wait for socket to become ready.
|
||||||
|
if (detail::socket_ops::poll_connect(
|
||||||
|
socket().native_handle(), timeout(), ec_) < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Get the error code from the connect operation.
|
||||||
|
int connect_error = 0;
|
||||||
|
size_t connect_error_len = sizeof(connect_error);
|
||||||
|
if (detail::socket_ops::getsockopt(socket().native_handle(), 0,
|
||||||
|
SOL_SOCKET, SO_ERROR, &connect_error, &connect_error_len, ec_)
|
||||||
|
== detail::socket_error_retval)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Check the result of the connect operation.
|
||||||
|
ec_ = asio::error_code(connect_error,
|
||||||
|
asio::error::get_system_category());
|
||||||
|
if (!ec_)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif // defined(ASIO_WINDOWS_RUNTIME)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to get the maximum expiry time.
|
||||||
|
static time_point max_expiry_time()
|
||||||
|
{
|
||||||
|
#if defined(ASIO_HAS_BOOST_DATE_TIME) \
|
||||||
|
&& defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
|
||||||
|
return boost::posix_time::pos_infin;
|
||||||
|
#else // defined(ASIO_HAS_BOOST_DATE_TIME)
|
||||||
|
// && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
|
||||||
|
return (time_point::max)();
|
||||||
|
#endif // defined(ASIO_HAS_BOOST_DATE_TIME)
|
||||||
|
// && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum { putback_max = 8 };
|
||||||
|
asio::error_code ec_;
|
||||||
|
time_point expiry_time_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace asio
|
||||||
|
|
||||||
|
#include "asio/detail/pop_options.hpp"
|
||||||
|
|
||||||
|
#if !defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
# undef ASIO_SVC_T1
|
||||||
|
#endif // !defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
|
||||||
|
#if !defined(ASIO_HAS_VARIADIC_TEMPLATES)
|
||||||
|
# undef ASIO_PRIVATE_CONNECT_DEF
|
||||||
|
#endif // !defined(ASIO_HAS_VARIADIC_TEMPLATES)
|
||||||
|
|
||||||
|
#endif // !defined(ASIO_NO_IOSTREAM)
|
||||||
|
|
||||||
|
#endif // ASIO_BASIC_SOCKET_STREAMBUF_HPP
|
@ -0,0 +1,921 @@
|
|||||||
|
//
|
||||||
|
// basic_stream_socket.hpp
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ASIO_BASIC_STREAM_SOCKET_HPP
|
||||||
|
#define ASIO_BASIC_STREAM_SOCKET_HPP
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
# pragma once
|
||||||
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
|
||||||
|
#include "asio/detail/config.hpp"
|
||||||
|
#include <cstddef>
|
||||||
|
#include "asio/async_result.hpp"
|
||||||
|
#include "asio/basic_socket.hpp"
|
||||||
|
#include "asio/detail/handler_type_requirements.hpp"
|
||||||
|
#include "asio/detail/throw_error.hpp"
|
||||||
|
#include "asio/error.hpp"
|
||||||
|
|
||||||
|
#if defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
# include "asio/stream_socket_service.hpp"
|
||||||
|
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
|
||||||
|
#include "asio/detail/push_options.hpp"
|
||||||
|
|
||||||
|
namespace asio {
|
||||||
|
|
||||||
|
/// Provides stream-oriented socket functionality.
|
||||||
|
/**
|
||||||
|
* The basic_stream_socket class template provides asynchronous and blocking
|
||||||
|
* stream-oriented socket functionality.
|
||||||
|
*
|
||||||
|
* @par Thread Safety
|
||||||
|
* @e Distinct @e objects: Safe.@n
|
||||||
|
* @e Shared @e objects: Unsafe.
|
||||||
|
*
|
||||||
|
* @par Concepts:
|
||||||
|
* AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream.
|
||||||
|
*/
|
||||||
|
template <typename Protocol
|
||||||
|
ASIO_SVC_TPARAM_DEF1(= stream_socket_service<Protocol>)>
|
||||||
|
class basic_stream_socket
|
||||||
|
: public basic_socket<Protocol ASIO_SVC_TARG>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// The native representation of a socket.
|
||||||
|
#if defined(GENERATING_DOCUMENTATION)
|
||||||
|
typedef implementation_defined native_handle_type;
|
||||||
|
#else
|
||||||
|
typedef typename basic_socket<
|
||||||
|
Protocol ASIO_SVC_TARG>::native_handle_type native_handle_type;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// The protocol type.
|
||||||
|
typedef Protocol protocol_type;
|
||||||
|
|
||||||
|
/// The endpoint type.
|
||||||
|
typedef typename Protocol::endpoint endpoint_type;
|
||||||
|
|
||||||
|
/// Construct a basic_stream_socket without opening it.
|
||||||
|
/**
|
||||||
|
* This constructor creates a stream socket without opening it. The socket
|
||||||
|
* needs to be opened and then connected or accepted before data can be sent
|
||||||
|
* or received on it.
|
||||||
|
*
|
||||||
|
* @param io_context The io_context object that the stream socket will use to
|
||||||
|
* dispatch handlers for any asynchronous operations performed on the socket.
|
||||||
|
*/
|
||||||
|
explicit basic_stream_socket(asio::io_context& io_context)
|
||||||
|
: basic_socket<Protocol ASIO_SVC_TARG>(io_context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct and open a basic_stream_socket.
|
||||||
|
/**
|
||||||
|
* This constructor creates and opens a stream socket. The socket needs to be
|
||||||
|
* connected or accepted before data can be sent or received on it.
|
||||||
|
*
|
||||||
|
* @param io_context The io_context object that the stream socket will use to
|
||||||
|
* dispatch handlers for any asynchronous operations performed on the socket.
|
||||||
|
*
|
||||||
|
* @param protocol An object specifying protocol parameters to be used.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*/
|
||||||
|
basic_stream_socket(asio::io_context& io_context,
|
||||||
|
const protocol_type& protocol)
|
||||||
|
: basic_socket<Protocol ASIO_SVC_TARG>(io_context, protocol)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a basic_stream_socket, opening it and binding it to the given
|
||||||
|
/// local endpoint.
|
||||||
|
/**
|
||||||
|
* This constructor creates a stream socket and automatically opens it bound
|
||||||
|
* to the specified endpoint on the local machine. The protocol used is the
|
||||||
|
* protocol associated with the given endpoint.
|
||||||
|
*
|
||||||
|
* @param io_context The io_context object that the stream socket will use to
|
||||||
|
* dispatch handlers for any asynchronous operations performed on the socket.
|
||||||
|
*
|
||||||
|
* @param endpoint An endpoint on the local machine to which the stream
|
||||||
|
* socket will be bound.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*/
|
||||||
|
basic_stream_socket(asio::io_context& io_context,
|
||||||
|
const endpoint_type& endpoint)
|
||||||
|
: basic_socket<Protocol ASIO_SVC_TARG>(io_context, endpoint)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a basic_stream_socket on an existing native socket.
|
||||||
|
/**
|
||||||
|
* This constructor creates a stream socket object to hold an existing native
|
||||||
|
* socket.
|
||||||
|
*
|
||||||
|
* @param io_context The io_context object that the stream socket will use to
|
||||||
|
* dispatch handlers for any asynchronous operations performed on the socket.
|
||||||
|
*
|
||||||
|
* @param protocol An object specifying protocol parameters to be used.
|
||||||
|
*
|
||||||
|
* @param native_socket The new underlying socket implementation.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*/
|
||||||
|
basic_stream_socket(asio::io_context& io_context,
|
||||||
|
const protocol_type& protocol, const native_handle_type& native_socket)
|
||||||
|
: basic_socket<Protocol ASIO_SVC_TARG>(
|
||||||
|
io_context, protocol, native_socket)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
/// Move-construct a basic_stream_socket from another.
|
||||||
|
/**
|
||||||
|
* This constructor moves a stream socket from one object to another.
|
||||||
|
*
|
||||||
|
* @param other The other basic_stream_socket object from which the move
|
||||||
|
* will occur.
|
||||||
|
*
|
||||||
|
* @note Following the move, the moved-from object is in the same state as if
|
||||||
|
* constructed using the @c basic_stream_socket(io_context&) constructor.
|
||||||
|
*/
|
||||||
|
basic_stream_socket(basic_stream_socket&& other)
|
||||||
|
: basic_socket<Protocol ASIO_SVC_TARG>(std::move(other))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move-assign a basic_stream_socket from another.
|
||||||
|
/**
|
||||||
|
* This assignment operator moves a stream socket from one object to another.
|
||||||
|
*
|
||||||
|
* @param other The other basic_stream_socket object from which the move
|
||||||
|
* will occur.
|
||||||
|
*
|
||||||
|
* @note Following the move, the moved-from object is in the same state as if
|
||||||
|
* constructed using the @c basic_stream_socket(io_context&) constructor.
|
||||||
|
*/
|
||||||
|
basic_stream_socket& operator=(basic_stream_socket&& other)
|
||||||
|
{
|
||||||
|
basic_socket<Protocol ASIO_SVC_TARG>::operator=(std::move(other));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move-construct a basic_stream_socket from a socket of another protocol
|
||||||
|
/// type.
|
||||||
|
/**
|
||||||
|
* This constructor moves a stream socket from one object to another.
|
||||||
|
*
|
||||||
|
* @param other The other basic_stream_socket object from which the move
|
||||||
|
* will occur.
|
||||||
|
*
|
||||||
|
* @note Following the move, the moved-from object is in the same state as if
|
||||||
|
* constructed using the @c basic_stream_socket(io_context&) constructor.
|
||||||
|
*/
|
||||||
|
template <typename Protocol1 ASIO_SVC_TPARAM1>
|
||||||
|
basic_stream_socket(
|
||||||
|
basic_stream_socket<Protocol1 ASIO_SVC_TARG1>&& other,
|
||||||
|
typename enable_if<is_convertible<Protocol1, Protocol>::value>::type* = 0)
|
||||||
|
: basic_socket<Protocol ASIO_SVC_TARG>(std::move(other))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move-assign a basic_stream_socket from a socket of another protocol type.
|
||||||
|
/**
|
||||||
|
* This assignment operator moves a stream socket from one object to another.
|
||||||
|
*
|
||||||
|
* @param other The other basic_stream_socket object from which the move
|
||||||
|
* will occur.
|
||||||
|
*
|
||||||
|
* @note Following the move, the moved-from object is in the same state as if
|
||||||
|
* constructed using the @c basic_stream_socket(io_context&) constructor.
|
||||||
|
*/
|
||||||
|
template <typename Protocol1 ASIO_SVC_TPARAM1>
|
||||||
|
typename enable_if<is_convertible<Protocol1, Protocol>::value,
|
||||||
|
basic_stream_socket>::type& operator=(
|
||||||
|
basic_stream_socket<Protocol1 ASIO_SVC_TARG1>&& other)
|
||||||
|
{
|
||||||
|
basic_socket<Protocol ASIO_SVC_TARG>::operator=(std::move(other));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
/// Destroys the socket.
|
||||||
|
/**
|
||||||
|
* This function destroys the socket, cancelling any outstanding asynchronous
|
||||||
|
* operations associated with the socket as if by calling @c cancel.
|
||||||
|
*/
|
||||||
|
~basic_stream_socket()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send some data on the socket.
|
||||||
|
/**
|
||||||
|
* This function is used to send data on the stream socket. The function
|
||||||
|
* call will block until one or more bytes of the data has been sent
|
||||||
|
* successfully, or an until error occurs.
|
||||||
|
*
|
||||||
|
* @param buffers One or more data buffers to be sent on the socket.
|
||||||
|
*
|
||||||
|
* @returns The number of bytes sent.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*
|
||||||
|
* @note The send operation may not transmit all of the data to the peer.
|
||||||
|
* Consider using the @ref write function if you need to ensure that all data
|
||||||
|
* is written before the blocking operation completes.
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* To send a single data buffer use the @ref buffer function as follows:
|
||||||
|
* @code
|
||||||
|
* socket.send(asio::buffer(data, size));
|
||||||
|
* @endcode
|
||||||
|
* See the @ref buffer documentation for information on sending multiple
|
||||||
|
* buffers in one go, and how to use it with arrays, boost::array or
|
||||||
|
* std::vector.
|
||||||
|
*/
|
||||||
|
template <typename ConstBufferSequence>
|
||||||
|
std::size_t send(const ConstBufferSequence& buffers)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
std::size_t s = this->get_service().send(
|
||||||
|
this->get_implementation(), buffers, 0, ec);
|
||||||
|
asio::detail::throw_error(ec, "send");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send some data on the socket.
|
||||||
|
/**
|
||||||
|
* This function is used to send data on the stream socket. The function
|
||||||
|
* call will block until one or more bytes of the data has been sent
|
||||||
|
* successfully, or an until error occurs.
|
||||||
|
*
|
||||||
|
* @param buffers One or more data buffers to be sent on the socket.
|
||||||
|
*
|
||||||
|
* @param flags Flags specifying how the send call is to be made.
|
||||||
|
*
|
||||||
|
* @returns The number of bytes sent.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*
|
||||||
|
* @note The send operation may not transmit all of the data to the peer.
|
||||||
|
* Consider using the @ref write function if you need to ensure that all data
|
||||||
|
* is written before the blocking operation completes.
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* To send a single data buffer use the @ref buffer function as follows:
|
||||||
|
* @code
|
||||||
|
* socket.send(asio::buffer(data, size), 0);
|
||||||
|
* @endcode
|
||||||
|
* See the @ref buffer documentation for information on sending multiple
|
||||||
|
* buffers in one go, and how to use it with arrays, boost::array or
|
||||||
|
* std::vector.
|
||||||
|
*/
|
||||||
|
template <typename ConstBufferSequence>
|
||||||
|
std::size_t send(const ConstBufferSequence& buffers,
|
||||||
|
socket_base::message_flags flags)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
std::size_t s = this->get_service().send(
|
||||||
|
this->get_implementation(), buffers, flags, ec);
|
||||||
|
asio::detail::throw_error(ec, "send");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send some data on the socket.
|
||||||
|
/**
|
||||||
|
* This function is used to send data on the stream socket. The function
|
||||||
|
* call will block until one or more bytes of the data has been sent
|
||||||
|
* successfully, or an until error occurs.
|
||||||
|
*
|
||||||
|
* @param buffers One or more data buffers to be sent on the socket.
|
||||||
|
*
|
||||||
|
* @param flags Flags specifying how the send call is to be made.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*
|
||||||
|
* @returns The number of bytes sent. Returns 0 if an error occurred.
|
||||||
|
*
|
||||||
|
* @note The send operation may not transmit all of the data to the peer.
|
||||||
|
* Consider using the @ref write function if you need to ensure that all data
|
||||||
|
* is written before the blocking operation completes.
|
||||||
|
*/
|
||||||
|
template <typename ConstBufferSequence>
|
||||||
|
std::size_t send(const ConstBufferSequence& buffers,
|
||||||
|
socket_base::message_flags flags, asio::error_code& ec)
|
||||||
|
{
|
||||||
|
return this->get_service().send(
|
||||||
|
this->get_implementation(), buffers, flags, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start an asynchronous send.
|
||||||
|
/**
|
||||||
|
* This function is used to asynchronously send data on the stream socket.
|
||||||
|
* The function call always returns immediately.
|
||||||
|
*
|
||||||
|
* @param buffers One or more data buffers to be sent on the socket. Although
|
||||||
|
* the buffers object may be copied as necessary, ownership of the underlying
|
||||||
|
* memory blocks is retained by the caller, which must guarantee that they
|
||||||
|
* remain valid until the handler is called.
|
||||||
|
*
|
||||||
|
* @param handler The handler to be called when the send operation completes.
|
||||||
|
* Copies will be made of the handler as required. The function signature of
|
||||||
|
* the handler must be:
|
||||||
|
* @code void handler(
|
||||||
|
* const asio::error_code& error, // Result of operation.
|
||||||
|
* std::size_t bytes_transferred // Number of bytes sent.
|
||||||
|
* ); @endcode
|
||||||
|
* Regardless of whether the asynchronous operation completes immediately or
|
||||||
|
* not, the handler will not be invoked from within this function. Invocation
|
||||||
|
* of the handler will be performed in a manner equivalent to using
|
||||||
|
* asio::io_context::post().
|
||||||
|
*
|
||||||
|
* @note The send operation may not transmit all of the data to the peer.
|
||||||
|
* Consider using the @ref async_write function if you need to ensure that all
|
||||||
|
* data is written before the asynchronous operation completes.
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* To send a single data buffer use the @ref buffer function as follows:
|
||||||
|
* @code
|
||||||
|
* socket.async_send(asio::buffer(data, size), handler);
|
||||||
|
* @endcode
|
||||||
|
* See the @ref buffer documentation for information on sending multiple
|
||||||
|
* buffers in one go, and how to use it with arrays, boost::array or
|
||||||
|
* std::vector.
|
||||||
|
*/
|
||||||
|
template <typename ConstBufferSequence, typename WriteHandler>
|
||||||
|
ASIO_INITFN_RESULT_TYPE(WriteHandler,
|
||||||
|
void (asio::error_code, std::size_t))
|
||||||
|
async_send(const ConstBufferSequence& buffers,
|
||||||
|
ASIO_MOVE_ARG(WriteHandler) handler)
|
||||||
|
{
|
||||||
|
// If you get an error on the following line it means that your handler does
|
||||||
|
// not meet the documented type requirements for a WriteHandler.
|
||||||
|
ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;
|
||||||
|
|
||||||
|
#if defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
return this->get_service().async_send(
|
||||||
|
this->get_implementation(), buffers, 0,
|
||||||
|
ASIO_MOVE_CAST(WriteHandler)(handler));
|
||||||
|
#else // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
async_completion<WriteHandler,
|
||||||
|
void (asio::error_code, std::size_t)> init(handler);
|
||||||
|
|
||||||
|
this->get_service().async_send(
|
||||||
|
this->get_implementation(), buffers, 0,
|
||||||
|
init.completion_handler);
|
||||||
|
|
||||||
|
return init.result.get();
|
||||||
|
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start an asynchronous send.
|
||||||
|
/**
|
||||||
|
* This function is used to asynchronously send data on the stream socket.
|
||||||
|
* The function call always returns immediately.
|
||||||
|
*
|
||||||
|
* @param buffers One or more data buffers to be sent on the socket. Although
|
||||||
|
* the buffers object may be copied as necessary, ownership of the underlying
|
||||||
|
* memory blocks is retained by the caller, which must guarantee that they
|
||||||
|
* remain valid until the handler is called.
|
||||||
|
*
|
||||||
|
* @param flags Flags specifying how the send call is to be made.
|
||||||
|
*
|
||||||
|
* @param handler The handler to be called when the send operation completes.
|
||||||
|
* Copies will be made of the handler as required. The function signature of
|
||||||
|
* the handler must be:
|
||||||
|
* @code void handler(
|
||||||
|
* const asio::error_code& error, // Result of operation.
|
||||||
|
* std::size_t bytes_transferred // Number of bytes sent.
|
||||||
|
* ); @endcode
|
||||||
|
* Regardless of whether the asynchronous operation completes immediately or
|
||||||
|
* not, the handler will not be invoked from within this function. Invocation
|
||||||
|
* of the handler will be performed in a manner equivalent to using
|
||||||
|
* asio::io_context::post().
|
||||||
|
*
|
||||||
|
* @note The send operation may not transmit all of the data to the peer.
|
||||||
|
* Consider using the @ref async_write function if you need to ensure that all
|
||||||
|
* data is written before the asynchronous operation completes.
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* To send a single data buffer use the @ref buffer function as follows:
|
||||||
|
* @code
|
||||||
|
* socket.async_send(asio::buffer(data, size), 0, handler);
|
||||||
|
* @endcode
|
||||||
|
* See the @ref buffer documentation for information on sending multiple
|
||||||
|
* buffers in one go, and how to use it with arrays, boost::array or
|
||||||
|
* std::vector.
|
||||||
|
*/
|
||||||
|
template <typename ConstBufferSequence, typename WriteHandler>
|
||||||
|
ASIO_INITFN_RESULT_TYPE(WriteHandler,
|
||||||
|
void (asio::error_code, std::size_t))
|
||||||
|
async_send(const ConstBufferSequence& buffers,
|
||||||
|
socket_base::message_flags flags,
|
||||||
|
ASIO_MOVE_ARG(WriteHandler) handler)
|
||||||
|
{
|
||||||
|
// If you get an error on the following line it means that your handler does
|
||||||
|
// not meet the documented type requirements for a WriteHandler.
|
||||||
|
ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;
|
||||||
|
|
||||||
|
#if defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
return this->get_service().async_send(
|
||||||
|
this->get_implementation(), buffers, flags,
|
||||||
|
ASIO_MOVE_CAST(WriteHandler)(handler));
|
||||||
|
#else // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
async_completion<WriteHandler,
|
||||||
|
void (asio::error_code, std::size_t)> init(handler);
|
||||||
|
|
||||||
|
this->get_service().async_send(
|
||||||
|
this->get_implementation(), buffers, flags,
|
||||||
|
init.completion_handler);
|
||||||
|
|
||||||
|
return init.result.get();
|
||||||
|
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Receive some data on the socket.
|
||||||
|
/**
|
||||||
|
* This function is used to receive data on the stream socket. The function
|
||||||
|
* call will block until one or more bytes of data has been received
|
||||||
|
* successfully, or until an error occurs.
|
||||||
|
*
|
||||||
|
* @param buffers One or more buffers into which the data will be received.
|
||||||
|
*
|
||||||
|
* @returns The number of bytes received.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure. An error code of
|
||||||
|
* asio::error::eof indicates that the connection was closed by the
|
||||||
|
* peer.
|
||||||
|
*
|
||||||
|
* @note The receive operation may not receive all of the requested number of
|
||||||
|
* bytes. Consider using the @ref read function if you need to ensure that the
|
||||||
|
* requested amount of data is read before the blocking operation completes.
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* To receive into a single data buffer use the @ref buffer function as
|
||||||
|
* follows:
|
||||||
|
* @code
|
||||||
|
* socket.receive(asio::buffer(data, size));
|
||||||
|
* @endcode
|
||||||
|
* See the @ref buffer documentation for information on receiving into
|
||||||
|
* multiple buffers in one go, and how to use it with arrays, boost::array or
|
||||||
|
* std::vector.
|
||||||
|
*/
|
||||||
|
template <typename MutableBufferSequence>
|
||||||
|
std::size_t receive(const MutableBufferSequence& buffers)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
std::size_t s = this->get_service().receive(
|
||||||
|
this->get_implementation(), buffers, 0, ec);
|
||||||
|
asio::detail::throw_error(ec, "receive");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Receive some data on the socket.
|
||||||
|
/**
|
||||||
|
* This function is used to receive data on the stream socket. The function
|
||||||
|
* call will block until one or more bytes of data has been received
|
||||||
|
* successfully, or until an error occurs.
|
||||||
|
*
|
||||||
|
* @param buffers One or more buffers into which the data will be received.
|
||||||
|
*
|
||||||
|
* @param flags Flags specifying how the receive call is to be made.
|
||||||
|
*
|
||||||
|
* @returns The number of bytes received.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure. An error code of
|
||||||
|
* asio::error::eof indicates that the connection was closed by the
|
||||||
|
* peer.
|
||||||
|
*
|
||||||
|
* @note The receive operation may not receive all of the requested number of
|
||||||
|
* bytes. Consider using the @ref read function if you need to ensure that the
|
||||||
|
* requested amount of data is read before the blocking operation completes.
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* To receive into a single data buffer use the @ref buffer function as
|
||||||
|
* follows:
|
||||||
|
* @code
|
||||||
|
* socket.receive(asio::buffer(data, size), 0);
|
||||||
|
* @endcode
|
||||||
|
* See the @ref buffer documentation for information on receiving into
|
||||||
|
* multiple buffers in one go, and how to use it with arrays, boost::array or
|
||||||
|
* std::vector.
|
||||||
|
*/
|
||||||
|
template <typename MutableBufferSequence>
|
||||||
|
std::size_t receive(const MutableBufferSequence& buffers,
|
||||||
|
socket_base::message_flags flags)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
std::size_t s = this->get_service().receive(
|
||||||
|
this->get_implementation(), buffers, flags, ec);
|
||||||
|
asio::detail::throw_error(ec, "receive");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Receive some data on a connected socket.
|
||||||
|
/**
|
||||||
|
* This function is used to receive data on the stream socket. The function
|
||||||
|
* call will block until one or more bytes of data has been received
|
||||||
|
* successfully, or until an error occurs.
|
||||||
|
*
|
||||||
|
* @param buffers One or more buffers into which the data will be received.
|
||||||
|
*
|
||||||
|
* @param flags Flags specifying how the receive call is to be made.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*
|
||||||
|
* @returns The number of bytes received. Returns 0 if an error occurred.
|
||||||
|
*
|
||||||
|
* @note The receive operation may not receive all of the requested number of
|
||||||
|
* bytes. Consider using the @ref read function if you need to ensure that the
|
||||||
|
* requested amount of data is read before the blocking operation completes.
|
||||||
|
*/
|
||||||
|
template <typename MutableBufferSequence>
|
||||||
|
std::size_t receive(const MutableBufferSequence& buffers,
|
||||||
|
socket_base::message_flags flags, asio::error_code& ec)
|
||||||
|
{
|
||||||
|
return this->get_service().receive(
|
||||||
|
this->get_implementation(), buffers, flags, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start an asynchronous receive.
|
||||||
|
/**
|
||||||
|
* This function is used to asynchronously receive data from the stream
|
||||||
|
* socket. The function call always returns immediately.
|
||||||
|
*
|
||||||
|
* @param buffers One or more buffers into which the data will be received.
|
||||||
|
* Although the buffers object may be copied as necessary, ownership of the
|
||||||
|
* underlying memory blocks is retained by the caller, which must guarantee
|
||||||
|
* that they remain valid until the handler is called.
|
||||||
|
*
|
||||||
|
* @param handler The handler to be called when the receive operation
|
||||||
|
* completes. Copies will be made of the handler as required. The function
|
||||||
|
* signature of the handler must be:
|
||||||
|
* @code void handler(
|
||||||
|
* const asio::error_code& error, // Result of operation.
|
||||||
|
* std::size_t bytes_transferred // Number of bytes received.
|
||||||
|
* ); @endcode
|
||||||
|
* Regardless of whether the asynchronous operation completes immediately or
|
||||||
|
* not, the handler will not be invoked from within this function. Invocation
|
||||||
|
* of the handler will be performed in a manner equivalent to using
|
||||||
|
* asio::io_context::post().
|
||||||
|
*
|
||||||
|
* @note The receive operation may not receive all of the requested number of
|
||||||
|
* bytes. Consider using the @ref async_read function if you need to ensure
|
||||||
|
* that the requested amount of data is received before the asynchronous
|
||||||
|
* operation completes.
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* To receive into a single data buffer use the @ref buffer function as
|
||||||
|
* follows:
|
||||||
|
* @code
|
||||||
|
* socket.async_receive(asio::buffer(data, size), handler);
|
||||||
|
* @endcode
|
||||||
|
* See the @ref buffer documentation for information on receiving into
|
||||||
|
* multiple buffers in one go, and how to use it with arrays, boost::array or
|
||||||
|
* std::vector.
|
||||||
|
*/
|
||||||
|
template <typename MutableBufferSequence, typename ReadHandler>
|
||||||
|
ASIO_INITFN_RESULT_TYPE(ReadHandler,
|
||||||
|
void (asio::error_code, std::size_t))
|
||||||
|
async_receive(const MutableBufferSequence& buffers,
|
||||||
|
ASIO_MOVE_ARG(ReadHandler) handler)
|
||||||
|
{
|
||||||
|
// If you get an error on the following line it means that your handler does
|
||||||
|
// not meet the documented type requirements for a ReadHandler.
|
||||||
|
ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
|
||||||
|
|
||||||
|
#if defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
return this->get_service().async_receive(this->get_implementation(),
|
||||||
|
buffers, 0, ASIO_MOVE_CAST(ReadHandler)(handler));
|
||||||
|
#else // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
async_completion<ReadHandler,
|
||||||
|
void (asio::error_code, std::size_t)> init(handler);
|
||||||
|
|
||||||
|
this->get_service().async_receive(this->get_implementation(),
|
||||||
|
buffers, 0, init.completion_handler);
|
||||||
|
|
||||||
|
return init.result.get();
|
||||||
|
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start an asynchronous receive.
|
||||||
|
/**
|
||||||
|
* This function is used to asynchronously receive data from the stream
|
||||||
|
* socket. The function call always returns immediately.
|
||||||
|
*
|
||||||
|
* @param buffers One or more buffers into which the data will be received.
|
||||||
|
* Although the buffers object may be copied as necessary, ownership of the
|
||||||
|
* underlying memory blocks is retained by the caller, which must guarantee
|
||||||
|
* that they remain valid until the handler is called.
|
||||||
|
*
|
||||||
|
* @param flags Flags specifying how the receive call is to be made.
|
||||||
|
*
|
||||||
|
* @param handler The handler to be called when the receive operation
|
||||||
|
* completes. Copies will be made of the handler as required. The function
|
||||||
|
* signature of the handler must be:
|
||||||
|
* @code void handler(
|
||||||
|
* const asio::error_code& error, // Result of operation.
|
||||||
|
* std::size_t bytes_transferred // Number of bytes received.
|
||||||
|
* ); @endcode
|
||||||
|
* Regardless of whether the asynchronous operation completes immediately or
|
||||||
|
* not, the handler will not be invoked from within this function. Invocation
|
||||||
|
* of the handler will be performed in a manner equivalent to using
|
||||||
|
* asio::io_context::post().
|
||||||
|
*
|
||||||
|
* @note The receive operation may not receive all of the requested number of
|
||||||
|
* bytes. Consider using the @ref async_read function if you need to ensure
|
||||||
|
* that the requested amount of data is received before the asynchronous
|
||||||
|
* operation completes.
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* To receive into a single data buffer use the @ref buffer function as
|
||||||
|
* follows:
|
||||||
|
* @code
|
||||||
|
* socket.async_receive(asio::buffer(data, size), 0, handler);
|
||||||
|
* @endcode
|
||||||
|
* See the @ref buffer documentation for information on receiving into
|
||||||
|
* multiple buffers in one go, and how to use it with arrays, boost::array or
|
||||||
|
* std::vector.
|
||||||
|
*/
|
||||||
|
template <typename MutableBufferSequence, typename ReadHandler>
|
||||||
|
ASIO_INITFN_RESULT_TYPE(ReadHandler,
|
||||||
|
void (asio::error_code, std::size_t))
|
||||||
|
async_receive(const MutableBufferSequence& buffers,
|
||||||
|
socket_base::message_flags flags,
|
||||||
|
ASIO_MOVE_ARG(ReadHandler) handler)
|
||||||
|
{
|
||||||
|
// If you get an error on the following line it means that your handler does
|
||||||
|
// not meet the documented type requirements for a ReadHandler.
|
||||||
|
ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
|
||||||
|
|
||||||
|
#if defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
return this->get_service().async_receive(this->get_implementation(),
|
||||||
|
buffers, flags, ASIO_MOVE_CAST(ReadHandler)(handler));
|
||||||
|
#else // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
async_completion<ReadHandler,
|
||||||
|
void (asio::error_code, std::size_t)> init(handler);
|
||||||
|
|
||||||
|
this->get_service().async_receive(this->get_implementation(),
|
||||||
|
buffers, flags, init.completion_handler);
|
||||||
|
|
||||||
|
return init.result.get();
|
||||||
|
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write some data to the socket.
|
||||||
|
/**
|
||||||
|
* This function is used to write data to the stream socket. The function call
|
||||||
|
* will block until one or more bytes of the data has been written
|
||||||
|
* successfully, or until an error occurs.
|
||||||
|
*
|
||||||
|
* @param buffers One or more data buffers to be written to the socket.
|
||||||
|
*
|
||||||
|
* @returns The number of bytes written.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure. An error code of
|
||||||
|
* asio::error::eof indicates that the connection was closed by the
|
||||||
|
* peer.
|
||||||
|
*
|
||||||
|
* @note The write_some operation may not transmit all of the data to the
|
||||||
|
* peer. Consider using the @ref write function if you need to ensure that
|
||||||
|
* all data is written before the blocking operation completes.
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* To write a single data buffer use the @ref buffer function as follows:
|
||||||
|
* @code
|
||||||
|
* socket.write_some(asio::buffer(data, size));
|
||||||
|
* @endcode
|
||||||
|
* See the @ref buffer documentation for information on writing multiple
|
||||||
|
* buffers in one go, and how to use it with arrays, boost::array or
|
||||||
|
* std::vector.
|
||||||
|
*/
|
||||||
|
template <typename ConstBufferSequence>
|
||||||
|
std::size_t write_some(const ConstBufferSequence& buffers)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
std::size_t s = this->get_service().send(
|
||||||
|
this->get_implementation(), buffers, 0, ec);
|
||||||
|
asio::detail::throw_error(ec, "write_some");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write some data to the socket.
|
||||||
|
/**
|
||||||
|
* This function is used to write data to the stream socket. The function call
|
||||||
|
* will block until one or more bytes of the data has been written
|
||||||
|
* successfully, or until an error occurs.
|
||||||
|
*
|
||||||
|
* @param buffers One or more data buffers to be written to the socket.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*
|
||||||
|
* @returns The number of bytes written. Returns 0 if an error occurred.
|
||||||
|
*
|
||||||
|
* @note The write_some operation may not transmit all of the data to the
|
||||||
|
* peer. Consider using the @ref write function if you need to ensure that
|
||||||
|
* all data is written before the blocking operation completes.
|
||||||
|
*/
|
||||||
|
template <typename ConstBufferSequence>
|
||||||
|
std::size_t write_some(const ConstBufferSequence& buffers,
|
||||||
|
asio::error_code& ec)
|
||||||
|
{
|
||||||
|
return this->get_service().send(this->get_implementation(), buffers, 0, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start an asynchronous write.
|
||||||
|
/**
|
||||||
|
* This function is used to asynchronously write data to the stream socket.
|
||||||
|
* The function call always returns immediately.
|
||||||
|
*
|
||||||
|
* @param buffers One or more data buffers to be written to the socket.
|
||||||
|
* Although the buffers object may be copied as necessary, ownership of the
|
||||||
|
* underlying memory blocks is retained by the caller, which must guarantee
|
||||||
|
* that they remain valid until the handler is called.
|
||||||
|
*
|
||||||
|
* @param handler The handler to be called when the write operation completes.
|
||||||
|
* Copies will be made of the handler as required. The function signature of
|
||||||
|
* the handler must be:
|
||||||
|
* @code void handler(
|
||||||
|
* const asio::error_code& error, // Result of operation.
|
||||||
|
* std::size_t bytes_transferred // Number of bytes written.
|
||||||
|
* ); @endcode
|
||||||
|
* Regardless of whether the asynchronous operation completes immediately or
|
||||||
|
* not, the handler will not be invoked from within this function. Invocation
|
||||||
|
* of the handler will be performed in a manner equivalent to using
|
||||||
|
* asio::io_context::post().
|
||||||
|
*
|
||||||
|
* @note The write operation may not transmit all of the data to the peer.
|
||||||
|
* Consider using the @ref async_write function if you need to ensure that all
|
||||||
|
* data is written before the asynchronous operation completes.
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* To write a single data buffer use the @ref buffer function as follows:
|
||||||
|
* @code
|
||||||
|
* socket.async_write_some(asio::buffer(data, size), handler);
|
||||||
|
* @endcode
|
||||||
|
* See the @ref buffer documentation for information on writing multiple
|
||||||
|
* buffers in one go, and how to use it with arrays, boost::array or
|
||||||
|
* std::vector.
|
||||||
|
*/
|
||||||
|
template <typename ConstBufferSequence, typename WriteHandler>
|
||||||
|
ASIO_INITFN_RESULT_TYPE(WriteHandler,
|
||||||
|
void (asio::error_code, std::size_t))
|
||||||
|
async_write_some(const ConstBufferSequence& buffers,
|
||||||
|
ASIO_MOVE_ARG(WriteHandler) handler)
|
||||||
|
{
|
||||||
|
// If you get an error on the following line it means that your handler does
|
||||||
|
// not meet the documented type requirements for a WriteHandler.
|
||||||
|
ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;
|
||||||
|
|
||||||
|
#if defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
return this->get_service().async_send(this->get_implementation(),
|
||||||
|
buffers, 0, ASIO_MOVE_CAST(WriteHandler)(handler));
|
||||||
|
#else // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
async_completion<WriteHandler,
|
||||||
|
void (asio::error_code, std::size_t)> init(handler);
|
||||||
|
|
||||||
|
this->get_service().async_send(this->get_implementation(),
|
||||||
|
buffers, 0, init.completion_handler);
|
||||||
|
|
||||||
|
return init.result.get();
|
||||||
|
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read some data from the socket.
|
||||||
|
/**
|
||||||
|
* This function is used to read data from the stream socket. The function
|
||||||
|
* call will block until one or more bytes of data has been read successfully,
|
||||||
|
* or until an error occurs.
|
||||||
|
*
|
||||||
|
* @param buffers One or more buffers into which the data will be read.
|
||||||
|
*
|
||||||
|
* @returns The number of bytes read.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure. An error code of
|
||||||
|
* asio::error::eof indicates that the connection was closed by the
|
||||||
|
* peer.
|
||||||
|
*
|
||||||
|
* @note The read_some operation may not read all of the requested number of
|
||||||
|
* bytes. Consider using the @ref read function if you need to ensure that
|
||||||
|
* the requested amount of data is read before the blocking operation
|
||||||
|
* completes.
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* To read into a single data buffer use the @ref buffer function as follows:
|
||||||
|
* @code
|
||||||
|
* socket.read_some(asio::buffer(data, size));
|
||||||
|
* @endcode
|
||||||
|
* See the @ref buffer documentation for information on reading into multiple
|
||||||
|
* buffers in one go, and how to use it with arrays, boost::array or
|
||||||
|
* std::vector.
|
||||||
|
*/
|
||||||
|
template <typename MutableBufferSequence>
|
||||||
|
std::size_t read_some(const MutableBufferSequence& buffers)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
std::size_t s = this->get_service().receive(
|
||||||
|
this->get_implementation(), buffers, 0, ec);
|
||||||
|
asio::detail::throw_error(ec, "read_some");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read some data from the socket.
|
||||||
|
/**
|
||||||
|
* This function is used to read data from the stream socket. The function
|
||||||
|
* call will block until one or more bytes of data has been read successfully,
|
||||||
|
* or until an error occurs.
|
||||||
|
*
|
||||||
|
* @param buffers One or more buffers into which the data will be read.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*
|
||||||
|
* @returns The number of bytes read. Returns 0 if an error occurred.
|
||||||
|
*
|
||||||
|
* @note The read_some operation may not read all of the requested number of
|
||||||
|
* bytes. Consider using the @ref read function if you need to ensure that
|
||||||
|
* the requested amount of data is read before the blocking operation
|
||||||
|
* completes.
|
||||||
|
*/
|
||||||
|
template <typename MutableBufferSequence>
|
||||||
|
std::size_t read_some(const MutableBufferSequence& buffers,
|
||||||
|
asio::error_code& ec)
|
||||||
|
{
|
||||||
|
return this->get_service().receive(
|
||||||
|
this->get_implementation(), buffers, 0, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start an asynchronous read.
|
||||||
|
/**
|
||||||
|
* This function is used to asynchronously read data from the stream socket.
|
||||||
|
* The function call always returns immediately.
|
||||||
|
*
|
||||||
|
* @param buffers One or more buffers into which the data will be read.
|
||||||
|
* Although the buffers object may be copied as necessary, ownership of the
|
||||||
|
* underlying memory blocks is retained by the caller, which must guarantee
|
||||||
|
* that they remain valid until the handler is called.
|
||||||
|
*
|
||||||
|
* @param handler The handler to be called when the read operation completes.
|
||||||
|
* Copies will be made of the handler as required. The function signature of
|
||||||
|
* the handler must be:
|
||||||
|
* @code void handler(
|
||||||
|
* const asio::error_code& error, // Result of operation.
|
||||||
|
* std::size_t bytes_transferred // Number of bytes read.
|
||||||
|
* ); @endcode
|
||||||
|
* Regardless of whether the asynchronous operation completes immediately or
|
||||||
|
* not, the handler will not be invoked from within this function. Invocation
|
||||||
|
* of the handler will be performed in a manner equivalent to using
|
||||||
|
* asio::io_context::post().
|
||||||
|
*
|
||||||
|
* @note The read operation may not read all of the requested number of bytes.
|
||||||
|
* Consider using the @ref async_read function if you need to ensure that the
|
||||||
|
* requested amount of data is read before the asynchronous operation
|
||||||
|
* completes.
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* To read into a single data buffer use the @ref buffer function as follows:
|
||||||
|
* @code
|
||||||
|
* socket.async_read_some(asio::buffer(data, size), handler);
|
||||||
|
* @endcode
|
||||||
|
* See the @ref buffer documentation for information on reading into multiple
|
||||||
|
* buffers in one go, and how to use it with arrays, boost::array or
|
||||||
|
* std::vector.
|
||||||
|
*/
|
||||||
|
template <typename MutableBufferSequence, typename ReadHandler>
|
||||||
|
ASIO_INITFN_RESULT_TYPE(ReadHandler,
|
||||||
|
void (asio::error_code, std::size_t))
|
||||||
|
async_read_some(const MutableBufferSequence& buffers,
|
||||||
|
ASIO_MOVE_ARG(ReadHandler) handler)
|
||||||
|
{
|
||||||
|
// If you get an error on the following line it means that your handler does
|
||||||
|
// not meet the documented type requirements for a ReadHandler.
|
||||||
|
ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
|
||||||
|
|
||||||
|
#if defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
return this->get_service().async_receive(this->get_implementation(),
|
||||||
|
buffers, 0, ASIO_MOVE_CAST(ReadHandler)(handler));
|
||||||
|
#else // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
async_completion<ReadHandler,
|
||||||
|
void (asio::error_code, std::size_t)> init(handler);
|
||||||
|
|
||||||
|
this->get_service().async_receive(this->get_implementation(),
|
||||||
|
buffers, 0, init.completion_handler);
|
||||||
|
|
||||||
|
return init.result.get();
|
||||||
|
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace asio
|
||||||
|
|
||||||
|
#include "asio/detail/pop_options.hpp"
|
||||||
|
|
||||||
|
#endif // ASIO_BASIC_STREAM_SOCKET_HPP
|
452
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/asio/basic_streambuf.hpp
vendored
Normal file
452
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/asio/basic_streambuf.hpp
vendored
Normal file
@ -0,0 +1,452 @@
|
|||||||
|
//
|
||||||
|
// basic_streambuf.hpp
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ASIO_BASIC_STREAMBUF_HPP
|
||||||
|
#define ASIO_BASIC_STREAMBUF_HPP
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
# pragma once
|
||||||
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
|
||||||
|
#include "asio/detail/config.hpp"
|
||||||
|
|
||||||
|
#if !defined(ASIO_NO_IOSTREAM)
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstring>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <streambuf>
|
||||||
|
#include <vector>
|
||||||
|
#include "asio/basic_streambuf_fwd.hpp"
|
||||||
|
#include "asio/buffer.hpp"
|
||||||
|
#include "asio/detail/limits.hpp"
|
||||||
|
#include "asio/detail/noncopyable.hpp"
|
||||||
|
#include "asio/detail/throw_exception.hpp"
|
||||||
|
|
||||||
|
#include "asio/detail/push_options.hpp"
|
||||||
|
|
||||||
|
namespace asio {
|
||||||
|
|
||||||
|
/// Automatically resizable buffer class based on std::streambuf.
|
||||||
|
/**
|
||||||
|
* The @c basic_streambuf class is derived from @c std::streambuf to associate
|
||||||
|
* the streambuf's input and output sequences with one or more character
|
||||||
|
* arrays. These character arrays are internal to the @c basic_streambuf
|
||||||
|
* object, but direct access to the array elements is provided to permit them
|
||||||
|
* to be used efficiently with I/O operations. Characters written to the output
|
||||||
|
* sequence of a @c basic_streambuf object are appended to the input sequence
|
||||||
|
* of the same object.
|
||||||
|
*
|
||||||
|
* The @c basic_streambuf class's public interface is intended to permit the
|
||||||
|
* following implementation strategies:
|
||||||
|
*
|
||||||
|
* @li A single contiguous character array, which is reallocated as necessary
|
||||||
|
* to accommodate changes in the size of the character sequence. This is the
|
||||||
|
* implementation approach currently used in Asio.
|
||||||
|
*
|
||||||
|
* @li A sequence of one or more character arrays, where each array is of the
|
||||||
|
* same size. Additional character array objects are appended to the sequence
|
||||||
|
* to accommodate changes in the size of the character sequence.
|
||||||
|
*
|
||||||
|
* @li A sequence of one or more character arrays of varying sizes. Additional
|
||||||
|
* character array objects are appended to the sequence to accommodate changes
|
||||||
|
* in the size of the character sequence.
|
||||||
|
*
|
||||||
|
* The constructor for basic_streambuf accepts a @c size_t argument specifying
|
||||||
|
* the maximum of the sum of the sizes of the input sequence and output
|
||||||
|
* sequence. During the lifetime of the @c basic_streambuf object, the following
|
||||||
|
* invariant holds:
|
||||||
|
* @code size() <= max_size()@endcode
|
||||||
|
* Any member function that would, if successful, cause the invariant to be
|
||||||
|
* violated shall throw an exception of class @c std::length_error.
|
||||||
|
*
|
||||||
|
* The constructor for @c basic_streambuf takes an Allocator argument. A copy
|
||||||
|
* of this argument is used for any memory allocation performed, by the
|
||||||
|
* constructor and by all member functions, during the lifetime of each @c
|
||||||
|
* basic_streambuf object.
|
||||||
|
*
|
||||||
|
* @par Examples
|
||||||
|
* Writing directly from an streambuf to a socket:
|
||||||
|
* @code
|
||||||
|
* asio::streambuf b;
|
||||||
|
* std::ostream os(&b);
|
||||||
|
* os << "Hello, World!\n";
|
||||||
|
*
|
||||||
|
* // try sending some data in input sequence
|
||||||
|
* size_t n = sock.send(b.data());
|
||||||
|
*
|
||||||
|
* b.consume(n); // sent data is removed from input sequence
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Reading from a socket directly into a streambuf:
|
||||||
|
* @code
|
||||||
|
* asio::streambuf b;
|
||||||
|
*
|
||||||
|
* // reserve 512 bytes in output sequence
|
||||||
|
* asio::streambuf::mutable_buffers_type bufs = b.prepare(512);
|
||||||
|
*
|
||||||
|
* size_t n = sock.receive(bufs);
|
||||||
|
*
|
||||||
|
* // received data is "committed" from output sequence to input sequence
|
||||||
|
* b.commit(n);
|
||||||
|
*
|
||||||
|
* std::istream is(&b);
|
||||||
|
* std::string s;
|
||||||
|
* is >> s;
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
#if defined(GENERATING_DOCUMENTATION)
|
||||||
|
template <typename Allocator = std::allocator<char> >
|
||||||
|
#else
|
||||||
|
template <typename Allocator>
|
||||||
|
#endif
|
||||||
|
class basic_streambuf
|
||||||
|
: public std::streambuf,
|
||||||
|
private noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
#if defined(GENERATING_DOCUMENTATION)
|
||||||
|
/// The type used to represent the input sequence as a list of buffers.
|
||||||
|
typedef implementation_defined const_buffers_type;
|
||||||
|
|
||||||
|
/// The type used to represent the output sequence as a list of buffers.
|
||||||
|
typedef implementation_defined mutable_buffers_type;
|
||||||
|
#else
|
||||||
|
typedef ASIO_CONST_BUFFER const_buffers_type;
|
||||||
|
typedef ASIO_MUTABLE_BUFFER mutable_buffers_type;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Construct a basic_streambuf object.
|
||||||
|
/**
|
||||||
|
* Constructs a streambuf with the specified maximum size. The initial size
|
||||||
|
* of the streambuf's input sequence is 0.
|
||||||
|
*/
|
||||||
|
explicit basic_streambuf(
|
||||||
|
std::size_t maximum_size = (std::numeric_limits<std::size_t>::max)(),
|
||||||
|
const Allocator& allocator = Allocator())
|
||||||
|
: max_size_(maximum_size),
|
||||||
|
buffer_(allocator)
|
||||||
|
{
|
||||||
|
std::size_t pend = (std::min<std::size_t>)(max_size_, buffer_delta);
|
||||||
|
buffer_.resize((std::max<std::size_t>)(pend, 1));
|
||||||
|
setg(&buffer_[0], &buffer_[0], &buffer_[0]);
|
||||||
|
setp(&buffer_[0], &buffer_[0] + pend);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the size of the input sequence.
|
||||||
|
/**
|
||||||
|
* @returns The size of the input sequence. The value is equal to that
|
||||||
|
* calculated for @c s in the following code:
|
||||||
|
* @code
|
||||||
|
* size_t s = 0;
|
||||||
|
* const_buffers_type bufs = data();
|
||||||
|
* const_buffers_type::const_iterator i = bufs.begin();
|
||||||
|
* while (i != bufs.end())
|
||||||
|
* {
|
||||||
|
* const_buffer buf(*i++);
|
||||||
|
* s += buf.size();
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
std::size_t size() const ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return pptr() - gptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the maximum size of the basic_streambuf.
|
||||||
|
/**
|
||||||
|
* @returns The allowed maximum of the sum of the sizes of the input sequence
|
||||||
|
* and output sequence.
|
||||||
|
*/
|
||||||
|
std::size_t max_size() const ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return max_size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current capacity of the basic_streambuf.
|
||||||
|
/**
|
||||||
|
* @returns The current total capacity of the streambuf, i.e. for both the
|
||||||
|
* input sequence and output sequence.
|
||||||
|
*/
|
||||||
|
std::size_t capacity() const ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return buffer_.capacity();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a list of buffers that represents the input sequence.
|
||||||
|
/**
|
||||||
|
* @returns An object of type @c const_buffers_type that satisfies
|
||||||
|
* ConstBufferSequence requirements, representing all character arrays in the
|
||||||
|
* input sequence.
|
||||||
|
*
|
||||||
|
* @note The returned object is invalidated by any @c basic_streambuf member
|
||||||
|
* function that modifies the input sequence or output sequence.
|
||||||
|
*/
|
||||||
|
const_buffers_type data() const ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return asio::buffer(asio::const_buffer(gptr(),
|
||||||
|
(pptr() - gptr()) * sizeof(char_type)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a list of buffers that represents the output sequence, with the given
|
||||||
|
/// size.
|
||||||
|
/**
|
||||||
|
* Ensures that the output sequence can accommodate @c n characters,
|
||||||
|
* reallocating character array objects as necessary.
|
||||||
|
*
|
||||||
|
* @returns An object of type @c mutable_buffers_type that satisfies
|
||||||
|
* MutableBufferSequence requirements, representing character array objects
|
||||||
|
* at the start of the output sequence such that the sum of the buffer sizes
|
||||||
|
* is @c n.
|
||||||
|
*
|
||||||
|
* @throws std::length_error If <tt>size() + n > max_size()</tt>.
|
||||||
|
*
|
||||||
|
* @note The returned object is invalidated by any @c basic_streambuf member
|
||||||
|
* function that modifies the input sequence or output sequence.
|
||||||
|
*/
|
||||||
|
mutable_buffers_type prepare(std::size_t n)
|
||||||
|
{
|
||||||
|
reserve(n);
|
||||||
|
return asio::buffer(asio::mutable_buffer(
|
||||||
|
pptr(), n * sizeof(char_type)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move characters from the output sequence to the input sequence.
|
||||||
|
/**
|
||||||
|
* Appends @c n characters from the start of the output sequence to the input
|
||||||
|
* sequence. The beginning of the output sequence is advanced by @c n
|
||||||
|
* characters.
|
||||||
|
*
|
||||||
|
* Requires a preceding call <tt>prepare(x)</tt> where <tt>x >= n</tt>, and
|
||||||
|
* no intervening operations that modify the input or output sequence.
|
||||||
|
*
|
||||||
|
* @note If @c n is greater than the size of the output sequence, the entire
|
||||||
|
* output sequence is moved to the input sequence and no error is issued.
|
||||||
|
*/
|
||||||
|
void commit(std::size_t n)
|
||||||
|
{
|
||||||
|
n = std::min<std::size_t>(n, epptr() - pptr());
|
||||||
|
pbump(static_cast<int>(n));
|
||||||
|
setg(eback(), gptr(), pptr());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove characters from the input sequence.
|
||||||
|
/**
|
||||||
|
* Removes @c n characters from the beginning of the input sequence.
|
||||||
|
*
|
||||||
|
* @note If @c n is greater than the size of the input sequence, the entire
|
||||||
|
* input sequence is consumed and no error is issued.
|
||||||
|
*/
|
||||||
|
void consume(std::size_t n)
|
||||||
|
{
|
||||||
|
if (egptr() < pptr())
|
||||||
|
setg(&buffer_[0], gptr(), pptr());
|
||||||
|
if (gptr() + n > pptr())
|
||||||
|
n = pptr() - gptr();
|
||||||
|
gbump(static_cast<int>(n));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum { buffer_delta = 128 };
|
||||||
|
|
||||||
|
/// Override std::streambuf behaviour.
|
||||||
|
/**
|
||||||
|
* Behaves according to the specification of @c std::streambuf::underflow().
|
||||||
|
*/
|
||||||
|
int_type underflow()
|
||||||
|
{
|
||||||
|
if (gptr() < pptr())
|
||||||
|
{
|
||||||
|
setg(&buffer_[0], gptr(), pptr());
|
||||||
|
return traits_type::to_int_type(*gptr());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return traits_type::eof();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Override std::streambuf behaviour.
|
||||||
|
/**
|
||||||
|
* Behaves according to the specification of @c std::streambuf::overflow(),
|
||||||
|
* with the specialisation that @c std::length_error is thrown if appending
|
||||||
|
* the character to the input sequence would require the condition
|
||||||
|
* <tt>size() > max_size()</tt> to be true.
|
||||||
|
*/
|
||||||
|
int_type overflow(int_type c)
|
||||||
|
{
|
||||||
|
if (!traits_type::eq_int_type(c, traits_type::eof()))
|
||||||
|
{
|
||||||
|
if (pptr() == epptr())
|
||||||
|
{
|
||||||
|
std::size_t buffer_size = pptr() - gptr();
|
||||||
|
if (buffer_size < max_size_ && max_size_ - buffer_size < buffer_delta)
|
||||||
|
{
|
||||||
|
reserve(max_size_ - buffer_size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reserve(buffer_delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*pptr() = traits_type::to_char_type(c);
|
||||||
|
pbump(1);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return traits_type::not_eof(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reserve(std::size_t n)
|
||||||
|
{
|
||||||
|
// Get current stream positions as offsets.
|
||||||
|
std::size_t gnext = gptr() - &buffer_[0];
|
||||||
|
std::size_t pnext = pptr() - &buffer_[0];
|
||||||
|
std::size_t pend = epptr() - &buffer_[0];
|
||||||
|
|
||||||
|
// Check if there is already enough space in the put area.
|
||||||
|
if (n <= pend - pnext)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift existing contents of get area to start of buffer.
|
||||||
|
if (gnext > 0)
|
||||||
|
{
|
||||||
|
pnext -= gnext;
|
||||||
|
std::memmove(&buffer_[0], &buffer_[0] + gnext, pnext);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure buffer is large enough to hold at least the specified size.
|
||||||
|
if (n > pend - pnext)
|
||||||
|
{
|
||||||
|
if (n <= max_size_ && pnext <= max_size_ - n)
|
||||||
|
{
|
||||||
|
pend = pnext + n;
|
||||||
|
buffer_.resize((std::max<std::size_t>)(pend, 1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::length_error ex("asio::streambuf too long");
|
||||||
|
asio::detail::throw_exception(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update stream positions.
|
||||||
|
setg(&buffer_[0], &buffer_[0], &buffer_[0] + pnext);
|
||||||
|
setp(&buffer_[0] + pnext, &buffer_[0] + pend);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::size_t max_size_;
|
||||||
|
std::vector<char_type, Allocator> buffer_;
|
||||||
|
|
||||||
|
// Helper function to get the preferred size for reading data.
|
||||||
|
friend std::size_t read_size_helper(
|
||||||
|
basic_streambuf& sb, std::size_t max_size)
|
||||||
|
{
|
||||||
|
return std::min<std::size_t>(
|
||||||
|
std::max<std::size_t>(512, sb.buffer_.capacity() - sb.size()),
|
||||||
|
std::min<std::size_t>(max_size, sb.max_size() - sb.size()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Adapts basic_streambuf to the dynamic buffer sequence type requirements.
|
||||||
|
#if defined(GENERATING_DOCUMENTATION)
|
||||||
|
template <typename Allocator = std::allocator<char> >
|
||||||
|
#else
|
||||||
|
template <typename Allocator>
|
||||||
|
#endif
|
||||||
|
class basic_streambuf_ref
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// The type used to represent the input sequence as a list of buffers.
|
||||||
|
typedef typename basic_streambuf<Allocator>::const_buffers_type
|
||||||
|
const_buffers_type;
|
||||||
|
|
||||||
|
/// The type used to represent the output sequence as a list of buffers.
|
||||||
|
typedef typename basic_streambuf<Allocator>::mutable_buffers_type
|
||||||
|
mutable_buffers_type;
|
||||||
|
|
||||||
|
/// Construct a basic_streambuf_ref for the given basic_streambuf object.
|
||||||
|
explicit basic_streambuf_ref(basic_streambuf<Allocator>& sb)
|
||||||
|
: sb_(sb)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copy construct a basic_streambuf_ref.
|
||||||
|
basic_streambuf_ref(const basic_streambuf_ref& other) ASIO_NOEXCEPT
|
||||||
|
: sb_(other.sb_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
/// Move construct a basic_streambuf_ref.
|
||||||
|
basic_streambuf_ref(basic_streambuf_ref&& other) ASIO_NOEXCEPT
|
||||||
|
: sb_(other.sb_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
/// Get the size of the input sequence.
|
||||||
|
std::size_t size() const ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return sb_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the maximum size of the dynamic buffer.
|
||||||
|
std::size_t max_size() const ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return sb_.max_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current capacity of the dynamic buffer.
|
||||||
|
std::size_t capacity() const ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return sb_.capacity();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a list of buffers that represents the input sequence.
|
||||||
|
const_buffers_type data() const ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return sb_.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a list of buffers that represents the output sequence, with the given
|
||||||
|
/// size.
|
||||||
|
mutable_buffers_type prepare(std::size_t n)
|
||||||
|
{
|
||||||
|
return sb_.prepare(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move bytes from the output sequence to the input sequence.
|
||||||
|
void commit(std::size_t n)
|
||||||
|
{
|
||||||
|
return sb_.commit(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove characters from the input sequence.
|
||||||
|
void consume(std::size_t n)
|
||||||
|
{
|
||||||
|
return sb_.consume(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
basic_streambuf<Allocator>& sb_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace asio
|
||||||
|
|
||||||
|
#include "asio/detail/pop_options.hpp"
|
||||||
|
|
||||||
|
#endif // !defined(ASIO_NO_IOSTREAM)
|
||||||
|
|
||||||
|
#endif // ASIO_BASIC_STREAMBUF_HPP
|
@ -0,0 +1,36 @@
|
|||||||
|
//
|
||||||
|
// basic_streambuf_fwd.hpp
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ASIO_BASIC_STREAMBUF_FWD_HPP
|
||||||
|
#define ASIO_BASIC_STREAMBUF_FWD_HPP
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
# pragma once
|
||||||
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
|
||||||
|
#include "asio/detail/config.hpp"
|
||||||
|
|
||||||
|
#if !defined(ASIO_NO_IOSTREAM)
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace asio {
|
||||||
|
|
||||||
|
template <typename Allocator = std::allocator<char> >
|
||||||
|
class basic_streambuf;
|
||||||
|
|
||||||
|
template <typename Allocator = std::allocator<char> >
|
||||||
|
class basic_streambuf_ref;
|
||||||
|
|
||||||
|
} // namespace asio
|
||||||
|
|
||||||
|
#endif // !defined(ASIO_NO_IOSTREAM)
|
||||||
|
|
||||||
|
#endif // ASIO_BASIC_STREAMBUF_FWD_HPP
|
@ -0,0 +1,705 @@
|
|||||||
|
//
|
||||||
|
// basic_waitable_timer.hpp
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ASIO_BASIC_WAITABLE_TIMER_HPP
|
||||||
|
#define ASIO_BASIC_WAITABLE_TIMER_HPP
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
# pragma once
|
||||||
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
|
||||||
|
#include "asio/detail/config.hpp"
|
||||||
|
#include <cstddef>
|
||||||
|
#include "asio/basic_io_object.hpp"
|
||||||
|
#include "asio/detail/handler_type_requirements.hpp"
|
||||||
|
#include "asio/detail/throw_error.hpp"
|
||||||
|
#include "asio/error.hpp"
|
||||||
|
#include "asio/wait_traits.hpp"
|
||||||
|
|
||||||
|
#if defined(ASIO_HAS_MOVE)
|
||||||
|
# include <utility>
|
||||||
|
#endif // defined(ASIO_HAS_MOVE)
|
||||||
|
|
||||||
|
#if defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
# include "asio/waitable_timer_service.hpp"
|
||||||
|
#else // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
# include "asio/detail/chrono_time_traits.hpp"
|
||||||
|
# include "asio/detail/deadline_timer_service.hpp"
|
||||||
|
# define ASIO_SVC_T \
|
||||||
|
detail::deadline_timer_service< \
|
||||||
|
detail::chrono_time_traits<Clock, WaitTraits> >
|
||||||
|
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
|
||||||
|
#include "asio/detail/push_options.hpp"
|
||||||
|
|
||||||
|
namespace asio {
|
||||||
|
|
||||||
|
#if !defined(ASIO_BASIC_WAITABLE_TIMER_FWD_DECL)
|
||||||
|
#define ASIO_BASIC_WAITABLE_TIMER_FWD_DECL
|
||||||
|
|
||||||
|
// Forward declaration with defaulted arguments.
|
||||||
|
template <typename Clock,
|
||||||
|
typename WaitTraits = asio::wait_traits<Clock>
|
||||||
|
ASIO_SVC_TPARAM_DEF2(= waitable_timer_service<Clock, WaitTraits>)>
|
||||||
|
class basic_waitable_timer;
|
||||||
|
|
||||||
|
#endif // !defined(ASIO_BASIC_WAITABLE_TIMER_FWD_DECL)
|
||||||
|
|
||||||
|
/// Provides waitable timer functionality.
|
||||||
|
/**
|
||||||
|
* The basic_waitable_timer class template provides the ability to perform a
|
||||||
|
* blocking or asynchronous wait for a timer to expire.
|
||||||
|
*
|
||||||
|
* A waitable timer is always in one of two states: "expired" or "not expired".
|
||||||
|
* If the wait() or async_wait() function is called on an expired timer, the
|
||||||
|
* wait operation will complete immediately.
|
||||||
|
*
|
||||||
|
* Most applications will use one of the asio::steady_timer,
|
||||||
|
* asio::system_timer or asio::high_resolution_timer typedefs.
|
||||||
|
*
|
||||||
|
* @note This waitable timer functionality is for use with the C++11 standard
|
||||||
|
* library's @c <chrono> facility, or with the Boost.Chrono library.
|
||||||
|
*
|
||||||
|
* @par Thread Safety
|
||||||
|
* @e Distinct @e objects: Safe.@n
|
||||||
|
* @e Shared @e objects: Unsafe.
|
||||||
|
*
|
||||||
|
* @par Examples
|
||||||
|
* Performing a blocking wait (C++11):
|
||||||
|
* @code
|
||||||
|
* // Construct a timer without setting an expiry time.
|
||||||
|
* asio::steady_timer timer(io_context);
|
||||||
|
*
|
||||||
|
* // Set an expiry time relative to now.
|
||||||
|
* timer.expires_after(std::chrono::seconds(5));
|
||||||
|
*
|
||||||
|
* // Wait for the timer to expire.
|
||||||
|
* timer.wait();
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @par
|
||||||
|
* Performing an asynchronous wait (C++11):
|
||||||
|
* @code
|
||||||
|
* void handler(const asio::error_code& error)
|
||||||
|
* {
|
||||||
|
* if (!error)
|
||||||
|
* {
|
||||||
|
* // Timer expired.
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* // Construct a timer with an absolute expiry time.
|
||||||
|
* asio::steady_timer timer(io_context,
|
||||||
|
* std::chrono::steady_clock::now() + std::chrono::seconds(60));
|
||||||
|
*
|
||||||
|
* // Start an asynchronous wait.
|
||||||
|
* timer.async_wait(handler);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @par Changing an active waitable timer's expiry time
|
||||||
|
*
|
||||||
|
* Changing the expiry time of a timer while there are pending asynchronous
|
||||||
|
* waits causes those wait operations to be cancelled. To ensure that the action
|
||||||
|
* associated with the timer is performed only once, use something like this:
|
||||||
|
* used:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* void on_some_event()
|
||||||
|
* {
|
||||||
|
* if (my_timer.expires_after(seconds(5)) > 0)
|
||||||
|
* {
|
||||||
|
* // We managed to cancel the timer. Start new asynchronous wait.
|
||||||
|
* my_timer.async_wait(on_timeout);
|
||||||
|
* }
|
||||||
|
* else
|
||||||
|
* {
|
||||||
|
* // Too late, timer has already expired!
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* void on_timeout(const asio::error_code& e)
|
||||||
|
* {
|
||||||
|
* if (e != asio::error::operation_aborted)
|
||||||
|
* {
|
||||||
|
* // Timer was not cancelled, take necessary action.
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @li The asio::basic_waitable_timer::expires_after() function
|
||||||
|
* cancels any pending asynchronous waits, and returns the number of
|
||||||
|
* asynchronous waits that were cancelled. If it returns 0 then you were too
|
||||||
|
* late and the wait handler has already been executed, or will soon be
|
||||||
|
* executed. If it returns 1 then the wait handler was successfully cancelled.
|
||||||
|
*
|
||||||
|
* @li If a wait handler is cancelled, the asio::error_code passed to
|
||||||
|
* it contains the value asio::error::operation_aborted.
|
||||||
|
*/
|
||||||
|
template <typename Clock, typename WaitTraits ASIO_SVC_TPARAM>
|
||||||
|
class basic_waitable_timer
|
||||||
|
: ASIO_SVC_ACCESS basic_io_object<ASIO_SVC_T>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// The type of the executor associated with the object.
|
||||||
|
typedef io_context::executor_type executor_type;
|
||||||
|
|
||||||
|
/// The clock type.
|
||||||
|
typedef Clock clock_type;
|
||||||
|
|
||||||
|
/// The duration type of the clock.
|
||||||
|
typedef typename clock_type::duration duration;
|
||||||
|
|
||||||
|
/// The time point type of the clock.
|
||||||
|
typedef typename clock_type::time_point time_point;
|
||||||
|
|
||||||
|
/// The wait traits type.
|
||||||
|
typedef WaitTraits traits_type;
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
/**
|
||||||
|
* This constructor creates a timer without setting an expiry time. The
|
||||||
|
* expires_at() or expires_after() functions must be called to set an expiry
|
||||||
|
* time before the timer can be waited on.
|
||||||
|
*
|
||||||
|
* @param io_context The io_context object that the timer will use to dispatch
|
||||||
|
* handlers for any asynchronous operations performed on the timer.
|
||||||
|
*/
|
||||||
|
explicit basic_waitable_timer(asio::io_context& io_context)
|
||||||
|
: basic_io_object<ASIO_SVC_T>(io_context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructor to set a particular expiry time as an absolute time.
|
||||||
|
/**
|
||||||
|
* This constructor creates a timer and sets the expiry time.
|
||||||
|
*
|
||||||
|
* @param io_context The io_context object that the timer will use to dispatch
|
||||||
|
* handlers for any asynchronous operations performed on the timer.
|
||||||
|
*
|
||||||
|
* @param expiry_time The expiry time to be used for the timer, expressed
|
||||||
|
* as an absolute time.
|
||||||
|
*/
|
||||||
|
basic_waitable_timer(asio::io_context& io_context,
|
||||||
|
const time_point& expiry_time)
|
||||||
|
: basic_io_object<ASIO_SVC_T>(io_context)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
this->get_service().expires_at(this->get_implementation(), expiry_time, ec);
|
||||||
|
asio::detail::throw_error(ec, "expires_at");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructor to set a particular expiry time relative to now.
|
||||||
|
/**
|
||||||
|
* This constructor creates a timer and sets the expiry time.
|
||||||
|
*
|
||||||
|
* @param io_context The io_context object that the timer will use to dispatch
|
||||||
|
* handlers for any asynchronous operations performed on the timer.
|
||||||
|
*
|
||||||
|
* @param expiry_time The expiry time to be used for the timer, relative to
|
||||||
|
* now.
|
||||||
|
*/
|
||||||
|
basic_waitable_timer(asio::io_context& io_context,
|
||||||
|
const duration& expiry_time)
|
||||||
|
: basic_io_object<ASIO_SVC_T>(io_context)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
this->get_service().expires_after(
|
||||||
|
this->get_implementation(), expiry_time, ec);
|
||||||
|
asio::detail::throw_error(ec, "expires_after");
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
/// Move-construct a basic_waitable_timer from another.
|
||||||
|
/**
|
||||||
|
* This constructor moves a timer from one object to another.
|
||||||
|
*
|
||||||
|
* @param other The other basic_waitable_timer object from which the move will
|
||||||
|
* occur.
|
||||||
|
*
|
||||||
|
* @note Following the move, the moved-from object is in the same state as if
|
||||||
|
* constructed using the @c basic_waitable_timer(io_context&) constructor.
|
||||||
|
*/
|
||||||
|
basic_waitable_timer(basic_waitable_timer&& other)
|
||||||
|
: basic_io_object<ASIO_SVC_T>(std::move(other))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move-assign a basic_waitable_timer from another.
|
||||||
|
/**
|
||||||
|
* This assignment operator moves a timer from one object to another. Cancels
|
||||||
|
* any outstanding asynchronous operations associated with the target object.
|
||||||
|
*
|
||||||
|
* @param other The other basic_waitable_timer object from which the move will
|
||||||
|
* occur.
|
||||||
|
*
|
||||||
|
* @note Following the move, the moved-from object is in the same state as if
|
||||||
|
* constructed using the @c basic_waitable_timer(io_context&) constructor.
|
||||||
|
*/
|
||||||
|
basic_waitable_timer& operator=(basic_waitable_timer&& other)
|
||||||
|
{
|
||||||
|
basic_io_object<ASIO_SVC_T>::operator=(std::move(other));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
/// Destroys the timer.
|
||||||
|
/**
|
||||||
|
* This function destroys the timer, cancelling any outstanding asynchronous
|
||||||
|
* wait operations associated with the timer as if by calling @c cancel.
|
||||||
|
*/
|
||||||
|
~basic_waitable_timer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
// These functions are provided by basic_io_object<>.
|
||||||
|
#else // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
#if !defined(ASIO_NO_DEPRECATED)
|
||||||
|
/// (Deprecated: Use get_executor().) Get the io_context associated with the
|
||||||
|
/// object.
|
||||||
|
/**
|
||||||
|
* This function may be used to obtain the io_context object that the I/O
|
||||||
|
* object uses to dispatch handlers for asynchronous operations.
|
||||||
|
*
|
||||||
|
* @return A reference to the io_context object that the I/O object will use
|
||||||
|
* to dispatch handlers. Ownership is not transferred to the caller.
|
||||||
|
*/
|
||||||
|
asio::io_context& get_io_context()
|
||||||
|
{
|
||||||
|
return basic_io_object<ASIO_SVC_T>::get_io_context();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// (Deprecated: Use get_executor().) Get the io_context associated with the
|
||||||
|
/// object.
|
||||||
|
/**
|
||||||
|
* This function may be used to obtain the io_context object that the I/O
|
||||||
|
* object uses to dispatch handlers for asynchronous operations.
|
||||||
|
*
|
||||||
|
* @return A reference to the io_context object that the I/O object will use
|
||||||
|
* to dispatch handlers. Ownership is not transferred to the caller.
|
||||||
|
*/
|
||||||
|
asio::io_context& get_io_service()
|
||||||
|
{
|
||||||
|
return basic_io_object<ASIO_SVC_T>::get_io_service();
|
||||||
|
}
|
||||||
|
#endif // !defined(ASIO_NO_DEPRECATED)
|
||||||
|
|
||||||
|
/// Get the executor associated with the object.
|
||||||
|
executor_type get_executor() ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return basic_io_object<ASIO_SVC_T>::get_executor();
|
||||||
|
}
|
||||||
|
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
|
||||||
|
/// Cancel any asynchronous operations that are waiting on the timer.
|
||||||
|
/**
|
||||||
|
* This function forces the completion of any pending asynchronous wait
|
||||||
|
* operations against the timer. The handler for each cancelled operation will
|
||||||
|
* be invoked with the asio::error::operation_aborted error code.
|
||||||
|
*
|
||||||
|
* Cancelling the timer does not change the expiry time.
|
||||||
|
*
|
||||||
|
* @return The number of asynchronous operations that were cancelled.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*
|
||||||
|
* @note If the timer has already expired when cancel() is called, then the
|
||||||
|
* handlers for asynchronous wait operations will:
|
||||||
|
*
|
||||||
|
* @li have already been invoked; or
|
||||||
|
*
|
||||||
|
* @li have been queued for invocation in the near future.
|
||||||
|
*
|
||||||
|
* These handlers can no longer be cancelled, and therefore are passed an
|
||||||
|
* error code that indicates the successful completion of the wait operation.
|
||||||
|
*/
|
||||||
|
std::size_t cancel()
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
std::size_t s = this->get_service().cancel(this->get_implementation(), ec);
|
||||||
|
asio::detail::throw_error(ec, "cancel");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(ASIO_NO_DEPRECATED)
|
||||||
|
/// (Deprecated: Use non-error_code overload.) Cancel any asynchronous
|
||||||
|
/// operations that are waiting on the timer.
|
||||||
|
/**
|
||||||
|
* This function forces the completion of any pending asynchronous wait
|
||||||
|
* operations against the timer. The handler for each cancelled operation will
|
||||||
|
* be invoked with the asio::error::operation_aborted error code.
|
||||||
|
*
|
||||||
|
* Cancelling the timer does not change the expiry time.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*
|
||||||
|
* @return The number of asynchronous operations that were cancelled.
|
||||||
|
*
|
||||||
|
* @note If the timer has already expired when cancel() is called, then the
|
||||||
|
* handlers for asynchronous wait operations will:
|
||||||
|
*
|
||||||
|
* @li have already been invoked; or
|
||||||
|
*
|
||||||
|
* @li have been queued for invocation in the near future.
|
||||||
|
*
|
||||||
|
* These handlers can no longer be cancelled, and therefore are passed an
|
||||||
|
* error code that indicates the successful completion of the wait operation.
|
||||||
|
*/
|
||||||
|
std::size_t cancel(asio::error_code& ec)
|
||||||
|
{
|
||||||
|
return this->get_service().cancel(this->get_implementation(), ec);
|
||||||
|
}
|
||||||
|
#endif // !defined(ASIO_NO_DEPRECATED)
|
||||||
|
|
||||||
|
/// Cancels one asynchronous operation that is waiting on the timer.
|
||||||
|
/**
|
||||||
|
* This function forces the completion of one pending asynchronous wait
|
||||||
|
* operation against the timer. Handlers are cancelled in FIFO order. The
|
||||||
|
* handler for the cancelled operation will be invoked with the
|
||||||
|
* asio::error::operation_aborted error code.
|
||||||
|
*
|
||||||
|
* Cancelling the timer does not change the expiry time.
|
||||||
|
*
|
||||||
|
* @return The number of asynchronous operations that were cancelled. That is,
|
||||||
|
* either 0 or 1.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*
|
||||||
|
* @note If the timer has already expired when cancel_one() is called, then
|
||||||
|
* the handlers for asynchronous wait operations will:
|
||||||
|
*
|
||||||
|
* @li have already been invoked; or
|
||||||
|
*
|
||||||
|
* @li have been queued for invocation in the near future.
|
||||||
|
*
|
||||||
|
* These handlers can no longer be cancelled, and therefore are passed an
|
||||||
|
* error code that indicates the successful completion of the wait operation.
|
||||||
|
*/
|
||||||
|
std::size_t cancel_one()
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
std::size_t s = this->get_service().cancel_one(
|
||||||
|
this->get_implementation(), ec);
|
||||||
|
asio::detail::throw_error(ec, "cancel_one");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(ASIO_NO_DEPRECATED)
|
||||||
|
/// (Deprecated: Use non-error_code overload.) Cancels one asynchronous
|
||||||
|
/// operation that is waiting on the timer.
|
||||||
|
/**
|
||||||
|
* This function forces the completion of one pending asynchronous wait
|
||||||
|
* operation against the timer. Handlers are cancelled in FIFO order. The
|
||||||
|
* handler for the cancelled operation will be invoked with the
|
||||||
|
* asio::error::operation_aborted error code.
|
||||||
|
*
|
||||||
|
* Cancelling the timer does not change the expiry time.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*
|
||||||
|
* @return The number of asynchronous operations that were cancelled. That is,
|
||||||
|
* either 0 or 1.
|
||||||
|
*
|
||||||
|
* @note If the timer has already expired when cancel_one() is called, then
|
||||||
|
* the handlers for asynchronous wait operations will:
|
||||||
|
*
|
||||||
|
* @li have already been invoked; or
|
||||||
|
*
|
||||||
|
* @li have been queued for invocation in the near future.
|
||||||
|
*
|
||||||
|
* These handlers can no longer be cancelled, and therefore are passed an
|
||||||
|
* error code that indicates the successful completion of the wait operation.
|
||||||
|
*/
|
||||||
|
std::size_t cancel_one(asio::error_code& ec)
|
||||||
|
{
|
||||||
|
return this->get_service().cancel_one(this->get_implementation(), ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// (Deprecated: Use expiry().) Get the timer's expiry time as an absolute
|
||||||
|
/// time.
|
||||||
|
/**
|
||||||
|
* This function may be used to obtain the timer's current expiry time.
|
||||||
|
* Whether the timer has expired or not does not affect this value.
|
||||||
|
*/
|
||||||
|
time_point expires_at() const
|
||||||
|
{
|
||||||
|
return this->get_service().expires_at(this->get_implementation());
|
||||||
|
}
|
||||||
|
#endif // !defined(ASIO_NO_DEPRECATED)
|
||||||
|
|
||||||
|
/// Get the timer's expiry time as an absolute time.
|
||||||
|
/**
|
||||||
|
* This function may be used to obtain the timer's current expiry time.
|
||||||
|
* Whether the timer has expired or not does not affect this value.
|
||||||
|
*/
|
||||||
|
time_point expiry() const
|
||||||
|
{
|
||||||
|
return this->get_service().expiry(this->get_implementation());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the timer's expiry time as an absolute time.
|
||||||
|
/**
|
||||||
|
* This function sets the expiry time. Any pending asynchronous wait
|
||||||
|
* operations will be cancelled. The handler for each cancelled operation will
|
||||||
|
* be invoked with the asio::error::operation_aborted error code.
|
||||||
|
*
|
||||||
|
* @param expiry_time The expiry time to be used for the timer.
|
||||||
|
*
|
||||||
|
* @return The number of asynchronous operations that were cancelled.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*
|
||||||
|
* @note If the timer has already expired when expires_at() is called, then
|
||||||
|
* the handlers for asynchronous wait operations will:
|
||||||
|
*
|
||||||
|
* @li have already been invoked; or
|
||||||
|
*
|
||||||
|
* @li have been queued for invocation in the near future.
|
||||||
|
*
|
||||||
|
* These handlers can no longer be cancelled, and therefore are passed an
|
||||||
|
* error code that indicates the successful completion of the wait operation.
|
||||||
|
*/
|
||||||
|
std::size_t expires_at(const time_point& expiry_time)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
std::size_t s = this->get_service().expires_at(
|
||||||
|
this->get_implementation(), expiry_time, ec);
|
||||||
|
asio::detail::throw_error(ec, "expires_at");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(ASIO_NO_DEPRECATED)
|
||||||
|
/// (Deprecated: Use non-error_code overload.) Set the timer's expiry time as
|
||||||
|
/// an absolute time.
|
||||||
|
/**
|
||||||
|
* This function sets the expiry time. Any pending asynchronous wait
|
||||||
|
* operations will be cancelled. The handler for each cancelled operation will
|
||||||
|
* be invoked with the asio::error::operation_aborted error code.
|
||||||
|
*
|
||||||
|
* @param expiry_time The expiry time to be used for the timer.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*
|
||||||
|
* @return The number of asynchronous operations that were cancelled.
|
||||||
|
*
|
||||||
|
* @note If the timer has already expired when expires_at() is called, then
|
||||||
|
* the handlers for asynchronous wait operations will:
|
||||||
|
*
|
||||||
|
* @li have already been invoked; or
|
||||||
|
*
|
||||||
|
* @li have been queued for invocation in the near future.
|
||||||
|
*
|
||||||
|
* These handlers can no longer be cancelled, and therefore are passed an
|
||||||
|
* error code that indicates the successful completion of the wait operation.
|
||||||
|
*/
|
||||||
|
std::size_t expires_at(const time_point& expiry_time,
|
||||||
|
asio::error_code& ec)
|
||||||
|
{
|
||||||
|
return this->get_service().expires_at(
|
||||||
|
this->get_implementation(), expiry_time, ec);
|
||||||
|
}
|
||||||
|
#endif // !defined(ASIO_NO_DEPRECATED)
|
||||||
|
|
||||||
|
/// Set the timer's expiry time relative to now.
|
||||||
|
/**
|
||||||
|
* This function sets the expiry time. Any pending asynchronous wait
|
||||||
|
* operations will be cancelled. The handler for each cancelled operation will
|
||||||
|
* be invoked with the asio::error::operation_aborted error code.
|
||||||
|
*
|
||||||
|
* @param expiry_time The expiry time to be used for the timer.
|
||||||
|
*
|
||||||
|
* @return The number of asynchronous operations that were cancelled.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*
|
||||||
|
* @note If the timer has already expired when expires_after() is called,
|
||||||
|
* then the handlers for asynchronous wait operations will:
|
||||||
|
*
|
||||||
|
* @li have already been invoked; or
|
||||||
|
*
|
||||||
|
* @li have been queued for invocation in the near future.
|
||||||
|
*
|
||||||
|
* These handlers can no longer be cancelled, and therefore are passed an
|
||||||
|
* error code that indicates the successful completion of the wait operation.
|
||||||
|
*/
|
||||||
|
std::size_t expires_after(const duration& expiry_time)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
std::size_t s = this->get_service().expires_after(
|
||||||
|
this->get_implementation(), expiry_time, ec);
|
||||||
|
asio::detail::throw_error(ec, "expires_after");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(ASIO_NO_DEPRECATED)
|
||||||
|
/// (Deprecated: Use expiry().) Get the timer's expiry time relative to now.
|
||||||
|
/**
|
||||||
|
* This function may be used to obtain the timer's current expiry time.
|
||||||
|
* Whether the timer has expired or not does not affect this value.
|
||||||
|
*/
|
||||||
|
duration expires_from_now() const
|
||||||
|
{
|
||||||
|
return this->get_service().expires_from_now(this->get_implementation());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// (Deprecated: Use expires_after().) Set the timer's expiry time relative
|
||||||
|
/// to now.
|
||||||
|
/**
|
||||||
|
* This function sets the expiry time. Any pending asynchronous wait
|
||||||
|
* operations will be cancelled. The handler for each cancelled operation will
|
||||||
|
* be invoked with the asio::error::operation_aborted error code.
|
||||||
|
*
|
||||||
|
* @param expiry_time The expiry time to be used for the timer.
|
||||||
|
*
|
||||||
|
* @return The number of asynchronous operations that were cancelled.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*
|
||||||
|
* @note If the timer has already expired when expires_from_now() is called,
|
||||||
|
* then the handlers for asynchronous wait operations will:
|
||||||
|
*
|
||||||
|
* @li have already been invoked; or
|
||||||
|
*
|
||||||
|
* @li have been queued for invocation in the near future.
|
||||||
|
*
|
||||||
|
* These handlers can no longer be cancelled, and therefore are passed an
|
||||||
|
* error code that indicates the successful completion of the wait operation.
|
||||||
|
*/
|
||||||
|
std::size_t expires_from_now(const duration& expiry_time)
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
std::size_t s = this->get_service().expires_from_now(
|
||||||
|
this->get_implementation(), expiry_time, ec);
|
||||||
|
asio::detail::throw_error(ec, "expires_from_now");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// (Deprecated: Use expires_after().) Set the timer's expiry time relative
|
||||||
|
/// to now.
|
||||||
|
/**
|
||||||
|
* This function sets the expiry time. Any pending asynchronous wait
|
||||||
|
* operations will be cancelled. The handler for each cancelled operation will
|
||||||
|
* be invoked with the asio::error::operation_aborted error code.
|
||||||
|
*
|
||||||
|
* @param expiry_time The expiry time to be used for the timer.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*
|
||||||
|
* @return The number of asynchronous operations that were cancelled.
|
||||||
|
*
|
||||||
|
* @note If the timer has already expired when expires_from_now() is called,
|
||||||
|
* then the handlers for asynchronous wait operations will:
|
||||||
|
*
|
||||||
|
* @li have already been invoked; or
|
||||||
|
*
|
||||||
|
* @li have been queued for invocation in the near future.
|
||||||
|
*
|
||||||
|
* These handlers can no longer be cancelled, and therefore are passed an
|
||||||
|
* error code that indicates the successful completion of the wait operation.
|
||||||
|
*/
|
||||||
|
std::size_t expires_from_now(const duration& expiry_time,
|
||||||
|
asio::error_code& ec)
|
||||||
|
{
|
||||||
|
return this->get_service().expires_from_now(
|
||||||
|
this->get_implementation(), expiry_time, ec);
|
||||||
|
}
|
||||||
|
#endif // !defined(ASIO_NO_DEPRECATED)
|
||||||
|
|
||||||
|
/// Perform a blocking wait on the timer.
|
||||||
|
/**
|
||||||
|
* This function is used to wait for the timer to expire. This function
|
||||||
|
* blocks and does not return until the timer has expired.
|
||||||
|
*
|
||||||
|
* @throws asio::system_error Thrown on failure.
|
||||||
|
*/
|
||||||
|
void wait()
|
||||||
|
{
|
||||||
|
asio::error_code ec;
|
||||||
|
this->get_service().wait(this->get_implementation(), ec);
|
||||||
|
asio::detail::throw_error(ec, "wait");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform a blocking wait on the timer.
|
||||||
|
/**
|
||||||
|
* This function is used to wait for the timer to expire. This function
|
||||||
|
* blocks and does not return until the timer has expired.
|
||||||
|
*
|
||||||
|
* @param ec Set to indicate what error occurred, if any.
|
||||||
|
*/
|
||||||
|
void wait(asio::error_code& ec)
|
||||||
|
{
|
||||||
|
this->get_service().wait(this->get_implementation(), ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start an asynchronous wait on the timer.
|
||||||
|
/**
|
||||||
|
* This function may be used to initiate an asynchronous wait against the
|
||||||
|
* timer. It always returns immediately.
|
||||||
|
*
|
||||||
|
* For each call to async_wait(), the supplied handler will be called exactly
|
||||||
|
* once. The handler will be called when:
|
||||||
|
*
|
||||||
|
* @li The timer has expired.
|
||||||
|
*
|
||||||
|
* @li The timer was cancelled, in which case the handler is passed the error
|
||||||
|
* code asio::error::operation_aborted.
|
||||||
|
*
|
||||||
|
* @param handler The handler to be called when the timer expires. Copies
|
||||||
|
* will be made of the handler as required. The function signature of the
|
||||||
|
* handler must be:
|
||||||
|
* @code void handler(
|
||||||
|
* const asio::error_code& error // Result of operation.
|
||||||
|
* ); @endcode
|
||||||
|
* Regardless of whether the asynchronous operation completes immediately or
|
||||||
|
* not, the handler will not be invoked from within this function. Invocation
|
||||||
|
* of the handler will be performed in a manner equivalent to using
|
||||||
|
* asio::io_context::post().
|
||||||
|
*/
|
||||||
|
template <typename WaitHandler>
|
||||||
|
ASIO_INITFN_RESULT_TYPE(WaitHandler,
|
||||||
|
void (asio::error_code))
|
||||||
|
async_wait(ASIO_MOVE_ARG(WaitHandler) handler)
|
||||||
|
{
|
||||||
|
// If you get an error on the following line it means that your handler does
|
||||||
|
// not meet the documented type requirements for a WaitHandler.
|
||||||
|
ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check;
|
||||||
|
|
||||||
|
#if defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
return this->get_service().async_wait(this->get_implementation(),
|
||||||
|
ASIO_MOVE_CAST(WaitHandler)(handler));
|
||||||
|
#else // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
async_completion<WaitHandler,
|
||||||
|
void (asio::error_code)> init(handler);
|
||||||
|
|
||||||
|
this->get_service().async_wait(this->get_implementation(),
|
||||||
|
init.completion_handler);
|
||||||
|
|
||||||
|
return init.result.get();
|
||||||
|
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Disallow copying and assignment.
|
||||||
|
basic_waitable_timer(const basic_waitable_timer&) ASIO_DELETED;
|
||||||
|
basic_waitable_timer& operator=(
|
||||||
|
const basic_waitable_timer&) ASIO_DELETED;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace asio
|
||||||
|
|
||||||
|
#include "asio/detail/pop_options.hpp"
|
||||||
|
|
||||||
|
#if !defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
# undef ASIO_SVC_T
|
||||||
|
#endif // !defined(ASIO_ENABLE_OLD_SERVICES)
|
||||||
|
|
||||||
|
#endif // ASIO_BASIC_WAITABLE_TIMER_HPP
|
611
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/asio/bind_executor.hpp
vendored
Normal file
611
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/asio/bind_executor.hpp
vendored
Normal file
@ -0,0 +1,611 @@
|
|||||||
|
//
|
||||||
|
// bind_executor.hpp
|
||||||
|
// ~~~~~~~~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ASIO_BIND_EXECUTOR_HPP
|
||||||
|
#define ASIO_BIND_EXECUTOR_HPP
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
# pragma once
|
||||||
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
|
||||||
|
#include "asio/detail/config.hpp"
|
||||||
|
#include "asio/detail/type_traits.hpp"
|
||||||
|
#include "asio/detail/variadic_templates.hpp"
|
||||||
|
#include "asio/associated_executor.hpp"
|
||||||
|
#include "asio/associated_allocator.hpp"
|
||||||
|
#include "asio/async_result.hpp"
|
||||||
|
#include "asio/execution_context.hpp"
|
||||||
|
#include "asio/is_executor.hpp"
|
||||||
|
#include "asio/uses_executor.hpp"
|
||||||
|
|
||||||
|
#include "asio/detail/push_options.hpp"
|
||||||
|
|
||||||
|
namespace asio {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct executor_binder_check
|
||||||
|
{
|
||||||
|
typedef void type;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper to automatically define nested typedef result_type.
|
||||||
|
|
||||||
|
template <typename T, typename = void>
|
||||||
|
struct executor_binder_result_type
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
typedef void result_type_or_void;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct executor_binder_result_type<T,
|
||||||
|
typename executor_binder_check<typename T::result_type>::type>
|
||||||
|
{
|
||||||
|
typedef typename T::result_type result_type;
|
||||||
|
protected:
|
||||||
|
typedef result_type result_type_or_void;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename R>
|
||||||
|
struct executor_binder_result_type<R(*)()>
|
||||||
|
{
|
||||||
|
typedef R result_type;
|
||||||
|
protected:
|
||||||
|
typedef result_type result_type_or_void;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename R>
|
||||||
|
struct executor_binder_result_type<R(&)()>
|
||||||
|
{
|
||||||
|
typedef R result_type;
|
||||||
|
protected:
|
||||||
|
typedef result_type result_type_or_void;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename R, typename A1>
|
||||||
|
struct executor_binder_result_type<R(*)(A1)>
|
||||||
|
{
|
||||||
|
typedef R result_type;
|
||||||
|
protected:
|
||||||
|
typedef result_type result_type_or_void;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename R, typename A1>
|
||||||
|
struct executor_binder_result_type<R(&)(A1)>
|
||||||
|
{
|
||||||
|
typedef R result_type;
|
||||||
|
protected:
|
||||||
|
typedef result_type result_type_or_void;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename R, typename A1, typename A2>
|
||||||
|
struct executor_binder_result_type<R(*)(A1, A2)>
|
||||||
|
{
|
||||||
|
typedef R result_type;
|
||||||
|
protected:
|
||||||
|
typedef result_type result_type_or_void;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename R, typename A1, typename A2>
|
||||||
|
struct executor_binder_result_type<R(&)(A1, A2)>
|
||||||
|
{
|
||||||
|
typedef R result_type;
|
||||||
|
protected:
|
||||||
|
typedef result_type result_type_or_void;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper to automatically define nested typedef argument_type.
|
||||||
|
|
||||||
|
template <typename T, typename = void>
|
||||||
|
struct executor_binder_argument_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct executor_binder_argument_type<T,
|
||||||
|
typename executor_binder_check<typename T::argument_type>::type>
|
||||||
|
{
|
||||||
|
typedef typename T::argument_type argument_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename R, typename A1>
|
||||||
|
struct executor_binder_argument_type<R(*)(A1)>
|
||||||
|
{
|
||||||
|
typedef A1 argument_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename R, typename A1>
|
||||||
|
struct executor_binder_argument_type<R(&)(A1)>
|
||||||
|
{
|
||||||
|
typedef A1 argument_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper to automatically define nested typedefs first_argument_type and
|
||||||
|
// second_argument_type.
|
||||||
|
|
||||||
|
template <typename T, typename = void>
|
||||||
|
struct executor_binder_argument_types {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct executor_binder_argument_types<T,
|
||||||
|
typename executor_binder_check<typename T::first_argument_type>::type>
|
||||||
|
{
|
||||||
|
typedef typename T::first_argument_type first_argument_type;
|
||||||
|
typedef typename T::second_argument_type second_argument_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename R, typename A1, typename A2>
|
||||||
|
struct executor_binder_argument_type<R(*)(A1, A2)>
|
||||||
|
{
|
||||||
|
typedef A1 first_argument_type;
|
||||||
|
typedef A2 second_argument_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename R, typename A1, typename A2>
|
||||||
|
struct executor_binder_argument_type<R(&)(A1, A2)>
|
||||||
|
{
|
||||||
|
typedef A1 first_argument_type;
|
||||||
|
typedef A2 second_argument_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper to:
|
||||||
|
// - Apply the empty base optimisation to the executor.
|
||||||
|
// - Perform uses_executor construction of the target type, if required.
|
||||||
|
|
||||||
|
template <typename T, typename Executor, bool UsesExecutor>
|
||||||
|
class executor_binder_base;
|
||||||
|
|
||||||
|
template <typename T, typename Executor>
|
||||||
|
class executor_binder_base<T, Executor, true>
|
||||||
|
: protected Executor
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
template <typename E, typename U>
|
||||||
|
executor_binder_base(ASIO_MOVE_ARG(E) e, ASIO_MOVE_ARG(U) u)
|
||||||
|
: executor_(ASIO_MOVE_CAST(E)(e)),
|
||||||
|
target_(executor_arg_t(), executor_, ASIO_MOVE_CAST(U)(u))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Executor executor_;
|
||||||
|
T target_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename Executor>
|
||||||
|
class executor_binder_base<T, Executor, false>
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
template <typename E, typename U>
|
||||||
|
executor_binder_base(ASIO_MOVE_ARG(E) e, ASIO_MOVE_ARG(U) u)
|
||||||
|
: executor_(ASIO_MOVE_CAST(E)(e)),
|
||||||
|
target_(ASIO_MOVE_CAST(U)(u))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Executor executor_;
|
||||||
|
T target_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper to enable SFINAE on zero-argument operator() below.
|
||||||
|
|
||||||
|
template <typename T, typename = void>
|
||||||
|
struct executor_binder_result_of0
|
||||||
|
{
|
||||||
|
typedef void type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct executor_binder_result_of0<T,
|
||||||
|
typename executor_binder_check<typename result_of<T()>::type>::type>
|
||||||
|
{
|
||||||
|
typedef typename result_of<T()>::type type;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
/// A call wrapper type to bind an executor of type @c Executor to an object of
|
||||||
|
/// type @c T.
|
||||||
|
template <typename T, typename Executor>
|
||||||
|
class executor_binder
|
||||||
|
#if !defined(GENERATING_DOCUMENTATION)
|
||||||
|
: public detail::executor_binder_result_type<T>,
|
||||||
|
public detail::executor_binder_argument_type<T>,
|
||||||
|
public detail::executor_binder_argument_types<T>,
|
||||||
|
private detail::executor_binder_base<
|
||||||
|
T, Executor, uses_executor<T, Executor>::value>
|
||||||
|
#endif // !defined(GENERATING_DOCUMENTATION)
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// The type of the target object.
|
||||||
|
typedef T target_type;
|
||||||
|
|
||||||
|
/// The type of the associated executor.
|
||||||
|
typedef Executor executor_type;
|
||||||
|
|
||||||
|
#if defined(GENERATING_DOCUMENTATION)
|
||||||
|
/// The return type if a function.
|
||||||
|
/**
|
||||||
|
* The type of @c result_type is based on the type @c T of the wrapper's
|
||||||
|
* target object:
|
||||||
|
*
|
||||||
|
* @li if @c T is a pointer to function type, @c result_type is a synonym for
|
||||||
|
* the return type of @c T;
|
||||||
|
*
|
||||||
|
* @li if @c T is a class type with a member type @c result_type, then @c
|
||||||
|
* result_type is a synonym for @c T::result_type;
|
||||||
|
*
|
||||||
|
* @li otherwise @c result_type is not defined.
|
||||||
|
*/
|
||||||
|
typedef see_below result_type;
|
||||||
|
|
||||||
|
/// The type of the function's argument.
|
||||||
|
/**
|
||||||
|
* The type of @c argument_type is based on the type @c T of the wrapper's
|
||||||
|
* target object:
|
||||||
|
*
|
||||||
|
* @li if @c T is a pointer to a function type accepting a single argument,
|
||||||
|
* @c argument_type is a synonym for the return type of @c T;
|
||||||
|
*
|
||||||
|
* @li if @c T is a class type with a member type @c argument_type, then @c
|
||||||
|
* argument_type is a synonym for @c T::argument_type;
|
||||||
|
*
|
||||||
|
* @li otherwise @c argument_type is not defined.
|
||||||
|
*/
|
||||||
|
typedef see_below argument_type;
|
||||||
|
|
||||||
|
/// The type of the function's first argument.
|
||||||
|
/**
|
||||||
|
* The type of @c first_argument_type is based on the type @c T of the
|
||||||
|
* wrapper's target object:
|
||||||
|
*
|
||||||
|
* @li if @c T is a pointer to a function type accepting two arguments, @c
|
||||||
|
* first_argument_type is a synonym for the return type of @c T;
|
||||||
|
*
|
||||||
|
* @li if @c T is a class type with a member type @c first_argument_type,
|
||||||
|
* then @c first_argument_type is a synonym for @c T::first_argument_type;
|
||||||
|
*
|
||||||
|
* @li otherwise @c first_argument_type is not defined.
|
||||||
|
*/
|
||||||
|
typedef see_below first_argument_type;
|
||||||
|
|
||||||
|
/// The type of the function's second argument.
|
||||||
|
/**
|
||||||
|
* The type of @c second_argument_type is based on the type @c T of the
|
||||||
|
* wrapper's target object:
|
||||||
|
*
|
||||||
|
* @li if @c T is a pointer to a function type accepting two arguments, @c
|
||||||
|
* second_argument_type is a synonym for the return type of @c T;
|
||||||
|
*
|
||||||
|
* @li if @c T is a class type with a member type @c first_argument_type,
|
||||||
|
* then @c second_argument_type is a synonym for @c T::second_argument_type;
|
||||||
|
*
|
||||||
|
* @li otherwise @c second_argument_type is not defined.
|
||||||
|
*/
|
||||||
|
typedef see_below second_argument_type;
|
||||||
|
#endif // defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
/// Construct an executor wrapper for the specified object.
|
||||||
|
/**
|
||||||
|
* This constructor is only valid if the type @c T is constructible from type
|
||||||
|
* @c U.
|
||||||
|
*/
|
||||||
|
template <typename U>
|
||||||
|
executor_binder(executor_arg_t, const executor_type& e,
|
||||||
|
ASIO_MOVE_ARG(U) u)
|
||||||
|
: base_type(e, ASIO_MOVE_CAST(U)(u))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copy constructor.
|
||||||
|
executor_binder(const executor_binder& other)
|
||||||
|
: base_type(other.get_executor(), other.get())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a copy, but specify a different executor.
|
||||||
|
executor_binder(executor_arg_t, const executor_type& e,
|
||||||
|
const executor_binder& other)
|
||||||
|
: base_type(e, other.get())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a copy of a different executor wrapper type.
|
||||||
|
/**
|
||||||
|
* This constructor is only valid if the @c Executor type is constructible
|
||||||
|
* from type @c OtherExecutor, and the type @c T is constructible from type
|
||||||
|
* @c U.
|
||||||
|
*/
|
||||||
|
template <typename U, typename OtherExecutor>
|
||||||
|
executor_binder(const executor_binder<U, OtherExecutor>& other)
|
||||||
|
: base_type(other.get_executor(), other.get())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a copy of a different executor wrapper type, but specify a
|
||||||
|
/// different executor.
|
||||||
|
/**
|
||||||
|
* This constructor is only valid if the type @c T is constructible from type
|
||||||
|
* @c U.
|
||||||
|
*/
|
||||||
|
template <typename U, typename OtherExecutor>
|
||||||
|
executor_binder(executor_arg_t, const executor_type& e,
|
||||||
|
const executor_binder<U, OtherExecutor>& other)
|
||||||
|
: base_type(e, other.get())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
/// Move constructor.
|
||||||
|
executor_binder(executor_binder&& other)
|
||||||
|
: base_type(ASIO_MOVE_CAST(executor_type)(other.get_executor()),
|
||||||
|
ASIO_MOVE_CAST(T)(other.get()))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move construct the target object, but specify a different executor.
|
||||||
|
executor_binder(executor_arg_t, const executor_type& e,
|
||||||
|
executor_binder&& other)
|
||||||
|
: base_type(e, ASIO_MOVE_CAST(T)(other.get()))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move construct from a different executor wrapper type.
|
||||||
|
template <typename U, typename OtherExecutor>
|
||||||
|
executor_binder(executor_binder<U, OtherExecutor>&& other)
|
||||||
|
: base_type(ASIO_MOVE_CAST(OtherExecutor)(other.get_executor()),
|
||||||
|
ASIO_MOVE_CAST(U)(other.get()))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move construct from a different executor wrapper type, but specify a
|
||||||
|
/// different executor.
|
||||||
|
template <typename U, typename OtherExecutor>
|
||||||
|
executor_binder(executor_arg_t, const executor_type& e,
|
||||||
|
executor_binder<U, OtherExecutor>&& other)
|
||||||
|
: base_type(e, ASIO_MOVE_CAST(U)(other.get()))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
/// Destructor.
|
||||||
|
~executor_binder()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtain a reference to the target object.
|
||||||
|
target_type& get() ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return this->target_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtain a reference to the target object.
|
||||||
|
const target_type& get() const ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return this->target_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtain the associated executor.
|
||||||
|
executor_type get_executor() const ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return this->executor_;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
template <typename... Args> auto operator()(Args&& ...);
|
||||||
|
template <typename... Args> auto operator()(Args&& ...) const;
|
||||||
|
|
||||||
|
#elif defined(ASIO_HAS_VARIADIC_TEMPLATES)
|
||||||
|
|
||||||
|
/// Forwarding function call operator.
|
||||||
|
template <typename... Args>
|
||||||
|
typename result_of<T(Args...)>::type operator()(
|
||||||
|
ASIO_MOVE_ARG(Args)... args)
|
||||||
|
{
|
||||||
|
return this->target_(ASIO_MOVE_CAST(Args)(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Forwarding function call operator.
|
||||||
|
template <typename... Args>
|
||||||
|
typename result_of<T(Args...)>::type operator()(
|
||||||
|
ASIO_MOVE_ARG(Args)... args) const
|
||||||
|
{
|
||||||
|
return this->target_(ASIO_MOVE_CAST(Args)(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(ASIO_HAS_STD_TYPE_TRAITS) && !defined(_MSC_VER)
|
||||||
|
|
||||||
|
typename detail::executor_binder_result_of0<T>::type operator()()
|
||||||
|
{
|
||||||
|
return this->target_();
|
||||||
|
}
|
||||||
|
|
||||||
|
typename detail::executor_binder_result_of0<T>::type operator()() const
|
||||||
|
{
|
||||||
|
return this->target_();
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF(n) \
|
||||||
|
template <ASIO_VARIADIC_TPARAMS(n)> \
|
||||||
|
typename result_of<T(ASIO_VARIADIC_TARGS(n))>::type operator()( \
|
||||||
|
ASIO_VARIADIC_MOVE_PARAMS(n)) \
|
||||||
|
{ \
|
||||||
|
return this->target_(ASIO_VARIADIC_MOVE_ARGS(n)); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
template <ASIO_VARIADIC_TPARAMS(n)> \
|
||||||
|
typename result_of<T(ASIO_VARIADIC_TARGS(n))>::type operator()( \
|
||||||
|
ASIO_VARIADIC_MOVE_PARAMS(n)) const \
|
||||||
|
{ \
|
||||||
|
return this->target_(ASIO_VARIADIC_MOVE_ARGS(n)); \
|
||||||
|
} \
|
||||||
|
/**/
|
||||||
|
ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF)
|
||||||
|
#undef ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF
|
||||||
|
|
||||||
|
#else // defined(ASIO_HAS_STD_TYPE_TRAITS) && !defined(_MSC_VER)
|
||||||
|
|
||||||
|
typedef typename detail::executor_binder_result_type<T>::result_type_or_void
|
||||||
|
result_type_or_void;
|
||||||
|
|
||||||
|
result_type_or_void operator()()
|
||||||
|
{
|
||||||
|
return this->target_();
|
||||||
|
}
|
||||||
|
|
||||||
|
result_type_or_void operator()() const
|
||||||
|
{
|
||||||
|
return this->target_();
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF(n) \
|
||||||
|
template <ASIO_VARIADIC_TPARAMS(n)> \
|
||||||
|
result_type_or_void operator()( \
|
||||||
|
ASIO_VARIADIC_MOVE_PARAMS(n)) \
|
||||||
|
{ \
|
||||||
|
return this->target_(ASIO_VARIADIC_MOVE_ARGS(n)); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
template <ASIO_VARIADIC_TPARAMS(n)> \
|
||||||
|
result_type_or_void operator()( \
|
||||||
|
ASIO_VARIADIC_MOVE_PARAMS(n)) const \
|
||||||
|
{ \
|
||||||
|
return this->target_(ASIO_VARIADIC_MOVE_ARGS(n)); \
|
||||||
|
} \
|
||||||
|
/**/
|
||||||
|
ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF)
|
||||||
|
#undef ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF
|
||||||
|
|
||||||
|
#endif // defined(ASIO_HAS_STD_TYPE_TRAITS) && !defined(_MSC_VER)
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef detail::executor_binder_base<T, Executor,
|
||||||
|
uses_executor<T, Executor>::value> base_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Associate an object of type @c T with an executor of type @c Executor.
|
||||||
|
template <typename Executor, typename T>
|
||||||
|
inline executor_binder<typename decay<T>::type, Executor>
|
||||||
|
bind_executor(const Executor& ex, ASIO_MOVE_ARG(T) t,
|
||||||
|
typename enable_if<is_executor<Executor>::value>::type* = 0)
|
||||||
|
{
|
||||||
|
return executor_binder<typename decay<T>::type, Executor>(
|
||||||
|
executor_arg_t(), ex, ASIO_MOVE_CAST(T)(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Associate an object of type @c T with an execution context's executor.
|
||||||
|
template <typename ExecutionContext, typename T>
|
||||||
|
inline executor_binder<typename decay<T>::type,
|
||||||
|
typename ExecutionContext::executor_type>
|
||||||
|
bind_executor(ExecutionContext& ctx, ASIO_MOVE_ARG(T) t,
|
||||||
|
typename enable_if<is_convertible<
|
||||||
|
ExecutionContext&, execution_context&>::value>::type* = 0)
|
||||||
|
{
|
||||||
|
return executor_binder<typename decay<T>::type,
|
||||||
|
typename ExecutionContext::executor_type>(
|
||||||
|
executor_arg_t(), ctx.get_executor(), ASIO_MOVE_CAST(T)(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
template <typename T, typename Executor>
|
||||||
|
struct uses_executor<executor_binder<T, Executor>, Executor>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
template <typename T, typename Executor, typename Signature>
|
||||||
|
class async_result<executor_binder<T, Executor>, Signature>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef executor_binder<
|
||||||
|
typename async_result<T, Signature>::completion_handler_type, Executor>
|
||||||
|
completion_handler_type;
|
||||||
|
|
||||||
|
typedef typename async_result<T, Signature>::return_type return_type;
|
||||||
|
|
||||||
|
explicit async_result(executor_binder<T, Executor>& b)
|
||||||
|
: target_(b.get())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
return_type get()
|
||||||
|
{
|
||||||
|
return target_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
async_result(const async_result&) ASIO_DELETED;
|
||||||
|
async_result& operator=(const async_result&) ASIO_DELETED;
|
||||||
|
|
||||||
|
async_result<T, Signature> target_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if !defined(ASIO_NO_DEPRECATED)
|
||||||
|
|
||||||
|
template <typename T, typename Executor, typename Signature>
|
||||||
|
struct handler_type<executor_binder<T, Executor>, Signature>
|
||||||
|
{
|
||||||
|
typedef executor_binder<
|
||||||
|
typename handler_type<T, Signature>::type, Executor> type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename Executor>
|
||||||
|
class async_result<executor_binder<T, Executor> >
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef typename async_result<T>::type type;
|
||||||
|
|
||||||
|
explicit async_result(executor_binder<T, Executor>& b)
|
||||||
|
: target_(b.get())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
type get()
|
||||||
|
{
|
||||||
|
return target_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
async_result<T> target_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // !defined(ASIO_NO_DEPRECATED)
|
||||||
|
|
||||||
|
template <typename T, typename Executor, typename Allocator>
|
||||||
|
struct associated_allocator<executor_binder<T, Executor>, Allocator>
|
||||||
|
{
|
||||||
|
typedef typename associated_allocator<T, Allocator>::type type;
|
||||||
|
|
||||||
|
static type get(const executor_binder<T, Executor>& b,
|
||||||
|
const Allocator& a = Allocator()) ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return associated_allocator<T, Allocator>::get(b.get(), a);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename Executor, typename Executor1>
|
||||||
|
struct associated_executor<executor_binder<T, Executor>, Executor1>
|
||||||
|
{
|
||||||
|
typedef Executor type;
|
||||||
|
|
||||||
|
static type get(const executor_binder<T, Executor>& b,
|
||||||
|
const Executor1& = Executor1()) ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return b.get_executor();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // !defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
} // namespace asio
|
||||||
|
|
||||||
|
#include "asio/detail/pop_options.hpp"
|
||||||
|
|
||||||
|
#endif // ASIO_BIND_EXECUTOR_HPP
|
2169
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/asio/buffer.hpp
vendored
Normal file
2169
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/asio/buffer.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,257 @@
|
|||||||
|
//
|
||||||
|
// buffered_read_stream.hpp
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ASIO_BUFFERED_READ_STREAM_HPP
|
||||||
|
#define ASIO_BUFFERED_READ_STREAM_HPP
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
# pragma once
|
||||||
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
|
||||||
|
#include "asio/detail/config.hpp"
|
||||||
|
#include <cstddef>
|
||||||
|
#include "asio/async_result.hpp"
|
||||||
|
#include "asio/buffered_read_stream_fwd.hpp"
|
||||||
|
#include "asio/buffer.hpp"
|
||||||
|
#include "asio/detail/bind_handler.hpp"
|
||||||
|
#include "asio/detail/buffer_resize_guard.hpp"
|
||||||
|
#include "asio/detail/buffered_stream_storage.hpp"
|
||||||
|
#include "asio/detail/noncopyable.hpp"
|
||||||
|
#include "asio/detail/type_traits.hpp"
|
||||||
|
#include "asio/error.hpp"
|
||||||
|
#include "asio/io_context.hpp"
|
||||||
|
|
||||||
|
#include "asio/detail/push_options.hpp"
|
||||||
|
|
||||||
|
namespace asio {
|
||||||
|
|
||||||
|
/// Adds buffering to the read-related operations of a stream.
|
||||||
|
/**
|
||||||
|
* The buffered_read_stream class template can be used to add buffering to the
|
||||||
|
* synchronous and asynchronous read operations of a stream.
|
||||||
|
*
|
||||||
|
* @par Thread Safety
|
||||||
|
* @e Distinct @e objects: Safe.@n
|
||||||
|
* @e Shared @e objects: Unsafe.
|
||||||
|
*
|
||||||
|
* @par Concepts:
|
||||||
|
* AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream.
|
||||||
|
*/
|
||||||
|
template <typename Stream>
|
||||||
|
class buffered_read_stream
|
||||||
|
: private noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// The type of the next layer.
|
||||||
|
typedef typename remove_reference<Stream>::type next_layer_type;
|
||||||
|
|
||||||
|
/// The type of the lowest layer.
|
||||||
|
typedef typename next_layer_type::lowest_layer_type lowest_layer_type;
|
||||||
|
|
||||||
|
/// The type of the executor associated with the object.
|
||||||
|
typedef typename lowest_layer_type::executor_type executor_type;
|
||||||
|
|
||||||
|
#if defined(GENERATING_DOCUMENTATION)
|
||||||
|
/// The default buffer size.
|
||||||
|
static const std::size_t default_buffer_size = implementation_defined;
|
||||||
|
#else
|
||||||
|
ASIO_STATIC_CONSTANT(std::size_t, default_buffer_size = 1024);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Construct, passing the specified argument to initialise the next layer.
|
||||||
|
template <typename Arg>
|
||||||
|
explicit buffered_read_stream(Arg& a)
|
||||||
|
: next_layer_(a),
|
||||||
|
storage_(default_buffer_size)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct, passing the specified argument to initialise the next layer.
|
||||||
|
template <typename Arg>
|
||||||
|
buffered_read_stream(Arg& a, std::size_t buffer_size)
|
||||||
|
: next_layer_(a),
|
||||||
|
storage_(buffer_size)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a reference to the next layer.
|
||||||
|
next_layer_type& next_layer()
|
||||||
|
{
|
||||||
|
return next_layer_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a reference to the lowest layer.
|
||||||
|
lowest_layer_type& lowest_layer()
|
||||||
|
{
|
||||||
|
return next_layer_.lowest_layer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a const reference to the lowest layer.
|
||||||
|
const lowest_layer_type& lowest_layer() const
|
||||||
|
{
|
||||||
|
return next_layer_.lowest_layer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the executor associated with the object.
|
||||||
|
executor_type get_executor() ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return next_layer_.lowest_layer().get_executor();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(ASIO_NO_DEPRECATED)
|
||||||
|
/// (Deprecated: Use get_executor().) Get the io_context associated with the
|
||||||
|
/// object.
|
||||||
|
asio::io_context& get_io_context()
|
||||||
|
{
|
||||||
|
return next_layer_.get_io_context();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// (Deprecated: Use get_executor().) Get the io_context associated with the
|
||||||
|
/// object.
|
||||||
|
asio::io_context& get_io_service()
|
||||||
|
{
|
||||||
|
return next_layer_.get_io_service();
|
||||||
|
}
|
||||||
|
#endif // !defined(ASIO_NO_DEPRECATED)
|
||||||
|
|
||||||
|
/// Close the stream.
|
||||||
|
void close()
|
||||||
|
{
|
||||||
|
next_layer_.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Close the stream.
|
||||||
|
ASIO_SYNC_OP_VOID close(asio::error_code& ec)
|
||||||
|
{
|
||||||
|
next_layer_.close(ec);
|
||||||
|
ASIO_SYNC_OP_VOID_RETURN(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the given data to the stream. Returns the number of bytes written.
|
||||||
|
/// Throws an exception on failure.
|
||||||
|
template <typename ConstBufferSequence>
|
||||||
|
std::size_t write_some(const ConstBufferSequence& buffers)
|
||||||
|
{
|
||||||
|
return next_layer_.write_some(buffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the given data to the stream. Returns the number of bytes written,
|
||||||
|
/// or 0 if an error occurred.
|
||||||
|
template <typename ConstBufferSequence>
|
||||||
|
std::size_t write_some(const ConstBufferSequence& buffers,
|
||||||
|
asio::error_code& ec)
|
||||||
|
{
|
||||||
|
return next_layer_.write_some(buffers, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start an asynchronous write. The data being written must be valid for the
|
||||||
|
/// lifetime of the asynchronous operation.
|
||||||
|
template <typename ConstBufferSequence, typename WriteHandler>
|
||||||
|
ASIO_INITFN_RESULT_TYPE(WriteHandler,
|
||||||
|
void (asio::error_code, std::size_t))
|
||||||
|
async_write_some(const ConstBufferSequence& buffers,
|
||||||
|
ASIO_MOVE_ARG(WriteHandler) handler)
|
||||||
|
{
|
||||||
|
return next_layer_.async_write_some(buffers,
|
||||||
|
ASIO_MOVE_CAST(WriteHandler)(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fill the buffer with some data. Returns the number of bytes placed in the
|
||||||
|
/// buffer as a result of the operation. Throws an exception on failure.
|
||||||
|
std::size_t fill();
|
||||||
|
|
||||||
|
/// Fill the buffer with some data. Returns the number of bytes placed in the
|
||||||
|
/// buffer as a result of the operation, or 0 if an error occurred.
|
||||||
|
std::size_t fill(asio::error_code& ec);
|
||||||
|
|
||||||
|
/// Start an asynchronous fill.
|
||||||
|
template <typename ReadHandler>
|
||||||
|
ASIO_INITFN_RESULT_TYPE(ReadHandler,
|
||||||
|
void (asio::error_code, std::size_t))
|
||||||
|
async_fill(ASIO_MOVE_ARG(ReadHandler) handler);
|
||||||
|
|
||||||
|
/// Read some data from the stream. Returns the number of bytes read. Throws
|
||||||
|
/// an exception on failure.
|
||||||
|
template <typename MutableBufferSequence>
|
||||||
|
std::size_t read_some(const MutableBufferSequence& buffers);
|
||||||
|
|
||||||
|
/// Read some data from the stream. Returns the number of bytes read or 0 if
|
||||||
|
/// an error occurred.
|
||||||
|
template <typename MutableBufferSequence>
|
||||||
|
std::size_t read_some(const MutableBufferSequence& buffers,
|
||||||
|
asio::error_code& ec);
|
||||||
|
|
||||||
|
/// Start an asynchronous read. The buffer into which the data will be read
|
||||||
|
/// must be valid for the lifetime of the asynchronous operation.
|
||||||
|
template <typename MutableBufferSequence, typename ReadHandler>
|
||||||
|
ASIO_INITFN_RESULT_TYPE(ReadHandler,
|
||||||
|
void (asio::error_code, std::size_t))
|
||||||
|
async_read_some(const MutableBufferSequence& buffers,
|
||||||
|
ASIO_MOVE_ARG(ReadHandler) handler);
|
||||||
|
|
||||||
|
/// Peek at the incoming data on the stream. Returns the number of bytes read.
|
||||||
|
/// Throws an exception on failure.
|
||||||
|
template <typename MutableBufferSequence>
|
||||||
|
std::size_t peek(const MutableBufferSequence& buffers);
|
||||||
|
|
||||||
|
/// Peek at the incoming data on the stream. Returns the number of bytes read,
|
||||||
|
/// or 0 if an error occurred.
|
||||||
|
template <typename MutableBufferSequence>
|
||||||
|
std::size_t peek(const MutableBufferSequence& buffers,
|
||||||
|
asio::error_code& ec);
|
||||||
|
|
||||||
|
/// Determine the amount of data that may be read without blocking.
|
||||||
|
std::size_t in_avail()
|
||||||
|
{
|
||||||
|
return storage_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determine the amount of data that may be read without blocking.
|
||||||
|
std::size_t in_avail(asio::error_code& ec)
|
||||||
|
{
|
||||||
|
ec = asio::error_code();
|
||||||
|
return storage_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Copy data out of the internal buffer to the specified target buffer.
|
||||||
|
/// Returns the number of bytes copied.
|
||||||
|
template <typename MutableBufferSequence>
|
||||||
|
std::size_t copy(const MutableBufferSequence& buffers)
|
||||||
|
{
|
||||||
|
std::size_t bytes_copied = asio::buffer_copy(
|
||||||
|
buffers, storage_.data(), storage_.size());
|
||||||
|
storage_.consume(bytes_copied);
|
||||||
|
return bytes_copied;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copy data from the internal buffer to the specified target buffer, without
|
||||||
|
/// removing the data from the internal buffer. Returns the number of bytes
|
||||||
|
/// copied.
|
||||||
|
template <typename MutableBufferSequence>
|
||||||
|
std::size_t peek_copy(const MutableBufferSequence& buffers)
|
||||||
|
{
|
||||||
|
return asio::buffer_copy(buffers, storage_.data(), storage_.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The next layer.
|
||||||
|
Stream next_layer_;
|
||||||
|
|
||||||
|
// The data in the buffer.
|
||||||
|
detail::buffered_stream_storage storage_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace asio
|
||||||
|
|
||||||
|
#include "asio/detail/pop_options.hpp"
|
||||||
|
|
||||||
|
#include "asio/impl/buffered_read_stream.hpp"
|
||||||
|
|
||||||
|
#endif // ASIO_BUFFERED_READ_STREAM_HPP
|
@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// buffered_read_stream_fwd.hpp
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ASIO_BUFFERED_READ_STREAM_FWD_HPP
|
||||||
|
#define ASIO_BUFFERED_READ_STREAM_FWD_HPP
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
# pragma once
|
||||||
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
|
||||||
|
namespace asio {
|
||||||
|
|
||||||
|
template <typename Stream>
|
||||||
|
class buffered_read_stream;
|
||||||
|
|
||||||
|
} // namespace asio
|
||||||
|
|
||||||
|
#endif // ASIO_BUFFERED_READ_STREAM_FWD_HPP
|
278
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/asio/buffered_stream.hpp
vendored
Normal file
278
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/asio/buffered_stream.hpp
vendored
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
//
|
||||||
|
// buffered_stream.hpp
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ASIO_BUFFERED_STREAM_HPP
|
||||||
|
#define ASIO_BUFFERED_STREAM_HPP
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
# pragma once
|
||||||
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
|
||||||
|
#include "asio/detail/config.hpp"
|
||||||
|
#include <cstddef>
|
||||||
|
#include "asio/async_result.hpp"
|
||||||
|
#include "asio/buffered_read_stream.hpp"
|
||||||
|
#include "asio/buffered_write_stream.hpp"
|
||||||
|
#include "asio/buffered_stream_fwd.hpp"
|
||||||
|
#include "asio/detail/noncopyable.hpp"
|
||||||
|
#include "asio/error.hpp"
|
||||||
|
#include "asio/io_context.hpp"
|
||||||
|
|
||||||
|
#include "asio/detail/push_options.hpp"
|
||||||
|
|
||||||
|
namespace asio {
|
||||||
|
|
||||||
|
/// Adds buffering to the read- and write-related operations of a stream.
|
||||||
|
/**
|
||||||
|
* The buffered_stream class template can be used to add buffering to the
|
||||||
|
* synchronous and asynchronous read and write operations of a stream.
|
||||||
|
*
|
||||||
|
* @par Thread Safety
|
||||||
|
* @e Distinct @e objects: Safe.@n
|
||||||
|
* @e Shared @e objects: Unsafe.
|
||||||
|
*
|
||||||
|
* @par Concepts:
|
||||||
|
* AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream.
|
||||||
|
*/
|
||||||
|
template <typename Stream>
|
||||||
|
class buffered_stream
|
||||||
|
: private noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// The type of the next layer.
|
||||||
|
typedef typename remove_reference<Stream>::type next_layer_type;
|
||||||
|
|
||||||
|
/// The type of the lowest layer.
|
||||||
|
typedef typename next_layer_type::lowest_layer_type lowest_layer_type;
|
||||||
|
|
||||||
|
/// The type of the executor associated with the object.
|
||||||
|
typedef typename lowest_layer_type::executor_type executor_type;
|
||||||
|
|
||||||
|
/// Construct, passing the specified argument to initialise the next layer.
|
||||||
|
template <typename Arg>
|
||||||
|
explicit buffered_stream(Arg& a)
|
||||||
|
: inner_stream_impl_(a),
|
||||||
|
stream_impl_(inner_stream_impl_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct, passing the specified argument to initialise the next layer.
|
||||||
|
template <typename Arg>
|
||||||
|
explicit buffered_stream(Arg& a, std::size_t read_buffer_size,
|
||||||
|
std::size_t write_buffer_size)
|
||||||
|
: inner_stream_impl_(a, write_buffer_size),
|
||||||
|
stream_impl_(inner_stream_impl_, read_buffer_size)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a reference to the next layer.
|
||||||
|
next_layer_type& next_layer()
|
||||||
|
{
|
||||||
|
return stream_impl_.next_layer().next_layer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a reference to the lowest layer.
|
||||||
|
lowest_layer_type& lowest_layer()
|
||||||
|
{
|
||||||
|
return stream_impl_.lowest_layer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a const reference to the lowest layer.
|
||||||
|
const lowest_layer_type& lowest_layer() const
|
||||||
|
{
|
||||||
|
return stream_impl_.lowest_layer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the executor associated with the object.
|
||||||
|
executor_type get_executor() ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return stream_impl_.lowest_layer().get_executor();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(ASIO_NO_DEPRECATED)
|
||||||
|
/// (Deprecated: Use get_executor().) Get the io_context associated with the
|
||||||
|
/// object.
|
||||||
|
asio::io_context& get_io_context()
|
||||||
|
{
|
||||||
|
return stream_impl_.get_io_context();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// (Deprecated: Use get_executor().) Get the io_context associated with the
|
||||||
|
/// object.
|
||||||
|
asio::io_context& get_io_service()
|
||||||
|
{
|
||||||
|
return stream_impl_.get_io_service();
|
||||||
|
}
|
||||||
|
#endif // !defined(ASIO_NO_DEPRECATED)
|
||||||
|
|
||||||
|
/// Close the stream.
|
||||||
|
void close()
|
||||||
|
{
|
||||||
|
stream_impl_.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Close the stream.
|
||||||
|
ASIO_SYNC_OP_VOID close(asio::error_code& ec)
|
||||||
|
{
|
||||||
|
stream_impl_.close(ec);
|
||||||
|
ASIO_SYNC_OP_VOID_RETURN(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flush all data from the buffer to the next layer. Returns the number of
|
||||||
|
/// bytes written to the next layer on the last write operation. Throws an
|
||||||
|
/// exception on failure.
|
||||||
|
std::size_t flush()
|
||||||
|
{
|
||||||
|
return stream_impl_.next_layer().flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flush all data from the buffer to the next layer. Returns the number of
|
||||||
|
/// bytes written to the next layer on the last write operation, or 0 if an
|
||||||
|
/// error occurred.
|
||||||
|
std::size_t flush(asio::error_code& ec)
|
||||||
|
{
|
||||||
|
return stream_impl_.next_layer().flush(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start an asynchronous flush.
|
||||||
|
template <typename WriteHandler>
|
||||||
|
ASIO_INITFN_RESULT_TYPE(WriteHandler,
|
||||||
|
void (asio::error_code, std::size_t))
|
||||||
|
async_flush(ASIO_MOVE_ARG(WriteHandler) handler)
|
||||||
|
{
|
||||||
|
return stream_impl_.next_layer().async_flush(
|
||||||
|
ASIO_MOVE_CAST(WriteHandler)(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the given data to the stream. Returns the number of bytes written.
|
||||||
|
/// Throws an exception on failure.
|
||||||
|
template <typename ConstBufferSequence>
|
||||||
|
std::size_t write_some(const ConstBufferSequence& buffers)
|
||||||
|
{
|
||||||
|
return stream_impl_.write_some(buffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the given data to the stream. Returns the number of bytes written,
|
||||||
|
/// or 0 if an error occurred.
|
||||||
|
template <typename ConstBufferSequence>
|
||||||
|
std::size_t write_some(const ConstBufferSequence& buffers,
|
||||||
|
asio::error_code& ec)
|
||||||
|
{
|
||||||
|
return stream_impl_.write_some(buffers, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start an asynchronous write. The data being written must be valid for the
|
||||||
|
/// lifetime of the asynchronous operation.
|
||||||
|
template <typename ConstBufferSequence, typename WriteHandler>
|
||||||
|
ASIO_INITFN_RESULT_TYPE(WriteHandler,
|
||||||
|
void (asio::error_code, std::size_t))
|
||||||
|
async_write_some(const ConstBufferSequence& buffers,
|
||||||
|
ASIO_MOVE_ARG(WriteHandler) handler)
|
||||||
|
{
|
||||||
|
return stream_impl_.async_write_some(buffers,
|
||||||
|
ASIO_MOVE_CAST(WriteHandler)(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fill the buffer with some data. Returns the number of bytes placed in the
|
||||||
|
/// buffer as a result of the operation. Throws an exception on failure.
|
||||||
|
std::size_t fill()
|
||||||
|
{
|
||||||
|
return stream_impl_.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fill the buffer with some data. Returns the number of bytes placed in the
|
||||||
|
/// buffer as a result of the operation, or 0 if an error occurred.
|
||||||
|
std::size_t fill(asio::error_code& ec)
|
||||||
|
{
|
||||||
|
return stream_impl_.fill(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start an asynchronous fill.
|
||||||
|
template <typename ReadHandler>
|
||||||
|
ASIO_INITFN_RESULT_TYPE(ReadHandler,
|
||||||
|
void (asio::error_code, std::size_t))
|
||||||
|
async_fill(ASIO_MOVE_ARG(ReadHandler) handler)
|
||||||
|
{
|
||||||
|
return stream_impl_.async_fill(ASIO_MOVE_CAST(ReadHandler)(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read some data from the stream. Returns the number of bytes read. Throws
|
||||||
|
/// an exception on failure.
|
||||||
|
template <typename MutableBufferSequence>
|
||||||
|
std::size_t read_some(const MutableBufferSequence& buffers)
|
||||||
|
{
|
||||||
|
return stream_impl_.read_some(buffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read some data from the stream. Returns the number of bytes read or 0 if
|
||||||
|
/// an error occurred.
|
||||||
|
template <typename MutableBufferSequence>
|
||||||
|
std::size_t read_some(const MutableBufferSequence& buffers,
|
||||||
|
asio::error_code& ec)
|
||||||
|
{
|
||||||
|
return stream_impl_.read_some(buffers, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start an asynchronous read. The buffer into which the data will be read
|
||||||
|
/// must be valid for the lifetime of the asynchronous operation.
|
||||||
|
template <typename MutableBufferSequence, typename ReadHandler>
|
||||||
|
ASIO_INITFN_RESULT_TYPE(ReadHandler,
|
||||||
|
void (asio::error_code, std::size_t))
|
||||||
|
async_read_some(const MutableBufferSequence& buffers,
|
||||||
|
ASIO_MOVE_ARG(ReadHandler) handler)
|
||||||
|
{
|
||||||
|
return stream_impl_.async_read_some(buffers,
|
||||||
|
ASIO_MOVE_CAST(ReadHandler)(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Peek at the incoming data on the stream. Returns the number of bytes read.
|
||||||
|
/// Throws an exception on failure.
|
||||||
|
template <typename MutableBufferSequence>
|
||||||
|
std::size_t peek(const MutableBufferSequence& buffers)
|
||||||
|
{
|
||||||
|
return stream_impl_.peek(buffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Peek at the incoming data on the stream. Returns the number of bytes read,
|
||||||
|
/// or 0 if an error occurred.
|
||||||
|
template <typename MutableBufferSequence>
|
||||||
|
std::size_t peek(const MutableBufferSequence& buffers,
|
||||||
|
asio::error_code& ec)
|
||||||
|
{
|
||||||
|
return stream_impl_.peek(buffers, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determine the amount of data that may be read without blocking.
|
||||||
|
std::size_t in_avail()
|
||||||
|
{
|
||||||
|
return stream_impl_.in_avail();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determine the amount of data that may be read without blocking.
|
||||||
|
std::size_t in_avail(asio::error_code& ec)
|
||||||
|
{
|
||||||
|
return stream_impl_.in_avail(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// The buffered write stream.
|
||||||
|
typedef buffered_write_stream<Stream> write_stream_type;
|
||||||
|
write_stream_type inner_stream_impl_;
|
||||||
|
|
||||||
|
// The buffered read stream.
|
||||||
|
typedef buffered_read_stream<write_stream_type&> read_stream_type;
|
||||||
|
read_stream_type stream_impl_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace asio
|
||||||
|
|
||||||
|
#include "asio/detail/pop_options.hpp"
|
||||||
|
|
||||||
|
#endif // ASIO_BUFFERED_STREAM_HPP
|
@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// buffered_stream_fwd.hpp
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ASIO_BUFFERED_STREAM_FWD_HPP
|
||||||
|
#define ASIO_BUFFERED_STREAM_FWD_HPP
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
# pragma once
|
||||||
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
|
||||||
|
namespace asio {
|
||||||
|
|
||||||
|
template <typename Stream>
|
||||||
|
class buffered_stream;
|
||||||
|
|
||||||
|
} // namespace asio
|
||||||
|
|
||||||
|
#endif // ASIO_BUFFERED_STREAM_FWD_HPP
|
@ -0,0 +1,249 @@
|
|||||||
|
//
|
||||||
|
// buffered_write_stream.hpp
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ASIO_BUFFERED_WRITE_STREAM_HPP
|
||||||
|
#define ASIO_BUFFERED_WRITE_STREAM_HPP
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
# pragma once
|
||||||
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
|
||||||
|
#include "asio/detail/config.hpp"
|
||||||
|
#include <cstddef>
|
||||||
|
#include "asio/buffered_write_stream_fwd.hpp"
|
||||||
|
#include "asio/buffer.hpp"
|
||||||
|
#include "asio/completion_condition.hpp"
|
||||||
|
#include "asio/detail/bind_handler.hpp"
|
||||||
|
#include "asio/detail/buffered_stream_storage.hpp"
|
||||||
|
#include "asio/detail/noncopyable.hpp"
|
||||||
|
#include "asio/detail/type_traits.hpp"
|
||||||
|
#include "asio/error.hpp"
|
||||||
|
#include "asio/io_context.hpp"
|
||||||
|
#include "asio/write.hpp"
|
||||||
|
|
||||||
|
#include "asio/detail/push_options.hpp"
|
||||||
|
|
||||||
|
namespace asio {
|
||||||
|
|
||||||
|
/// Adds buffering to the write-related operations of a stream.
|
||||||
|
/**
|
||||||
|
* The buffered_write_stream class template can be used to add buffering to the
|
||||||
|
* synchronous and asynchronous write operations of a stream.
|
||||||
|
*
|
||||||
|
* @par Thread Safety
|
||||||
|
* @e Distinct @e objects: Safe.@n
|
||||||
|
* @e Shared @e objects: Unsafe.
|
||||||
|
*
|
||||||
|
* @par Concepts:
|
||||||
|
* AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream.
|
||||||
|
*/
|
||||||
|
template <typename Stream>
|
||||||
|
class buffered_write_stream
|
||||||
|
: private noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// The type of the next layer.
|
||||||
|
typedef typename remove_reference<Stream>::type next_layer_type;
|
||||||
|
|
||||||
|
/// The type of the lowest layer.
|
||||||
|
typedef typename next_layer_type::lowest_layer_type lowest_layer_type;
|
||||||
|
|
||||||
|
/// The type of the executor associated with the object.
|
||||||
|
typedef typename lowest_layer_type::executor_type executor_type;
|
||||||
|
|
||||||
|
#if defined(GENERATING_DOCUMENTATION)
|
||||||
|
/// The default buffer size.
|
||||||
|
static const std::size_t default_buffer_size = implementation_defined;
|
||||||
|
#else
|
||||||
|
ASIO_STATIC_CONSTANT(std::size_t, default_buffer_size = 1024);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Construct, passing the specified argument to initialise the next layer.
|
||||||
|
template <typename Arg>
|
||||||
|
explicit buffered_write_stream(Arg& a)
|
||||||
|
: next_layer_(a),
|
||||||
|
storage_(default_buffer_size)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct, passing the specified argument to initialise the next layer.
|
||||||
|
template <typename Arg>
|
||||||
|
buffered_write_stream(Arg& a, std::size_t buffer_size)
|
||||||
|
: next_layer_(a),
|
||||||
|
storage_(buffer_size)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a reference to the next layer.
|
||||||
|
next_layer_type& next_layer()
|
||||||
|
{
|
||||||
|
return next_layer_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a reference to the lowest layer.
|
||||||
|
lowest_layer_type& lowest_layer()
|
||||||
|
{
|
||||||
|
return next_layer_.lowest_layer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a const reference to the lowest layer.
|
||||||
|
const lowest_layer_type& lowest_layer() const
|
||||||
|
{
|
||||||
|
return next_layer_.lowest_layer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the executor associated with the object.
|
||||||
|
executor_type get_executor() ASIO_NOEXCEPT
|
||||||
|
{
|
||||||
|
return next_layer_.lowest_layer().get_executor();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(ASIO_NO_DEPRECATED)
|
||||||
|
/// (Deprecated: Use get_executor().) Get the io_context associated with the
|
||||||
|
/// object.
|
||||||
|
asio::io_context& get_io_context()
|
||||||
|
{
|
||||||
|
return next_layer_.get_io_context();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// (Deprecated: Use get_executor().) Get the io_context associated with the
|
||||||
|
/// object.
|
||||||
|
asio::io_context& get_io_service()
|
||||||
|
{
|
||||||
|
return next_layer_.get_io_service();
|
||||||
|
}
|
||||||
|
#endif // !defined(ASIO_NO_DEPRECATED)
|
||||||
|
|
||||||
|
/// Close the stream.
|
||||||
|
void close()
|
||||||
|
{
|
||||||
|
next_layer_.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Close the stream.
|
||||||
|
ASIO_SYNC_OP_VOID close(asio::error_code& ec)
|
||||||
|
{
|
||||||
|
next_layer_.close(ec);
|
||||||
|
ASIO_SYNC_OP_VOID_RETURN(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flush all data from the buffer to the next layer. Returns the number of
|
||||||
|
/// bytes written to the next layer on the last write operation. Throws an
|
||||||
|
/// exception on failure.
|
||||||
|
std::size_t flush();
|
||||||
|
|
||||||
|
/// Flush all data from the buffer to the next layer. Returns the number of
|
||||||
|
/// bytes written to the next layer on the last write operation, or 0 if an
|
||||||
|
/// error occurred.
|
||||||
|
std::size_t flush(asio::error_code& ec);
|
||||||
|
|
||||||
|
/// Start an asynchronous flush.
|
||||||
|
template <typename WriteHandler>
|
||||||
|
ASIO_INITFN_RESULT_TYPE(WriteHandler,
|
||||||
|
void (asio::error_code, std::size_t))
|
||||||
|
async_flush(ASIO_MOVE_ARG(WriteHandler) handler);
|
||||||
|
|
||||||
|
/// Write the given data to the stream. Returns the number of bytes written.
|
||||||
|
/// Throws an exception on failure.
|
||||||
|
template <typename ConstBufferSequence>
|
||||||
|
std::size_t write_some(const ConstBufferSequence& buffers);
|
||||||
|
|
||||||
|
/// Write the given data to the stream. Returns the number of bytes written,
|
||||||
|
/// or 0 if an error occurred and the error handler did not throw.
|
||||||
|
template <typename ConstBufferSequence>
|
||||||
|
std::size_t write_some(const ConstBufferSequence& buffers,
|
||||||
|
asio::error_code& ec);
|
||||||
|
|
||||||
|
/// Start an asynchronous write. The data being written must be valid for the
|
||||||
|
/// lifetime of the asynchronous operation.
|
||||||
|
template <typename ConstBufferSequence, typename WriteHandler>
|
||||||
|
ASIO_INITFN_RESULT_TYPE(WriteHandler,
|
||||||
|
void (asio::error_code, std::size_t))
|
||||||
|
async_write_some(const ConstBufferSequence& buffers,
|
||||||
|
ASIO_MOVE_ARG(WriteHandler) handler);
|
||||||
|
|
||||||
|
/// Read some data from the stream. Returns the number of bytes read. Throws
|
||||||
|
/// an exception on failure.
|
||||||
|
template <typename MutableBufferSequence>
|
||||||
|
std::size_t read_some(const MutableBufferSequence& buffers)
|
||||||
|
{
|
||||||
|
return next_layer_.read_some(buffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read some data from the stream. Returns the number of bytes read or 0 if
|
||||||
|
/// an error occurred.
|
||||||
|
template <typename MutableBufferSequence>
|
||||||
|
std::size_t read_some(const MutableBufferSequence& buffers,
|
||||||
|
asio::error_code& ec)
|
||||||
|
{
|
||||||
|
return next_layer_.read_some(buffers, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start an asynchronous read. The buffer into which the data will be read
|
||||||
|
/// must be valid for the lifetime of the asynchronous operation.
|
||||||
|
template <typename MutableBufferSequence, typename ReadHandler>
|
||||||
|
ASIO_INITFN_RESULT_TYPE(ReadHandler,
|
||||||
|
void (asio::error_code, std::size_t))
|
||||||
|
async_read_some(const MutableBufferSequence& buffers,
|
||||||
|
ASIO_MOVE_ARG(ReadHandler) handler)
|
||||||
|
{
|
||||||
|
return next_layer_.async_read_some(buffers,
|
||||||
|
ASIO_MOVE_CAST(ReadHandler)(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Peek at the incoming data on the stream. Returns the number of bytes read.
|
||||||
|
/// Throws an exception on failure.
|
||||||
|
template <typename MutableBufferSequence>
|
||||||
|
std::size_t peek(const MutableBufferSequence& buffers)
|
||||||
|
{
|
||||||
|
return next_layer_.peek(buffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Peek at the incoming data on the stream. Returns the number of bytes read,
|
||||||
|
/// or 0 if an error occurred.
|
||||||
|
template <typename MutableBufferSequence>
|
||||||
|
std::size_t peek(const MutableBufferSequence& buffers,
|
||||||
|
asio::error_code& ec)
|
||||||
|
{
|
||||||
|
return next_layer_.peek(buffers, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determine the amount of data that may be read without blocking.
|
||||||
|
std::size_t in_avail()
|
||||||
|
{
|
||||||
|
return next_layer_.in_avail();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determine the amount of data that may be read without blocking.
|
||||||
|
std::size_t in_avail(asio::error_code& ec)
|
||||||
|
{
|
||||||
|
return next_layer_.in_avail(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Copy data into the internal buffer from the specified source buffer.
|
||||||
|
/// Returns the number of bytes copied.
|
||||||
|
template <typename ConstBufferSequence>
|
||||||
|
std::size_t copy(const ConstBufferSequence& buffers);
|
||||||
|
|
||||||
|
/// The next layer.
|
||||||
|
Stream next_layer_;
|
||||||
|
|
||||||
|
// The data in the buffer.
|
||||||
|
detail::buffered_stream_storage storage_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace asio
|
||||||
|
|
||||||
|
#include "asio/detail/pop_options.hpp"
|
||||||
|
|
||||||
|
#include "asio/impl/buffered_write_stream.hpp"
|
||||||
|
|
||||||
|
#endif // ASIO_BUFFERED_WRITE_STREAM_HPP
|
@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// buffered_write_stream_fwd.hpp
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ASIO_BUFFERED_WRITE_STREAM_FWD_HPP
|
||||||
|
#define ASIO_BUFFERED_WRITE_STREAM_FWD_HPP
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
# pragma once
|
||||||
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
|
||||||
|
namespace asio {
|
||||||
|
|
||||||
|
template <typename Stream>
|
||||||
|
class buffered_write_stream;
|
||||||
|
|
||||||
|
} // namespace asio
|
||||||
|
|
||||||
|
#endif // ASIO_BUFFERED_WRITE_STREAM_FWD_HPP
|
521
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/asio/buffers_iterator.hpp
vendored
Normal file
521
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/asio/buffers_iterator.hpp
vendored
Normal file
@ -0,0 +1,521 @@
|
|||||||
|
//
|
||||||
|
// buffers_iterator.hpp
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ASIO_BUFFERS_ITERATOR_HPP
|
||||||
|
#define ASIO_BUFFERS_ITERATOR_HPP
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
# pragma once
|
||||||
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
|
||||||
|
#include "asio/detail/config.hpp"
|
||||||
|
#include <cstddef>
|
||||||
|
#include <iterator>
|
||||||
|
#include "asio/buffer.hpp"
|
||||||
|
#include "asio/detail/assert.hpp"
|
||||||
|
#include "asio/detail/type_traits.hpp"
|
||||||
|
|
||||||
|
#include "asio/detail/push_options.hpp"
|
||||||
|
|
||||||
|
namespace asio {
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
template <bool IsMutable>
|
||||||
|
struct buffers_iterator_types_helper;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct buffers_iterator_types_helper<false>
|
||||||
|
{
|
||||||
|
typedef const_buffer buffer_type;
|
||||||
|
template <typename ByteType>
|
||||||
|
struct byte_type
|
||||||
|
{
|
||||||
|
typedef typename add_const<ByteType>::type type;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct buffers_iterator_types_helper<true>
|
||||||
|
{
|
||||||
|
typedef mutable_buffer buffer_type;
|
||||||
|
template <typename ByteType>
|
||||||
|
struct byte_type
|
||||||
|
{
|
||||||
|
typedef ByteType type;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename BufferSequence, typename ByteType>
|
||||||
|
struct buffers_iterator_types
|
||||||
|
{
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
is_mutable = is_convertible<
|
||||||
|
typename BufferSequence::value_type,
|
||||||
|
mutable_buffer>::value
|
||||||
|
};
|
||||||
|
typedef buffers_iterator_types_helper<is_mutable> helper;
|
||||||
|
typedef typename helper::buffer_type buffer_type;
|
||||||
|
typedef typename helper::template byte_type<ByteType>::type byte_type;
|
||||||
|
typedef typename BufferSequence::const_iterator const_iterator;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ByteType>
|
||||||
|
struct buffers_iterator_types<mutable_buffer, ByteType>
|
||||||
|
{
|
||||||
|
typedef mutable_buffer buffer_type;
|
||||||
|
typedef ByteType byte_type;
|
||||||
|
typedef const mutable_buffer* const_iterator;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ByteType>
|
||||||
|
struct buffers_iterator_types<const_buffer, ByteType>
|
||||||
|
{
|
||||||
|
typedef const_buffer buffer_type;
|
||||||
|
typedef typename add_const<ByteType>::type byte_type;
|
||||||
|
typedef const const_buffer* const_iterator;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if !defined(ASIO_NO_DEPRECATED)
|
||||||
|
|
||||||
|
template <typename ByteType>
|
||||||
|
struct buffers_iterator_types<mutable_buffers_1, ByteType>
|
||||||
|
{
|
||||||
|
typedef mutable_buffer buffer_type;
|
||||||
|
typedef ByteType byte_type;
|
||||||
|
typedef const mutable_buffer* const_iterator;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ByteType>
|
||||||
|
struct buffers_iterator_types<const_buffers_1, ByteType>
|
||||||
|
{
|
||||||
|
typedef const_buffer buffer_type;
|
||||||
|
typedef typename add_const<ByteType>::type byte_type;
|
||||||
|
typedef const const_buffer* const_iterator;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // !defined(ASIO_NO_DEPRECATED)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A random access iterator over the bytes in a buffer sequence.
|
||||||
|
template <typename BufferSequence, typename ByteType = char>
|
||||||
|
class buffers_iterator
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
typedef typename detail::buffers_iterator_types<
|
||||||
|
BufferSequence, ByteType>::buffer_type buffer_type;
|
||||||
|
|
||||||
|
typedef typename detail::buffers_iterator_types<BufferSequence,
|
||||||
|
ByteType>::const_iterator buffer_sequence_iterator_type;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// The type used for the distance between two iterators.
|
||||||
|
typedef std::ptrdiff_t difference_type;
|
||||||
|
|
||||||
|
/// The type of the value pointed to by the iterator.
|
||||||
|
typedef ByteType value_type;
|
||||||
|
|
||||||
|
#if defined(GENERATING_DOCUMENTATION)
|
||||||
|
/// The type of the result of applying operator->() to the iterator.
|
||||||
|
/**
|
||||||
|
* If the buffer sequence stores buffer objects that are convertible to
|
||||||
|
* mutable_buffer, this is a pointer to a non-const ByteType. Otherwise, a
|
||||||
|
* pointer to a const ByteType.
|
||||||
|
*/
|
||||||
|
typedef const_or_non_const_ByteType* pointer;
|
||||||
|
#else // defined(GENERATING_DOCUMENTATION)
|
||||||
|
typedef typename detail::buffers_iterator_types<
|
||||||
|
BufferSequence, ByteType>::byte_type* pointer;
|
||||||
|
#endif // defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
#if defined(GENERATING_DOCUMENTATION)
|
||||||
|
/// The type of the result of applying operator*() to the iterator.
|
||||||
|
/**
|
||||||
|
* If the buffer sequence stores buffer objects that are convertible to
|
||||||
|
* mutable_buffer, this is a reference to a non-const ByteType. Otherwise, a
|
||||||
|
* reference to a const ByteType.
|
||||||
|
*/
|
||||||
|
typedef const_or_non_const_ByteType& reference;
|
||||||
|
#else // defined(GENERATING_DOCUMENTATION)
|
||||||
|
typedef typename detail::buffers_iterator_types<
|
||||||
|
BufferSequence, ByteType>::byte_type& reference;
|
||||||
|
#endif // defined(GENERATING_DOCUMENTATION)
|
||||||
|
|
||||||
|
/// The iterator category.
|
||||||
|
typedef std::random_access_iterator_tag iterator_category;
|
||||||
|
|
||||||
|
/// Default constructor. Creates an iterator in an undefined state.
|
||||||
|
buffers_iterator()
|
||||||
|
: current_buffer_(),
|
||||||
|
current_buffer_position_(0),
|
||||||
|
begin_(),
|
||||||
|
current_(),
|
||||||
|
end_(),
|
||||||
|
position_(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct an iterator representing the beginning of the buffers' data.
|
||||||
|
static buffers_iterator begin(const BufferSequence& buffers)
|
||||||
|
#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3)
|
||||||
|
__attribute__ ((__noinline__))
|
||||||
|
#endif // defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3)
|
||||||
|
{
|
||||||
|
buffers_iterator new_iter;
|
||||||
|
new_iter.begin_ = asio::buffer_sequence_begin(buffers);
|
||||||
|
new_iter.current_ = asio::buffer_sequence_begin(buffers);
|
||||||
|
new_iter.end_ = asio::buffer_sequence_end(buffers);
|
||||||
|
while (new_iter.current_ != new_iter.end_)
|
||||||
|
{
|
||||||
|
new_iter.current_buffer_ = *new_iter.current_;
|
||||||
|
if (new_iter.current_buffer_.size() > 0)
|
||||||
|
break;
|
||||||
|
++new_iter.current_;
|
||||||
|
}
|
||||||
|
return new_iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct an iterator representing the end of the buffers' data.
|
||||||
|
static buffers_iterator end(const BufferSequence& buffers)
|
||||||
|
#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3)
|
||||||
|
__attribute__ ((__noinline__))
|
||||||
|
#endif // defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3)
|
||||||
|
{
|
||||||
|
buffers_iterator new_iter;
|
||||||
|
new_iter.begin_ = asio::buffer_sequence_begin(buffers);
|
||||||
|
new_iter.current_ = asio::buffer_sequence_begin(buffers);
|
||||||
|
new_iter.end_ = asio::buffer_sequence_end(buffers);
|
||||||
|
while (new_iter.current_ != new_iter.end_)
|
||||||
|
{
|
||||||
|
buffer_type buffer = *new_iter.current_;
|
||||||
|
new_iter.position_ += buffer.size();
|
||||||
|
++new_iter.current_;
|
||||||
|
}
|
||||||
|
return new_iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dereference an iterator.
|
||||||
|
reference operator*() const
|
||||||
|
{
|
||||||
|
return dereference();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dereference an iterator.
|
||||||
|
pointer operator->() const
|
||||||
|
{
|
||||||
|
return &dereference();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access an individual element.
|
||||||
|
reference operator[](std::ptrdiff_t difference) const
|
||||||
|
{
|
||||||
|
buffers_iterator tmp(*this);
|
||||||
|
tmp.advance(difference);
|
||||||
|
return *tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Increment operator (prefix).
|
||||||
|
buffers_iterator& operator++()
|
||||||
|
{
|
||||||
|
increment();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Increment operator (postfix).
|
||||||
|
buffers_iterator operator++(int)
|
||||||
|
{
|
||||||
|
buffers_iterator tmp(*this);
|
||||||
|
++*this;
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decrement operator (prefix).
|
||||||
|
buffers_iterator& operator--()
|
||||||
|
{
|
||||||
|
decrement();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decrement operator (postfix).
|
||||||
|
buffers_iterator operator--(int)
|
||||||
|
{
|
||||||
|
buffers_iterator tmp(*this);
|
||||||
|
--*this;
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Addition operator.
|
||||||
|
buffers_iterator& operator+=(std::ptrdiff_t difference)
|
||||||
|
{
|
||||||
|
advance(difference);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Subtraction operator.
|
||||||
|
buffers_iterator& operator-=(std::ptrdiff_t difference)
|
||||||
|
{
|
||||||
|
advance(-difference);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Addition operator.
|
||||||
|
friend buffers_iterator operator+(const buffers_iterator& iter,
|
||||||
|
std::ptrdiff_t difference)
|
||||||
|
{
|
||||||
|
buffers_iterator tmp(iter);
|
||||||
|
tmp.advance(difference);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Addition operator.
|
||||||
|
friend buffers_iterator operator+(std::ptrdiff_t difference,
|
||||||
|
const buffers_iterator& iter)
|
||||||
|
{
|
||||||
|
buffers_iterator tmp(iter);
|
||||||
|
tmp.advance(difference);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Subtraction operator.
|
||||||
|
friend buffers_iterator operator-(const buffers_iterator& iter,
|
||||||
|
std::ptrdiff_t difference)
|
||||||
|
{
|
||||||
|
buffers_iterator tmp(iter);
|
||||||
|
tmp.advance(-difference);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Subtraction operator.
|
||||||
|
friend std::ptrdiff_t operator-(const buffers_iterator& a,
|
||||||
|
const buffers_iterator& b)
|
||||||
|
{
|
||||||
|
return b.distance_to(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test two iterators for equality.
|
||||||
|
friend bool operator==(const buffers_iterator& a, const buffers_iterator& b)
|
||||||
|
{
|
||||||
|
return a.equal(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test two iterators for inequality.
|
||||||
|
friend bool operator!=(const buffers_iterator& a, const buffers_iterator& b)
|
||||||
|
{
|
||||||
|
return !a.equal(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compare two iterators.
|
||||||
|
friend bool operator<(const buffers_iterator& a, const buffers_iterator& b)
|
||||||
|
{
|
||||||
|
return a.distance_to(b) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compare two iterators.
|
||||||
|
friend bool operator<=(const buffers_iterator& a, const buffers_iterator& b)
|
||||||
|
{
|
||||||
|
return !(b < a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compare two iterators.
|
||||||
|
friend bool operator>(const buffers_iterator& a, const buffers_iterator& b)
|
||||||
|
{
|
||||||
|
return b < a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compare two iterators.
|
||||||
|
friend bool operator>=(const buffers_iterator& a, const buffers_iterator& b)
|
||||||
|
{
|
||||||
|
return !(a < b);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Dereference the iterator.
|
||||||
|
reference dereference() const
|
||||||
|
{
|
||||||
|
return static_cast<pointer>(
|
||||||
|
current_buffer_.data())[current_buffer_position_];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare two iterators for equality.
|
||||||
|
bool equal(const buffers_iterator& other) const
|
||||||
|
{
|
||||||
|
return position_ == other.position_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment the iterator.
|
||||||
|
void increment()
|
||||||
|
{
|
||||||
|
ASIO_ASSERT(current_ != end_ && "iterator out of bounds");
|
||||||
|
++position_;
|
||||||
|
|
||||||
|
// Check if the increment can be satisfied by the current buffer.
|
||||||
|
++current_buffer_position_;
|
||||||
|
if (current_buffer_position_ != current_buffer_.size())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Find the next non-empty buffer.
|
||||||
|
++current_;
|
||||||
|
current_buffer_position_ = 0;
|
||||||
|
while (current_ != end_)
|
||||||
|
{
|
||||||
|
current_buffer_ = *current_;
|
||||||
|
if (current_buffer_.size() > 0)
|
||||||
|
return;
|
||||||
|
++current_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrement the iterator.
|
||||||
|
void decrement()
|
||||||
|
{
|
||||||
|
ASIO_ASSERT(position_ > 0 && "iterator out of bounds");
|
||||||
|
--position_;
|
||||||
|
|
||||||
|
// Check if the decrement can be satisfied by the current buffer.
|
||||||
|
if (current_buffer_position_ != 0)
|
||||||
|
{
|
||||||
|
--current_buffer_position_;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the previous non-empty buffer.
|
||||||
|
buffer_sequence_iterator_type iter = current_;
|
||||||
|
while (iter != begin_)
|
||||||
|
{
|
||||||
|
--iter;
|
||||||
|
buffer_type buffer = *iter;
|
||||||
|
std::size_t buffer_size = buffer.size();
|
||||||
|
if (buffer_size > 0)
|
||||||
|
{
|
||||||
|
current_ = iter;
|
||||||
|
current_buffer_ = buffer;
|
||||||
|
current_buffer_position_ = buffer_size - 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance the iterator by the specified distance.
|
||||||
|
void advance(std::ptrdiff_t n)
|
||||||
|
{
|
||||||
|
if (n > 0)
|
||||||
|
{
|
||||||
|
ASIO_ASSERT(current_ != end_ && "iterator out of bounds");
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
std::ptrdiff_t current_buffer_balance
|
||||||
|
= current_buffer_.size() - current_buffer_position_;
|
||||||
|
|
||||||
|
// Check if the advance can be satisfied by the current buffer.
|
||||||
|
if (current_buffer_balance > n)
|
||||||
|
{
|
||||||
|
position_ += n;
|
||||||
|
current_buffer_position_ += n;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update position.
|
||||||
|
n -= current_buffer_balance;
|
||||||
|
position_ += current_buffer_balance;
|
||||||
|
|
||||||
|
// Move to next buffer. If it is empty then it will be skipped on the
|
||||||
|
// next iteration of this loop.
|
||||||
|
if (++current_ == end_)
|
||||||
|
{
|
||||||
|
ASIO_ASSERT(n == 0 && "iterator out of bounds");
|
||||||
|
current_buffer_ = buffer_type();
|
||||||
|
current_buffer_position_ = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
current_buffer_ = *current_;
|
||||||
|
current_buffer_position_ = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (n < 0)
|
||||||
|
{
|
||||||
|
std::size_t abs_n = -n;
|
||||||
|
ASIO_ASSERT(position_ >= abs_n && "iterator out of bounds");
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
// Check if the advance can be satisfied by the current buffer.
|
||||||
|
if (current_buffer_position_ >= abs_n)
|
||||||
|
{
|
||||||
|
position_ -= abs_n;
|
||||||
|
current_buffer_position_ -= abs_n;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update position.
|
||||||
|
abs_n -= current_buffer_position_;
|
||||||
|
position_ -= current_buffer_position_;
|
||||||
|
|
||||||
|
// Check if we've reached the beginning of the buffers.
|
||||||
|
if (current_ == begin_)
|
||||||
|
{
|
||||||
|
ASIO_ASSERT(abs_n == 0 && "iterator out of bounds");
|
||||||
|
current_buffer_position_ = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the previous non-empty buffer.
|
||||||
|
buffer_sequence_iterator_type iter = current_;
|
||||||
|
while (iter != begin_)
|
||||||
|
{
|
||||||
|
--iter;
|
||||||
|
buffer_type buffer = *iter;
|
||||||
|
std::size_t buffer_size = buffer.size();
|
||||||
|
if (buffer_size > 0)
|
||||||
|
{
|
||||||
|
current_ = iter;
|
||||||
|
current_buffer_ = buffer;
|
||||||
|
current_buffer_position_ = buffer_size;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the distance between two iterators.
|
||||||
|
std::ptrdiff_t distance_to(const buffers_iterator& other) const
|
||||||
|
{
|
||||||
|
return other.position_ - position_;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_type current_buffer_;
|
||||||
|
std::size_t current_buffer_position_;
|
||||||
|
buffer_sequence_iterator_type begin_;
|
||||||
|
buffer_sequence_iterator_type current_;
|
||||||
|
buffer_sequence_iterator_type end_;
|
||||||
|
std::size_t position_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Construct an iterator representing the beginning of the buffers' data.
|
||||||
|
template <typename BufferSequence>
|
||||||
|
inline buffers_iterator<BufferSequence> buffers_begin(
|
||||||
|
const BufferSequence& buffers)
|
||||||
|
{
|
||||||
|
return buffers_iterator<BufferSequence>::begin(buffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct an iterator representing the end of the buffers' data.
|
||||||
|
template <typename BufferSequence>
|
||||||
|
inline buffers_iterator<BufferSequence> buffers_end(
|
||||||
|
const BufferSequence& buffers)
|
||||||
|
{
|
||||||
|
return buffers_iterator<BufferSequence>::end(buffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace asio
|
||||||
|
|
||||||
|
#include "asio/detail/pop_options.hpp"
|
||||||
|
|
||||||
|
#endif // ASIO_BUFFERS_ITERATOR_HPP
|
@ -0,0 +1,218 @@
|
|||||||
|
//
|
||||||
|
// completion_condition.hpp
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ASIO_COMPLETION_CONDITION_HPP
|
||||||
|
#define ASIO_COMPLETION_CONDITION_HPP
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
# pragma once
|
||||||
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
|
||||||
|
#include "asio/detail/config.hpp"
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#include "asio/detail/push_options.hpp"
|
||||||
|
|
||||||
|
namespace asio {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
// The default maximum number of bytes to transfer in a single operation.
|
||||||
|
enum default_max_transfer_size_t { default_max_transfer_size = 65536 };
|
||||||
|
|
||||||
|
// Adapt result of old-style completion conditions (which had a bool result
|
||||||
|
// where true indicated that the operation was complete).
|
||||||
|
inline std::size_t adapt_completion_condition_result(bool result)
|
||||||
|
{
|
||||||
|
return result ? 0 : default_max_transfer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adapt result of current completion conditions (which have a size_t result
|
||||||
|
// where 0 means the operation is complete, and otherwise the result is the
|
||||||
|
// maximum number of bytes to transfer on the next underlying operation).
|
||||||
|
inline std::size_t adapt_completion_condition_result(std::size_t result)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
class transfer_all_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef std::size_t result_type;
|
||||||
|
|
||||||
|
template <typename Error>
|
||||||
|
std::size_t operator()(const Error& err, std::size_t)
|
||||||
|
{
|
||||||
|
return !!err ? 0 : default_max_transfer_size;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class transfer_at_least_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef std::size_t result_type;
|
||||||
|
|
||||||
|
explicit transfer_at_least_t(std::size_t minimum)
|
||||||
|
: minimum_(minimum)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Error>
|
||||||
|
std::size_t operator()(const Error& err, std::size_t bytes_transferred)
|
||||||
|
{
|
||||||
|
return (!!err || bytes_transferred >= minimum_)
|
||||||
|
? 0 : default_max_transfer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::size_t minimum_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class transfer_exactly_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef std::size_t result_type;
|
||||||
|
|
||||||
|
explicit transfer_exactly_t(std::size_t size)
|
||||||
|
: size_(size)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Error>
|
||||||
|
std::size_t operator()(const Error& err, std::size_t bytes_transferred)
|
||||||
|
{
|
||||||
|
return (!!err || bytes_transferred >= size_) ? 0 :
|
||||||
|
(size_ - bytes_transferred < default_max_transfer_size
|
||||||
|
? size_ - bytes_transferred : std::size_t(default_max_transfer_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::size_t size_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup completion_condition Completion Condition Function Objects
|
||||||
|
*
|
||||||
|
* Function objects used for determining when a read or write operation should
|
||||||
|
* complete.
|
||||||
|
*/
|
||||||
|
/*@{*/
|
||||||
|
|
||||||
|
/// Return a completion condition function object that indicates that a read or
|
||||||
|
/// write operation should continue until all of the data has been transferred,
|
||||||
|
/// or until an error occurs.
|
||||||
|
/**
|
||||||
|
* This function is used to create an object, of unspecified type, that meets
|
||||||
|
* CompletionCondition requirements.
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* Reading until a buffer is full:
|
||||||
|
* @code
|
||||||
|
* boost::array<char, 128> buf;
|
||||||
|
* asio::error_code ec;
|
||||||
|
* std::size_t n = asio::read(
|
||||||
|
* sock, asio::buffer(buf),
|
||||||
|
* asio::transfer_all(), ec);
|
||||||
|
* if (ec)
|
||||||
|
* {
|
||||||
|
* // An error occurred.
|
||||||
|
* }
|
||||||
|
* else
|
||||||
|
* {
|
||||||
|
* // n == 128
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
#if defined(GENERATING_DOCUMENTATION)
|
||||||
|
unspecified transfer_all();
|
||||||
|
#else
|
||||||
|
inline detail::transfer_all_t transfer_all()
|
||||||
|
{
|
||||||
|
return detail::transfer_all_t();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Return a completion condition function object that indicates that a read or
|
||||||
|
/// write operation should continue until a minimum number of bytes has been
|
||||||
|
/// transferred, or until an error occurs.
|
||||||
|
/**
|
||||||
|
* This function is used to create an object, of unspecified type, that meets
|
||||||
|
* CompletionCondition requirements.
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* Reading until a buffer is full or contains at least 64 bytes:
|
||||||
|
* @code
|
||||||
|
* boost::array<char, 128> buf;
|
||||||
|
* asio::error_code ec;
|
||||||
|
* std::size_t n = asio::read(
|
||||||
|
* sock, asio::buffer(buf),
|
||||||
|
* asio::transfer_at_least(64), ec);
|
||||||
|
* if (ec)
|
||||||
|
* {
|
||||||
|
* // An error occurred.
|
||||||
|
* }
|
||||||
|
* else
|
||||||
|
* {
|
||||||
|
* // n >= 64 && n <= 128
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
#if defined(GENERATING_DOCUMENTATION)
|
||||||
|
unspecified transfer_at_least(std::size_t minimum);
|
||||||
|
#else
|
||||||
|
inline detail::transfer_at_least_t transfer_at_least(std::size_t minimum)
|
||||||
|
{
|
||||||
|
return detail::transfer_at_least_t(minimum);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Return a completion condition function object that indicates that a read or
|
||||||
|
/// write operation should continue until an exact number of bytes has been
|
||||||
|
/// transferred, or until an error occurs.
|
||||||
|
/**
|
||||||
|
* This function is used to create an object, of unspecified type, that meets
|
||||||
|
* CompletionCondition requirements.
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* Reading until a buffer is full or contains exactly 64 bytes:
|
||||||
|
* @code
|
||||||
|
* boost::array<char, 128> buf;
|
||||||
|
* asio::error_code ec;
|
||||||
|
* std::size_t n = asio::read(
|
||||||
|
* sock, asio::buffer(buf),
|
||||||
|
* asio::transfer_exactly(64), ec);
|
||||||
|
* if (ec)
|
||||||
|
* {
|
||||||
|
* // An error occurred.
|
||||||
|
* }
|
||||||
|
* else
|
||||||
|
* {
|
||||||
|
* // n == 64
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
#if defined(GENERATING_DOCUMENTATION)
|
||||||
|
unspecified transfer_exactly(std::size_t size);
|
||||||
|
#else
|
||||||
|
inline detail::transfer_exactly_t transfer_exactly(std::size_t size)
|
||||||
|
{
|
||||||
|
return detail::transfer_exactly_t(size);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*@}*/
|
||||||
|
|
||||||
|
} // namespace asio
|
||||||
|
|
||||||
|
#include "asio/detail/pop_options.hpp"
|
||||||
|
|
||||||
|
#endif // ASIO_COMPLETION_CONDITION_HPP
|
1060
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/asio/connect.hpp
vendored
Normal file
1060
CorruptedMemory/Plugins/SocketIOClient/Source/ThirdParty/asio/asio/include/asio/connect.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user