mirror of
https://github.com/smyalygames/monopoly.git
synced 2026-01-04 02:38:42 +01:00
Added multiplayer plugin
This commit is contained in:
61
Assets/Mirror/Cloud/ApiConnector.cs
Normal file
61
Assets/Mirror/Cloud/ApiConnector.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using Mirror.Cloud.ListServerService;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror.Cloud
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to requests and responses from the mirror api
|
||||
/// </summary>
|
||||
public interface IApiConnector
|
||||
{
|
||||
ListServer ListServer { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to requests and responses from the mirror api
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
[AddComponentMenu("Network/CloudServices/ApiConnector")]
|
||||
[HelpURL("https://mirror-networking.com/docs/CloudServices/ApiConnector.html")]
|
||||
public class ApiConnector : MonoBehaviour, IApiConnector, ICoroutineRunner
|
||||
{
|
||||
#region Inspector
|
||||
[Header("Settings")]
|
||||
|
||||
[Tooltip("Base URL of api, including https")]
|
||||
[SerializeField] string ApiAddress = "";
|
||||
|
||||
[Tooltip("Api key required to access api")]
|
||||
[SerializeField] string ApiKey = "";
|
||||
|
||||
[Header("Events")]
|
||||
|
||||
[Tooltip("Triggered when server list updates")]
|
||||
[SerializeField] ServerListEvent _onServerListUpdated = new ServerListEvent();
|
||||
#endregion
|
||||
|
||||
IRequestCreator requestCreator;
|
||||
|
||||
public ListServer ListServer { get; private set; }
|
||||
|
||||
void Awake()
|
||||
{
|
||||
requestCreator = new RequestCreator(ApiAddress, ApiKey, this);
|
||||
|
||||
InitListServer();
|
||||
}
|
||||
|
||||
void InitListServer()
|
||||
{
|
||||
IListServerServerApi serverApi = new ListServerServerApi(this, requestCreator);
|
||||
IListServerClientApi clientApi = new ListServerClientApi(this, requestCreator, _onServerListUpdated);
|
||||
ListServer = new ListServer(serverApi, clientApi);
|
||||
}
|
||||
|
||||
public void OnDestroy()
|
||||
{
|
||||
ListServer?.ServerApi.Shutdown();
|
||||
ListServer?.ClientApi.Shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Mirror/Cloud/ApiConnector.cs.meta
Normal file
11
Assets/Mirror/Cloud/ApiConnector.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8bdb99a29e179d14cb0acc43f175d9ad
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Mirror/Cloud/Core.meta
Normal file
8
Assets/Mirror/Cloud/Core.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f34c32971e65984c93a15376ec11c65
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
25
Assets/Mirror/Cloud/Core/BaseApi.cs
Normal file
25
Assets/Mirror/Cloud/Core/BaseApi.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace Mirror.Cloud
|
||||
{
|
||||
public interface IBaseApi
|
||||
{
|
||||
/// <summary>
|
||||
/// Cleans up any data created by the instance
|
||||
/// <para>For Example: removing server from list</para>
|
||||
/// </summary>
|
||||
void Shutdown();
|
||||
}
|
||||
|
||||
public abstract class BaseApi
|
||||
{
|
||||
protected readonly ICoroutineRunner runner;
|
||||
protected readonly IRequestCreator requestCreator;
|
||||
|
||||
protected BaseApi(ICoroutineRunner runner, IRequestCreator requestCreator)
|
||||
{
|
||||
this.runner = runner ?? throw new ArgumentNullException(nameof(runner));
|
||||
this.requestCreator = requestCreator ?? throw new ArgumentNullException(nameof(requestCreator));
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Mirror/Cloud/Core/BaseApi.cs.meta
Normal file
11
Assets/Mirror/Cloud/Core/BaseApi.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70f563b7a7210ae43bbcde5cb7721a94
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
12
Assets/Mirror/Cloud/Core/Events.cs
Normal file
12
Assets/Mirror/Cloud/Core/Events.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using Mirror.Cloud.ListServerService;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace Mirror.Cloud
|
||||
{
|
||||
[Serializable]
|
||||
public class ServerListEvent : UnityEvent<ServerCollectionJson> { }
|
||||
|
||||
[Serializable]
|
||||
public class MatchFoundEvent : UnityEvent<ServerJson> { }
|
||||
}
|
||||
11
Assets/Mirror/Cloud/Core/Events.cs.meta
Normal file
11
Assets/Mirror/Cloud/Core/Events.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c7c472a3ea1bc4348bd5a0b05bf7cc3b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
12
Assets/Mirror/Cloud/Core/Extensions.cs
Normal file
12
Assets/Mirror/Cloud/Core/Extensions.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace Mirror.Cloud
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
public static bool IsOk(this UnityWebRequest webRequest)
|
||||
{
|
||||
return 200 <= webRequest.responseCode && webRequest.responseCode <= 299;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Mirror/Cloud/Core/Extensions.cs.meta
Normal file
11
Assets/Mirror/Cloud/Core/Extensions.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 97501e783fc67a4459b15d10e6c63563
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
12
Assets/Mirror/Cloud/Core/ICoroutineRunner.cs
Normal file
12
Assets/Mirror/Cloud/Core/ICoroutineRunner.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror.Cloud
|
||||
{
|
||||
public interface ICoroutineRunner : IUnityEqualCheck
|
||||
{
|
||||
Coroutine StartCoroutine(IEnumerator routine);
|
||||
void StopCoroutine(IEnumerator routine);
|
||||
void StopCoroutine(Coroutine routine);
|
||||
}
|
||||
}
|
||||
11
Assets/Mirror/Cloud/Core/ICoroutineRunner.cs.meta
Normal file
11
Assets/Mirror/Cloud/Core/ICoroutineRunner.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 43472c60a7c72e54eafe559290dd0fc6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
42
Assets/Mirror/Cloud/Core/IRequestCreator.cs
Normal file
42
Assets/Mirror/Cloud/Core/IRequestCreator.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System.Collections;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace Mirror.Cloud
|
||||
{
|
||||
public delegate void RequestSuccess(string responseBody);
|
||||
|
||||
public delegate void RequestFail(string responseBody);
|
||||
|
||||
/// <summary>
|
||||
/// Objects that can be sent to the Api must have this interface
|
||||
/// </summary>
|
||||
public interface ICanBeJson { }
|
||||
|
||||
/// <summary>
|
||||
/// Methods to create and send UnityWebRequest
|
||||
/// </summary>
|
||||
public interface IRequestCreator
|
||||
{
|
||||
UnityWebRequest Delete(string page);
|
||||
UnityWebRequest Get(string page);
|
||||
UnityWebRequest Patch<T>(string page, T json) where T : struct, ICanBeJson;
|
||||
UnityWebRequest Post<T>(string page, T json) where T : struct, ICanBeJson;
|
||||
|
||||
/// <summary>
|
||||
/// Sends Request to api and invokes callback when finished
|
||||
/// <para>Starts Coroutine of SendRequestEnumerator</para>
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="onSuccess"></param>
|
||||
/// <param name="onFail"></param>
|
||||
void SendRequest(UnityWebRequest request, RequestSuccess onSuccess = null, RequestFail onFail = null);
|
||||
/// <summary>
|
||||
/// Sends Request to api and invokes callback when finished
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="onSuccess"></param>
|
||||
/// <param name="onFail"></param>
|
||||
/// <returns></returns>
|
||||
IEnumerator SendRequestEnumerator(UnityWebRequest request, RequestSuccess onSuccess = null, RequestFail onFail = null);
|
||||
}
|
||||
}
|
||||
11
Assets/Mirror/Cloud/Core/IRequestCreator.cs.meta
Normal file
11
Assets/Mirror/Cloud/Core/IRequestCreator.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b80b95532a9d6e8418aa676a261e4f69
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
26
Assets/Mirror/Cloud/Core/IUnityEqualCheck.cs
Normal file
26
Assets/Mirror/Cloud/Core/IUnityEqualCheck.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror.Cloud
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds Extension to check if unity object is null.
|
||||
/// <para>Use these methods to stop MissingReferenceException</para>
|
||||
/// </summary>
|
||||
public interface IUnityEqualCheck
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static class UnityEqualCheckExtension
|
||||
{
|
||||
public static bool IsNull(this IUnityEqualCheck obj)
|
||||
{
|
||||
return (obj as Object) == null;
|
||||
}
|
||||
|
||||
public static bool IsNotNull(this IUnityEqualCheck obj)
|
||||
{
|
||||
return (obj as Object) != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Mirror/Cloud/Core/IUnityEqualCheck.cs.meta
Normal file
11
Assets/Mirror/Cloud/Core/IUnityEqualCheck.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 05185b973ba389a4588fc8a99c75a4f6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
24
Assets/Mirror/Cloud/Core/JsonStructs.cs
Normal file
24
Assets/Mirror/Cloud/Core/JsonStructs.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
|
||||
namespace Mirror.Cloud
|
||||
{
|
||||
[Serializable]
|
||||
public struct CreatedIdJson : ICanBeJson
|
||||
{
|
||||
public string id;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct ErrorJson : ICanBeJson
|
||||
{
|
||||
public string code;
|
||||
public string message;
|
||||
|
||||
public int HtmlCode => int.Parse(code);
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct EmptyJson : ICanBeJson
|
||||
{
|
||||
}
|
||||
}
|
||||
11
Assets/Mirror/Cloud/Core/JsonStructs.cs.meta
Normal file
11
Assets/Mirror/Cloud/Core/JsonStructs.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0688c0fdae5376e4ea74d5c3904eed17
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
72
Assets/Mirror/Cloud/Core/Logger.cs
Normal file
72
Assets/Mirror/Cloud/Core/Logger.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace Mirror.Cloud
|
||||
{
|
||||
public static class Logger
|
||||
{
|
||||
public static bool VerboseLogging = false;
|
||||
static readonly ILogger logger = LogFactory.GetLogger("MirrorCloudServices");
|
||||
|
||||
public static void LogRequest(string page, string method, bool hasJson, string json)
|
||||
{
|
||||
if (hasJson)
|
||||
{
|
||||
logger.LogFormat(LogType.Log, "Request: {0} {1} {2}", method, page, json);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogFormat(LogType.Log, "Request: {0} {1}", method, page);
|
||||
}
|
||||
}
|
||||
|
||||
public static void LogResponse(UnityWebRequest statusRequest)
|
||||
{
|
||||
long code = statusRequest.responseCode;
|
||||
LogType logType = statusRequest.IsOk()
|
||||
? LogType.Log
|
||||
: LogType.Error;
|
||||
|
||||
string format = "Response: {0} {1} {2} {3}";
|
||||
if (logger.IsLogTypeAllowed(logType))
|
||||
{
|
||||
// we split path like this to make sure api key doesn't leak
|
||||
Uri uri = new Uri(statusRequest.url);
|
||||
string path = string.Join("", uri.Segments);
|
||||
string msg = string.Format(format, statusRequest.method, code, path, statusRequest.downloadHandler.text);
|
||||
logger.Log(logType, msg);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(statusRequest.error))
|
||||
{
|
||||
string msg = string.Format("WEB REQUEST ERROR: {0}", statusRequest.error);
|
||||
logger.Log(LogType.Error, msg);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Log(string msg)
|
||||
{
|
||||
if (logger.LogEnabled())
|
||||
logger.Log(msg);
|
||||
}
|
||||
|
||||
internal static void LogWarning(string msg)
|
||||
{
|
||||
if (logger.WarnEnabled())
|
||||
logger.LogWarning(msg);
|
||||
}
|
||||
|
||||
internal static void LogError(string msg)
|
||||
{
|
||||
if (logger.ErrorEnabled())
|
||||
logger.LogError(msg);
|
||||
}
|
||||
|
||||
internal static void Verbose(string msg)
|
||||
{
|
||||
if (VerboseLogging && logger.LogEnabled())
|
||||
logger.Log(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Mirror/Cloud/Core/Logger.cs.meta
Normal file
11
Assets/Mirror/Cloud/Core/Logger.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 457ba2df6cb6e1542996c17c715ee81b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
144
Assets/Mirror/Cloud/Core/RequestCreator.cs
Normal file
144
Assets/Mirror/Cloud/Core/RequestCreator.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace Mirror.Cloud
|
||||
{
|
||||
/// <summary>
|
||||
/// Methods to create and send UnityWebRequest
|
||||
/// </summary>
|
||||
public class RequestCreator : IRequestCreator
|
||||
{
|
||||
const string GET = "GET";
|
||||
const string POST = "POST";
|
||||
const string PATCH = "PATCH";
|
||||
const string DELETE = "DELETE";
|
||||
|
||||
public readonly string baseAddress;
|
||||
public readonly string apiKey;
|
||||
readonly ICoroutineRunner runner;
|
||||
|
||||
public RequestCreator(string baseAddress, string apiKey, ICoroutineRunner coroutineRunner)
|
||||
{
|
||||
if (string.IsNullOrEmpty(baseAddress))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(baseAddress));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(apiKey))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(apiKey));
|
||||
}
|
||||
|
||||
this.baseAddress = baseAddress;
|
||||
this.apiKey = apiKey;
|
||||
|
||||
runner = coroutineRunner ?? throw new ArgumentNullException(nameof(coroutineRunner));
|
||||
}
|
||||
|
||||
|
||||
Uri CreateUri(string page)
|
||||
{
|
||||
return new Uri(string.Format("{0}/{1}?key={2}", baseAddress, page, apiKey));
|
||||
}
|
||||
|
||||
UnityWebRequest CreateWebRequest(string page, string method, string json = null)
|
||||
{
|
||||
bool hasJson = !string.IsNullOrEmpty(json);
|
||||
Logger.LogRequest(page, method, hasJson, json);
|
||||
|
||||
UnityWebRequest request = new UnityWebRequest(CreateUri(page));
|
||||
request.method = method;
|
||||
if (hasJson)
|
||||
{
|
||||
request.SetRequestHeader("Content-Type", "application/json");
|
||||
}
|
||||
|
||||
request.downloadHandler = new DownloadHandlerBuffer();
|
||||
|
||||
byte[] bodyRaw = hasJson
|
||||
? Encoding.UTF8.GetBytes(json)
|
||||
: null;
|
||||
|
||||
request.uploadHandler = new UploadHandlerRaw(bodyRaw);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create Get Request to page
|
||||
/// </summary>
|
||||
/// <param name="page"></param>
|
||||
/// <returns></returns>
|
||||
public UnityWebRequest Get(string page)
|
||||
{
|
||||
return CreateWebRequest(page, GET);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates Post Request to page with Json body
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="page"></param>
|
||||
/// <param name="json"></param>
|
||||
/// <returns></returns>
|
||||
public UnityWebRequest Post<T>(string page, T json) where T : struct, ICanBeJson
|
||||
{
|
||||
string jsonString = JsonUtility.ToJson(json);
|
||||
return CreateWebRequest(page, POST, jsonString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates Patch Request to page with Json body
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="page"></param>
|
||||
/// <param name="json"></param>
|
||||
/// <returns></returns>
|
||||
public UnityWebRequest Patch<T>(string page, T json) where T : struct, ICanBeJson
|
||||
{
|
||||
string jsonString = JsonUtility.ToJson(json);
|
||||
return CreateWebRequest(page, PATCH, jsonString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create Delete Request to page
|
||||
/// </summary>
|
||||
/// <param name="page"></param>
|
||||
/// <returns></returns>
|
||||
public UnityWebRequest Delete(string page)
|
||||
{
|
||||
return CreateWebRequest(page, DELETE);
|
||||
}
|
||||
|
||||
|
||||
public void SendRequest(UnityWebRequest request, RequestSuccess onSuccess = null, RequestFail onFail = null)
|
||||
{
|
||||
runner.StartCoroutine(SendRequestEnumerator(request, onSuccess, onFail));
|
||||
}
|
||||
|
||||
public IEnumerator SendRequestEnumerator(UnityWebRequest request, RequestSuccess onSuccess = null, RequestFail onFail = null)
|
||||
{
|
||||
using (UnityWebRequest webRequest = request)
|
||||
{
|
||||
yield return webRequest.SendWebRequest();
|
||||
Logger.LogResponse(webRequest);
|
||||
|
||||
string text = webRequest.downloadHandler.text;
|
||||
Logger.Verbose(text);
|
||||
if (webRequest.IsOk())
|
||||
{
|
||||
onSuccess?.Invoke(text);
|
||||
}
|
||||
else
|
||||
{
|
||||
onFail?.Invoke(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Mirror/Cloud/Core/RequestCreator.cs.meta
Normal file
11
Assets/Mirror/Cloud/Core/RequestCreator.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cfaa626443cc7c94eae138a2e3a04d7c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Mirror/Cloud/ListServer.meta
Normal file
8
Assets/Mirror/Cloud/ListServer.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c4c4be148a492b143a881cd08bf7e320
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
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:
|
||||
14
Assets/Mirror/Cloud/Mirror.Cloud.asmdef
Normal file
14
Assets/Mirror/Cloud/Mirror.Cloud.asmdef
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "Mirror.Cloud",
|
||||
"references": [
|
||||
"Mirror"
|
||||
],
|
||||
"optionalUnityReferences": [],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": []
|
||||
}
|
||||
7
Assets/Mirror/Cloud/Mirror.Cloud.asmdef.meta
Normal file
7
Assets/Mirror/Cloud/Mirror.Cloud.asmdef.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c21ba7b8c3183cb47b7fe3b3799d49c4
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
152
Assets/Mirror/Cloud/README.md
Normal file
152
Assets/Mirror/Cloud/README.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# Mirror Cloud Services
|
||||
|
||||
## Mirror List Server
|
||||
|
||||
Example has an API key which can be used as a demo.
|
||||
|
||||
To get an API key to use within your game you can subscribe on the [Mirror Networking Website](https://mirror-networking.com/list-server/)
|
||||
|
||||
### Key features
|
||||
|
||||
- The Cloud Service works via https so it is secure and can be used from any platform.
|
||||
- It runs on Google Cloud so there is no worry about server downtime.
|
||||
- It scales really well. Default quota is 1000 API requests per minute. If you have high demands, contact us and we can increase that limit.
|
||||
|
||||
## List Server Examples
|
||||
|
||||
An example for this can be found in [Mirror/Examples/Cloud/](https://github.com/vis2k/Mirror/tree/master/Assets/Mirror/Examples/Cloud)
|
||||
|
||||
*Note: you cannot connect to your own public IP address, you will need at least one other person to test this*
|
||||
|
||||
## How to use
|
||||
|
||||
Add `ApiConnector` component to an object in your game. It is probably best to put this on the same object as your NetworkManager. Once it has been added set the `ApiAddress` and `ApiKey` fields.
|
||||
|
||||
To use `ApiConnector` either directly reference it in an inspector field or get it when your script awakes
|
||||
```cs
|
||||
ApiConnector connector;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
connector = FindObjectOfType<ApiConnector>();
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
The Api calls are grouped into objects. `connector.ListServer.ServerApi` has the Server Api calls like `AddServer`. `connector.ListServer.ClientApi` has the Client Api calls like `GetServerList`.
|
||||
|
||||
### Server Api Example
|
||||
|
||||
Example of how to add server
|
||||
```cs
|
||||
void AddServer(int playerCount)
|
||||
{
|
||||
Transport transport = Transport.activeTransport;
|
||||
|
||||
Uri uri = transport.ServerUri();
|
||||
int port = uri.Port;
|
||||
string protocol = uri.Scheme;
|
||||
|
||||
connector.ListServer.ServerApi.AddServer(new ServerJson
|
||||
{
|
||||
displayName = "Fun game!!!",
|
||||
protocol = protocol,
|
||||
port = port,
|
||||
maxPlayerCount = NetworkManager.singleton.maxConnections,
|
||||
playerCount = playerCount
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Client Api Example
|
||||
Example of how to list servers
|
||||
|
||||
```cs
|
||||
ApiConnector connector;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
connector = FindObjectOfType<ApiConnector>();
|
||||
// add listener to event that will update UI when Server list is refreshed
|
||||
connector.ListServer.ClientApi.onServerListUpdated += onServerListUpdated;
|
||||
|
||||
// add listen to button so that player can refresh server list
|
||||
refreshButton.onClick.AddListener(RefreshButtonHandler);
|
||||
}
|
||||
|
||||
public void RefreshButtonHandler()
|
||||
{
|
||||
connector.ListServer.ClientApi.GetServerList();
|
||||
}
|
||||
|
||||
void onServerListUpdated()
|
||||
{
|
||||
// Update UI here
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Debug
|
||||
|
||||
If something doesn't seem to be working then here are some tips to help solve the problem
|
||||
|
||||
### Check logs
|
||||
|
||||
Enable `showDebugMessages` on your NetworkManager or use the log level window to enable logging for the cloud scripts
|
||||
|
||||
Below are some example logs to look for to check things are working.
|
||||
|
||||
#### Add Server
|
||||
|
||||
The add request is sent to add a server to the list server
|
||||
|
||||
```
|
||||
Request: POST servers {"protocol":"tcp4","port":7777,"playerCount":0,"maxPlayerCount":4,"displayName":"Tanks Game 521","address":"","customAddress":"","customData":[]}
|
||||
```
|
||||
```
|
||||
Response: POST 200 /servers {"id":"BI6bQQ2TbNiqhdp1D7UB"}
|
||||
```
|
||||
|
||||
#### Update Server
|
||||
|
||||
The object sent in update request maybe be empty, this is sent to keep the server record alive so it shows up.
|
||||
|
||||
The update request can also be used to change info. For example the player count when someone joins or leaves
|
||||
|
||||
```
|
||||
Request: PATCH servers/BI6bQQ2TbNiqhdp1D7UB {}
|
||||
```
|
||||
```
|
||||
Response: PATCH 204 /servers/BI6bQQ2TbNiqhdp1D7UB
|
||||
```
|
||||
|
||||
#### Remove Server
|
||||
|
||||
The remove request is sent to remove a server from the list server. This is automatically called when the ApiConnection is destroyed.
|
||||
|
||||
```
|
||||
Request: DELETE servers/BI6bQQ2TbNiqhdp1D7UB
|
||||
```
|
||||
```
|
||||
Response: DELETE 204 /servers/BI6bQQ2TbNiqhdp1D7UB
|
||||
```
|
||||
|
||||
|
||||
#### Get Servers
|
||||
|
||||
The get request is sent in order to get the list of servers.
|
||||
|
||||
The example below shows an array of 2 servers, one with name `Tanks Game 521` and the other with name `Tanks Game 212`
|
||||
|
||||
```
|
||||
Request: GET servers
|
||||
```
|
||||
```
|
||||
Response: GET 200 /servers {"servers":[{"address":"tcp4://xx.xx.xx.xx:7777","displayName":"Tanks Game 521","port":7777,"protocol":"tcp4","playerCount":0,"maxPlayerCount":4,"customAddress":"","customData":[]},{"address":"tcp4://xx.xx.xx.xx:7777","displayName":"Tanks Game 212","port":7777,"protocol":"tcp4","playerCount":0,"maxPlayerCount":4,"customData":[]}]}
|
||||
```
|
||||
*xx.xx.xx.xx will be the IP address for the server*
|
||||
|
||||
|
||||
### Use the QuickListServerDebug
|
||||
|
||||
The QuickListServerDebug script uses `OnGUI` to show the list of servers. This script can be used to check the server list without using Canvas UI.
|
||||
7
Assets/Mirror/Cloud/README.md.meta
Normal file
7
Assets/Mirror/Cloud/README.md.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 04945d14ccbed964597a1ee00805c059
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1
Assets/Mirror/Cloud/version.txt
Normal file
1
Assets/Mirror/Cloud/version.txt
Normal file
@@ -0,0 +1 @@
|
||||
MirrorCloudServices v0.1.0
|
||||
7
Assets/Mirror/Cloud/version.txt.meta
Normal file
7
Assets/Mirror/Cloud/version.txt.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bf81e376b88e68e48a47531b8bfeb0f4
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user