Skip to content

Commit 530370d

Browse files
committed
+Download bytes support (for storage files) - version 0.2.0
0 parents  commit 530370d

17 files changed

+1096
-0
lines changed

.icon.png

6.3 KB
Loading

LICENSE.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
MIT License
2+
3+
Copyright (c) 2015, Unity Technologies
4+
5+
Copyright (c) 2020 Game Workstore
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in all
15+
copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
SOFTWARE.

LICENSE.md.meta

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Async Network Engine
2+
3+
Unity http requests made easy to interoperability with Go Lang + Google Protobuf functions!
4+
5+
Supported Cloud Functions:
6+
- Google Cloud Functions
7+
- Amazon Web Services Lambdas
8+
9+
Use at your own risk!
10+
11+
# How to install
12+
13+
At package.json, add these 3 lines of code:
14+
```json
15+
"com.gameworkstore.asyncnetworkengine": "git://github.com/GameWorkstore/async-network-engine.git",
16+
"com.gameworkstore.googleprotobufunity": "git://github.com/GameWorkstore/google-protobuf-unity.git",
17+
"com.gameworkstore.patterns": "git://github.com/GameWorkstore/patterns.git"
18+
```
19+
20+
And wait for unity to download and compile the package.
21+
22+
for update package for a newer version, install UpmGitExtension and update on [ Window > Package Manager ]!
23+
> https://github.com/mob-sakai/UpmGitExtension
24+
25+
# Usage Examples
26+
27+
You can find usage examples on Assets/Tests/ folder, for each cloud.
28+
29+
## Uploading to Google Cloud Functions
30+
31+
on upmsync, the job 'upload_gcp' illustrates a possible way to upload your functions into GCP.
32+
Requires a service account to enable it to upload.
33+
34+
## Uploading to Amazon Web Services
35+
36+
on upmsync.yaml, the job 'upload_aws' illustrates a possible way to upload your functions into AWS.
37+
Requires a service account to enable it to upload.
38+
> You need to set the template 'cloudformation_function.yaml' public in your bucket repository to enable AWS::Stack to read from it.
39+
40+
# Implementation
41+
42+
## GCP Troubleshoot
43+
44+
> My function is returning ErrorProtocol for any input.
45+
if you don't give access public for your function it might fail
46+
47+
## AWS Troubleshoot
48+
> CloudFormation is returning errors
49+
50+
Verify all variables, !Ref and links, you might be forgetting something. CloudFormations is very sensitive to linkage errors.
51+
52+
> My lambda is returning error 500 Internal Server error.
53+
If you are receiving this and error object is returning null, it might be a bad configuration causing your lambda to not run.
54+
Verify if you function is running by adding a fmt.Println("test") at main() function to ensure the function is starting.
55+
Some issues that may prevent the start of function:
56+
- The functions ins't at correct path when extracted from the zip.
57+
- The function package name isn't main where main() function is declared.
58+
- lambda.Start() is never begin called to initialize the function.
59+
- The function is crashing upon initialization
60+
61+
If the function is working normally, them you might receive error 500 - ErrorInternalServer with AWSError, when specified by the programmer.
62+
63+
# Contributions
64+
65+
If you are using this library and want to submit a change, go ahead! Overall, this project accepts contributions if:
66+
- Is a bug fix;
67+
- Or, is a generalization of a well-known issue;
68+
- Or is performance improvement;
69+
- Or is an improvement to already supported feature.
70+
71+
Also, you can donate to allow us to drink coffee while we improve it for you!

README.md.meta

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Runtime.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Runtime/AsyncNetworkEngine.cs

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
using Google.Protobuf;
2+
using GameWorkstore.Patterns;
3+
using System;
4+
using System.Collections;
5+
using System.Text;
6+
using UnityEngine.Networking;
7+
using System.Collections.Generic;
8+
9+
namespace GameWorkstore.AsyncNetworkEngine
10+
{
11+
public enum CloudProvider
12+
{
13+
GCP = 0,
14+
AWS = 1
15+
}
16+
17+
public static class AsyncNetworkEngineMap
18+
{
19+
internal static bool IsSingleCloud = true;
20+
internal static CloudProvider SingleCloudProvider = CloudProvider.AWS;
21+
internal static Dictionary<string, CloudProvider> MapCloudProvider = null;
22+
23+
/// <summary>
24+
/// Setup a single cloud provider for all functions.
25+
/// </summary>
26+
/// <param name="cloudProvider">Target cloud provider implementation.</param>
27+
public static void SetupCloud(CloudProvider cloudProvider)
28+
{
29+
IsSingleCloud = true;
30+
SingleCloudProvider = cloudProvider;
31+
}
32+
33+
/// <summary>
34+
/// Setup a multi cloud provider for all functions.
35+
/// </summary>
36+
/// <param name="mapCloudProvider">Maps base url to cloud provider. Use the lowest possible string to differentiate clouds.</param>
37+
public static void SetupCloudMap(Dictionary<string, CloudProvider> mapCloudProvider)
38+
{
39+
IsSingleCloud = false;
40+
MapCloudProvider = mapCloudProvider;
41+
}
42+
}
43+
44+
public static class AsyncNetworkEngine
45+
{
46+
private static EventService _eventService = null;
47+
48+
public static void Download(string uri,Action<Transmission,byte[]> result)
49+
{
50+
if (_eventService == null) _eventService = ServiceProvider.GetService<EventService>();
51+
_eventService.StartCoroutine(SendRequest(uri, result));
52+
}
53+
54+
public static IEnumerator SendRequest(string uri, Action<Transmission, byte[]> result)
55+
{
56+
using var rqt = UnityWebRequest.Get(uri);
57+
yield return rqt.SendWebRequest();
58+
59+
switch(rqt.result){
60+
case UnityWebRequest.Result.ConnectionError:
61+
Return(Transmission.ErrorConnection, null, result);
62+
break;
63+
case UnityWebRequest.Result.Success:
64+
HandleSuccess(rqt,result);
65+
break;
66+
}
67+
}
68+
69+
private static void HandleSuccess(UnityWebRequest rqt, Action<Transmission, byte[]> result)
70+
{
71+
if (rqt.downloadHandler.data == null)
72+
{
73+
Return(Transmission.ErrorNoData, null, result);
74+
return;
75+
}
76+
if (rqt.downloadHandler.data.Length <= 0)
77+
{
78+
Return(Transmission.ErrorNoData, null, result);
79+
return;
80+
}
81+
Return(Transmission.Success, rqt.downloadHandler.data, result);
82+
}
83+
84+
private static void Return(Transmission result, byte[] data, Action<Transmission, byte[]> callback)
85+
{
86+
callback?.Invoke(result,data);
87+
}
88+
}
89+
90+
/// <summary>
91+
/// Implements a UnityRequest for google protobuf web functions.
92+
/// </summary>
93+
/// <typeparam name="TR">Request</typeparam>
94+
/// <typeparam name="TU">Response</typeparam>
95+
public static class AsyncNetworkEngine<TR, TU>
96+
where TR : IMessage<TR>, new()
97+
where TU : IMessage<TU>, new()
98+
{
99+
private static readonly MessageParser<TU> _tuParser = new MessageParser<TU>(() => new TU());
100+
private static readonly MessageParser<GenericErrorResponse> _tvParser = new MessageParser<GenericErrorResponse>(() => new GenericErrorResponse());
101+
private static EventService _eventService = null;
102+
103+
public static void Send(string uri, TR request, Action<Transmission, TU, GenericErrorResponse> result)
104+
{
105+
if (_eventService == null) _eventService = ServiceProvider.GetService<EventService>();
106+
_eventService.StartCoroutine(SendRequest(uri, request, result));
107+
}
108+
109+
public static IEnumerator SendRequest(string uri, TR request, Action<Transmission, TU, GenericErrorResponse> result)
110+
{
111+
//Notice: APIGateway automatically converts binary data into base64 strings
112+
using var rqt = new UnityWebRequest(uri, "POST")
113+
{
114+
uploadHandler = new UploadHandlerRaw(request.ToByteArray()),
115+
downloadHandler = new DownloadHandlerBuffer()
116+
};
117+
yield return rqt.SendWebRequest();
118+
119+
switch (rqt.result)
120+
{
121+
case UnityWebRequest.Result.ConnectionError:
122+
Return(Transmission.ErrorConnection, result);
123+
break;
124+
case UnityWebRequest.Result.ProtocolError:
125+
HandleError(GetCloudProvider(ref uri), rqt, result);
126+
break;
127+
case UnityWebRequest.Result.Success:
128+
while (!rqt.downloadHandler.isDone) yield return null;
129+
HandleSuccess(GetCloudProvider(ref uri), rqt, result);
130+
break;
131+
}
132+
}
133+
134+
private static CloudProvider GetCloudProvider(ref string url)
135+
{
136+
if (!AsyncNetworkEngineMap.IsSingleCloud)
137+
{
138+
foreach (var pair in AsyncNetworkEngineMap.MapCloudProvider)
139+
{
140+
if (!url.StartsWith(pair.Key)) continue;
141+
return pair.Value;
142+
}
143+
}
144+
return AsyncNetworkEngineMap.SingleCloudProvider;
145+
}
146+
147+
private static void HandleSuccess(CloudProvider provider, UnityWebRequest rqt, Action<Transmission, TU, GenericErrorResponse> result)
148+
{
149+
if (rqt.downloadHandler.data == null)
150+
{
151+
Return(Transmission.ErrorNoData, result);
152+
return;
153+
}
154+
if (rqt.downloadHandler.data.Length <= 0)
155+
{
156+
Return(Transmission.ErrorNoData, result);
157+
return;
158+
}
159+
160+
byte[] data = rqt.downloadHandler.data;
161+
if (provider == CloudProvider.AWS)
162+
{
163+
data = Base64StdEncoding.Decode(Encoding.ASCII.GetString(rqt.downloadHandler.data));
164+
}
165+
166+
TU packet;
167+
try
168+
{
169+
packet = _tuParser.ParseFrom(data);
170+
}
171+
catch
172+
{
173+
packet = default;
174+
}
175+
Return(packet != null ? Transmission.Success : Transmission.ErrorParser, packet, default, result);
176+
}
177+
178+
private static void HandleError(CloudProvider provider, UnityWebRequest rqt, Action<Transmission, TU, GenericErrorResponse> result)
179+
{
180+
if (rqt.downloadHandler.data == null)
181+
{
182+
Return(Transmission.ErrorProtocol, result);
183+
return;
184+
}
185+
if (rqt.downloadHandler.data.Length <= 0)
186+
{
187+
Return(Transmission.ErrorProtocol, result);
188+
return;
189+
}
190+
191+
byte[] data = rqt.downloadHandler.data;
192+
if (provider == CloudProvider.AWS)
193+
{
194+
data = Base64StdEncoding.Decode(Encoding.ASCII.GetString(rqt.downloadHandler.data));
195+
}
196+
197+
var transmission = (Transmission)rqt.responseCode;
198+
GenericErrorResponse packet;
199+
try
200+
{
201+
packet = _tvParser.ParseFrom(data);
202+
}
203+
catch
204+
{
205+
packet = default;
206+
}
207+
Return(packet != null ? transmission : Transmission.ErrorParser, default, packet, result);
208+
}
209+
210+
private static void Return(Transmission result, Action<Transmission, TU, GenericErrorResponse> callback)
211+
{
212+
Return(result, default, default, callback);
213+
}
214+
215+
private static void Return(Transmission result, TU data, GenericErrorResponse error, Action<Transmission, TU, GenericErrorResponse> callback)
216+
{
217+
if (callback == null) return;
218+
_eventService.QueueAction(() => callback.Invoke(result, data, error));
219+
//callback.Invoke(result, data, error);
220+
}
221+
}
222+
}

Runtime/AsyncNetworkEngine.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Runtime/Base64StdEncoding.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using System;
2+
3+
namespace GameWorkstore.AsyncNetworkEngine
4+
{
5+
6+
public static class Base64StdEncoding
7+
{
8+
public static string Encode(byte[] input)
9+
{
10+
var output = Convert.ToBase64String(input);
11+
//output = output.Replace('+', '-'); // 62nd char of encoding
12+
//output = output.Replace('/', '_'); // 63rd char of encoding
13+
output = output.Split('=')[0]; // Remove any trailing '='s
14+
15+
return output;
16+
}
17+
18+
public static byte[] Decode(string input)
19+
{
20+
//var output = input;
21+
//output = output.Replace('-', '+'); // 62nd char of encoding
22+
//output = output.Replace('_', '/'); // 63rd char of encoding
23+
24+
switch (input.Length % 4) // Pad with trailing '='s
25+
{
26+
case 0:
27+
break; // No pad chars in this case
28+
case 2:
29+
input += "==";
30+
break; // Two pad chars
31+
case 3:
32+
input += "=";
33+
break; // One pad char
34+
default:
35+
throw new ArgumentOutOfRangeException(nameof(input), "Illegal base64url string!");
36+
}
37+
38+
return Convert.FromBase64String(input);
39+
}
40+
}
41+
}

0 commit comments

Comments
 (0)