mirror of
https://github.com/smyalygames/monopoly.git
synced 2025-12-29 07:48:48 +01:00
Added multiplayer plugin
This commit is contained in:
66
Assets/Mirror/Cloud/ListServer/ListServer.cs
Normal file
66
Assets/Mirror/Cloud/ListServer/ListServer.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace Mirror.Cloud.ListServerService
|
||||
{
|
||||
public sealed class ListServer
|
||||
{
|
||||
public readonly IListServerServerApi ServerApi;
|
||||
public readonly IListServerClientApi ClientApi;
|
||||
|
||||
public ListServer(IListServerServerApi serverApi, IListServerClientApi clientApi)
|
||||
{
|
||||
ServerApi = serverApi ?? throw new ArgumentNullException(nameof(serverApi));
|
||||
ClientApi = clientApi ?? throw new ArgumentNullException(nameof(clientApi));
|
||||
}
|
||||
}
|
||||
|
||||
public interface IListServerServerApi : IBaseApi
|
||||
{
|
||||
/// <summary>
|
||||
/// Has a server been added to the list with this connection
|
||||
/// </summary>
|
||||
bool ServerInList { get; }
|
||||
/// <summary>
|
||||
/// Add a server to the list
|
||||
/// </summary>
|
||||
/// <param name="server"></param>
|
||||
void AddServer(ServerJson server);
|
||||
/// <summary>
|
||||
/// Update the current server
|
||||
/// </summary>
|
||||
/// <param name="newPlayerCount"></param>
|
||||
void UpdateServer(int newPlayerCount);
|
||||
/// <summary>
|
||||
/// Update the current server
|
||||
/// </summary>
|
||||
/// <param name="server"></param>
|
||||
void UpdateServer(ServerJson server);
|
||||
/// <summary>
|
||||
/// Removes the current server
|
||||
/// </summary>
|
||||
void RemoveServer();
|
||||
}
|
||||
|
||||
public interface IListServerClientApi : IBaseApi
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the server list is updated
|
||||
/// </summary>
|
||||
event UnityAction<ServerCollectionJson> onServerListUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Get the server list once
|
||||
/// </summary>
|
||||
void GetServerList();
|
||||
/// <summary>
|
||||
/// Start getting the server list every interval
|
||||
/// </summary>
|
||||
/// <param name="interval"></param>
|
||||
void StartGetServerListRepeat(int interval);
|
||||
/// <summary>
|
||||
/// Stop getting the server list
|
||||
/// </summary>
|
||||
void StopGetServerListRepeat();
|
||||
}
|
||||
}
|
||||
11
Assets/Mirror/Cloud/ListServer/ListServer.cs.meta
Normal file
11
Assets/Mirror/Cloud/ListServer/ListServer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f0311899162c5b49a3c11fa9bd9c133
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
9
Assets/Mirror/Cloud/ListServer/ListServerBaseApi.cs
Normal file
9
Assets/Mirror/Cloud/ListServer/ListServerBaseApi.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace Mirror.Cloud.ListServerService
|
||||
{
|
||||
public abstract class ListServerBaseApi : BaseApi
|
||||
{
|
||||
protected ListServerBaseApi(ICoroutineRunner runner, IRequestCreator requestCreator) : base(runner, requestCreator)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Mirror/Cloud/ListServer/ListServerBaseApi.cs.meta
Normal file
11
Assets/Mirror/Cloud/ListServer/ListServerBaseApi.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b6838f9df45594d48873518cbb75b329
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
70
Assets/Mirror/Cloud/ListServer/ListServerClientApi.cs
Normal file
70
Assets/Mirror/Cloud/ListServer/ListServerClientApi.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace Mirror.Cloud.ListServerService
|
||||
{
|
||||
public sealed class ListServerClientApi : ListServerBaseApi, IListServerClientApi
|
||||
{
|
||||
readonly ServerListEvent _onServerListUpdated;
|
||||
|
||||
Coroutine getServerListRepeatCoroutine;
|
||||
|
||||
public event UnityAction<ServerCollectionJson> onServerListUpdated
|
||||
{
|
||||
add => _onServerListUpdated.AddListener(value);
|
||||
remove => _onServerListUpdated.RemoveListener(value);
|
||||
}
|
||||
|
||||
public ListServerClientApi(ICoroutineRunner runner, IRequestCreator requestCreator, ServerListEvent onServerListUpdated) : base(runner, requestCreator)
|
||||
{
|
||||
_onServerListUpdated = onServerListUpdated;
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
StopGetServerListRepeat();
|
||||
}
|
||||
|
||||
public void GetServerList()
|
||||
{
|
||||
runner.StartCoroutine(getServerList());
|
||||
}
|
||||
|
||||
public void StartGetServerListRepeat(int interval)
|
||||
{
|
||||
getServerListRepeatCoroutine = runner.StartCoroutine(GetServerListRepeat(interval));
|
||||
}
|
||||
|
||||
public void StopGetServerListRepeat()
|
||||
{
|
||||
// if runner is null it has been destroyed and will alraedy be null
|
||||
if (runner.IsNotNull() && getServerListRepeatCoroutine != null)
|
||||
{
|
||||
runner.StopCoroutine(getServerListRepeatCoroutine);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator GetServerListRepeat(int interval)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
yield return getServerList();
|
||||
|
||||
yield return new WaitForSeconds(interval);
|
||||
}
|
||||
}
|
||||
IEnumerator getServerList()
|
||||
{
|
||||
UnityWebRequest request = requestCreator.Get("servers");
|
||||
yield return requestCreator.SendRequestEnumerator(request, onSuccess);
|
||||
|
||||
void onSuccess(string responseBody)
|
||||
{
|
||||
ServerCollectionJson serverlist = JsonUtility.FromJson<ServerCollectionJson>(responseBody);
|
||||
_onServerListUpdated?.Invoke(serverlist);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Mirror/Cloud/ListServer/ListServerClientApi.cs.meta
Normal file
11
Assets/Mirror/Cloud/ListServer/ListServerClientApi.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d49649fb32cb96b46b10f013b38a4b50
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
207
Assets/Mirror/Cloud/ListServer/ListServerJson.cs
Normal file
207
Assets/Mirror/Cloud/ListServer/ListServerJson.cs
Normal file
@@ -0,0 +1,207 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Mirror.Cloud.ListServerService
|
||||
{
|
||||
[Serializable]
|
||||
public struct ServerCollectionJson : ICanBeJson
|
||||
{
|
||||
public ServerJson[] servers;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct ServerJson : ICanBeJson
|
||||
{
|
||||
public string protocol;
|
||||
public int port;
|
||||
public int playerCount;
|
||||
public int maxPlayerCount;
|
||||
|
||||
/// <summary>
|
||||
/// optional
|
||||
/// </summary>
|
||||
public string displayName;
|
||||
|
||||
/// <summary>
|
||||
/// Uri string of the ip and port of the server.
|
||||
/// <para>The ip is calculated by the request to the API</para>
|
||||
/// <para>This is returns from the api, any incoming address fields will be ignored</para>
|
||||
/// </summary>
|
||||
public string address;
|
||||
|
||||
/// <summary>
|
||||
/// Can be used to set custom uri
|
||||
/// <para>optional</para>
|
||||
/// </summary>
|
||||
public string customAddress;
|
||||
|
||||
/// <summary>
|
||||
/// Array of custom data, use SetCustomData to set values
|
||||
/// <para>optional</para>
|
||||
/// </summary>
|
||||
public KeyValue[] customData;
|
||||
|
||||
/// <summary>
|
||||
/// Uri from address field
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Uri GetServerUri() => new Uri(address);
|
||||
|
||||
/// <summary>
|
||||
/// Uri from customAddress field
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Uri GetCustomUri() => new Uri(customAddress);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the customData array
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
public void SetCustomData(Dictionary<string, string> data)
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
customData = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
customData = data.ToKeyValueArray();
|
||||
CustomDataHelper.ValidateCustomData(customData);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Validate()
|
||||
{
|
||||
CustomDataHelper.ValidateCustomData(customData);
|
||||
|
||||
if (string.IsNullOrEmpty(protocol))
|
||||
{
|
||||
Logger.LogError("ServerJson should not have empty protocol");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (port == 0)
|
||||
{
|
||||
Logger.LogError("ServerJson should not have port equal 0");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (maxPlayerCount == 0)
|
||||
{
|
||||
Logger.LogError("ServerJson should not have maxPlayerCount equal 0");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct PartialServerJson : ICanBeJson
|
||||
{
|
||||
/// <summary>
|
||||
/// optional
|
||||
/// </summary>
|
||||
public int playerCount;
|
||||
|
||||
/// <summary>
|
||||
/// optional
|
||||
/// </summary>
|
||||
public int maxPlayerCount;
|
||||
|
||||
/// <summary>
|
||||
/// optional
|
||||
/// </summary>
|
||||
public string displayName;
|
||||
|
||||
/// <summary>
|
||||
/// Array of custom data, use SetCustomData to set values
|
||||
/// <para>optional</para>
|
||||
/// </summary>
|
||||
public KeyValue[] customData;
|
||||
|
||||
|
||||
public void SetCustomData(Dictionary<string, string> data)
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
customData = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
customData = data.ToKeyValueArray();
|
||||
CustomDataHelper.ValidateCustomData(customData);
|
||||
}
|
||||
}
|
||||
|
||||
public void Validate()
|
||||
{
|
||||
CustomDataHelper.ValidateCustomData(customData);
|
||||
}
|
||||
}
|
||||
|
||||
public static class CustomDataHelper
|
||||
{
|
||||
const int MaxCustomData = 16;
|
||||
|
||||
public static Dictionary<string, string> ToDictionary(this KeyValue[] keyValues)
|
||||
{
|
||||
return keyValues.ToDictionary(x => x.key, x => x.value);
|
||||
}
|
||||
public static KeyValue[] ToKeyValueArray(this Dictionary<string, string> dictionary)
|
||||
{
|
||||
return dictionary.Select(kvp => new KeyValue(kvp.Key, kvp.Value)).ToArray();
|
||||
}
|
||||
|
||||
public static void ValidateCustomData(KeyValue[] customData)
|
||||
{
|
||||
if (customData == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (customData.Length > MaxCustomData)
|
||||
{
|
||||
Logger.LogError($"There can only be {MaxCustomData} custom data but there was {customData.Length} values given");
|
||||
Array.Resize(ref customData, MaxCustomData);
|
||||
}
|
||||
|
||||
foreach (KeyValue item in customData)
|
||||
{
|
||||
item.Validate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct KeyValue
|
||||
{
|
||||
const int MaxKeySize = 32;
|
||||
const int MaxValueSize = 256;
|
||||
|
||||
public string key;
|
||||
public string value;
|
||||
|
||||
public KeyValue(string key, string value)
|
||||
{
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void Validate()
|
||||
{
|
||||
if (key.Length > MaxKeySize)
|
||||
{
|
||||
Logger.LogError($"Custom Data must have key with length less than {MaxKeySize}");
|
||||
key = key.Substring(0, MaxKeySize);
|
||||
}
|
||||
|
||||
if (value.Length > MaxValueSize)
|
||||
{
|
||||
Logger.LogError($"Custom Data must have value with length less than {MaxValueSize}");
|
||||
value = value.Substring(0, MaxValueSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Mirror/Cloud/ListServer/ListServerJson.cs.meta
Normal file
11
Assets/Mirror/Cloud/ListServer/ListServerJson.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a963606335eae0f47abe7ecb5fd028ea
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
219
Assets/Mirror/Cloud/ListServer/ListServerServerApi.cs
Normal file
219
Assets/Mirror/Cloud/ListServer/ListServerServerApi.cs
Normal file
@@ -0,0 +1,219 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace Mirror.Cloud.ListServerService
|
||||
{
|
||||
public sealed class ListServerServerApi : ListServerBaseApi, IListServerServerApi
|
||||
{
|
||||
const int PingInterval = 20;
|
||||
const int MaxPingFails = 15;
|
||||
|
||||
ServerJson currentServer;
|
||||
string serverId;
|
||||
|
||||
Coroutine _pingCoroutine;
|
||||
/// <summary>
|
||||
/// If the server has already been added
|
||||
/// </summary>
|
||||
bool added;
|
||||
/// <summary>
|
||||
/// if a request is currently sending
|
||||
/// </summary>
|
||||
bool sending;
|
||||
/// <summary>
|
||||
/// If an update request was recently sent
|
||||
/// </summary>
|
||||
bool skipNextPing;
|
||||
/// <summary>
|
||||
/// How many failed pings in a row
|
||||
/// </summary>
|
||||
int pingFails = 0;
|
||||
|
||||
public bool ServerInList => added;
|
||||
|
||||
public ListServerServerApi(ICoroutineRunner runner, IRequestCreator requestCreator) : base(runner, requestCreator)
|
||||
{
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
stopPingCoroutine();
|
||||
if (added)
|
||||
{
|
||||
removeServerWithoutCoroutine();
|
||||
}
|
||||
added = false;
|
||||
}
|
||||
|
||||
public void AddServer(ServerJson server)
|
||||
{
|
||||
if (added) { Logger.LogWarning("AddServer called when server was already adding or added"); return; }
|
||||
bool valid = server.Validate();
|
||||
if (!valid) { return; }
|
||||
|
||||
runner.StartCoroutine(addServer(server));
|
||||
}
|
||||
|
||||
public void UpdateServer(int newPlayerCount)
|
||||
{
|
||||
if (!added) { Logger.LogWarning("UpdateServer called when before server was added"); return; }
|
||||
|
||||
currentServer.playerCount = newPlayerCount;
|
||||
UpdateServer(currentServer);
|
||||
}
|
||||
|
||||
public void UpdateServer(ServerJson server)
|
||||
{
|
||||
// TODO, use PartialServerJson as Arg Instead
|
||||
if (!added) { Logger.LogWarning("UpdateServer called when before server was added"); return; }
|
||||
|
||||
PartialServerJson partialServer = new PartialServerJson
|
||||
{
|
||||
displayName = server.displayName,
|
||||
playerCount = server.playerCount,
|
||||
maxPlayerCount = server.maxPlayerCount,
|
||||
customData = server.customData,
|
||||
};
|
||||
partialServer.Validate();
|
||||
|
||||
runner.StartCoroutine(updateServer(partialServer));
|
||||
}
|
||||
|
||||
public void RemoveServer()
|
||||
{
|
||||
if (!added) { return; }
|
||||
|
||||
if (string.IsNullOrEmpty(serverId))
|
||||
{
|
||||
Logger.LogWarning("Can not remove server because serverId was empty");
|
||||
return;
|
||||
}
|
||||
|
||||
stopPingCoroutine();
|
||||
runner.StartCoroutine(removeServer());
|
||||
}
|
||||
|
||||
void stopPingCoroutine()
|
||||
{
|
||||
if (_pingCoroutine != null)
|
||||
{
|
||||
runner.StopCoroutine(_pingCoroutine);
|
||||
_pingCoroutine = null;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator addServer(ServerJson server)
|
||||
{
|
||||
added = true;
|
||||
sending = true;
|
||||
currentServer = server;
|
||||
|
||||
UnityWebRequest request = requestCreator.Post("servers", currentServer);
|
||||
yield return requestCreator.SendRequestEnumerator(request, onSuccess, onFail);
|
||||
sending = false;
|
||||
|
||||
void onSuccess(string responseBody)
|
||||
{
|
||||
CreatedIdJson created = JsonUtility.FromJson<CreatedIdJson>(responseBody);
|
||||
serverId = created.id;
|
||||
|
||||
// Start ping to keep server alive
|
||||
_pingCoroutine = runner.StartCoroutine(ping());
|
||||
}
|
||||
void onFail(string responseBody)
|
||||
{
|
||||
added = false;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator updateServer(PartialServerJson server)
|
||||
{
|
||||
// wait to not be sending
|
||||
while (sending)
|
||||
{
|
||||
yield return new WaitForSeconds(1);
|
||||
}
|
||||
|
||||
// We need to check added incase Update is called soon after Add, and add failed
|
||||
if (!added) { Logger.LogWarning("UpdateServer called when before server was added"); yield break; }
|
||||
|
||||
sending = true;
|
||||
UnityWebRequest request = requestCreator.Patch("servers/" + serverId, server);
|
||||
yield return requestCreator.SendRequestEnumerator(request, onSuccess);
|
||||
sending = false;
|
||||
|
||||
void onSuccess(string responseBody)
|
||||
{
|
||||
skipNextPing = true;
|
||||
|
||||
if (_pingCoroutine == null)
|
||||
{
|
||||
_pingCoroutine = runner.StartCoroutine(ping());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Keeps server alive in database
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IEnumerator ping()
|
||||
{
|
||||
while (pingFails <= MaxPingFails)
|
||||
{
|
||||
yield return new WaitForSeconds(PingInterval);
|
||||
if (skipNextPing)
|
||||
{
|
||||
skipNextPing = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
sending = true;
|
||||
UnityWebRequest request = requestCreator.Patch("servers/" + serverId, new EmptyJson());
|
||||
yield return requestCreator.SendRequestEnumerator(request, onSuccess, onFail);
|
||||
sending = false;
|
||||
}
|
||||
|
||||
Logger.LogWarning("Max ping fails reached, stoping to ping server");
|
||||
_pingCoroutine = null;
|
||||
|
||||
|
||||
void onSuccess(string responseBody)
|
||||
{
|
||||
pingFails = 0;
|
||||
}
|
||||
void onFail(string responseBody)
|
||||
{
|
||||
pingFails++;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator removeServer()
|
||||
{
|
||||
sending = true;
|
||||
UnityWebRequest request = requestCreator.Delete("servers/" + serverId);
|
||||
yield return requestCreator.SendRequestEnumerator(request);
|
||||
sending = false;
|
||||
|
||||
added = false;
|
||||
}
|
||||
|
||||
void removeServerWithoutCoroutine()
|
||||
{
|
||||
if (string.IsNullOrEmpty(serverId))
|
||||
{
|
||||
Logger.LogWarning("Can not remove server becuase serverId was empty");
|
||||
return;
|
||||
}
|
||||
|
||||
UnityWebRequest request = requestCreator.Delete("servers/" + serverId);
|
||||
UnityWebRequestAsyncOperation operation = request.SendWebRequest();
|
||||
|
||||
operation.completed += (op) =>
|
||||
{
|
||||
Logger.LogResponse(request);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Mirror/Cloud/ListServer/ListServerServerApi.cs.meta
Normal file
11
Assets/Mirror/Cloud/ListServer/ListServerServerApi.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 675f0d0fd4e82b04290c4d30c8d78ede
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user