diff --git a/CHANGELOG.md b/CHANGELOG.md
index 63f7e30e..a0a8cd6c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased]
+### Changed
+- TinyJson DataMember attributes now require a `Name` field to be passed in order for them to be parsed.
+- TinyJson public fields and properties will no longer automatically get parsed without a DataMember attribute.
+
+### Added
+- Added Protobuf support for WebSockets via the `Nakama.Protobuf` namespace.
+
## [2.7.0] - 2020-10-19
### Changed
- Upgrade code generator to new Swagger format.
diff --git a/Nakama.sln b/Nakama.sln
index 338971c2..c3016704 100644
--- a/Nakama.sln
+++ b/Nakama.sln
@@ -11,6 +11,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{4CBC1D9A
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nakama.Tests", "tests\Nakama.Tests\Nakama.Tests.csproj", "{AFD0AF63-EA45-49A6-8889-3E65A48F0521}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nakama.Protobuf", "src\Nakama.Protobuf\Nakama.Protobuf.csproj", "{C9CECF38-A4B4-4B91-9ADD-1F37ACA78E2D}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -48,9 +50,22 @@ Global
{AFD0AF63-EA45-49A6-8889-3E65A48F0521}.Release|x64.Build.0 = Release|Any CPU
{AFD0AF63-EA45-49A6-8889-3E65A48F0521}.Release|x86.ActiveCfg = Release|Any CPU
{AFD0AF63-EA45-49A6-8889-3E65A48F0521}.Release|x86.Build.0 = Release|Any CPU
+ {C9CECF38-A4B4-4B91-9ADD-1F37ACA78E2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C9CECF38-A4B4-4B91-9ADD-1F37ACA78E2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C9CECF38-A4B4-4B91-9ADD-1F37ACA78E2D}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {C9CECF38-A4B4-4B91-9ADD-1F37ACA78E2D}.Debug|x64.Build.0 = Debug|Any CPU
+ {C9CECF38-A4B4-4B91-9ADD-1F37ACA78E2D}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C9CECF38-A4B4-4B91-9ADD-1F37ACA78E2D}.Debug|x86.Build.0 = Debug|Any CPU
+ {C9CECF38-A4B4-4B91-9ADD-1F37ACA78E2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C9CECF38-A4B4-4B91-9ADD-1F37ACA78E2D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C9CECF38-A4B4-4B91-9ADD-1F37ACA78E2D}.Release|x64.ActiveCfg = Release|Any CPU
+ {C9CECF38-A4B4-4B91-9ADD-1F37ACA78E2D}.Release|x64.Build.0 = Release|Any CPU
+ {C9CECF38-A4B4-4B91-9ADD-1F37ACA78E2D}.Release|x86.ActiveCfg = Release|Any CPU
+ {C9CECF38-A4B4-4B91-9ADD-1F37ACA78E2D}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{10E81453-27EC-4D8E-82A7-DEDEFABBD648} = {651F309B-3615-44A7-A5B4-832B4ACE2258}
{AFD0AF63-EA45-49A6-8889-3E65A48F0521} = {4CBC1D9A-0727-4D31-A3BC-484EF98E7617}
+ {C9CECF38-A4B4-4B91-9ADD-1F37ACA78E2D} = {651F309B-3615-44A7-A5B4-832B4ACE2258}
EndGlobalSection
EndGlobal
diff --git a/src/Nakama.Protobuf/Nakama.Protobuf.csproj b/src/Nakama.Protobuf/Nakama.Protobuf.csproj
new file mode 100644
index 00000000..a4193514
--- /dev/null
+++ b/src/Nakama.Protobuf/Nakama.Protobuf.csproj
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+ netstandard2.0;net461
+
+
+ Nakama Authors & contributors
+ Heroic Labs
+ Nakama is an open-source server designed to power modern games and apps. This package adds Protobuf support to Websocket messages sent betweens the dotnet-client and the Nakama server.
+ NakamaClientProtobuf
+ Apache-2.0
+ clientsdk;nakama;gameserver;backend;restapi
+ https://github.com/heroiclabs/nakama-dotnet
+
+
diff --git a/src/Nakama.Protobuf/ProtobufAdapter.cs b/src/Nakama.Protobuf/ProtobufAdapter.cs
new file mode 100644
index 00000000..659b996a
--- /dev/null
+++ b/src/Nakama.Protobuf/ProtobufAdapter.cs
@@ -0,0 +1,263 @@
+/**
+ * Copyright 2020 The Nakama Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Net.Sockets;
+using System.Net.WebSockets;
+using System.Threading;
+using System.Threading.Tasks;
+using Nakama.SocketInternal;
+using Nakama.Ninja.WebSockets;
+using ProtoBuf;
+using System.IO;
+
+[module: CompatibilityLevel(CompatibilityLevel.Level300)]
+
+namespace Nakama.Protobuf
+{
+ ///
+ /// A Protobuf adapter which uses the WebSocket protocol with Nakama server.
+ ///
+ public class ProtobufAdapter : ISocketAdapter
+ {
+ private const int KeepAliveIntervalSec = 15;
+ private const int MaxMessageSize = 1024 * 256;
+ private const int SendTimeoutSec = 10;
+
+ ///
+ public event Action Connected;
+
+ ///
+ public event Action Closed;
+
+ ///
+ public event Action ReceivedError;
+
+ ///
+ public event Action> Received;
+
+ ///
+ public string Format
+ {
+ get
+ {
+ return "protobuf";
+ }
+ }
+
+ ///
+ /// If the WebSocket is connected.
+ ///
+ public bool IsConnected { get; private set; }
+
+ ///
+ /// If the WebSocket is connecting.
+ ///
+ public bool IsConnecting { get; private set; }
+
+ private readonly WebSocketClientOptions _options;
+ private readonly TimeSpan _sendTimeoutSec;
+ private CancellationTokenSource _cancellationSource;
+ private WebSocket _webSocket;
+ private Uri _uri;
+
+ public ProtobufAdapter(int keepAliveIntervalSec = KeepAliveIntervalSec, int sendTimeoutSec = SendTimeoutSec) :
+ this(new WebSocketClientOptions
+ {
+ IncludeExceptionInCloseResponse = true,
+ KeepAliveInterval = TimeSpan.FromSeconds(keepAliveIntervalSec),
+ NoDelay = true
+ }, sendTimeoutSec) {}
+
+ public ProtobufAdapter(WebSocketClientOptions options, int sendTimeoutSec)
+ {
+ _options = options;
+ _sendTimeoutSec = TimeSpan.FromSeconds(sendTimeoutSec);
+ }
+
+ ///
+ public void Close()
+ {
+ _cancellationSource?.Cancel();
+
+ if (_webSocket == null) return;
+ _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
+ _webSocket = null;
+ IsConnecting = false;
+ IsConnected = false;
+ }
+
+ ///
+ public async void Connect(Uri uri, int timeout)
+ {
+ if (_webSocket != null)
+ {
+ ReceivedError?.Invoke(new SocketException((int) SocketError.IsConnected));
+ return;
+ }
+
+ _cancellationSource = new CancellationTokenSource();
+ _uri = uri;
+ IsConnecting = true;
+
+ var clientFactory = new WebSocketClientFactory();
+ try
+ {
+ var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeout));
+ var lcts = CancellationTokenSource.CreateLinkedTokenSource(_cancellationSource.Token, cts.Token);
+ using (_webSocket = await clientFactory.ConnectAsync(_uri, _options, lcts.Token))
+ {
+ IsConnected = true;
+ IsConnecting = false;
+ Connected?.Invoke();
+
+ await ReceiveLoop(_webSocket, _cancellationSource.Token);
+ }
+ }
+ catch (TaskCanceledException)
+ {
+ // No error, the socket got closed via the cancellation signal.
+ }
+ catch (ObjectDisposedException)
+ {
+ // No error, the socket got closed.
+ }
+ catch (Exception e)
+ {
+ ReceivedError?.Invoke(e);
+ }
+ finally
+ {
+ Close();
+ Closed?.Invoke();
+ }
+ }
+
+ public WebSocketMessageEnvelope DeserializeEnvelope(ArraySegment buffer)
+ {
+ WebSocketMessageEnvelope envelope = null;
+
+ try
+ {
+ envelope = Serializer.Deserialize(buffer.AsMemory());
+ }
+ catch (Exception e)
+ {
+ ReceivedError?.Invoke(new FormatException("Could not deserialize protocol buffer.", e));
+ }
+
+ return envelope;
+ }
+
+ public void Dispose()
+ {
+ _webSocket?.Dispose();
+ }
+
+ ///
+ public async void Send(WebSocketMessageEnvelope envelope, CancellationToken cancellationToken,
+ bool reliable = true)
+ {
+
+ if (_webSocket == null)
+ {
+ ReceivedError?.Invoke(new SocketException((int) SocketError.NotConnected));
+ return;
+ }
+
+ try
+ {
+ var stream = new MemoryStream();
+ Serializer.Serialize(stream, envelope);
+
+ var asByteArray = stream.ToArray();
+
+ var sendTask = _webSocket.SendAsync(new ArraySegment(asByteArray), WebSocketMessageType.Binary, true, cancellationToken);
+
+ await Task.WhenAny(sendTask, Task.Delay(_sendTimeoutSec, cancellationToken));
+ }
+ catch (Exception e)
+ {
+ Close();
+ ReceivedError?.Invoke(e);
+ }
+ }
+
+ public override string ToString()
+ {
+ return
+ $"WebSocketAdapter(IsConnected={IsConnected}, IsConnecting={IsConnecting}, MaxMessageSize={MaxMessageSize}, Uri='{_uri}')";
+ }
+
+ private async Task ReceiveLoop(WebSocket webSocket, CancellationToken cancellationToken)
+ {
+ var buffer = new byte[MaxMessageSize];
+ while (true)
+ {
+ var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), cancellationToken)
+ .ConfigureAwait(false);
+ if (result == null)
+ {
+ break;
+ }
+
+ if (result.MessageType == WebSocketMessageType.Close)
+ {
+ break;
+ }
+
+ var data = await ReadFrames(result, webSocket, buffer);
+
+ if (data.Count == 0)
+ {
+ break;
+ }
+
+ try
+ {
+ Received?.Invoke(data);
+ }
+ catch (Exception e)
+ {
+ ReceivedError?.Invoke(e);
+ }
+ }
+ }
+
+ private async Task> ReadFrames(WebSocketReceiveResult result, WebSocket webSocket,
+ byte[] buffer)
+ {
+ var count = result.Count;
+ while (!result.EndOfMessage)
+ {
+ if (count >= MaxMessageSize)
+ {
+ var closeMessage = $"Maximum message size {MaxMessageSize} bytes reached.";
+ await webSocket.CloseAsync(WebSocketCloseStatus.MessageTooBig, closeMessage,
+ CancellationToken.None);
+ ReceivedError?.Invoke(new WebSocketException(WebSocketError.HeaderError));
+ return new ArraySegment();
+ }
+
+ result = await webSocket.ReceiveAsync(new ArraySegment(buffer, count, MaxMessageSize - count),
+ CancellationToken.None).ConfigureAwait(false);
+ count += result.Count;
+ }
+
+ return new ArraySegment(buffer, 0, count);
+ }
+ }
+}
diff --git a/src/Nakama/ApiClient.gen.cs b/src/Nakama/ApiClient.gen.cs
index af154375..9faa2089 100644
--- a/src/Nakama/ApiClient.gen.cs
+++ b/src/Nakama/ApiClient.gen.cs
@@ -545,7 +545,7 @@ public interface IApiAccountFacebookInstantGame
{
///
- ///
+ ///
///
string SignedPlayerInfo { get; }
@@ -3182,23 +3182,23 @@ public override string ToString()
}
///
- ///
+ ///
///
public interface IRpcStatus
{
///
- ///
+ ///
///
int Code { get; }
///
- ///
+ ///
///
IEnumerable Details { get; }
///
- ///
+ ///
///
string Message { get; }
}
diff --git a/src/Nakama/ChannelJoinMessage.cs b/src/Nakama/ChannelType.cs
similarity index 58%
rename from src/Nakama/ChannelJoinMessage.cs
rename to src/Nakama/ChannelType.cs
index 3ea2e67e..f6f33e23 100644
--- a/src/Nakama/ChannelJoinMessage.cs
+++ b/src/Nakama/ChannelType.cs
@@ -1,5 +1,5 @@
/**
- * Copyright 2018 The Nakama Authors
+ * Copyright 2020 The Nakama Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,33 +14,8 @@
* limitations under the License.
*/
-using System.Runtime.Serialization;
-
namespace Nakama
{
- ///
- /// Send a channel join message to the server.
- ///
- internal class ChannelJoinMessage
- {
- [DataMember(Name="hidden"), Preserve]
- public bool Hidden { get; set; }
-
- [DataMember(Name="persistence"), Preserve]
- public bool Persistence { get; set; }
-
- [DataMember(Name="target"), Preserve]
- public string Target { get; set; }
-
- [DataMember(Name="type"), Preserve]
- public int Type { get; set; }
-
- public override string ToString()
- {
- return $"ChannelJoinMessage(Hidden={Hidden}, Persistence={Persistence}, Target='{Target}', Type={Type})";
- }
- }
-
///
/// The available channel types on the server.
///
diff --git a/src/Nakama/IChannel.cs b/src/Nakama/IChannel.cs
index 2f4a17cc..ca712a8d 100644
--- a/src/Nakama/IChannel.cs
+++ b/src/Nakama/IChannel.cs
@@ -15,7 +15,6 @@
*/
using System.Collections.Generic;
-using System.Runtime.Serialization;
namespace Nakama
{
@@ -59,50 +58,4 @@ public interface IChannel
///
string UserIdTwo { get; }
}
-
- ///
- internal class Channel : IChannel
- {
- [DataMember(Name="id"), Preserve]
- public string Id { get; set; }
-
- public IEnumerable Presences => _presences ?? UserPresence.NoPresences;
- [DataMember(Name="presences"), Preserve]
- public List _presences { get; set; }
-
- public IUserPresence Self => _self;
- [DataMember(Name="self"), Preserve]
- public UserPresence _self { get; set; }
-
- [DataMember(Name="room_name"), Preserve]
- public string RoomName { get; set; }
-
- [DataMember(Name="group_id"), Preserve]
- public string GroupId { get; set; }
-
- [DataMember(Name="user_id_one"), Preserve]
- public string UserIdOne { get; set; }
-
- [DataMember(Name="user_id_two"), Preserve]
- public string UserIdTwo { get; set; }
-
- public override bool Equals(object obj)
- {
- if (!(obj is Channel item))
- {
- return false;
- }
- return Equals(item);
- }
-
- private bool Equals(IChannel other) => string.Equals(Id, other.Id);
-
- public override int GetHashCode() => Id != null ? Id.GetHashCode() : 0;
-
- public override string ToString()
- {
- var presences = string.Join(", ", Presences);
- return $"Channel(Id='{Id}', Presences=[{presences}], Self={Self}, RoomName='{RoomName}', GroupId='{GroupId}', UserIdOne='{UserIdOne}', UserIdTwo='{UserIdTwo}')";
- }
- }
}
diff --git a/src/Nakama/IChannelMessageAck.cs b/src/Nakama/IChannelMessageAck.cs
index 8d122317..40f61bc9 100644
--- a/src/Nakama/IChannelMessageAck.cs
+++ b/src/Nakama/IChannelMessageAck.cs
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-using System.Runtime.Serialization;
-
namespace Nakama
{
///
@@ -78,36 +76,4 @@ public interface IChannelMessageAck
///
string UserIdTwo { get; }
}
-
- ///
- internal class ChannelMessageAck : IChannelMessageAck
- {
- [DataMember(Name = "channel_id"), Preserve] public string ChannelId { get; set; }
-
- [DataMember(Name = "code"), Preserve] public int Code { get; set; }
-
- [DataMember(Name = "create_time"), Preserve] public string CreateTime { get; set; }
-
- [DataMember(Name = "message_id"), Preserve] public string MessageId { get; set; }
-
- [DataMember(Name = "persistent"), Preserve] public bool Persistent { get; set; }
-
- [DataMember(Name = "update_time"), Preserve] public string UpdateTime { get; set; }
-
- [DataMember(Name = "username"), Preserve] public string Username { get; set; }
-
- [DataMember(Name="room_name"), Preserve] public string RoomName { get; set; }
-
- [DataMember(Name="group_id"), Preserve] public string GroupId { get; set; }
-
- [DataMember(Name="user_id_one"), Preserve] public string UserIdOne { get; set; }
-
- [DataMember(Name="user_id_two"), Preserve] public string UserIdTwo { get; set; }
-
- public override string ToString()
- {
- return
- $"ChannelMessageAck(ChannelId='{ChannelId}', Code={Code}, CreateTime={CreateTime}, MessageId='{MessageId}', Persistent={Persistent}, UpdateTime={UpdateTime}, Username='{Username}', RoomName='{RoomName}', GroupId='{GroupId}', UserIdOne='{UserIdOne}', UserIdTwo='{UserIdTwo}')";
- }
- }
}
diff --git a/src/Nakama/IChannelPresenceEvent.cs b/src/Nakama/IChannelPresenceEvent.cs
index a6cb4605..33b9b28a 100644
--- a/src/Nakama/IChannelPresenceEvent.cs
+++ b/src/Nakama/IChannelPresenceEvent.cs
@@ -15,7 +15,6 @@
*/
using System.Collections.Generic;
-using System.Runtime.Serialization;
namespace Nakama
{
@@ -59,38 +58,4 @@ public interface IChannelPresenceEvent
///
string UserIdTwo { get; }
}
-
- ///
- internal class ChannelPresenceEvent : IChannelPresenceEvent
- {
- [DataMember(Name="channel_id"), Preserve]
- public string ChannelId { get; set; }
-
- public IEnumerable Joins => _joins ?? new List(0);
- [DataMember(Name="joins"), Preserve]
- public List _joins { get; set; }
-
- public IEnumerable Leaves => _leaves ?? new List(0);
- [DataMember(Name="leaves"), Preserve]
- public List _leaves { get; set; }
-
- [DataMember(Name="room_name"), Preserve]
- public string RoomName { get; set; }
-
- [DataMember(Name="group_id"), Preserve]
- public string GroupId { get; set; }
-
- [DataMember(Name="user_id_one"), Preserve]
- public string UserIdOne { get; set; }
-
- [DataMember(Name="user_id_two"), Preserve]
- public string UserIdTwo { get; set; }
-
- public override string ToString()
- {
- var joins = string.Join(",", Joins);
- var leaves = string.Join(",", Leaves);
- return $"ChannelPresenceEvent(ChannelId='{ChannelId}', Joins=[{joins}], Leaves=[{leaves}], RoomName='{RoomName}', GroupId='{GroupId}', UserIdOne='{UserIdOne}', UserIdTwo='{UserIdTwo}')";
- }
- }
}
diff --git a/src/Nakama/IMatch.cs b/src/Nakama/IMatch.cs
index c9ef0bc4..00af1c80 100644
--- a/src/Nakama/IMatch.cs
+++ b/src/Nakama/IMatch.cs
@@ -54,29 +54,4 @@ public interface IMatch
///
IUserPresence Self { get; }
}
-
- ///
- internal class Match : IMatch
- {
- [DataMember(Name = "authoritative"), Preserve] public bool Authoritative { get; set; }
-
- [DataMember(Name = "match_id"), Preserve] public string Id { get; set; }
-
- [DataMember(Name = "label"), Preserve] public string Label { get; set; }
-
- public IEnumerable Presences => _presences ?? UserPresence.NoPresences;
- [DataMember(Name = "presences"), Preserve] public List _presences { get; set; }
-
- [DataMember(Name = "size"), Preserve] public int Size { get; set; }
-
- public IUserPresence Self => _self;
- [DataMember(Name = "self"), Preserve] public UserPresence _self { get; set; }
-
- public override string ToString()
- {
- var presences = string.Join(", ", Presences);
- return
- $"Match(Authoritative={Authoritative}, Id='{Id}', Label='{Label}', Presences=[{presences}], Size={Size}, Self={Self})";
- }
- }
}
diff --git a/src/Nakama/IMatchPresenceEvent.cs b/src/Nakama/IMatchPresenceEvent.cs
index 404fa37e..ea548540 100644
--- a/src/Nakama/IMatchPresenceEvent.cs
+++ b/src/Nakama/IMatchPresenceEvent.cs
@@ -1,5 +1,5 @@
/**
- * Copyright 2018 The Nakama Authors
+ * Copyright 2020 The Nakama Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +15,6 @@
*/
using System.Collections.Generic;
-using System.Runtime.Serialization;
namespace Nakama
{
@@ -39,23 +38,4 @@ public interface IMatchPresenceEvent
///
string MatchId { get; }
}
-
- ///
- internal class MatchPresenceEvent : IMatchPresenceEvent
- {
- public IEnumerable Joins => _joins ?? UserPresence.NoPresences;
- [DataMember(Name = "joins"), Preserve] public List _joins { get; set; }
-
- public IEnumerable Leaves => _leaves ?? UserPresence.NoPresences;
- [DataMember(Name = "leaves"), Preserve] public List _leaves { get; set; }
-
- [DataMember(Name = "match_id"), Preserve] public string MatchId { get; set; }
-
- public override string ToString()
- {
- var joins = string.Join(", ", Joins);
- var leaves = string.Join(", ", Leaves);
- return $"MatchPresenceEvent(Joins=[{joins}], Leaves=[{leaves}], MatchId='{MatchId}')";
- }
- }
}
diff --git a/src/Nakama/IMatchState.cs b/src/Nakama/IMatchState.cs
index b6f27522..6d2de3e6 100644
--- a/src/Nakama/IMatchState.cs
+++ b/src/Nakama/IMatchState.cs
@@ -1,5 +1,5 @@
/**
- * Copyright 2018 The Nakama Authors
+ * Copyright 2020 The Nakama Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-using System;
-using System.Runtime.Serialization;
-
namespace Nakama
{
///
@@ -47,26 +44,4 @@ public interface IMatchState
///
IUserPresence UserPresence { get; }
}
-
- ///
- internal class MatchState : IMatchState
- {
- private static readonly byte[] NoBytes = new byte[0];
-
- [DataMember(Name = "match_id"), Preserve] public string MatchId { get; set; }
-
- public long OpCode => Convert.ToInt64(_opCode);
- [DataMember(Name = "op_code"), Preserve] public string _opCode { get; set; }
-
- public byte[] State => _state == null ? NoBytes : Convert.FromBase64String(_state);
- [DataMember(Name = "data"), Preserve] public string _state { get; set; }
-
- public IUserPresence UserPresence => _userPresence;
- [DataMember(Name = "presence"), Preserve] public UserPresence _userPresence { get; set; }
-
- public override string ToString()
- {
- return $"MatchState(MatchId='{MatchId}', OpCode={OpCode}, State='{_state}', UserPresence={UserPresence})";
- }
- }
}
diff --git a/src/Nakama/IMatchmakerMatched.cs b/src/Nakama/IMatchmakerMatched.cs
index 85e69c29..4a42d02b 100644
--- a/src/Nakama/IMatchmakerMatched.cs
+++ b/src/Nakama/IMatchmakerMatched.cs
@@ -1,5 +1,5 @@
/**
- * Copyright 2018 The Nakama Authors
+ * Copyright 2020 The Nakama Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +15,6 @@
*/
using System.Collections.Generic;
-using System.Runtime.Serialization;
namespace Nakama
{
@@ -73,50 +72,4 @@ public interface IMatchmakerUser
///
IDictionary StringProperties { get; }
}
-
- ///
- internal class MatchmakerMatched : IMatchmakerMatched
- {
- [DataMember(Name = "match_id"), Preserve] public string MatchId { get; set; }
-
- [DataMember(Name = "ticket"), Preserve] public string Ticket { get; set; }
-
- [DataMember(Name = "token"), Preserve] public string Token { get; set; }
-
- public IEnumerable Users => _users ?? new List(0);
- [DataMember(Name = "users"), Preserve] public List _users { get; set; }
-
- public IMatchmakerUser Self => _self;
- [DataMember(Name = "self"), Preserve] public MatchmakerUser _self { get; set; }
-
- public override string ToString()
- {
- var users = string.Join(", ", Users);
- return
- $"MatchmakerMatched(MatchId='{MatchId}', Ticket='{Ticket}', Token='{Token}', Users=[{users}], Self={Self})";
- }
- }
-
- ///
- internal class MatchmakerUser : IMatchmakerUser
- {
- public IDictionary NumericProperties => _numericProperties ?? new Dictionary();
-
- [DataMember(Name = "numeric_properties"), Preserve]
- public Dictionary _numericProperties { get; set; }
-
- public IUserPresence Presence => _presence;
- [DataMember(Name = "presence"), Preserve] public UserPresence _presence { get; set; }
-
- public IDictionary StringProperties => _stringProperties ?? new Dictionary();
-
- [DataMember(Name = "string_properties"), Preserve]
- public Dictionary _stringProperties { get; set; }
-
- public override string ToString()
- {
- return
- $"MatchmakerUser(NumericProperties={NumericProperties}, Presence={Presence}, StringProperties={StringProperties})";
- }
- }
}
diff --git a/src/Nakama/IMatchmakerTicket.cs b/src/Nakama/IMatchmakerTicket.cs
index 18003bb5..be79b65d 100644
--- a/src/Nakama/IMatchmakerTicket.cs
+++ b/src/Nakama/IMatchmakerTicket.cs
@@ -28,16 +28,4 @@ public interface IMatchmakerTicket
///
string Ticket { get; }
}
-
- ///
- internal class MatchmakerTicket : IMatchmakerTicket
- {
- [DataMember(Name="ticket"), Preserve]
- public string Ticket { get; set; }
-
- public override string ToString()
- {
- return $"MatchmakerTicket(Ticket='{Ticket}')";
- }
- }
}
diff --git a/src/Nakama/ISocket.cs b/src/Nakama/ISocket.cs
index 1d8d467a..e18c47e6 100644
--- a/src/Nakama/ISocket.cs
+++ b/src/Nakama/ISocket.cs
@@ -17,6 +17,7 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
+using Nakama.SocketInternal;
namespace Nakama
{
@@ -268,7 +269,7 @@ Task SendMatchStateAsync(string matchId, long opCode, byte[] state,
/// The users to unfollow.
/// A task which represents the asynchronous operation.
Task UnfollowUsersAsync(IEnumerable users);
-
+
///
/// Unfollow one or more users from their status updates.
///
diff --git a/src/Nakama/IStatus.cs b/src/Nakama/IStatus.cs
index 79d69e70..260ceb6f 100644
--- a/src/Nakama/IStatus.cs
+++ b/src/Nakama/IStatus.cs
@@ -29,18 +29,4 @@ public interface IStatus
///
IEnumerable Presences { get; }
}
-
- ///
- internal class Status : IStatus
- {
- public IEnumerable Presences => _presences ?? UserPresence.NoPresences;
- [DataMember(Name="presences"), Preserve]
- public List _presences { get; set; }
-
- public override string ToString()
- {
- var presences = string.Join(", ", Presences);
- return $"Status(Presences=[{presences}])";
- }
- }
}
diff --git a/src/Nakama/IStatusPresenceEvent.cs b/src/Nakama/IStatusPresenceEvent.cs
index a4a9b6ed..b36347da 100644
--- a/src/Nakama/IStatusPresenceEvent.cs
+++ b/src/Nakama/IStatusPresenceEvent.cs
@@ -15,7 +15,6 @@
*/
using System.Collections.Generic;
-using System.Runtime.Serialization;
namespace Nakama
{
@@ -40,21 +39,4 @@ public interface IStatusPresenceEvent
///
IEnumerable Joins { get; }
}
-
- ///
- internal class StatusPresenceEvent : IStatusPresenceEvent
- {
- public IEnumerable Leaves => _leaves ?? UserPresence.NoPresences;
- [DataMember(Name = "leaves"), Preserve] public List _leaves { get; set; }
-
- public IEnumerable Joins => _joins ?? UserPresence.NoPresences;
- [DataMember(Name = "joins"), Preserve] public List _joins { get; set; }
-
- public override string ToString()
- {
- var joins = string.Join(", ", Joins);
- var leaves = string.Join(", ", Leaves);
- return $"StatusPresenceEvent(Leaves=[{leaves}], Joins=[{joins}])";
- }
- }
}
diff --git a/src/Nakama/IStreamPresenceEvent.cs b/src/Nakama/IStreamPresenceEvent.cs
index 56e25ece..d16edd97 100644
--- a/src/Nakama/IStreamPresenceEvent.cs
+++ b/src/Nakama/IStreamPresenceEvent.cs
@@ -90,59 +90,4 @@ public interface IStream
///
string Subject { get; }
}
-
- ///
- internal class StreamPresenceEvent : IStreamPresenceEvent
- {
- public IEnumerable Leaves => _leaves ?? UserPresence.NoPresences;
- [DataMember(Name = "leaves"), Preserve] public List _leaves { get; set; }
-
- public IEnumerable Joins => _joins ?? UserPresence.NoPresences;
- [DataMember(Name = "joins"), Preserve] public List _joins { get; set; }
-
- public IStream Stream => _stream;
- [DataMember(Name = "stream"), Preserve] public Stream _stream { get; set; }
-
- public override string ToString()
- {
- var leaves = string.Join(", ", Leaves);
- var joins = string.Join(", ", Joins);
- return $"StreamPresenceEvent(Leaves=[{leaves}], Joins=[{joins}], Stream={Stream})";
- }
- }
-
- ///
- internal class StreamState : IStreamState
- {
- public IUserPresence Sender => _sender;
- [DataMember(Name = "sender"), Preserve] public UserPresence _sender { get; set; }
-
- public string State => _state;
- [DataMember(Name = "data"), Preserve] public string _state { get; set; }
-
- public IStream Stream => _stream;
- [DataMember(Name = "stream"), Preserve] public Stream _stream { get; set; }
-
- public override string ToString()
- {
- return $"StreamState(Sender={Sender}, State='{_state}', Stream={Stream})";
- }
- }
-
- ///
- internal class Stream : IStream
- {
- [DataMember(Name = "descriptor"), Preserve] public string Descriptor { get; set; }
-
- [DataMember(Name = "label"), Preserve] public string Label { get; set; }
-
- [DataMember(Name = "mode"), Preserve] public int Mode { get; set; }
-
- [DataMember(Name = "subject"), Preserve] public string Subject { get; set; }
-
- public override string ToString()
- {
- return $"Stream(Descriptor='{Descriptor}', Label='{Label}', Mode={Mode}, Subject='{Subject}')";
- }
- }
}
diff --git a/src/Nakama/IUserPresence.cs b/src/Nakama/IUserPresence.cs
index 87264b62..4cbcb281 100644
--- a/src/Nakama/IUserPresence.cs
+++ b/src/Nakama/IUserPresence.cs
@@ -53,46 +53,4 @@ public interface IUserPresence
///
string UserId { get; }
}
-
- ///
- internal class UserPresence : IUserPresence
- {
- internal static readonly IReadOnlyList NoPresences = new List(0);
-
- [DataMember(Name = "persistence"), Preserve] public bool Persistence { get; set; }
-
- [DataMember(Name = "session_id"), Preserve] public string SessionId { get; set; }
-
- [DataMember(Name = "status"), Preserve] public string Status { get; set; }
-
- [DataMember(Name = "username"), Preserve] public string Username { get; set; }
-
- [DataMember(Name = "user_id"), Preserve] public string UserId { get; set; }
-
- public override bool Equals(object obj)
- {
- if (!(obj is UserPresence item))
- {
- return false;
- }
- return Equals(item);
- }
-
- private bool Equals(IUserPresence other) => string.Equals(SessionId, other.SessionId) && string.Equals(UserId, other.UserId);
-
- public override int GetHashCode()
- {
- unchecked
- {
- // ReSharper disable twice NonReadonlyMemberInGetHashCode
- return ((SessionId?.GetHashCode() ?? 0) * 397) ^ (UserId?.GetHashCode() ?? 0);
- }
- }
-
- public override string ToString()
- {
- return
- $"UserPresence(Persistence={Persistence}, SessionId='{SessionId}', Status='{Status}', Username='{Username}', UserId='{UserId}')";
- }
- }
}
diff --git a/src/Nakama/Socket.cs b/src/Nakama/Socket.cs
index 6c23d5aa..b059dbbc 100644
--- a/src/Nakama/Socket.cs
+++ b/src/Nakama/Socket.cs
@@ -21,6 +21,7 @@
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
+using Nakama.SocketInternal;
using Nakama.TinyJson;
namespace Nakama
@@ -162,6 +163,7 @@ public async Task AddMatchmakerAsync(string query = "*", int
NumericProperties = numericProperties
}
};
+
var response = await SendAsync(envelope);
return response.MatchmakerTicket;
}
@@ -185,7 +187,7 @@ public Task ConnectAsync(ISession session, bool appearOnline = false,
var uri = new UriBuilder(_baseUri)
{
Path = "/ws",
- Query = $"lang=en&status={appearOnline}&token={session.AuthToken}"
+ Query = $"lang=en&status={appearOnline}&token={session.AuthToken}&format={_adapter.Format}"
}.Uri;
tcs.Task.ContinueWith(_ =>
{
@@ -256,6 +258,7 @@ public async Task JoinChatAsync(string target, ChannelType type, bool
Type = (int) type
}
};
+
var response = await SendAsync(envelope);
return response.Channel;
}
@@ -375,7 +378,7 @@ public async Task RpcAsync(string funcId, string payload = null)
var envelope = new WebSocketMessageEnvelope
{
Cid = $"{_cid++}",
- Rpc = new ApiRpc
+ Rpc = new Nakama.SocketInternal.ApiRpc
{
Id = funcId,
Payload = payload
@@ -509,8 +512,18 @@ public static ISocket From(IClient client, ISocketAdapter adapter)
private void ReceivedMessage(ArraySegment buffer)
{
- var contents = System.Text.Encoding.UTF8.GetString(buffer.Array, buffer.Offset, buffer.Count);
- var envelope = contents.FromJson();
+ WebSocketMessageEnvelope envelope;
+
+ try
+ {
+ envelope = _adapter.DeserializeEnvelope(buffer);
+ }
+ catch (Exception e)
+ {
+ Logger?.ErrorFormat("Error deserializing socket envelope: '{0}'", e.Message);
+ return;
+ }
+
try
{
if (!string.IsNullOrEmpty(envelope.Cid))
@@ -580,7 +593,7 @@ private void ReceivedMessage(ArraySegment buffer)
}
else
{
- Logger?.ErrorFormat("Received unrecognised message: '{0}'", contents);
+ Logger?.ErrorFormat("Received unrecognised message: '{0}'", envelope);
}
}
catch (Exception e)
@@ -591,17 +604,15 @@ private void ReceivedMessage(ArraySegment buffer)
private Task SendAsync(WebSocketMessageEnvelope envelope)
{
- var json = envelope.ToJson();
- var buffer = System.Text.Encoding.UTF8.GetBytes(json);
if (string.IsNullOrEmpty(envelope.Cid))
{
- _adapter.Send(new ArraySegment(buffer), CancellationToken.None);
+ _adapter.Send(envelope, CancellationToken.None);
return null; // No response required.
}
var completer = new TaskCompletionSource();
_responses[envelope.Cid] = completer;
- _adapter.Send(new ArraySegment(buffer), CancellationToken.None);
+ _adapter.Send(envelope, CancellationToken.None);
return completer.Task;
}
diff --git a/src/Nakama/SocketInternal/ApiChannelMessage.cs b/src/Nakama/SocketInternal/ApiChannelMessage.cs
new file mode 100644
index 00000000..411b1693
--- /dev/null
+++ b/src/Nakama/SocketInternal/ApiChannelMessage.cs
@@ -0,0 +1,118 @@
+
+
+/**
+* Copyright 2020 The Nakama Authors
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System.Runtime.Serialization;
+
+namespace Nakama.SocketInternal
+{
+ ///
+ [DataContract]
+ public class ApiChannelMessage : IApiChannelMessage
+ {
+ ///
+ [DataMember(Name="channel_id", Order = 1), Preserve]
+ public string ChannelId { get; set; }
+
+ ///
+ public int Code => _codeValue.HasValue ? _codeValue.Value : _code;
+
+ ///
+ [DataMember(Name="content", Order = 6), Preserve]
+ public string Content { get; set; }
+
+ ///
+ public string CreateTime => _createTimeValue.HasValue ? _createTimeValue.Value.ToString() : _createTime;
+
+ ///
+ [DataMember(Name="group_id", Order = 11), Preserve]
+ public string GroupId { get; set; }
+
+ ///
+ [DataMember(Name="message_id", Order = 2), Preserve]
+ public string MessageId { get; set; }
+
+ ///
+ public bool Persistent => _persistentValue.HasValue ? _persistentValue.Value : _persistent;
+
+ ///
+ [DataMember(Name="room_name", Order = 10), Preserve]
+ public string RoomName { get; set; }
+
+ ///
+ [DataMember(Name="sender_id", Order = 4), Preserve]
+ public string SenderId { get; set; }
+
+ ///
+ public string UpdateTime => _updateTimeValue.HasValue ? _updateTimeValue.Value.ToString() : _updateTime.ToString();
+
+ ///
+ [DataMember(Name="user_id_one", Order = 12), Preserve]
+ public string UserIdOne { get; set; }
+
+ ///
+ [DataMember(Name="user_id_two", Order = 13), Preserve]
+ public string UserIdTwo { get; set; }
+
+ ///
+ [DataMember(Name="username", Order = 5), Preserve]
+ public string Username { get; set; }
+
+ [DataMember(Name="code"), Preserve]
+ private int _code { get; set; }
+
+ [DataMember(Order = 3), Preserve]
+ private IntValue _codeValue;
+
+ [DataMember(Name="create_time"), Preserve]
+ private string _createTime;
+
+ [DataMember(Order = 7), Preserve]
+ private IntValue _createTimeValue;
+
+ [DataMember(Name="persistent"), Preserve]
+ private bool _persistent;
+
+ [DataMember(Order = 9), Preserve]
+ private BoolValue _persistentValue;
+
+ [DataMember(Name="update_time"), Preserve]
+ private int _updateTime;
+
+ [DataMember(Order = 8), Preserve]
+ private IntValue _updateTimeValue;
+
+ public override string ToString()
+ {
+ var output = "";
+ output = string.Concat(output, "ChannelId: ", ChannelId, ", ");
+ output = string.Concat(output, "Code: ", _code, ", ");
+ output = string.Concat(output, "Content: ", Content, ", ");
+ output = string.Concat(output, "CreateTime: ", CreateTime, ", ");
+ output = string.Concat(output, "GroupId: ", GroupId, ", ");
+ output = string.Concat(output, "MessageId: ", MessageId, ", ");
+ output = string.Concat(output, "Persistent: ", Persistent, ", ");
+ output = string.Concat(output, "RoomName: ", RoomName, ", ");
+ output = string.Concat(output, "SenderId: ", SenderId, ", ");
+ output = string.Concat(output, "UpdateTime: ", UpdateTime, ", ");
+ output = string.Concat(output, "UserIdOne: ", UserIdOne, ", ");
+ output = string.Concat(output, "UserIdTwo: ", UserIdTwo, ", ");
+ output = string.Concat(output, "Username: ", Username, ", ");
+ return output;
+ }
+ }
+}
diff --git a/src/Nakama/SocketInternal/ApiNotification.cs b/src/Nakama/SocketInternal/ApiNotification.cs
new file mode 100644
index 00000000..e82ad038
--- /dev/null
+++ b/src/Nakama/SocketInternal/ApiNotification.cs
@@ -0,0 +1,75 @@
+/**
+* Copyright 2020 The Nakama Authors
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System.Runtime.Serialization;
+
+namespace Nakama.SocketInternal
+{
+ [DataContract]
+ public class ApiNotification : IApiNotification
+ {
+ ///
+ public int Code => _codeValue.HasValue ? _codeValue.Value : _code;
+
+ ///
+ [DataMember(Name="content", Order = 3), Preserve]
+ public string Content { get; set; }
+
+ ///
+ public string CreateTime => _createTimeValue.HasValue ? _createTimeValue.Value.ToString() : _createTime;
+
+ ///
+ [DataMember(Name="id", Order = 1), Preserve]
+ public string Id { get; set; }
+
+ ///
+ [DataMember(Name="persistent", Order = 7), Preserve]
+ public bool Persistent { get; set; }
+
+ ///
+ [DataMember(Name="sender_id", Order = 5), Preserve]
+ public string SenderId { get; set; }
+
+ ///
+ [DataMember(Name="subject", Order = 2), Preserve]
+ public string Subject { get; set; }
+
+ [DataMember(Name="code"), Preserve]
+ private int _code;
+
+ [DataMember(Order = 4), Preserve]
+ private int? _codeValue { get; set; }
+
+ [DataMember(Name="create_time"), Preserve]
+ private string _createTime;
+
+ [DataMember(Order = 6), Preserve]
+ private IntValue _createTimeValue;
+
+ public override string ToString()
+ {
+ var output = "";
+ output = string.Concat(output, "Code: ", Code, ", ");
+ output = string.Concat(output, "Content: ", Content, ", ");
+ output = string.Concat(output, "CreateTime: ", CreateTime, ", ");
+ output = string.Concat(output, "Id: ", Id, ", ");
+ output = string.Concat(output, "Persistent: ", Persistent, ", ");
+ output = string.Concat(output, "SenderId: ", SenderId, ", ");
+ output = string.Concat(output, "Subject: ", Subject, ", ");
+ return output;
+ }
+ }
+}
diff --git a/src/Nakama/SocketInternal/ApiRpc.cs b/src/Nakama/SocketInternal/ApiRpc.cs
new file mode 100644
index 00000000..4f870131
--- /dev/null
+++ b/src/Nakama/SocketInternal/ApiRpc.cs
@@ -0,0 +1,46 @@
+/**
+* Copyright 2020 The Nakama Authors
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System.Runtime.Serialization;
+
+namespace Nakama.SocketInternal
+{
+///
+ [DataContract]
+ public class ApiRpc : IApiRpc
+ {
+ ///
+ [DataMember(Name="http_key", Order = 1), Preserve]
+ public string HttpKey { get; set; }
+
+ ///
+ [DataMember(Name="id", Order = 2), Preserve]
+ public string Id { get; set; }
+
+ ///
+ [DataMember(Name="payload", Order = 3), Preserve]
+ public string Payload { get; set; }
+
+ public override string ToString()
+ {
+ var output = "";
+ output = string.Concat(output, "HttpKey: ", HttpKey, ", ");
+ output = string.Concat(output, "Id: ", Id, ", ");
+ output = string.Concat(output, "Payload: ", Payload, ", ");
+ return output;
+ }
+ }
+}
diff --git a/src/Nakama/SocketInternal/Channel.cs b/src/Nakama/SocketInternal/Channel.cs
new file mode 100644
index 00000000..1dfaeac6
--- /dev/null
+++ b/src/Nakama/SocketInternal/Channel.cs
@@ -0,0 +1,70 @@
+
+
+/**
+* Copyright 2020 The Nakama Authors
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Nakama.SocketInternal
+ {
+ ///
+ [DataContract]
+ public class Channel : IChannel
+ {
+ [DataMember(Name="id", Order = 1), Preserve]
+ public string Id { get; set; }
+
+ public IEnumerable Presences => _presences ?? UserPresence.NoPresences;
+ [DataMember(Name="presences", Order = 2), Preserve]
+ public List _presences { get; set; }
+
+ public IUserPresence Self => _self;
+ [DataMember(Name="self", Order = 3), Preserve]
+ public UserPresence _self { get; set; }
+
+ [DataMember(Name="room_name", Order = 4), Preserve]
+ public string RoomName { get; set; }
+
+ [DataMember(Name="group_id", Order = 5), Preserve]
+ public string GroupId { get; set; }
+
+ [DataMember(Name="user_id_one", Order = 6), Preserve]
+ public string UserIdOne { get; set; }
+
+ [DataMember(Name="user_id_two", Order = 7), Preserve]
+ public string UserIdTwo { get; set; }
+
+ public override bool Equals(object obj)
+ {
+ if (!(obj is Channel item))
+ {
+ return false;
+ }
+ return Equals(item);
+ }
+
+ private bool Equals(IChannel other) => string.Equals(Id, other.Id);
+
+ public override int GetHashCode() => Id != null ? Id.GetHashCode() : 0;
+
+ public override string ToString()
+ {
+ var presences = string.Join(", ", Presences);
+ return $"Channel(Id='{Id}', Presences=[{presences}], Self={Self}, RoomName='{RoomName}', GroupId='{GroupId}', UserIdOne='{UserIdOne}', UserIdTwo='{UserIdTwo}')";
+ }
+ }
+ }
diff --git a/src/Nakama/SocketInternal/ChannelJoinMessage.cs b/src/Nakama/SocketInternal/ChannelJoinMessage.cs
new file mode 100644
index 00000000..5a6ded40
--- /dev/null
+++ b/src/Nakama/SocketInternal/ChannelJoinMessage.cs
@@ -0,0 +1,70 @@
+/**
+ * Copyright 2020 The Nakama Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Runtime.Serialization;
+
+namespace Nakama.SocketInternal
+{
+ ///
+ /// Send a channel join message to the server.
+ ///
+ [DataContract]
+ public class ChannelJoinMessage
+ {
+ public bool Hidden
+ {
+ get => _hiddenValue.HasValue ? _hiddenValue.Value : _hidden;
+ set
+ {
+ _hidden = value;
+ _hiddenValue.Value = value;
+ }
+ }
+
+ public bool Persistence
+ {
+ get => _persistenceValue.HasValue ? _persistenceValue.Value : _persistence;
+ set
+ {
+ _persistenceValue.Value = value;
+ _persistence = value;
+ }
+ }
+
+ [DataMember(Name="target", Order = 1), Preserve]
+ public string Target { get; set; }
+
+ [DataMember(Name="type", Order = 2), Preserve]
+ public int Type { get; set; }
+
+ [DataMember(Order = 4), Preserve]
+ private BoolValue _hiddenValue;
+
+ [DataMember(Name="hidden"), Preserve]
+ private bool _hidden;
+
+ [DataMember(Name="persistence"), Preserve]
+ private bool _persistence;
+
+ [DataMember(Order = 3), Preserve]
+ private BoolValue _persistenceValue;
+
+ public override string ToString()
+ {
+ return $"ChannelJoinMessage(Hidden={Hidden}, Persistence={Persistence}, Target='{Target}', Type={Type})";
+ }
+ }
+}
diff --git a/src/Nakama/ChannelLeaveMessage.cs b/src/Nakama/SocketInternal/ChannelLeaveMessage.cs
similarity index 82%
rename from src/Nakama/ChannelLeaveMessage.cs
rename to src/Nakama/SocketInternal/ChannelLeaveMessage.cs
index 37cf3268..8b25ffc8 100644
--- a/src/Nakama/ChannelLeaveMessage.cs
+++ b/src/Nakama/SocketInternal/ChannelLeaveMessage.cs
@@ -1,5 +1,5 @@
/**
- * Copyright 2018 The Nakama Authors
+ * Copyright 2020 The Nakama Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,14 +16,15 @@
using System.Runtime.Serialization;
-namespace Nakama
+namespace Nakama.SocketInternal
{
///
/// A leave message to a chat channel.
///
- internal class ChannelLeaveMessage
+ [DataContract]
+ public class ChannelLeaveMessage
{
- [DataMember(Name="channel_id"), Preserve]
+ [DataMember(Name="channel_id", Order = 1), Preserve]
public string ChannelId { get; set; }
public override string ToString()
diff --git a/src/Nakama/SocketInternal/ChannelMessageAck.cs b/src/Nakama/SocketInternal/ChannelMessageAck.cs
new file mode 100644
index 00000000..75b97d96
--- /dev/null
+++ b/src/Nakama/SocketInternal/ChannelMessageAck.cs
@@ -0,0 +1,84 @@
+/**
+* Copyright 2020 The Nakama Authors
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System.Runtime.Serialization;
+
+namespace Nakama.SocketInternal
+{
+ ///
+ [DataContract]
+ public class ChannelMessageAck : IChannelMessageAck
+ {
+ [DataMember(Name = "channel_id", Order = 1), Preserve]
+ public string ChannelId { get; set; }
+
+ public int Code => _codeValue.HasValue ? _codeValue.Value : _code;
+
+ public string CreateTime => _createTimeValue.HasValue ? _createTimeValue.Value.ToString() : _createTime;
+
+ [DataMember(Name = "message_id", Order = 2), Preserve]
+ public string MessageId { get; set; }
+
+ public bool Persistent => _persistentValue.HasValue ? _persistentValue.Value : _persistent;
+
+ public string UpdateTime => _updateTimeValue.HasValue ? _updateTimeValue.Value.ToString() : _updateTime;
+
+ [DataMember(Name = "username", Order = 4), Preserve]
+ public string Username { get; set; }
+
+ [DataMember(Name="room_name", Order = 8), Preserve]
+ public string RoomName { get; set; }
+
+ [DataMember(Name="group_id", Order = 9), Preserve]
+ public string GroupId { get; set; }
+
+ [DataMember(Name="user_id_one", Order = 10), Preserve]
+ public string UserIdOne { get; set; }
+
+ [DataMember(Name="user_id_two", Order = 11), Preserve]
+ public string UserIdTwo { get; set; }
+
+ [DataMember(Name = "code"), Preserve]
+ private int _code;
+
+ [DataMember(Order = 3), Preserve]
+ private IntValue _codeValue;
+
+ [DataMember(Name = "create_time"), Preserve]
+ private string _createTime;
+
+ [DataMember(Order = 5), Preserve]
+ private IntValue _createTimeValue;
+
+ [DataMember(Name = "persistent"), Preserve]
+ private bool _persistent;
+
+ [DataMember(Order = 7), Preserve]
+ private BoolValue _persistentValue;
+
+ [DataMember(Name = "update_time"), Preserve]
+ private string _updateTime;
+
+ [DataMember(Order = 6), Preserve]
+ private IntValue _updateTimeValue;
+
+ public override string ToString()
+ {
+ return
+ $"ChannelMessageAck(ChannelId='{ChannelId}', Code={Code}, CreateTime={CreateTime}, MessageId='{MessageId}', Persistent={Persistent}, UpdateTime={UpdateTime}, Username='{Username}', RoomName='{RoomName}', GroupId='{GroupId}', UserIdOne='{UserIdOne}', UserIdTwo='{UserIdTwo}')";
+ }
+ }
+}
diff --git a/src/Nakama/SocketInternal/ChannelPresenceEvent.cs b/src/Nakama/SocketInternal/ChannelPresenceEvent.cs
new file mode 100644
index 00000000..7d2387b8
--- /dev/null
+++ b/src/Nakama/SocketInternal/ChannelPresenceEvent.cs
@@ -0,0 +1,57 @@
+/**
+* Copyright 2020 The Nakama Authors
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Nakama.SocketInternal
+{
+///
+ [DataContract]
+ public class ChannelPresenceEvent : IChannelPresenceEvent
+ {
+ [DataMember(Name="channel_id", Order = 1), Preserve]
+ public string ChannelId { get; set; }
+
+ public IEnumerable Joins => _joins ?? new List(0);
+ [DataMember(Name="joins", Order = 2), Preserve]
+ public List _joins { get; set; }
+
+ public IEnumerable Leaves => _leaves ?? new List(0);
+ [DataMember(Name="leaves", Order = 3), Preserve]
+ public List _leaves { get; set; }
+
+ [DataMember(Name="room_name", Order = 4), Preserve]
+ public string RoomName { get; set; }
+
+ [DataMember(Name="group_id", Order= 5), Preserve]
+ public string GroupId { get; set; }
+
+ [DataMember(Name="user_id_one", Order = 6), Preserve]
+ public string UserIdOne { get; set; }
+
+ [DataMember(Name="user_id_two", Order = 7), Preserve]
+ public string UserIdTwo { get; set; }
+
+ public override string ToString()
+ {
+ var joins = string.Join(",", Joins);
+ var leaves = string.Join(",", Leaves);
+ return $"ChannelPresenceEvent(ChannelId='{ChannelId}', Joins=[{joins}], Leaves=[{leaves}], RoomName='{RoomName}', GroupId='{GroupId}', UserIdOne='{UserIdOne}', UserIdTwo='{UserIdTwo}')";
+ }
+ }
+}
+
diff --git a/src/Nakama/ChannelRemoveMessage.cs b/src/Nakama/SocketInternal/ChannelRemoveMessage.cs
similarity index 79%
rename from src/Nakama/ChannelRemoveMessage.cs
rename to src/Nakama/SocketInternal/ChannelRemoveMessage.cs
index cadebf49..a110e8d8 100644
--- a/src/Nakama/ChannelRemoveMessage.cs
+++ b/src/Nakama/SocketInternal/ChannelRemoveMessage.cs
@@ -1,5 +1,5 @@
/**
- * Copyright 2018 The Nakama Authors
+ * Copyright 2020 The Nakama Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,17 +16,18 @@
using System.Runtime.Serialization;
-namespace Nakama
+namespace Nakama.SocketInternal
{
///
/// Remove a message from a chat channel.
///
- internal class ChannelRemoveMessage
+ [DataContract]
+ public class ChannelRemoveMessage
{
- [DataMember(Name="channel_id"), Preserve]
+ [DataMember(Name="channel_id", Order = 1), Preserve]
public string ChannelId { get; set; }
- [DataMember(Name="message_id"), Preserve]
+ [DataMember(Name="message_id", Order = 2), Preserve]
public string MessageId { get; set; }
public override string ToString()
diff --git a/src/Nakama/ChannelSendMessage.cs b/src/Nakama/SocketInternal/ChannelSendMessage.cs
similarity index 79%
rename from src/Nakama/ChannelSendMessage.cs
rename to src/Nakama/SocketInternal/ChannelSendMessage.cs
index 7ca2135e..e5647f9e 100644
--- a/src/Nakama/ChannelSendMessage.cs
+++ b/src/Nakama/SocketInternal/ChannelSendMessage.cs
@@ -1,5 +1,5 @@
/**
- * Copyright 2018 The Nakama Authors
+ * Copyright 2020 The Nakama Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,17 +16,18 @@
using System.Runtime.Serialization;
-namespace Nakama
+namespace Nakama.SocketInternal
{
///
/// Send a chat message to a channel on the server.
///
- internal class ChannelSendMessage
+ [DataContract]
+ public class ChannelSendMessage
{
- [DataMember(Name="channel_id"), Preserve]
+ [DataMember(Name="channel_id", Order = 1), Preserve]
public string ChannelId { get; set; }
- [DataMember(Name="content"), Preserve]
+ [DataMember(Name="content", Order = 2), Preserve]
public string Content { get; set; }
public override string ToString()
diff --git a/src/Nakama/ChannelUpdateMessage.cs b/src/Nakama/SocketInternal/ChannelUpdateMessage.cs
similarity index 79%
rename from src/Nakama/ChannelUpdateMessage.cs
rename to src/Nakama/SocketInternal/ChannelUpdateMessage.cs
index 085d3a87..aff73b61 100644
--- a/src/Nakama/ChannelUpdateMessage.cs
+++ b/src/Nakama/SocketInternal/ChannelUpdateMessage.cs
@@ -16,20 +16,21 @@
using System.Runtime.Serialization;
-namespace Nakama
+namespace Nakama.SocketInternal
{
///
/// Update a chat message which has been sent to a channel.
///
- internal class ChannelUpdateMessage
+ [DataContract]
+ public class ChannelUpdateMessage
{
- [DataMember(Name="channel_id"), Preserve]
+ [DataMember(Name="channel_id", Order = 1), Preserve]
public string ChannelId { get; set; }
- [DataMember(Name="message_id"), Preserve]
+ [DataMember(Name="message_id", Order = 2), Preserve]
public string MessageId { get; set; }
- [DataMember(Name="content"), Preserve]
+ [DataMember(Name="content", Order = 3), Preserve]
public string Content { get; set; }
public override string ToString()
diff --git a/src/Nakama/ISocketAdapter.cs b/src/Nakama/SocketInternal/ISocketAdapter.cs
similarity index 78%
rename from src/Nakama/ISocketAdapter.cs
rename to src/Nakama/SocketInternal/ISocketAdapter.cs
index 48c3a83b..f4aefccc 100644
--- a/src/Nakama/ISocketAdapter.cs
+++ b/src/Nakama/SocketInternal/ISocketAdapter.cs
@@ -17,7 +17,7 @@
using System;
using System.Threading;
-namespace Nakama
+namespace Nakama.SocketInternal
{
///
/// An adapter which implements a socket with a protocol supported by Nakama.
@@ -44,6 +44,11 @@ public interface ISocketAdapter : IDisposable
///
event Action> Received;
+ ///
+ /// The format of the socket messages.
+ ///
+ string Format { get; }
+
///
/// If the socket is connected.
///
@@ -66,12 +71,20 @@ public interface ISocketAdapter : IDisposable
/// The timeout for the connect attempt on the socket.
void Connect(Uri uri, int timeout);
+
+ ///
+ /// Deserialize a WebSocketMessageEnvelope from an array of bytes.
+ ///
+ /// The array of bytes.
+ /// The deserialized envelope.
+ WebSocketMessageEnvelope DeserializeEnvelope(ArraySegment buffer);
+
///
/// Send data to the server with an asynchronous operation.
///
- /// The buffer with the message to send.
+ /// The envelope with the message to send.
/// A cancellation token used to propagate when the operation should be canceled.
/// If the message should be sent reliably (will be ignored by some protocols).
- void Send(ArraySegment buffer, CancellationToken cancellationToken, bool reliable = true);
+ void Send(WebSocketMessageEnvelope envelope, CancellationToken cancellationToken, bool reliable = true);
}
}
diff --git a/src/Nakama/SocketInternal/Match.cs b/src/Nakama/SocketInternal/Match.cs
new file mode 100644
index 00000000..4267db28
--- /dev/null
+++ b/src/Nakama/SocketInternal/Match.cs
@@ -0,0 +1,60 @@
+/**
+* Copyright 2020 The Nakama Authors
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Nakama.SocketInternal
+{
+ ///
+ [DataContract]
+ public class Match : IMatch
+ {
+ [DataMember(Name = "authoritative", Order = 2), Preserve]
+ public bool Authoritative { get; set; }
+
+ [DataMember(Name = "match_id", Order = 1), Preserve]
+ public string Id { get; set; }
+
+ public string Label => _labelValue.Value ?? _label;
+
+ public IEnumerable Presences => _presences ?? UserPresence.NoPresences;
+
+ [DataMember(Name = "presences", Order = 5), Preserve]
+ private List _presences { get; set; }
+
+ [DataMember(Name = "size", Order = 4), Preserve]
+ public int Size { get; set; }
+
+ public IUserPresence Self => _self;
+
+ [DataMember(Name = "self", Order = 6), Preserve]
+ private UserPresence _self;
+
+ [DataMember(Order = 3), Preserve]
+ private StringValue _labelValue;
+
+ [DataMember(Name = "label"), Preserve]
+ private string _label;
+
+ public override string ToString()
+ {
+ var presences = string.Join(", ", Presences);
+ return
+ $"Match(Authoritative={Authoritative}, Id='{Id}', Label='{Label}', Presences=[{presences}], Size={Size}, Self={Self})";
+ }
+ }
+}
diff --git a/src/Nakama/SocketInternal/MatchCreateMessage.cs b/src/Nakama/SocketInternal/MatchCreateMessage.cs
new file mode 100644
index 00000000..52a7ffb0
--- /dev/null
+++ b/src/Nakama/SocketInternal/MatchCreateMessage.cs
@@ -0,0 +1,32 @@
+
+
+using System.Runtime.Serialization;
+/**
+* Copyright 2020 The Nakama Authors
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+namespace Nakama.SocketInternal
+{
+ ///
+ /// A create message for a match on the server.
+ ///
+ [DataContract]
+ public class MatchCreateMessage
+ {
+ public override string ToString()
+ {
+ return "MatchCreateMessage()";
+ }
+ }
+}
diff --git a/src/Nakama/MatchJoinMessage.cs b/src/Nakama/SocketInternal/MatchJoinMessage.cs
similarity index 78%
rename from src/Nakama/MatchJoinMessage.cs
rename to src/Nakama/SocketInternal/MatchJoinMessage.cs
index 835074ea..90482e94 100644
--- a/src/Nakama/MatchJoinMessage.cs
+++ b/src/Nakama/SocketInternal/MatchJoinMessage.cs
@@ -1,5 +1,5 @@
/**
- * Copyright 2018 The Nakama Authors
+ * Copyright 2020 The Nakama Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,20 +17,21 @@
using System.Collections.Generic;
using System.Runtime.Serialization;
-namespace Nakama
+namespace Nakama.SocketInternal
{
///
/// A join message for a match on the server.
///
- internal class MatchJoinMessage
+ [DataContract]
+ public class MatchJoinMessage
{
- [DataMember(Name="match_id"), Preserve]
+ [DataMember(Name="match_id", Order = 1), Preserve]
public string MatchId { get; set; }
- [DataMember(Name="token"), Preserve]
+ [DataMember(Name="token", Order = 2), Preserve]
public string Token { get; set; }
- [DataMember(Name="metadata"), Preserve]
+ [DataMember(Name="metadata", Order = 3), Preserve]
public IDictionary Metadata { get; set; }
public override string ToString()
diff --git a/src/Nakama/MatchLeaveMessage.cs b/src/Nakama/SocketInternal/MatchLeaveMessage.cs
similarity index 82%
rename from src/Nakama/MatchLeaveMessage.cs
rename to src/Nakama/SocketInternal/MatchLeaveMessage.cs
index 5ea9cb63..d30c57ab 100644
--- a/src/Nakama/MatchLeaveMessage.cs
+++ b/src/Nakama/SocketInternal/MatchLeaveMessage.cs
@@ -1,5 +1,5 @@
/**
- * Copyright 2018 The Nakama Authors
+ * Copyright 2020 The Nakama Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,14 +16,15 @@
using System.Runtime.Serialization;
-namespace Nakama
+namespace Nakama.SocketInternal
{
///
/// A leave message for a match on the server.
///
- internal class MatchLeaveMessage
+ [DataContract]
+ public class MatchLeaveMessage
{
- [DataMember(Name="match_id"), Preserve]
+ [DataMember(Name="match_id", Order = 1), Preserve]
public string MatchId { get; set; }
public override string ToString()
diff --git a/src/Nakama/SocketInternal/MatchPresenceEvent.cs b/src/Nakama/SocketInternal/MatchPresenceEvent.cs
new file mode 100644
index 00000000..aa83d0a7
--- /dev/null
+++ b/src/Nakama/SocketInternal/MatchPresenceEvent.cs
@@ -0,0 +1,47 @@
+/**
+* Copyright 2020 The Nakama Authors
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Nakama.SocketInternal
+{
+ ///
+ [DataContract]
+ public class MatchPresenceEvent : IMatchPresenceEvent
+ {
+ public IEnumerable Joins => _joins ?? UserPresence.NoPresences;
+
+ [DataMember(Name = "joins", Order = 2), Preserve]
+ private List _joins { get; set; }
+
+ public IEnumerable Leaves => _leaves ?? UserPresence.NoPresences;
+
+ [DataMember(Name = "leaves", Order = 3), Preserve]
+ private List _leaves;
+
+ [DataMember(Name = "match_id", Order = 1), Preserve]
+ public string MatchId { get; set; }
+
+ public override string ToString()
+ {
+ var joins = string.Join(", ", Joins);
+ var leaves = string.Join(", ", Leaves);
+ return $"MatchPresenceEvent(Joins=[{joins}], Leaves=[{leaves}], MatchId='{MatchId}')";
+ }
+ }
+
+}
diff --git a/src/Nakama/MatchSendMessage.cs b/src/Nakama/SocketInternal/MatchSendMessage.cs
similarity index 71%
rename from src/Nakama/MatchSendMessage.cs
rename to src/Nakama/SocketInternal/MatchSendMessage.cs
index f26e3130..3ab8b425 100644
--- a/src/Nakama/MatchSendMessage.cs
+++ b/src/Nakama/SocketInternal/MatchSendMessage.cs
@@ -1,5 +1,5 @@
/**
- * Copyright 2018 The Nakama Authors
+ * Copyright 2020 The Nakama Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,25 +17,29 @@
using System.Collections.Generic;
using System.Runtime.Serialization;
-namespace Nakama
+namespace Nakama.SocketInternal
{
///
/// Send new state to a match on the server.
///
- internal class MatchSendMessage
+ [DataContract]
+ public class MatchSendMessage
{
- [DataMember(Name="match_id"), Preserve]
+ [DataMember(Name="match_id", Order = 1), Preserve]
public string MatchId { get; set; }
- [DataMember(Name="op_code"), Preserve]
+ [DataMember(Name="op_code", Order = 2), Preserve]
public string OpCode { get; set; }
- [DataMember(Name="presences"), Preserve]
+ [DataMember(Name="presences", Order = 4), Preserve]
public List Presences { get; set; }
- [DataMember(Name="data"), Preserve]
+ [DataMember(Name="data", Order = 3), Preserve]
public string State { get; set; }
+ [DataMember(Name="reliable", Order = 5), Preserve]
+ public bool Reliable { get; set; }
+
public override string ToString()
{
var presences = string.Join(", ", Presences);
diff --git a/src/Nakama/SocketInternal/MatchState.cs b/src/Nakama/SocketInternal/MatchState.cs
new file mode 100644
index 00000000..3ad49c89
--- /dev/null
+++ b/src/Nakama/SocketInternal/MatchState.cs
@@ -0,0 +1,51 @@
+/**
+* Copyright 2020 The Nakama Authors
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System;
+using System.Runtime.Serialization;
+
+namespace Nakama.SocketInternal
+{
+ ///
+ [DataContract]
+ public class MatchState : IMatchState
+ {
+ private static readonly byte[] NoBytes = new byte[0];
+
+ [DataMember(Name = "match_id", Order = 1), Preserve]
+ public string MatchId { get; set; }
+
+ public long OpCode => Convert.ToInt64(_opCode);
+
+ [DataMember(Name = "op_code", Order = 3), Preserve]
+ private string _opCode { get; set; }
+
+ public byte[] State => _state == null ? NoBytes : Convert.FromBase64String(_state);
+
+ [DataMember(Name = "data", Order = 4), Preserve]
+ private string _state;
+
+ public IUserPresence UserPresence => _userPresence;
+
+ [DataMember(Name = "presence", Order = 2), Preserve]
+ private UserPresence _userPresence;
+
+ public override string ToString()
+ {
+ return $"MatchState(MatchId='{MatchId}', OpCode={OpCode}, State='{_state}', UserPresence={UserPresence})";
+ }
+ }
+}
diff --git a/src/Nakama/MatchmakerAddMessage.cs b/src/Nakama/SocketInternal/MatchmakerAddMessage.cs
similarity index 66%
rename from src/Nakama/MatchmakerAddMessage.cs
rename to src/Nakama/SocketInternal/MatchmakerAddMessage.cs
index e5c36147..c96fef9e 100644
--- a/src/Nakama/MatchmakerAddMessage.cs
+++ b/src/Nakama/SocketInternal/MatchmakerAddMessage.cs
@@ -1,5 +1,5 @@
/**
- * Copyright 2018 The Nakama Authors
+ * Copyright 2020 The Nakama Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,25 +17,30 @@
using System.Collections.Generic;
using System.Runtime.Serialization;
-namespace Nakama
+namespace Nakama.SocketInternal
{
///
/// Add the user to the matchmaker pool with properties.
///
- internal class MatchmakerAddMessage
+ [DataContract]
+ public class MatchmakerAddMessage
{
- [DataMember(Name = "max_count"), Preserve] public int MaxCount { get; set; }
- [DataMember(Name = "min_count"), Preserve] public int MinCount { get; set; }
+ [DataMember(Name = "min_count", Order = 1), Preserve]
+ public int MinCount { get; set; }
- [DataMember(Name = "numeric_properties"), Preserve]
- public Dictionary NumericProperties { get; set; }
+ [DataMember(Name = "max_count", Order = 2), Preserve]
+ public int MaxCount { get; set; }
- [DataMember(Name = "query"), Preserve] public string Query { get; set; }
+ [DataMember(Name = "query", Order = 3), Preserve]
+ public string Query { get; set; }
- [DataMember(Name = "string_properties"), Preserve]
+ [DataMember(Name = "string_properties", Order = 4), Preserve]
public Dictionary StringProperties { get; set; }
+ [DataMember(Name = "numeric_properties", Order = 5), Preserve]
+ public Dictionary NumericProperties { get; set; }
+
public override string ToString()
{
return
diff --git a/src/Nakama/SocketInternal/MatchmakerMatched.cs b/src/Nakama/SocketInternal/MatchmakerMatched.cs
new file mode 100644
index 00000000..e412d363
--- /dev/null
+++ b/src/Nakama/SocketInternal/MatchmakerMatched.cs
@@ -0,0 +1,78 @@
+/**
+* Copyright 2020 The Nakama Authors
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Nakama.SocketInternal
+{
+ ///
+ [DataContract]
+ public class MatchmakerMatched : IMatchmakerMatched
+ {
+ [DataMember(Name = "match_id", Order = 2), Preserve]
+ public string MatchId { get; set; }
+
+ [DataMember(Name = "ticket", Order = 1), Preserve]
+ public string Ticket { get; set; }
+
+ [DataMember(Name = "token", Order = 3), Preserve]
+ public string Token { get; set; }
+
+ public IEnumerable Users => _users ?? new List(0);
+
+ [DataMember(Name = "users", Order = 4), Preserve]
+ private List _users { get; set; }
+
+ public IMatchmakerUser Self => _self;
+
+ [DataMember(Name = "self", Order = 5), Preserve]
+ private MatchmakerUser _self { get; set; }
+
+ public override string ToString()
+ {
+ var users = string.Join(", ", Users);
+ return
+ $"MatchmakerMatched(MatchId='{MatchId}', Ticket='{Ticket}', Token='{Token}', Users=[{users}], Self={Self})";
+ }
+ }
+
+ ///
+ [DataContract]
+ public class MatchmakerUser : IMatchmakerUser
+ {
+ public IDictionary NumericProperties => _numericProperties ?? new Dictionary();
+
+ [DataMember(Name = "numeric_properties", Order = 1), Preserve]
+ private Dictionary _numericProperties { get; set; }
+
+ public IUserPresence Presence => _presence;
+
+ [DataMember(Name = "presence", Order = 2), Preserve]
+ private UserPresence _presence { get; set; }
+
+ public IDictionary StringProperties => _stringProperties ?? new Dictionary();
+
+ [DataMember(Name = "string_properties", Order = 3), Preserve]
+ private Dictionary _stringProperties;
+
+ public override string ToString()
+ {
+ return
+ $"MatchmakerUser(NumericProperties={NumericProperties}, Presence={Presence}, StringProperties={StringProperties})";
+ }
+ }
+}
diff --git a/src/Nakama/MatchmakerRemoveMessage.cs b/src/Nakama/SocketInternal/MatchmakerRemoveMessage.cs
similarity index 82%
rename from src/Nakama/MatchmakerRemoveMessage.cs
rename to src/Nakama/SocketInternal/MatchmakerRemoveMessage.cs
index 01acebb8..a7d4ce6f 100644
--- a/src/Nakama/MatchmakerRemoveMessage.cs
+++ b/src/Nakama/SocketInternal/MatchmakerRemoveMessage.cs
@@ -1,5 +1,5 @@
/**
- * Copyright 2018 The Nakama Authors
+ * Copyright 2020 The Nakama Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,14 +16,15 @@
using System.Runtime.Serialization;
-namespace Nakama
+namespace Nakama.SocketInternal
{
///
/// Remove the user from the matchmaker pool by ticket.
///
- internal class MatchmakerRemoveMessage
+ [DataContract]
+ public class MatchmakerRemoveMessage
{
- [DataMember(Name="ticket"), Preserve]
+ [DataMember(Name="ticket", Order = 1), Preserve]
public string Ticket { get; set; }
public override string ToString()
diff --git a/src/Nakama/SocketInternal/MatchmakerTicket.cs b/src/Nakama/SocketInternal/MatchmakerTicket.cs
new file mode 100644
index 00000000..f88aa557
--- /dev/null
+++ b/src/Nakama/SocketInternal/MatchmakerTicket.cs
@@ -0,0 +1,33 @@
+/**
+* Copyright 2020 The Nakama Authors
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System.Runtime.Serialization;
+
+namespace Nakama.SocketInternal
+{
+ ///
+ [DataContract]
+ public class MatchmakerTicket : IMatchmakerTicket
+ {
+ [DataMember(Name="ticket", Order = 1), Preserve]
+ public string Ticket { get; set; }
+
+ public override string ToString()
+ {
+ return $"MatchmakerTicket(Ticket='{Ticket}')";
+ }
+ }
+}
diff --git a/src/Nakama/SocketInternal/NotificationList.cs b/src/Nakama/SocketInternal/NotificationList.cs
new file mode 100644
index 00000000..adcec009
--- /dev/null
+++ b/src/Nakama/SocketInternal/NotificationList.cs
@@ -0,0 +1,46 @@
+/**
+* Copyright 2020 The Nakama Authors
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Nakama.SocketInternal
+{
+ ///
+ [DataContract]
+ public class ApiNotificationList : IApiNotificationList
+ {
+
+ ///
+ [DataMember(Name="cacheable_cursor", Order = 2), Preserve]
+ public string CacheableCursor { get; set; }
+
+ ///
+ public IEnumerable Notifications => _notifications ?? new List(0);
+
+ [DataMember(Name="notifications", Order = 1), Preserve]
+ private List _notifications;
+
+ public override string ToString()
+ {
+ var output = "";
+ output = string.Concat(output, "CacheableCursor: ", CacheableCursor, ", ");
+ output = string.Concat(output, "Notifications: [", string.Join(", ", Notifications), "], ");
+ return output;
+ }
+ }
+}
+
diff --git a/src/Nakama/MatchCreateMessage.cs b/src/Nakama/SocketInternal/Status.cs
similarity index 53%
rename from src/Nakama/MatchCreateMessage.cs
rename to src/Nakama/SocketInternal/Status.cs
index 2223454b..148fd00b 100644
--- a/src/Nakama/MatchCreateMessage.cs
+++ b/src/Nakama/SocketInternal/Status.cs
@@ -1,5 +1,5 @@
/**
- * Copyright 2018 The Nakama Authors
+ * Copyright 2020 The Nakama Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,24 @@
* limitations under the License.
*/
-namespace Nakama
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Nakama.SocketInternal
{
- ///
- /// A create message for a match on the server.
- ///
- internal class MatchCreateMessage
+ ///
+ [DataContract]
+ public class Status : IStatus
{
+ public IEnumerable Presences => _presences ?? UserPresence.NoPresences;
+
+ [DataMember(Name="presences", Order = 1), Preserve]
+ private List _presences;
+
public override string ToString()
{
- return "MatchCreateMessage()";
+ var presences = string.Join(", ", Presences);
+ return $"Status(Presences=[{presences}])";
}
}
}
diff --git a/src/Nakama/StatusFollowMessage.cs b/src/Nakama/SocketInternal/StatusFollowMessage.cs
similarity index 74%
rename from src/Nakama/StatusFollowMessage.cs
rename to src/Nakama/SocketInternal/StatusFollowMessage.cs
index 80d63508..93f6c163 100644
--- a/src/Nakama/StatusFollowMessage.cs
+++ b/src/Nakama/SocketInternal/StatusFollowMessage.cs
@@ -1,5 +1,5 @@
/**
- * Copyright 2018 The Nakama Authors
+ * Copyright 2020 The Nakama Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,16 +17,19 @@
using System.Collections.Generic;
using System.Runtime.Serialization;
-namespace Nakama
+namespace Nakama.SocketInternal
{
///
/// Follow one or more other users for status updates.
///
- internal class StatusFollowMessage
+ [DataContract]
+ public class StatusFollowMessage
{
- [DataMember(Name = "user_ids"), Preserve] public List UserIds { get; set; }
+ [DataMember(Name = "user_ids", Order = 1), Preserve]
+ public List UserIds { get; set; }
- [DataMember(Name = "usernames"), Preserve] public List Usernames { get; set; }
+ [DataMember(Name = "usernames", Order = 2), Preserve]
+ public List Usernames { get; set; }
public override string ToString()
{
diff --git a/src/Nakama/SocketInternal/StatusPrescenceEvent.cs b/src/Nakama/SocketInternal/StatusPrescenceEvent.cs
new file mode 100644
index 00000000..9ab78321
--- /dev/null
+++ b/src/Nakama/SocketInternal/StatusPrescenceEvent.cs
@@ -0,0 +1,43 @@
+/**
+* Copyright 2020 The Nakama Authors
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Nakama.SocketInternal
+{
+ ///
+ [DataContract]
+ public class StatusPresenceEvent : IStatusPresenceEvent
+ {
+ public IEnumerable Leaves => _leaves ?? UserPresence.NoPresences;
+
+ [DataMember(Name = "leaves", Order = 3), Preserve]
+ private List _leaves;
+
+ public IEnumerable Joins => _joins ?? UserPresence.NoPresences;
+
+ [DataMember(Name = "joins", Order = 2), Preserve]
+ private List _joins;
+
+ public override string ToString()
+ {
+ var joins = string.Join(", ", Joins);
+ var leaves = string.Join(", ", Leaves);
+ return $"StatusPresenceEvent(Leaves=[{leaves}], Joins=[{joins}])";
+ }
+ }
+}
diff --git a/src/Nakama/StatusUnfollowMessage.cs b/src/Nakama/SocketInternal/StatusUnfollowMessage.cs
similarity index 83%
rename from src/Nakama/StatusUnfollowMessage.cs
rename to src/Nakama/SocketInternal/StatusUnfollowMessage.cs
index 58746bf1..38b49157 100644
--- a/src/Nakama/StatusUnfollowMessage.cs
+++ b/src/Nakama/SocketInternal/StatusUnfollowMessage.cs
@@ -1,5 +1,5 @@
/**
- * Copyright 2018 The Nakama Authors
+ * Copyright 2020 The Nakama Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,14 +17,15 @@
using System.Collections.Generic;
using System.Runtime.Serialization;
-namespace Nakama
+namespace Nakama.SocketInternal
{
///
/// Unfollow one or more users on the server.
///
- internal class StatusUnfollowMessage
+ [DataContract]
+ public class StatusUnfollowMessage
{
- [DataMember(Name="user_ids"), Preserve]
+ [DataMember(Name="user_ids", Order = 1), Preserve]
public List UserIds { get; set; }
public override string ToString()
diff --git a/src/Nakama/StatusUpdateMessage.cs b/src/Nakama/SocketInternal/StatusUpdateMessage.cs
similarity index 64%
rename from src/Nakama/StatusUpdateMessage.cs
rename to src/Nakama/SocketInternal/StatusUpdateMessage.cs
index 37b20d91..dea34289 100644
--- a/src/Nakama/StatusUpdateMessage.cs
+++ b/src/Nakama/SocketInternal/StatusUpdateMessage.cs
@@ -1,5 +1,5 @@
/**
- * Copyright 2018 The Nakama Authors
+ * Copyright 2020 The Nakama Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,16 +15,29 @@
*/
using System.Runtime.Serialization;
-
-namespace Nakama
+namespace Nakama.SocketInternal
{
///
/// Update the status of the current user.
///
- internal class StatusUpdateMessage
+ [DataContract]
+ public class StatusUpdateMessage
{
+ public string Status
+ {
+ get => _statusValue.Value ?? _status;
+ set
+ {
+ _status = value;
+ _statusValue.Value = value;
+ }
+ }
+
[DataMember(Name="status"), Preserve]
- public string Status { get; set; }
+ private string _status;
+
+ [DataMember(Order = 1), Preserve]
+ private StringValue _statusValue = new StringValue();
public override string ToString()
{
diff --git a/src/Nakama/SocketInternal/StreamPresenceEvent.cs b/src/Nakama/SocketInternal/StreamPresenceEvent.cs
new file mode 100644
index 00000000..629854ab
--- /dev/null
+++ b/src/Nakama/SocketInternal/StreamPresenceEvent.cs
@@ -0,0 +1,95 @@
+/**
+* Copyright 2020 The Nakama Authors
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System.Runtime.Serialization;
+using System.Collections.Generic;
+
+namespace Nakama.SocketInternal
+{
+ ///
+ [DataContract]
+ public class StreamPresenceEvent : IStreamPresenceEvent
+ {
+ public IEnumerable Leaves => _leaves ?? UserPresence.NoPresences;
+
+ [DataMember(Name = "leaves", Order = 1), Preserve]
+ private List _leaves;
+
+ public IEnumerable Joins => _joins ?? UserPresence.NoPresences;
+
+ [DataMember(Name = "joins", Order = 2), Preserve]
+ private List _joins;
+
+ public IStream Stream => _stream;
+
+ [DataMember(Name = "stream", Order = 3), Preserve]
+ private Stream _stream;
+
+ public override string ToString()
+ {
+ var leaves = string.Join(", ", Leaves);
+ var joins = string.Join(", ", Joins);
+ return $"StreamPresenceEvent(Leaves=[{leaves}], Joins=[{joins}], Stream={Stream})";
+ }
+ }
+
+ ///
+ [DataContract]
+ public class StreamState : IStreamState
+ {
+ public IUserPresence Sender => _sender;
+
+ [DataMember(Name = "sender", Order = 2), Preserve]
+ private UserPresence _sender;
+
+ public string State => _state;
+
+ [DataMember(Name = "data", Order = 3), Preserve]
+ public string _state;
+
+ public IStream Stream => _stream;
+
+ [DataMember(Name = "stream", Order = 1), Preserve]
+ private Stream _stream;
+
+ [DataMember(Name = "reliable", Order = 4), Preserve]
+ public string Reliable { get; set; }
+
+ public override string ToString()
+ {
+ return $"StreamState(Sender={Sender}, State='{_state}', Stream={Stream})";
+ }
+ }
+
+ ///
+ [DataContract]
+ public class Stream : IStream
+ {
+ [DataMember(Name = "descriptor", Order = 3), Preserve] public string Descriptor { get; set; }
+
+ [DataMember(Name = "label", Order = 4), Preserve] public string Label { get; set; }
+
+ [DataMember(Name = "mode", Order = 1), Preserve] public int Mode { get; set; }
+
+ [DataMember(Name = "subject", Order = 2), Preserve] public string Subject { get; set; }
+
+ public override string ToString()
+ {
+ return $"Stream(Descriptor='{Descriptor}', Label='{Label}', Mode={Mode}, Subject='{Subject}')";
+ }
+ }
+}
+
diff --git a/src/Nakama/SocketInternal/UserPresence.cs b/src/Nakama/SocketInternal/UserPresence.cs
new file mode 100644
index 00000000..f8dfeed3
--- /dev/null
+++ b/src/Nakama/SocketInternal/UserPresence.cs
@@ -0,0 +1,76 @@
+
+
+/**
+* Copyright 2020 The Nakama Authors
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Nakama.SocketInternal
+{
+///
+ [DataContract]
+ public class UserPresence : IUserPresence
+ {
+ public static readonly IReadOnlyList NoPresences = new List(0);
+
+ [DataMember(Name = "persistence", Order = 4), Preserve]
+ public bool Persistence { get; set; }
+
+ [DataMember(Name = "session_id", Order = 2), Preserve]
+ public string SessionId { get; set; }
+
+ public string Status => _statusValue.Value ?? _status;
+
+ [DataMember(Name = "username", Order = 3), Preserve]
+ public string Username { get; set; }
+
+ [DataMember(Name = "user_id", Order = 1), Preserve]
+ public string UserId { get; set; }
+
+ [DataMember(Name = "status"), Preserve]
+ private string _status;
+
+ [DataMember(Order = 5), Preserve]
+ private StringValue _statusValue = new StringValue();
+
+ public override bool Equals(object obj)
+ {
+ if (!(obj is UserPresence item))
+ {
+ return false;
+ }
+ return Equals(item);
+ }
+
+ private bool Equals(IUserPresence other) => string.Equals(SessionId, other.SessionId) && string.Equals(UserId, other.UserId);
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ // ReSharper disable twice NonReadonlyMemberInGetHashCode
+ return ((SessionId?.GetHashCode() ?? 0) * 397) ^ (UserId?.GetHashCode() ?? 0);
+ }
+ }
+
+ public override string ToString()
+ {
+ return
+ $"UserPresence(Persistence={Persistence}, SessionId='{SessionId}', Status='{Status}', Username='{Username}', UserId='{UserId}')";
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Nakama/WebSocketAdapter.cs b/src/Nakama/SocketInternal/WebSocketAdapter.cs
similarity index 86%
rename from src/Nakama/WebSocketAdapter.cs
rename to src/Nakama/SocketInternal/WebSocketAdapter.cs
index eb716511..7c32ab2b 100644
--- a/src/Nakama/WebSocketAdapter.cs
+++ b/src/Nakama/SocketInternal/WebSocketAdapter.cs
@@ -20,11 +20,12 @@
using System.Threading;
using System.Threading.Tasks;
using Nakama.Ninja.WebSockets;
+using Nakama.TinyJson;
-namespace Nakama
+namespace Nakama.SocketInternal
{
///
- /// An adapter which uses the WebSocket protocol with Nakama server.
+ /// A JSON-based adapter which uses the WebSocket protocol with Nakama server.
///
public class WebSocketAdapter : ISocketAdapter
{
@@ -44,6 +45,15 @@ public class WebSocketAdapter : ISocketAdapter
///
public event Action> Received;
+ ///
+ public string Format
+ {
+ get
+ {
+ return "json";
+ }
+ }
+
///
/// If the WebSocket is connected.
///
@@ -139,8 +149,14 @@ public void Dispose()
_webSocket?.Dispose();
}
+ public WebSocketMessageEnvelope DeserializeEnvelope(ArraySegment buffer)
+ {
+ var contents = System.Text.Encoding.UTF8.GetString(buffer.Array, buffer.Offset, buffer.Count);
+ return contents.FromJson();
+ }
+
///
- public async void Send(ArraySegment buffer, CancellationToken cancellationToken,
+ public async void Send(WebSocketMessageEnvelope envelope, CancellationToken cancellationToken,
bool reliable = true)
{
if (_webSocket == null)
@@ -151,7 +167,9 @@ public async void Send(ArraySegment buffer, CancellationToken cancellation
try
{
- var sendTask = _webSocket.SendAsync(buffer, WebSocketMessageType.Text, true, cancellationToken);
+ var json = envelope.ToJson();
+ var buffer = System.Text.Encoding.UTF8.GetBytes(json);
+ var sendTask = _webSocket.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, true, cancellationToken);
await Task.WhenAny(sendTask, Task.Delay(_sendTimeoutSec, cancellationToken));
}
catch (Exception e)
@@ -164,7 +182,7 @@ public async void Send(ArraySegment buffer, CancellationToken cancellation
public override string ToString()
{
return
- $"WebSocketDriver(IsConnected={IsConnected}, IsConnecting={IsConnecting}, MaxMessageSize={MaxMessageSize}, Uri='{_uri}')";
+ $"WebSocketAdapter(IsConnected={IsConnected}, IsConnecting={IsConnecting}, MaxMessageSize={MaxMessageSize}, Uri='{_uri}')";
}
private async Task ReceiveLoop(WebSocket webSocket, CancellationToken cancellationToken)
diff --git a/src/Nakama/WebSocketErrorMessage.cs b/src/Nakama/SocketInternal/WebSocketErrorMessage.cs
similarity index 66%
rename from src/Nakama/WebSocketErrorMessage.cs
rename to src/Nakama/SocketInternal/WebSocketErrorMessage.cs
index 64a91f26..be00e815 100644
--- a/src/Nakama/WebSocketErrorMessage.cs
+++ b/src/Nakama/SocketInternal/WebSocketErrorMessage.cs
@@ -1,5 +1,5 @@
/**
- * Copyright 2018 The Nakama Authors
+ * Copyright 2020 The Nakama Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,18 +17,22 @@
using System.Collections.Generic;
using System.Runtime.Serialization;
-namespace Nakama
+namespace Nakama.SocketInternal
{
///
/// A logical error received on the WebSocket connection.
///
- internal class WebSocketErrorMessage
+ [DataContract]
+ public class WebSocketErrorMessage
{
- [DataMember(Name = "code"), Preserve] public int Code { get; set; }
+ [DataMember(Name = "code", Order = 1), Preserve]
+ public int Code { get; set; }
- [DataMember(Name = "context"), Preserve] public Dictionary Context { get; set; }
+ [DataMember(Name = "context", Order = 3), Preserve]
+ public Dictionary Context { get; set; }
- [DataMember(Name = "message"), Preserve] public string Message { get; set; }
+ [DataMember(Name = "message", Order = 2), Preserve]
+ public string Message { get; set; }
public override string ToString()
{
diff --git a/src/Nakama/WebSocketMessageEnvelope.cs b/src/Nakama/SocketInternal/WebSocketMessageEnvelope.cs
similarity index 55%
rename from src/Nakama/WebSocketMessageEnvelope.cs
rename to src/Nakama/SocketInternal/WebSocketMessageEnvelope.cs
index 0f322dd1..8186746c 100644
--- a/src/Nakama/WebSocketMessageEnvelope.cs
+++ b/src/Nakama/SocketInternal/WebSocketMessageEnvelope.cs
@@ -1,5 +1,5 @@
/**
- * Copyright 2018 The Nakama Authors
+ * Copyright 2020 The Nakama Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,104 +16,105 @@
using System.Runtime.Serialization;
-namespace Nakama
+namespace Nakama.SocketInternal
{
///
/// An envelope for messages received or sent on a WebSocket.
///
- internal class WebSocketMessageEnvelope
+ [DataContract]
+ public class WebSocketMessageEnvelope
{
- [DataMember(Name="cid"), Preserve]
+ [DataMember(Name="cid", Order = 1), Preserve]
public string Cid { get; set; }
- [DataMember(Name="channel"), Preserve]
+ [DataMember(Name="channel", Order = 2), Preserve]
public Channel Channel { get; set; }
- [DataMember(Name="channel_join"), Preserve]
+ [DataMember(Name="channel_join", Order = 3), Preserve]
public ChannelJoinMessage ChannelJoin { get; set; }
- [DataMember(Name="channel_leave"), Preserve]
+ [DataMember(Name="channel_leave", Order = 4), Preserve]
public ChannelLeaveMessage ChannelLeave { get; set; }
- [DataMember(Name="channel_message"), Preserve]
+ [DataMember(Name="channel_message", Order = 5), Preserve]
public ApiChannelMessage ChannelMessage { get; set; }
- [DataMember(Name="channel_message_ack"), Preserve]
+ [DataMember(Name="channel_message_ack", Order = 6), Preserve]
public ChannelMessageAck ChannelMessageAck { get; set; }
- [DataMember(Name="channel_message_remove"), Preserve]
+ [DataMember(Name="channel_message_remove", Order = 9), Preserve]
public ChannelRemoveMessage ChannelMessageRemove { get; set; }
- [DataMember(Name="channel_message_send"), Preserve]
+ [DataMember(Name="channel_message_send", Order = 7), Preserve]
public ChannelSendMessage ChannelMessageSend { get; set; }
- [DataMember(Name="channel_message_update"), Preserve]
+ [DataMember(Name="channel_message_update", Order = 8), Preserve]
public ChannelUpdateMessage ChannelMessageUpdate { get; set; }
- [DataMember(Name="channel_presence_event"), Preserve]
+ [DataMember(Name="channel_presence_event", Order = 10), Preserve]
public ChannelPresenceEvent ChannelPresenceEvent { get; set; }
- [DataMember(Name="error"), Preserve]
+ [DataMember(Name="error", Order = 11), Preserve]
public WebSocketErrorMessage Error { get; set; }
- [DataMember(Name="matchmaker_add"), Preserve]
+ [DataMember(Name="matchmaker_add", Order = 19), Preserve]
public MatchmakerAddMessage MatchmakerAdd { get; set; }
- [DataMember(Name="matchmaker_matched"), Preserve]
+ [DataMember(Name="matchmaker_matched", Order = 20), Preserve]
public MatchmakerMatched MatchmakerMatched { get; set; }
- [DataMember(Name="matchmaker_remove"), Preserve]
+ [DataMember(Name="matchmaker_remove", Order = 21), Preserve]
public MatchmakerRemoveMessage MatchmakerRemove { get; set; }
- [DataMember(Name="matchmaker_ticket"), Preserve]
+ [DataMember(Name="matchmaker_ticket", Order = 22), Preserve]
public MatchmakerTicket MatchmakerTicket { get; set; }
- [DataMember(Name="match"), Preserve]
+ [DataMember(Name="match", Order = 12), Preserve]
public Match Match { get; set; }
- [DataMember(Name="match_create"), Preserve]
+ [DataMember(Name="match_create", Order = 13), Preserve]
public MatchCreateMessage MatchCreate { get; set; }
- [DataMember(Name="match_join"), Preserve]
+ [DataMember(Name="match_join", Order = 16), Preserve]
public MatchJoinMessage MatchJoin { get; set; }
- [DataMember(Name="match_leave"), Preserve]
+ [DataMember(Name="match_leave", Order = 17), Preserve]
public MatchLeaveMessage MatchLeave { get; set; }
- [DataMember(Name="match_presence_event"), Preserve]
+ [DataMember(Name="match_presence_event", Order = 18), Preserve]
public MatchPresenceEvent MatchPresenceEvent { get; set; }
- [DataMember(Name="match_data"), Preserve]
+ [DataMember(Name="match_data", Order = 14), Preserve]
public MatchState MatchState { get; set; }
- [DataMember(Name="match_data_send"), Preserve]
+ [DataMember(Name="match_data_send", Order = 15), Preserve]
public MatchSendMessage MatchStateSend { get; set; }
- [DataMember(Name="notifications"), Preserve]
+ [DataMember(Name="notifications", Order = 23), Preserve]
public ApiNotificationList NotificationList { get; set; }
- [DataMember(Name="rpc"), Preserve]
+ [DataMember(Name="rpc", Order = 24), Preserve]
public ApiRpc Rpc { get; set; }
- [DataMember(Name="status"), Preserve]
+ [DataMember(Name="status", Order = 25), Preserve]
public Status Status { get; set; }
- [DataMember(Name="status_follow"), Preserve]
+ [DataMember(Name="status_follow", Order = 26), Preserve]
public StatusFollowMessage StatusFollow { get; set; }
- [DataMember(Name="status_presence_event"), Preserve]
+ [DataMember(Name="status_presence_event", Order = 27), Preserve]
public StatusPresenceEvent StatusPresenceEvent { get; set; }
- [DataMember(Name="status_unfollow"), Preserve]
+ [DataMember(Name="status_unfollow", Order = 28), Preserve]
public StatusUnfollowMessage StatusUnfollow { get; set; }
- [DataMember(Name="status_update"), Preserve]
+ [DataMember(Name="status_update", Order = 29), Preserve]
public StatusUpdateMessage StatusUpdate { get; set; }
- [DataMember(Name="stream_presence_event"), Preserve]
+ [DataMember(Name="stream_presence_event", Order = 31), Preserve]
public StreamPresenceEvent StreamPresenceEvent { get; set; }
- [DataMember(Name="stream_data"), Preserve]
+ [DataMember(Name="stream_data", Order = 30), Preserve]
public StreamState StreamState { get; set; }
public override string ToString()
diff --git a/src/Nakama/SocketInternal/WellKnownTypes/BoolValue.cs b/src/Nakama/SocketInternal/WellKnownTypes/BoolValue.cs
new file mode 100644
index 00000000..bd342a98
--- /dev/null
+++ b/src/Nakama/SocketInternal/WellKnownTypes/BoolValue.cs
@@ -0,0 +1,46 @@
+/**
+ * Copyright 2020 The Nakama Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Runtime.Serialization;
+
+namespace Nakama.SocketInternal
+{
+ ///
+ /// A Protobuf-Net serializable class corresponding to the well-known
+ /// google.protobuf.BoolValue type.
+ ///
+ /// Keep in mind that grpc-gateway will automatically deserialize a vool into a BoolValue;
+ /// there is no need to for the client to utilize this for JSON.
+ ///
+ [DataContract]
+ public struct BoolValue
+ {
+ public bool HasValue => _nullable.HasValue;
+ public bool Value
+ {
+ get => _nullable.Value;
+ set => _nullable = value;
+ }
+
+ [DataMember(Order = 1), Preserve]
+ private bool? _nullable { get; set; }
+
+ public override string ToString()
+ {
+ return $"BoolValue(Value='{_nullable}')";
+ }
+ }
+}
diff --git a/src/Nakama/SocketInternal/WellKnownTypes/IntValue.cs b/src/Nakama/SocketInternal/WellKnownTypes/IntValue.cs
new file mode 100644
index 00000000..d74f9f6e
--- /dev/null
+++ b/src/Nakama/SocketInternal/WellKnownTypes/IntValue.cs
@@ -0,0 +1,43 @@
+/**
+ * Copyright 2020 The Nakama Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Runtime.Serialization;
+
+namespace Nakama.SocketInternal
+{
+ ///
+ /// A Protobuf-Net serializable class corresponding to the well-known
+ /// google.protobuf.IntValue type.
+ ///
+ /// Keep in mind that grpc-gateway will automatically deserialize a vool into a IntValue;
+ /// there is no need to for the client to utilize this for JSON.
+ ///
+ [DataContract]
+ public struct IntValue
+ {
+ public bool HasValue => _nullable.HasValue;
+ public int Value => _nullable.Value;
+
+ [DataMember(Order = 1), Preserve]
+ private int? _nullable { get; set; }
+
+ public override string ToString()
+ {
+ return $"IntValue(Value='{_nullable}')";
+ }
+
+ }
+}
diff --git a/src/Nakama/SocketInternal/WellKnownTypes/StringValue.cs b/src/Nakama/SocketInternal/WellKnownTypes/StringValue.cs
new file mode 100644
index 00000000..c300441b
--- /dev/null
+++ b/src/Nakama/SocketInternal/WellKnownTypes/StringValue.cs
@@ -0,0 +1,50 @@
+/**
+ * Copyright 2020 The Nakama Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Runtime.Serialization;
+
+namespace Nakama.SocketInternal
+{
+ ///
+ /// A Protobuf-Net serializable class corresponding to the well-known
+ /// google.protobuf.StringValue type.
+ ///
+ /// Keep in mind that grpc-gateway will automatically deserialize a string into a StringValue;
+ /// there is no need to for the client to utilize this for JSON.
+ ///
+ [DataContract]
+ public struct StringValue
+ {
+ public string Value
+ {
+ get => _value;
+ set => _value = value;
+ }
+
+ [DataMember(Order = 1), Preserve]
+ private string _value { get; set; }
+
+ public override string ToString()
+ {
+ return $"StringValue(Value='{_value}')";
+ }
+
+ public static implicit operator StringValue(string value)
+ {
+ return new StringValue{_value = value};
+ }
+ }
+}
diff --git a/src/Nakama/TinyJson/JsonParser.cs b/src/Nakama/TinyJson/JsonParser.cs
index cda51d51..53741f31 100644
--- a/src/Nakama/TinyJson/JsonParser.cs
+++ b/src/Nakama/TinyJson/JsonParser.cs
@@ -375,16 +375,19 @@ private static Dictionary CreateMemberNameDictionary(IEnumerable();
foreach (var member in members)
{
- if (member.IsDefined(typeof(IgnoreDataMemberAttribute), true))
- continue;
-
var name = member.Name;
if (member.IsDefined(typeof(DataMemberAttribute), true))
{
var dataMemberAttribute =
(DataMemberAttribute) Attribute.GetCustomAttribute(member, typeof(DataMemberAttribute), true);
- if (!string.IsNullOrEmpty(dataMemberAttribute.Name))
- name = dataMemberAttribute.Name;
+ if (string.IsNullOrEmpty(dataMemberAttribute.Name))
+ continue;
+
+ name = dataMemberAttribute.Name;
+ }
+ else
+ {
+ continue;
}
nameToMember.Add(name, member);
@@ -407,14 +410,14 @@ private static object ParseObject(Type type, string json)
if (!_fieldInfoCache.TryGetValue(type, out nameToField))
{
nameToField = CreateMemberNameDictionary(
- type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy));
+ type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic));
_fieldInfoCache.Add(type, nameToField);
}
if (!_propertyInfoCache.TryGetValue(type, out nameToProperty))
{
nameToProperty = CreateMemberNameDictionary(
- type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy));
+ type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic));
_propertyInfoCache.Add(type, nameToProperty);
}
diff --git a/src/Nakama/TinyJson/JsonWriter.cs b/src/Nakama/TinyJson/JsonWriter.cs
index b28dff55..741db69f 100644
--- a/src/Nakama/TinyJson/JsonWriter.cs
+++ b/src/Nakama/TinyJson/JsonWriter.cs
@@ -155,11 +155,22 @@ private static void AppendValue(StringBuilder stringBuilder, object item)
var isFirst = true;
var fieldInfos =
- type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy);
+ type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic);
foreach (var t in fieldInfos)
{
- if (t.IsDefined(typeof(IgnoreDataMemberAttribute), true))
+ if (t.IsDefined(typeof(DataMemberAttribute), true))
+ {
+ var dataMemberAttribute = (DataMemberAttribute) Attribute.GetCustomAttribute(t, typeof(DataMemberAttribute), true);
+
+ if (string.IsNullOrEmpty(dataMemberAttribute.Name))
+ {
+ continue;
+ }
+ }
+ else
+ {
continue;
+ }
var value = t.GetValue(item);
if (value == null) continue;
@@ -174,11 +185,25 @@ private static void AppendValue(StringBuilder stringBuilder, object item)
}
var propertyInfo =
- type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy);
+ type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic);
foreach (var t in propertyInfo)
{
- if (!t.CanRead || t.IsDefined(typeof(IgnoreDataMemberAttribute), true))
+ if (!t.CanRead)
+ continue;
+
+ if (t.IsDefined(typeof(DataMemberAttribute), true))
+ {
+ var dataMemberAttribute = (DataMemberAttribute) Attribute.GetCustomAttribute(t, typeof(DataMemberAttribute), true);
+
+ if (string.IsNullOrEmpty(dataMemberAttribute.Name))
+ {
+ continue;
+ }
+ }
+ else
+ {
continue;
+ }
var value = t.GetValue(item, null);
if (value == null) continue;
@@ -198,10 +223,8 @@ private static void AppendValue(StringBuilder stringBuilder, object item)
private static string GetMemberName(MemberInfo member)
{
- if (!member.IsDefined(typeof(DataMemberAttribute), true)) return member.Name;
- var dataMemberAttribute =
- (DataMemberAttribute) Attribute.GetCustomAttribute(member, typeof(DataMemberAttribute), true);
- return !string.IsNullOrEmpty(dataMemberAttribute.Name) ? dataMemberAttribute.Name : member.Name;
+ var dataMemberAttribute = (DataMemberAttribute) Attribute.GetCustomAttribute(member, typeof(DataMemberAttribute), true);
+ return dataMemberAttribute.Name;
}
}
}
\ No newline at end of file
diff --git a/tests/Nakama.Tests/AssemblyInfo.cs b/tests/Nakama.Tests/AssemblyInfo.cs
new file mode 100644
index 00000000..238fb56d
--- /dev/null
+++ b/tests/Nakama.Tests/AssemblyInfo.cs
@@ -0,0 +1,4 @@
+using Xunit;
+
+// see: https://stackoverflow.com/questions/52389298/howto-resolve-net-test-hangs-on-starting-test-execution-please-wait
+[assembly: CollectionBehavior(DisableTestParallelization = true)]
diff --git a/tests/Nakama.Tests/GroupTest.cs b/tests/Nakama.Tests/GroupTest.cs
index 3f05adbb..9da3cbac 100644
--- a/tests/Nakama.Tests/GroupTest.cs
+++ b/tests/Nakama.Tests/GroupTest.cs
@@ -25,12 +25,10 @@ namespace Nakama.Tests.Api
public class GroupTest
{
private IClient _client;
- private ISocket _socket;
public GroupTest()
{
_client = ClientUtil.FromSettingsFile();
- _socket = Nakama.Socket.From(_client);
}
[Fact]
@@ -205,7 +203,7 @@ public async Task ShouldPromoteAndDemoteUsers()
Assert.Equal(admins.GroupUsers.Count(), 2);
- await _client.DemoteGroupUsersAsync(session1, group.Id, new string[]{session2.UserId, session3.UserId});
+ await _client.DemoteGroupUsersAsync(session1, group.Id, new string[]{null});
admins = await _client.ListGroupUsersAsync(session1, group.Id, state: 1, limit: 2);
Assert.Equal(admins.GroupUsers.Count(), 0);
diff --git a/tests/Nakama.Tests/Nakama.Tests.csproj b/tests/Nakama.Tests/Nakama.Tests.csproj
index ac5e80b6..05adc50d 100644
--- a/tests/Nakama.Tests/Nakama.Tests.csproj
+++ b/tests/Nakama.Tests/Nakama.Tests.csproj
@@ -15,6 +15,7 @@
+
diff --git a/tests/Nakama.Tests/AwaitedSocketTaskTest.cs b/tests/Nakama.Tests/Socket/AwaitedSocketTaskTest.cs
similarity index 57%
rename from tests/Nakama.Tests/AwaitedSocketTaskTest.cs
rename to tests/Nakama.Tests/Socket/AwaitedSocketTaskTest.cs
index d0ca303d..7f48bce2 100644
--- a/tests/Nakama.Tests/AwaitedSocketTaskTest.cs
+++ b/tests/Nakama.Tests/Socket/AwaitedSocketTaskTest.cs
@@ -1,5 +1,5 @@
/**
- * Copyright 2019 The Nakama Authors
+ * Copyright 2020 The Nakama Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,49 +18,48 @@
using System.Threading.Tasks;
using Xunit;
-namespace Nakama.Tests
+namespace Nakama.Tests.Socket
{
public class AwaitedSocketTaskTest : IDisposable
{
private IClient _client;
- private readonly ISocket _socket;
public AwaitedSocketTaskTest()
{
_client = ClientUtil.FromSettingsFile();
- _socket = Nakama.Socket.From(_client);
}
- public void Dispose()
- {
- _socket.Dispose();
- _client = null;
- }
+ public void Dispose() { _client = null; }
- [Fact]
- public async void Socket_AwaitedTasks_AreCanceled()
+ [Theory]
+ [ClassData(typeof(WebSocketTestData))]
+ public async void Socket_AwaitedTasks_AreCanceled(TestAdapterFactory adapterFactory)
{
+ var socket = Nakama.Socket.From(_client, adapterFactory());
var id = Guid.NewGuid().ToString();
var session = await _client.AuthenticateCustomAsync(id);
- await _socket.ConnectAsync(session);
+ await socket.ConnectAsync(session);
- var matchmakerTask1 = _socket.AddMatchmakerAsync("+label.foo:\"val\"", 15, 20);
- var matchmakerTask2 = _socket.AddMatchmakerAsync("+label.bar:\"val\"", 15, 20);
- await _socket.CloseAsync();
+ var matchmakerTask1 = socket.AddMatchmakerAsync("+label.foo:\"val\"", 15, 20);
+ var matchmakerTask2 = socket.AddMatchmakerAsync("+label.bar:\"val\"", 15, 20);
+ await socket.CloseAsync();
await Assert.ThrowsAsync(() => Task.WhenAll(matchmakerTask1, matchmakerTask2));
}
- [Fact]
- public async void Socket_AwaitedTasksAfterDisconnect_AreCanceled()
+ [Theory]
+ [ClassData(typeof(WebSocketTestData))]
+ public async void Socket_AwaitedTasksAfterDisconnect_AreCanceled(TestAdapterFactory adapterFactory)
{
var id = Guid.NewGuid().ToString();
var session = await _client.AuthenticateCustomAsync(id);
- await _socket.ConnectAsync(session);
+ var socket = Nakama.Socket.From(_client, adapterFactory());
+
+ await socket.ConnectAsync(session);
+ await socket.CloseAsync();
- await _socket.CloseAsync();
- var statusTask1 = _socket.FollowUsersAsync(new[] {session.UserId});
- var statusTask2 = _socket.FollowUsersAsync(new[] {session.UserId});
+ var statusTask1 = socket.FollowUsersAsync(new[] {session.UserId});
+ var statusTask2 = socket.FollowUsersAsync(new[] {session.UserId});
await Assert.ThrowsAsync(() => Task.WhenAll(statusTask1, statusTask2));
}
diff --git a/tests/Nakama.Tests/Socket/WebSocketChannelTest.cs b/tests/Nakama.Tests/Socket/WebSocketChannelTest.cs
index 19859d66..e0cbe8e5 100644
--- a/tests/Nakama.Tests/Socket/WebSocketChannelTest.cs
+++ b/tests/Nakama.Tests/Socket/WebSocketChannelTest.cs
@@ -22,43 +22,54 @@ namespace Nakama.Tests.Socket
using Xunit;
using TinyJson;
- public class WebSocketChannelTest : IAsyncLifetime
+ public class WebSocketChannelTest
{
private IClient _client;
- private ISocket _socket;
public WebSocketChannelTest()
{
_client = ClientUtil.FromSettingsFile();
- _socket = Nakama.Socket.From(_client);
}
- [Fact]
- public async Task ShouldCreateRoomChannel()
+ [Theory]
+ [ClassData(typeof(WebSocketTestData))]
+ public async Task ShouldCreateRoomChannel(TestAdapterFactory adapterFactory)
{
var session = await _client.AuthenticateCustomAsync($"{Guid.NewGuid()}");
- await _socket.ConnectAsync(session);
- var channel = await _socket.JoinChatAsync("myroom", ChannelType.Room);
+ var socket = Nakama.Socket.From(_client, adapterFactory());
+ socket.ReceivedError += e => System.Console.WriteLine(e.Message);
+ await socket.ConnectAsync(session);
+
+ var channel = await socket.JoinChatAsync("myroom", ChannelType.Room);
Assert.NotNull(channel);
Assert.NotNull(channel.Id);
Assert.Equal(channel.Self.UserId, session.UserId);
Assert.Equal(channel.Self.Username, session.Username);
+
+ await socket.CloseAsync();
}
- [Fact]
- public async Task ShouldSendMessageRoomChannel()
+
+ [Theory]
+ [ClassData(typeof(WebSocketTestData))]
+ public async Task ShouldSendMessageRoomChannel(TestAdapterFactory adapterFactory)
{
var session = await _client.AuthenticateCustomAsync($"{Guid.NewGuid()}");
+ var socket = Nakama.Socket.From(_client, adapterFactory());
var completer = new TaskCompletionSource();
- _socket.ReceivedChannelMessage += (chatMessage) => completer.SetResult(chatMessage);
- await _socket.ConnectAsync(session);
- var channel = await _socket.JoinChatAsync("myroom", ChannelType.Room);
+ socket.ReceivedChannelMessage += (chatMessage) => completer.SetResult(chatMessage);
+ await socket.ConnectAsync(session);
+ socket.ReceivedError += e => System.Console.WriteLine(e.Message);
+
+ var channel = await socket.JoinChatAsync("myroom", ChannelType.Room);
// Send chat message.
var content = new Dictionary {{"hello", "world"}}.ToJson();
- var sendAck = await _socket.WriteChatMessageAsync(channel, content);
+
+ var sendAck = await socket.WriteChatMessageAsync(channel, content);
+
var message = await completer.Task.ConfigureAwait(false);
Assert.NotNull(sendAck);
@@ -66,10 +77,13 @@ public async Task ShouldSendMessageRoomChannel()
Assert.Equal(sendAck.ChannelId, message.ChannelId);
Assert.Equal(sendAck.MessageId, message.MessageId);
Assert.Equal(sendAck.Username, message.Username);
+
+ await socket.CloseAsync();
}
- [Fact]
- public async Task ShouldSendMessageDirectChannel()
+ [Theory]
+ [ClassData(typeof(WebSocketTestData))]
+ public async Task ShouldSendMessageDirectChannel(TestAdapterFactory adapterFactory)
{
var session1 = await _client.AuthenticateCustomAsync($"{Guid.NewGuid()}");
var session2 = await _client.AuthenticateCustomAsync($"{Guid.NewGuid()}");
@@ -77,17 +91,23 @@ public async Task ShouldSendMessageDirectChannel()
await _client.AddFriendsAsync(session1, new[] {session2.UserId});
await _client.AddFriendsAsync(session2, new[] {session1.UserId});
+ var socket1 = Nakama.Socket.From(_client, adapterFactory());
+ socket1.ReceivedError += e => Console.WriteLine(e.Message);
+
var completer = new TaskCompletionSource();
- _socket.ReceivedChannelMessage += (chatMessage) => completer.SetResult(chatMessage);
- await _socket.ConnectAsync(session1);
+ socket1.ReceivedChannelMessage += (chatMessage) => completer.SetResult(chatMessage);
+ await socket1.ConnectAsync(session1);
- var socket2 = Nakama.Socket.From(_client);
+ var socket2 = Nakama.Socket.From(_client, adapterFactory());
await socket2.ConnectAsync(session2);
- var channel = await _socket.JoinChatAsync(session2.UserId, ChannelType.DirectMessage, false, false);
+ socket2.ReceivedError += e => Console.WriteLine(e.Message);
+
+ var channel = await socket1.JoinChatAsync(session2.UserId, ChannelType.DirectMessage, false, false);
// Send chat message.
var content = new Dictionary {{"hello", "world"}}.ToJson();
- var sendAck = await _socket.WriteChatMessageAsync(channel, content);
+
+ var sendAck = await socket1.WriteChatMessageAsync(channel, content);
var message = await completer.Task.ConfigureAwait(false);
Assert.NotNull(sendAck);
@@ -95,16 +115,9 @@ public async Task ShouldSendMessageDirectChannel()
Assert.Equal(sendAck.ChannelId, message.ChannelId);
Assert.Equal(sendAck.MessageId, message.MessageId);
Assert.Equal(sendAck.Username, message.Username);
- }
- Task IAsyncLifetime.InitializeAsync()
- {
- return Task.CompletedTask;
- }
-
- Task IAsyncLifetime.DisposeAsync()
- {
- return _socket.CloseAsync();
+ await socket1.CloseAsync();
+ await socket2.CloseAsync();
}
}
}
diff --git a/tests/Nakama.Tests/Socket/WebSocketMatchTest.cs b/tests/Nakama.Tests/Socket/WebSocketMatchTest.cs
index bff23220..9340fce8 100644
--- a/tests/Nakama.Tests/Socket/WebSocketMatchTest.cs
+++ b/tests/Nakama.Tests/Socket/WebSocketMatchTest.cs
@@ -25,42 +25,49 @@ namespace Nakama.Tests.Socket
using TinyJson;
// "Flakey. Needs improvement."
- public class WebSocketMatchTest : IAsyncLifetime
+ public class WebSocketMatchTest
{
private IClient _client;
- private ISocket _socket;
// ReSharper disable RedundantArgumentDefaultValue
public WebSocketMatchTest()
{
_client = ClientUtil.FromSettingsFile();
- _socket = Nakama.Socket.From(_client);
}
- [Fact]
- public async Task ShouldCreateMatch()
+ [Theory]
+ [ClassData(typeof(WebSocketTestData))]
+ public async Task ShouldCreateMatch(TestAdapterFactory adapterFactory)
{
var session = await _client.AuthenticateCustomAsync($"{Guid.NewGuid()}");
- await _socket.ConnectAsync(session);
- var match = await _socket.CreateMatchAsync();
+ var socket = Nakama.Socket.From(_client, adapterFactory());
+
+ await socket.ConnectAsync(session);
+ var match = await socket.CreateMatchAsync();
Assert.NotNull(match);
Assert.NotNull(match.Id);
Assert.NotEmpty(match.Id);
Assert.False(match.Authoritative);
Assert.True(match.Size > 0);
+
+ await socket.CloseAsync();
}
- [Fact]
- public async Task ShouldCreateMatchAndSecondUserJoin()
+ [Theory]
+ [ClassData(typeof(WebSocketTestData))]
+ public async Task ShouldCreateMatchAndSecondUserJoin(TestAdapterFactory adapterFactory)
{
var session1 = await _client.AuthenticateCustomAsync($"{Guid.NewGuid()}");
var session2 = await _client.AuthenticateCustomAsync($"{Guid.NewGuid()}");
- await _socket.ConnectAsync(session1);
- var socket2 = Nakama.Socket.From(_client);
+
+ var socket1 = Nakama.Socket.From(_client, adapterFactory());
+ await socket1.ConnectAsync(session1);
+
+ var socket2 = Nakama.Socket.From(_client, adapterFactory());
await socket2.ConnectAsync(session2);
- var match1 = await _socket.CreateMatchAsync();
+ var match1 = await socket1.CreateMatchAsync();
var match2 = await socket2.JoinMatchAsync(match1.Id);
Assert.NotNull(match1);
@@ -71,54 +78,55 @@ public async Task ShouldCreateMatchAndSecondUserJoin()
Assert.True(match1.Presences.Count() == 0 && match1.Self.UserId == session1.UserId);
Assert.True(match2.Presences.Count() == 1);
+ await socket1.CloseAsync();
await socket2.CloseAsync();
}
- [Fact]
- public async Task ShouldCreateMatchAndLeave()
+
+ [Theory]
+ [ClassData(typeof(WebSocketTestData))]
+ public async Task ShouldCreateMatchAndLeave(TestAdapterFactory adapterFactory)
{
var session = await _client.AuthenticateCustomAsync($"{Guid.NewGuid()}");
- await _socket.ConnectAsync(session);
- var match = await _socket.CreateMatchAsync();
+
+ var socket = Nakama.Socket.From(_client, adapterFactory());
+ await socket.ConnectAsync(session);
+ var match = await socket.CreateMatchAsync();
Assert.NotNull(match);
Assert.NotNull(match.Id);
- await _socket.LeaveMatchAsync(match.Id);
+
+ await socket.LeaveMatchAsync(match.Id);
+ await socket.CloseAsync();
}
- [Fact]
- public async Task ShouldCreateMatchAndSendState()
+ [Theory]
+ [ClassData(typeof(WebSocketTestData))]
+ public async Task ShouldCreateMatchAndSendState(TestAdapterFactory adapterFactory)
{
var session1 = await _client.AuthenticateCustomAsync($"{Guid.NewGuid()}");
var session2 = await _client.AuthenticateCustomAsync($"{Guid.NewGuid()}");
- _socket = Nakama.Socket.From(_client);
- await _socket.ConnectAsync(session1);
+ var socket = Nakama.Socket.From(_client, adapterFactory());
+ await socket.ConnectAsync(session1);
- var socket2 = Nakama.Socket.From(_client);
+ var socket2 = Nakama.Socket.From(_client, adapterFactory());
var completer = new TaskCompletionSource();
socket2.ReceivedMatchState += (state) => completer.SetResult(state);
await socket2.ConnectAsync(session2);
- var match = await _socket.CreateMatchAsync();
+ var match = await socket.CreateMatchAsync();
await socket2.JoinMatchAsync(match.Id);
var newState = new Dictionary {{"hello", "world"}}.ToJson();
- await _socket.SendMatchStateAsync(match.Id, 0, newState);
+ await socket.SendMatchStateAsync(match.Id, 0, newState);
var result = await completer.Task;
Assert.NotNull(result);
Assert.Equal(newState, Encoding.UTF8.GetString(result.State));
- }
-
- Task IAsyncLifetime.InitializeAsync()
- {
- return Task.CompletedTask;
- }
- Task IAsyncLifetime.DisposeAsync()
- {
- return _socket.CloseAsync();
+ await socket.CloseAsync();
+ await socket2.CloseAsync();
}
}
}
diff --git a/tests/Nakama.Tests/Socket/WebSocketMatchmakerTest.cs b/tests/Nakama.Tests/Socket/WebSocketMatchmakerTest.cs
index 86f0686d..a22a77c1 100644
--- a/tests/Nakama.Tests/Socket/WebSocketMatchmakerTest.cs
+++ b/tests/Nakama.Tests/Socket/WebSocketMatchmakerTest.cs
@@ -20,49 +20,47 @@ namespace Nakama.Tests.Socket
using System.Threading.Tasks;
using Xunit;
- public class WebSocketMatchmakerTest : IAsyncLifetime
+ public class WebSocketMatchmakerTest
{
private IClient _client;
- private ISocket _socket;
public WebSocketMatchmakerTest()
{
_client = ClientUtil.FromSettingsFile();
- _socket = Nakama.Socket.From(_client);
}
- [Fact]
- public async Task ShouldJoinMatchmaker()
+ [Theory]
+ [ClassData(typeof(WebSocketTestData))]
+ public async Task ShouldJoinMatchmaker(TestAdapterFactory adapterFactory)
{
var session = await _client.AuthenticateCustomAsync($"{Guid.NewGuid()}");
- await _socket.ConnectAsync(session);
- var matchmakerTicket = await _socket.AddMatchmakerAsync("*");
+ var socket = Nakama.Socket.From(_client, adapterFactory());
+
+ await socket.ConnectAsync(session);
+ var matchmakerTicket = await socket.AddMatchmakerAsync("*");
Assert.NotNull(matchmakerTicket);
Assert.NotEmpty(matchmakerTicket.Ticket);
+
+ await socket.CloseAsync();
}
- // "Flakey. Needs improvement."
- [Fact]
- public async Task ShouldJoinAndLeaveMatchmaker()
+ // flakey, needs improvement
+ [Theory]
+ [ClassData(typeof(WebSocketTestData))]
+ public async Task ShouldJoinAndLeaveMatchmaker(TestAdapterFactory adapterFactory)
{
var session = await _client.AuthenticateCustomAsync($"{Guid.NewGuid()}");
- await _socket.ConnectAsync(session);
- var matchmakerTicket = await _socket.AddMatchmakerAsync("*");
+ var socket = Nakama.Socket.From(_client, adapterFactory());
+
+ await socket.ConnectAsync(session);
+ var matchmakerTicket = await socket.AddMatchmakerAsync("*");
Assert.NotNull(matchmakerTicket);
Assert.NotEmpty(matchmakerTicket.Ticket);
- await _socket.RemoveMatchmakerAsync(matchmakerTicket);
- }
+ await socket.RemoveMatchmakerAsync(matchmakerTicket);
- Task IAsyncLifetime.InitializeAsync()
- {
- return Task.CompletedTask;
- }
-
- Task IAsyncLifetime.DisposeAsync()
- {
- return _socket.CloseAsync();
+ await socket.CloseAsync();
}
}
}
diff --git a/tests/Nakama.Tests/Socket/WebSocketNotificationTest.cs b/tests/Nakama.Tests/Socket/WebSocketNotificationTest.cs
index faba66ae..102bba20 100644
--- a/tests/Nakama.Tests/Socket/WebSocketNotificationTest.cs
+++ b/tests/Nakama.Tests/Socket/WebSocketNotificationTest.cs
@@ -22,25 +22,27 @@ namespace Nakama.Tests.Socket
using Xunit;
using TinyJson;
- public class WebSocketNotificationTest : IAsyncLifetime
+ public class WebSocketNotificationTest
{
private IClient _client;
- private ISocket _socket;
public WebSocketNotificationTest()
{
_client = ClientUtil.FromSettingsFile();
- _socket = Nakama.Socket.From(_client);
}
- [Fact]
- public async Task ShouldReceiveNotification()
+ [Theory]
+ [ClassData(typeof(WebSocketTestData))]
+ public async Task ShouldReceiveNotification(TestAdapterFactory adapterFactory)
{
var session = await _client.AuthenticateCustomAsync($"{Guid.NewGuid()}");
var completer = new TaskCompletionSource();
- _socket.ReceivedNotification += (notification) => completer.SetResult(notification);
- await _socket.ConnectAsync(session);
+
+ var socket = Nakama.Socket.From(_client, adapterFactory());
+
+ socket.ReceivedNotification += (notification) => completer.SetResult(notification);
+ await socket.ConnectAsync(session);
var payload = new Dictionary {{"user_id", session.UserId}};
var _ = _client.RpcAsync(session, "clientrpc.send_notification", payload.ToJson());
@@ -48,16 +50,8 @@ public async Task ShouldReceiveNotification()
var result = await completer.Task;
Assert.NotNull(result);
Assert.Equal(session.UserId, result.SenderId);
- }
- Task IAsyncLifetime.InitializeAsync()
- {
- return Task.CompletedTask;
- }
-
- Task IAsyncLifetime.DisposeAsync()
- {
- return _socket.CloseAsync();
+ await socket.CloseAsync();
}
}
}
diff --git a/tests/Nakama.Tests/Socket/WebSocketRpcTest.cs b/tests/Nakama.Tests/Socket/WebSocketRpcTest.cs
index fee13eec..6f1c592b 100644
--- a/tests/Nakama.Tests/Socket/WebSocketRpcTest.cs
+++ b/tests/Nakama.Tests/Socket/WebSocketRpcTest.cs
@@ -22,40 +22,33 @@ namespace Nakama.Tests.Socket
using Xunit;
using TinyJson;
- public class WebSocketRpcTest : IAsyncLifetime
+ public class WebSocketRpcTest
{
private IClient _client;
- private ISocket _socket;
public WebSocketRpcTest()
{
_client = ClientUtil.FromSettingsFile();
- _socket = Nakama.Socket.From(_client);
}
- [Fact]
- public async Task ShouldSendRpcRoundtrip()
+ [Theory]
+ [ClassData(typeof(WebSocketTestData))]
+ public async Task ShouldSendRpcRoundtrip(TestAdapterFactory adapterFactory)
{
var session = await _client.AuthenticateCustomAsync($"{Guid.NewGuid()}");
- await _socket.ConnectAsync(session);
+ var socket = Nakama.Socket.From(_client, adapterFactory());
+
+ await socket.ConnectAsync(session);
const string funcid = "clientrpc.rpc";
var payload = new Dictionary {{"hello", "world"}}.ToJson();
- var response = await _socket.RpcAsync(funcid, payload);
+ var response = await socket.RpcAsync(funcid, payload);
Assert.NotNull(response);
Assert.Equal(funcid, response.Id);
Assert.Equal(payload, response.Payload);
- }
-
- Task IAsyncLifetime.InitializeAsync()
- {
- return Task.CompletedTask;
- }
- Task IAsyncLifetime.DisposeAsync()
- {
- return _socket.CloseAsync();
+ await socket.CloseAsync();
}
}
}
diff --git a/tests/Nakama.Tests/Socket/WebSocketTest.cs b/tests/Nakama.Tests/Socket/WebSocketTest.cs
index 2a618669..35773fe4 100644
--- a/tests/Nakama.Tests/Socket/WebSocketTest.cs
+++ b/tests/Nakama.Tests/Socket/WebSocketTest.cs
@@ -24,69 +24,99 @@ namespace Nakama.Tests.Socket
public class WebSocketTest
{
private IClient _client;
- private ISocket _socket;
// ReSharper disable RedundantArgumentDefaultValue
public WebSocketTest()
{
_client = ClientUtil.FromSettingsFile();
- _socket = Nakama.Socket.From(_client);
}
- [Fact]
- public void ShouldCreateSocket()
+ [Theory]
+ [ClassData(typeof(WebSocketTestData))]
+ public void ShouldCreateSocket(TestAdapterFactory adapterFactory)
{
var client = ClientUtil.FromSettingsFile();
- var socket = Nakama.Socket.From(client);
+ var socket = Nakama.Socket.From(client, adapterFactory());
Assert.NotNull(socket);
}
- [Fact]
- public async Task ShouldCreateSocketAndConnect()
+ [Theory]
+ [ClassData(typeof(WebSocketTestData))]
+ public async Task ShouldCreateSocketAndConnect(TestAdapterFactory adapterFactory)
{
var session = await _client.AuthenticateCustomAsync($"{Guid.NewGuid()}");
var completer = new TaskCompletionSource();
- _socket.Connected += () => completer.SetResult(true);
+ var socket = Nakama.Socket.From(_client, adapterFactory());
- await _socket.ConnectAsync(session);
+ socket.Connected += () => completer.SetResult(true);
+
+ await socket.ConnectAsync(session);
Assert.True(await completer.Task);
- await _socket.CloseAsync();
+ await socket.CloseAsync();
}
- [Fact]
- public async Task ShouldCreateSocketAndDisconnect()
+ [Theory]
+ [ClassData(typeof(WebSocketTestData))]
+ public async Task ShouldCreateSocketAndDisconnect(TestAdapterFactory adapterFactory)
{
var session = await _client.AuthenticateCustomAsync($"{Guid.NewGuid()}");
var completer = new TaskCompletionSource();
- _socket.Closed += () => completer.SetResult(true);
- await _socket.ConnectAsync(session);
- await _socket.CloseAsync();
+ var socket = Nakama.Socket.From(_client, adapterFactory());
+ socket.Closed += () => completer.SetResult(true);
+
+ await socket.ConnectAsync(session);
+ await socket.CloseAsync();
Assert.True(await completer.Task);
}
- [Fact]
- public async Task ShouldCreateSocketAndDisconnectSilent()
+ [Theory]
+ [ClassData(typeof(WebSocketTestData))]
+ public async Task ShouldCreateSocketAndDisconnectSilent(TestAdapterFactory adapterFactory)
{
var session = await _client.AuthenticateCustomAsync($"{Guid.NewGuid()}");
+ var socket = Nakama.Socket.From(_client, adapterFactory());
- await _socket.ConnectAsync(session);
- Assert.True(_socket.IsConnected);
+ await socket.ConnectAsync(session);
+ Assert.True(socket.IsConnected);
- await _socket.CloseAsync();
- Assert.False(_socket.IsConnected);
+ await socket.CloseAsync();
+ Assert.False(socket.IsConnected);
}
- [Fact]
- public async Task MultipleConnectAttemptsThrowException()
+ [Theory]
+ [ClassData(typeof(WebSocketTestData))]
+ public async Task MultipleConnectAttemptsThrowException(TestAdapterFactory adapterFactory)
{
var session = await _client.AuthenticateCustomAsync($"{Guid.NewGuid()}");
- await _socket.ConnectAsync(session);
- Assert.True(_socket.IsConnected);
- await Assert.ThrowsAsync(() => _socket.ConnectAsync(session));
+ var socket = Nakama.Socket.From(_client, adapterFactory());
+
+ await socket.ConnectAsync(session);
+ Assert.True(socket.IsConnected);
+ await Assert.ThrowsAsync(() => socket.ConnectAsync(session));
+ }
+
+ [Theory]
+ [ClassData(typeof(WebSocketTestData))]
+ public async Task MultipleSocketsDoNotThrowException(TestAdapterFactory adapterFactory)
+ {
+ var id1 = $"{Guid.NewGuid()}";
+ var id2 = $"{Guid.NewGuid()}";
+
+ var session1 = await _client.AuthenticateCustomAsync(id1);
+ var session2 = await _client.AuthenticateCustomAsync(id2);
+
+ var socket1 = Nakama.Socket.From(_client, adapterFactory());
+ var socket2 = Nakama.Socket.From(_client, adapterFactory());
+
+ await socket1.ConnectAsync(session1);
+ await socket2.ConnectAsync(session2);
+
+ await socket1.CloseAsync();
+ await socket2.CloseAsync();
}
}
}
diff --git a/tests/Nakama.Tests/Socket/WebSocketTestData.cs b/tests/Nakama.Tests/Socket/WebSocketTestData.cs
new file mode 100644
index 00000000..ecd5c792
--- /dev/null
+++ b/tests/Nakama.Tests/Socket/WebSocketTestData.cs
@@ -0,0 +1,39 @@
+/**
+* Copyright 2020 The Nakama Authors
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System.Collections;
+using System.Collections.Generic;
+using Nakama.SocketInternal;
+using Nakama.Protobuf;
+
+namespace Nakama.Tests.Socket
+{
+ public delegate ISocketAdapter TestAdapterFactory();
+
+ public class WebSocketTestData : IEnumerable