Mirror 41

This commit is contained in:
Derek S 2021-06-14 01:46:15 -05:00
parent 68ad564785
commit f988f2d8de
714 changed files with 3296 additions and 11542 deletions

View file

@ -3,6 +3,6 @@ guid: 1b2f9d254154cd942ba40b06b869b8f3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -13,6 +13,9 @@ namespace Mirror.Authenticators
public string username;
public string password;
// this is set if authentication fails to prevent garbage AuthRequestMessage spam
bool ServerAuthFailed;
#region Messages
public struct AuthRequestMessage : NetworkMessage
@ -101,7 +104,13 @@ namespace Mirror.Authenticators
conn.isAuthenticated = false;
// disconnect the client after 1 second so that response message gets delivered
StartCoroutine(DelayedDisconnect(conn, 1));
if (!ServerAuthFailed)
{
// set this false so this coroutine can only be started once
ServerAuthFailed = true;
StartCoroutine(DelayedDisconnect(conn, 1));
}
}
}
@ -140,8 +149,7 @@ namespace Mirror.Authenticators
/// <summary>
/// Called on client from OnClientAuthenticateInternal when a client needs to authenticate
/// </summary>
/// <param name="conn">Connection of the client.</param>
public override void OnClientAuthenticate(NetworkConnection conn)
public override void OnClientAuthenticate()
{
AuthRequestMessage authRequestMessage = new AuthRequestMessage
{
@ -149,13 +157,15 @@ namespace Mirror.Authenticators
authPassword = password
};
conn.Send(authRequestMessage);
NetworkClient.connection.Send(authRequestMessage);
}
[Obsolete("Call OnAuthResponseMessage without the NetworkConnection parameter. It always points to NetworkClient.connection anyway.")]
public void OnAuthResponseMessage(NetworkConnection conn, AuthResponseMessage msg) => OnAuthResponseMessage(msg);
/// <summary>
/// Called on client when the server's AuthResponseMessage arrives
/// </summary>
/// <param name="conn">Connection to client.</param>
/// <param name="msg">The message payload</param>
public void OnAuthResponseMessage(AuthResponseMessage msg)
{
@ -164,20 +174,17 @@ namespace Mirror.Authenticators
// Debug.LogFormat(LogType.Log, "Authentication Response: {0}", msg.message);
// Authentication has been accepted
ClientAccept(NetworkClient.connection);
ClientAccept();
}
else
{
Debug.LogError($"Authentication Response: {msg.message}");
// Authentication has been rejected
ClientReject(NetworkClient.connection);
ClientReject();
}
}
[Obsolete("Call OnAuthResponseMessage without the NetworkConnection parameter. It always points to NetworkClient.connection anyway.")]
public void OnAuthResponseMessage(NetworkConnection conn, AuthResponseMessage msg) => OnAuthResponseMessage(msg);
#endregion
}
}

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -2,6 +2,6 @@ fileFormatVersion: 2
guid: e720aa64e3f58fb4880566a322584340
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -48,23 +48,21 @@ namespace Mirror.Authenticators
StartCoroutine(BeginAuthentication(conn));
}
public override void OnClientAuthenticate(NetworkConnection conn)
public override void OnClientAuthenticate()
{
authenticator.OnClientAuthenticate(conn);
authenticator.OnClientAuthenticate();
if (timeout > 0)
StartCoroutine(BeginAuthentication(conn));
StartCoroutine(BeginAuthentication(NetworkClient.connection));
}
IEnumerator BeginAuthentication(NetworkConnection conn)
{
// Debug.Log($"Authentication countdown started {conn} {timeout}");
yield return new WaitForSecondsRealtime(timeout);
if (!conn.isAuthenticated)
{
// Debug.Log($"Authentication Timeout {conn}");
conn.Disconnect();
}
}

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -1,61 +0,0 @@
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/api/Mirror.Cloud.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();
}
}
}

View file

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 3f34c32971e65984c93a15376ec11c65
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -1,25 +0,0 @@
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));
}
}
}

View file

@ -1,12 +0,0 @@
using System;
using Mirror.Cloud.ListServerService;
using UnityEngine.Events;
namespace Mirror.Cloud
{
[Serializable]
public class ServerListEvent : UnityEvent<ServerCollectionJson> {}
[Serializable]
public class MatchFoundEvent : UnityEvent<ServerJson> {}
}

View file

@ -1,12 +0,0 @@
using UnityEngine.Networking;
namespace Mirror.Cloud
{
public static class Extensions
{
public static bool IsOk(this UnityWebRequest webRequest)
{
return 200 <= webRequest.responseCode && webRequest.responseCode <= 299;
}
}
}

View file

@ -1,12 +0,0 @@
using System.Collections;
using UnityEngine;
namespace Mirror.Cloud
{
public interface ICoroutineRunner : IUnityEqualCheck
{
Coroutine StartCoroutine(IEnumerator routine);
void StopCoroutine(IEnumerator routine);
void StopCoroutine(Coroutine routine);
}
}

View file

@ -1,42 +0,0 @@
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);
}
}

View file

@ -1,26 +0,0 @@
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;
}
}
}

View file

@ -1,24 +0,0 @@
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
{
}
}

View file

@ -1,62 +0,0 @@
using System;
using UnityEngine;
using UnityEngine.Networking;
namespace Mirror.Cloud
{
public static class Logger
{
public static bool VerboseLogging = false;
public static void LogRequest(string page, string method, bool hasJson, string json)
{
if (hasJson)
{
Debug.LogFormat("Request: {0} {1} {2}", method, page, json);
}
else
{
Debug.LogFormat("Request: {0} {1}", method, page);
}
}
public static void LogResponse(UnityWebRequest statusRequest)
{
long code = statusRequest.responseCode;
string format = "Response: {0} {1} {2} {3}";
// 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);
Debug.Log(msg);
if (!string.IsNullOrEmpty(statusRequest.error))
{
msg = string.Format("WEB REQUEST ERROR: {0}", statusRequest.error);
Debug.LogError(msg);
}
}
internal static void Log(string msg)
{
Debug.Log(msg);
}
internal static void LogWarning(string msg)
{
Debug.LogWarning(msg);
}
internal static void LogError(string msg)
{
Debug.LogError(msg);
}
internal static void Verbose(string msg)
{
if (VerboseLogging)
Debug.Log(msg);
}
}
}

View file

@ -1,144 +0,0 @@
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);
}
}
}
}
}

View file

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: c4c4be148a492b143a881cd08bf7e320
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -1,66 +0,0 @@
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();
}
}

View file

@ -1,9 +0,0 @@
namespace Mirror.Cloud.ListServerService
{
public abstract class ListServerBaseApi : BaseApi
{
protected ListServerBaseApi(ICoroutineRunner runner, IRequestCreator requestCreator) : base(runner, requestCreator)
{
}
}
}

View file

@ -1,70 +0,0 @@
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 already 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);
}
}
}
}

View file

@ -1,207 +0,0 @@
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);
}
}
}
}

View file

@ -1,219 +0,0 @@
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 in case 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, stopping 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 because serverId was empty");
return;
}
UnityWebRequest request = requestCreator.Delete("servers/" + serverId);
UnityWebRequestAsyncOperation operation = request.SendWebRequest();
operation.completed += (op) =>
{
Logger.LogResponse(request);
};
}
}
}

View file

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: c21ba7b8c3183cb47b7fe3b3799d49c4
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -1,152 +0,0 @@
# 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.

View file

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 04945d14ccbed964597a1ee00805c059
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -1 +0,0 @@
MirrorCloudServices v0.1.0

View file

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: bf81e376b88e68e48a47531b8bfeb0f4
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -3,6 +3,6 @@ guid: 1f8b918bcd89f5c488b06f5574f34760
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -2,6 +2,6 @@ fileFormatVersion: 2
guid: 325984b52e4128546bc7558552f8b1d2
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -27,7 +27,11 @@ namespace Mirror
"MIRROR_32_1_2_OR_NEWER",
"MIRROR_32_1_4_OR_NEWER",
"MIRROR_35_0_OR_NEWER",
"MIRROR_35_1_OR_NEWER"
"MIRROR_35_1_OR_NEWER",
"MIRROR_37_0_OR_NEWER",
"MIRROR_38_0_OR_NEWER",
"MIRROR_39_0_OR_NEWER",
"MIRROR_40_0_OR_NEWER"
};
// only touch PlayerSettings if we actually modified it.

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -3,6 +3,6 @@ guid: 9bee879fbc8ef4b1a9a9f7088bfbf726
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -3,6 +3,6 @@ guid: b5dcf9618f5e14a4eb60bff5480284a6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -16,7 +16,7 @@ namespace Mirror.Discovery
/// <see cref="NetworkDiscovery">NetworkDiscovery</see> for a sample implementation
/// </summary>
[DisallowMultipleComponent]
[HelpURL("https://mirror-networking.com/docs/Articles/Components/NetworkDiscovery.html")]
[HelpURL("https://mirror-networking.gitbook.io/docs/components/network-discovery")]
public abstract class NetworkDiscoveryBase<Request, Response> : MonoBehaviour
where Request : NetworkMessage
where Response : NetworkMessage
@ -75,6 +75,19 @@ namespace Mirror.Discovery
// Ensure the ports are cleared no matter when Game/Unity UI exits
void OnApplicationQuit()
{
//Debug.Log("NetworkDiscoveryBase OnApplicationQuit");
Shutdown();
}
void OnDisable()
{
//Debug.Log("NetworkDiscoveryBase OnDisable");
Shutdown();
}
void OnDestroy()
{
//Debug.Log("NetworkDiscoveryBase OnDestroy");
Shutdown();
}
@ -162,7 +175,7 @@ namespace Mirror.Discovery
using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(udpReceiveResult.Buffer))
{
long handshake = networkReader.ReadInt64();
long handshake = networkReader.ReadLong();
if (handshake != secretHandshake)
{
// message is not for us
@ -195,7 +208,7 @@ namespace Mirror.Discovery
{
try
{
writer.WriteInt64(secretHandshake);
writer.WriteLong(secretHandshake);
writer.Write(info);
@ -249,6 +262,7 @@ namespace Mirror.Discovery
catch (Exception)
{
// Free the port if we took it
//Debug.LogError("NetworkDiscoveryBase StartDiscovery Exception");
Shutdown();
throw;
}
@ -263,6 +277,7 @@ namespace Mirror.Discovery
/// </summary>
public void StopDiscovery()
{
//Debug.Log("NetworkDiscoveryBase StopDiscovery");
Shutdown();
}
@ -298,11 +313,17 @@ namespace Mirror.Discovery
if (clientUdpClient == null)
return;
if (NetworkClient.isConnected)
{
StopDiscovery();
return;
}
IPEndPoint endPoint = new IPEndPoint(IPAddress.Broadcast, serverBroadcastListenPort);
using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
{
writer.WriteInt64(secretHandshake);
writer.WriteLong(secretHandshake);
try
{
@ -339,7 +360,7 @@ namespace Mirror.Discovery
using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(udpReceiveResult.Buffer))
{
if (networkReader.ReadInt64() != secretHandshake)
if (networkReader.ReadLong() != secretHandshake)
return;
Response response = networkReader.Read<Response>();

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -5,7 +5,7 @@ namespace Mirror.Discovery
{
[DisallowMultipleComponent]
[AddComponentMenu("Network/NetworkDiscoveryHUD")]
[HelpURL("https://mirror-networking.com/docs/Articles/Components/NetworkDiscovery.html")]
[HelpURL("https://mirror-networking.gitbook.io/docs/components/network-discovery")]
[RequireComponent(typeof(NetworkDiscovery))]
public class NetworkDiscoveryHUD : MonoBehaviour
{
@ -31,15 +31,16 @@ namespace Mirror.Discovery
if (NetworkManager.singleton == null)
return;
if (NetworkServer.active || NetworkClient.active)
return;
if (!NetworkClient.isConnected && !NetworkServer.active && !NetworkClient.active)
DrawGUI();
if (NetworkServer.active || NetworkClient.active)
StopButtons();
}
void DrawGUI()
{
GUILayout.BeginArea(new Rect(10, 10, 300, 500));
GUILayout.BeginHorizontal();
if (GUILayout.Button("Find Servers"))
@ -79,6 +80,39 @@ namespace Mirror.Discovery
Connect(info);
GUILayout.EndScrollView();
GUILayout.EndArea();
}
void StopButtons()
{
GUILayout.BeginArea(new Rect(10, 40, 100, 25));
// stop host if host mode
if (NetworkServer.active && NetworkClient.isConnected)
{
if (GUILayout.Button("Stop Host"))
{
NetworkManager.singleton.StopHost();
}
}
// stop client if client-only
else if (NetworkClient.isConnected)
{
if (GUILayout.Button("Stop Client"))
{
NetworkManager.singleton.StopClient();
}
}
// stop server if server-only
else if (NetworkServer.active)
{
if (GUILayout.Button("Stop Server"))
{
NetworkManager.singleton.StopServer();
}
}
GUILayout.EndArea();
}
void Connect(ServerResponse info)

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -3,6 +3,6 @@ guid: bfbf2a1f2b300c5489dcab219ef2846e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -3,7 +3,7 @@ using UnityEngine;
namespace Mirror.Experimental
{
[AddComponentMenu("Network/Experimental/NetworkLerpRigidbody")]
[HelpURL("https://mirror-networking.com/docs/Articles/Components/NetworkLerpRigidbody.html")]
[HelpURL("https://mirror-networking.gitbook.io/docs/components/network-lerp-rigidbody")]
public class NetworkLerpRigidbody : NetworkBehaviour
{
[Header("Settings")]

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -3,7 +3,7 @@ using UnityEngine;
namespace Mirror.Experimental
{
[AddComponentMenu("Network/Experimental/NetworkRigidbody")]
[HelpURL("https://mirror-networking.com/docs/Articles/Components/NetworkRigidbody.html")]
[HelpURL("https://mirror-networking.gitbook.io/docs/components/network-rigidbody")]
public class NetworkRigidbody : NetworkBehaviour
{
[Header("Settings")]

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -4,7 +4,7 @@ namespace Mirror.Experimental
{
[DisallowMultipleComponent]
[AddComponentMenu("Network/Experimental/NetworkTransformExperimental")]
[HelpURL("https://mirror-networking.com/docs/Articles/Components/NetworkTransform.html")]
[HelpURL("https://mirror-networking.gitbook.io/docs/components/network-transform")]
public class NetworkTransform : NetworkTransformBase
{
protected override Transform targetTransform => transform;

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -7,7 +7,7 @@ namespace Mirror.Experimental
/// <para>There must be a NetworkTransform on the root object of the hierarchy. There can be multiple NetworkTransformChild components on an object. This does not use physics for synchronization, it simply synchronizes the localPosition and localRotation of the child transform and lerps towards the received values.</para>
/// </summary>
[AddComponentMenu("Network/Experimental/NetworkTransformChildExperimentalExperimental")]
[HelpURL("https://mirror-networking.com/docs/Articles/Components/NetworkTransformChild.html")]
[HelpURL("https://mirror-networking.gitbook.io/docs/components/network-transform-child")]
public class NetworkTransformChild : NetworkTransformBase
{
[Header("Target")]

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -3,6 +3,6 @@ guid: c66f27e006ab94253b39a55a3b213651
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -1,3 +1,3 @@
fileFormatVersion: 2
fileFormatVersion: 2
guid: fa4cbc6b9c584db4971985cb9f369077
timeCreated: 1613110605

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -1,3 +1,3 @@
fileFormatVersion: 2
fileFormatVersion: 2
guid: cfa12b73503344d49b398b01bcb07967
timeCreated: 1613110634

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -2,6 +2,6 @@ fileFormatVersion: 2
guid: 72872094b21c16e48b631b2224833d49
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -15,7 +15,7 @@ namespace Mirror
/// </remarks>
[AddComponentMenu("Network/NetworkAnimator")]
[RequireComponent(typeof(NetworkIdentity))]
[HelpURL("https://mirror-networking.com/docs/Articles/Components/NetworkAnimator.html")]
[HelpURL("https://mirror-networking.gitbook.io/docs/components/network-animator")]
public class NetworkAnimator : NetworkBehaviour
{
[Header("Authority")]
@ -311,7 +311,7 @@ namespace Mirror
bool WriteParameters(NetworkWriter writer, bool forceAll = false)
{
ulong dirtyBits = forceAll ? (~0ul) : NextDirtyBits();
writer.WriteUInt64(dirtyBits);
writer.WriteULong(dirtyBits);
for (int i = 0; i < parameters.Length; i++)
{
if ((dirtyBits & (1ul << i)) == 0)
@ -321,17 +321,17 @@ namespace Mirror
if (par.type == AnimatorControllerParameterType.Int)
{
int newIntValue = animator.GetInteger(par.nameHash);
writer.WriteInt32(newIntValue);
writer.WriteInt(newIntValue);
}
else if (par.type == AnimatorControllerParameterType.Float)
{
float newFloatValue = animator.GetFloat(par.nameHash);
writer.WriteSingle(newFloatValue);
writer.WriteFloat(newFloatValue);
}
else if (par.type == AnimatorControllerParameterType.Bool)
{
bool newBoolValue = animator.GetBool(par.nameHash);
writer.WriteBoolean(newBoolValue);
writer.WriteBool(newBoolValue);
}
}
return dirtyBits != 0;
@ -342,7 +342,7 @@ namespace Mirror
bool animatorEnabled = animator.enabled;
// need to read values from NetworkReader even if animator is disabled
ulong dirtyBits = reader.ReadUInt64();
ulong dirtyBits = reader.ReadULong();
for (int i = 0; i < parameters.Length; i++)
{
if ((dirtyBits & (1ul << i)) == 0)
@ -351,19 +351,19 @@ namespace Mirror
AnimatorControllerParameter par = parameters[i];
if (par.type == AnimatorControllerParameterType.Int)
{
int newIntValue = reader.ReadInt32();
int newIntValue = reader.ReadInt();
if (animatorEnabled)
animator.SetInteger(par.nameHash, newIntValue);
}
else if (par.type == AnimatorControllerParameterType.Float)
{
float newFloatValue = reader.ReadSingle();
float newFloatValue = reader.ReadFloat();
if (animatorEnabled)
animator.SetFloat(par.nameHash, newFloatValue);
}
else if (par.type == AnimatorControllerParameterType.Bool)
{
bool newBoolValue = reader.ReadBoolean();
bool newBoolValue = reader.ReadBool();
if (animatorEnabled)
animator.SetBool(par.nameHash, newBoolValue);
}
@ -386,16 +386,16 @@ namespace Mirror
if (animator.IsInTransition(i))
{
AnimatorStateInfo st = animator.GetNextAnimatorStateInfo(i);
writer.WriteInt32(st.fullPathHash);
writer.WriteSingle(st.normalizedTime);
writer.WriteInt(st.fullPathHash);
writer.WriteFloat(st.normalizedTime);
}
else
{
AnimatorStateInfo st = animator.GetCurrentAnimatorStateInfo(i);
writer.WriteInt32(st.fullPathHash);
writer.WriteSingle(st.normalizedTime);
writer.WriteInt(st.fullPathHash);
writer.WriteFloat(st.normalizedTime);
}
writer.WriteSingle(animator.GetLayerWeight(i));
writer.WriteFloat(animator.GetLayerWeight(i));
}
WriteParameters(writer, initialState);
return true;
@ -415,9 +415,9 @@ namespace Mirror
{
for (int i = 0; i < animator.layerCount; i++)
{
int stateHash = reader.ReadInt32();
float normalizedTime = reader.ReadSingle();
animator.SetLayerWeight(i, reader.ReadSingle());
int stateHash = reader.ReadInt();
float normalizedTime = reader.ReadFloat();
animator.SetLayerWeight(i, reader.ReadFloat());
animator.Play(stateHash, i, normalizedTime);
}

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -12,7 +12,7 @@ namespace Mirror
/// <para>The OnLobby*() functions have empty implementations on the NetworkLobbyManager base class, so the base class functions do not have to be called.</para>
/// </remarks>
[AddComponentMenu("Network/NetworkLobbyManager")]
[HelpURL("https://mirror-networking.com/docs/Articles/Components/NetworkRoomManager.html")]
[HelpURL("https://mirror-networking.gitbook.io/docs/components/network-room-manager")]
[Obsolete("Use / inherit from NetworkRoomManager instead")]
public class NetworkLobbyManager : NetworkRoomManager {}
}

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -9,7 +9,7 @@ namespace Mirror
/// </summary>
[DisallowMultipleComponent]
[AddComponentMenu("Network/NetworkLobbyPlayer")]
[HelpURL("https://mirror-networking.com/docs/Articles/Components/NetworkRoomPlayer.html")]
[HelpURL("https://mirror-networking.gitbook.io/docs/components/network-room-player")]
[Obsolete("Use / inherit from NetworkRoomPlayer instead")]
public class NetworkLobbyPlayer : NetworkRoomPlayer {}
}

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -13,12 +13,15 @@ namespace Mirror
[DisallowMultipleComponent]
[AddComponentMenu("Network/NetworkMatchChecker")]
[RequireComponent(typeof(NetworkIdentity))]
[HelpURL("https://mirror-networking.com/docs/Articles/Components/NetworkMatchChecker.html")]
[HelpURL("https://mirror-networking.gitbook.io/docs/components/network-match-checker")]
public class NetworkMatchChecker : NetworkVisibility
{
static readonly Dictionary<Guid, HashSet<NetworkIdentity>> matchPlayers = new Dictionary<Guid, HashSet<NetworkIdentity>>();
// internal for tests
internal static readonly Dictionary<Guid, HashSet<NetworkIdentity>> matchPlayers =
new Dictionary<Guid, HashSet<NetworkIdentity>>();
Guid currentMatch = Guid.Empty;
// internal for tests
internal Guid currentMatch = Guid.Empty;
[Header("Diagnostics")]
[SyncVar]

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -13,7 +13,7 @@ namespace Mirror
[DisallowMultipleComponent]
[AddComponentMenu("Network/NetworkOwnerChecker")]
[RequireComponent(typeof(NetworkIdentity))]
[HelpURL("https://mirror-networking.com/docs/Components/NetworkOwnerChecker.html")]
[HelpURL("https://mirror-networking.gitbook.io/docs/components/network-owner-checker")]
public class NetworkOwnerChecker : NetworkVisibility
{
/// <summary>

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -8,7 +8,7 @@ namespace Mirror
/// </summary>
[DisallowMultipleComponent]
[AddComponentMenu("Network/NetworkPingDisplay")]
[HelpURL("https://mirror-networking.com/docs/Articles/Components/NetworkPingDisplay.html")]
[HelpURL("https://mirror-networking.gitbook.io/docs/components/network-ping-display")]
public class NetworkPingDisplay : MonoBehaviour
{
public Color color = Color.white;

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -11,7 +11,7 @@ namespace Mirror
[Obsolete(NetworkVisibilityObsoleteMessage.Message)]
[AddComponentMenu("Network/NetworkProximityChecker")]
[RequireComponent(typeof(NetworkIdentity))]
[HelpURL("https://mirror-networking.com/docs/Articles/Components/NetworkProximityChecker.html")]
[HelpURL("https://mirror-networking.gitbook.io/docs/components/network-proximity-checker")]
public class NetworkProximityChecker : NetworkVisibility
{
/// <summary>
@ -30,6 +30,7 @@ namespace Mirror
/// Flag to force this object to be hidden for players.
/// <para>If this object is a player object, it will not be hidden for that player.</para>
/// </summary>
// Deprecated 2021-02-17
[Obsolete("Use NetworkIdentity.visible mode instead of forceHidden!")]
public bool forceHidden
{

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -14,7 +14,7 @@ namespace Mirror
/// <para>The OnRoom*() functions have empty implementations on the NetworkRoomManager base class, so the base class functions do not have to be called.</para>
/// </remarks>
[AddComponentMenu("Network/NetworkRoomManager")]
[HelpURL("https://mirror-networking.com/docs/Articles/Components/NetworkRoomManager.html")]
[HelpURL("https://mirror-networking.gitbook.io/docs/components/network-room-manager")]
public class NetworkRoomManager : NetworkManager
{
public struct PendingPlayer
@ -297,6 +297,11 @@ namespace Mirror
OnRoomServerDisconnect(conn);
base.OnServerDisconnect(conn);
#if UNITY_SERVER
if (numPlayers < 1)
StopServer();
#endif
}
// Sequential index used in round-robin deployment of players into instances and score positioning
@ -439,9 +444,9 @@ namespace Mirror
OnRoomStopHost();
}
#endregion
#endregion
#region client handlers
#region client handlers
/// <summary>
/// This is invoked when the client is started.
@ -510,9 +515,9 @@ namespace Mirror
OnRoomClientSceneChanged(conn);
}
#endregion
#endregion
#region room server virtuals
#region room server virtuals
/// <summary>
/// This is called on the host when a host is started.
@ -616,9 +621,9 @@ namespace Mirror
/// </summary>
public virtual void OnRoomServerPlayersNotReady() {}
#endregion
#endregion
#region room client virtuals
#region room client virtuals
/// <summary>
/// This is a hook to allow custom behaviour when the game client enters the room.
@ -665,9 +670,9 @@ namespace Mirror
/// </summary>
public virtual void OnRoomClientAddPlayerFailed() {}
#endregion
#endregion
#region optional UI
#region optional UI
/// <summary>
/// virtual so inheriting classes can roll their own
@ -689,6 +694,6 @@ namespace Mirror
GUI.Box(new Rect(10f, 180f, 520f, 150f), "PLAYERS");
}
#endregion
#endregion
}
}

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -8,7 +8,7 @@ namespace Mirror
/// </summary>
[DisallowMultipleComponent]
[AddComponentMenu("Network/NetworkRoomPlayer")]
[HelpURL("https://mirror-networking.com/docs/Articles/Components/NetworkRoomPlayer.html")]
[HelpURL("https://mirror-networking.gitbook.io/docs/components/network-room-player")]
public class NetworkRoomPlayer : NetworkBehaviour
{
/// <summary>

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -14,7 +14,7 @@ namespace Mirror
[DisallowMultipleComponent]
[AddComponentMenu("Network/NetworkSceneChecker")]
[RequireComponent(typeof(NetworkIdentity))]
[HelpURL("https://mirror-networking.com/docs/Articles/Components/NetworkSceneChecker.html")]
[HelpURL("https://mirror-networking.gitbook.io/docs/components/network-scene-checker")]
public class NetworkSceneChecker : NetworkVisibility
{
/// <summary>

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -4,7 +4,7 @@ namespace Mirror
{
[DisallowMultipleComponent]
[AddComponentMenu("Network/NetworkTransform")]
[HelpURL("https://mirror-networking.com/docs/Articles/Components/NetworkTransform.html")]
[HelpURL("https://mirror-networking.gitbook.io/docs/components/network-transform")]
public class NetworkTransform : NetworkTransformBase
{
protected override Transform targetComponent => transform;

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -53,6 +53,10 @@ namespace Mirror
[Header("Interpolation")]
[Tooltip("Set to true if scale should be interpolated, false is ideal for instant sprite flipping.")]
public bool interpolateScale = true;
[Tooltip("Set to true if rotation should be interpolated, false is ideal for instant turning, common in retro 2d style games")]
public bool interpolateRotation = true;
[Tooltip("Set to true if position should be interpolated, false is ideal for grid bassed movement")]
public bool interpolatePosition = true;
[Header("Synchronization")]
// It should be very rare cases that people want to continuously sync scale, true by default to not break previous projects that use it
@ -96,7 +100,7 @@ namespace Mirror
if (compressRotation)
{
// smalles three compression for 3D
writer.WriteUInt32(Compression.CompressQuaternion(rotation));
writer.WriteUInt(Compression.CompressQuaternion(rotation));
}
else
{
@ -136,7 +140,7 @@ namespace Mirror
// (rotation is optionally compressed)
localPosition = reader.ReadVector3(),
localRotation = compressRotation
? Compression.DecompressQuaternion(reader.ReadUInt32())
? Compression.DecompressQuaternion(reader.ReadUInt())
: reader.ReadQuaternion(),
// use current target scale, so we can check boolean and reader later, to see if the data is actually sent.
localScale = targetComponent.localScale,
@ -265,9 +269,13 @@ namespace Mirror
return 0;
}
static Vector3 InterpolatePosition(DataPoint start, DataPoint goal, Vector3 currentPosition)
Vector3 InterpolatePosition(DataPoint start, DataPoint goal, Vector3 currentPosition)
{
if (start != null)
if (!interpolatePosition)
{
return goal.localPosition;
}
else if (start != null)
{
// Option 1: simply interpolate based on time. but stutter
// will happen, it's not that smooth. especially noticeable if
@ -284,9 +292,13 @@ namespace Mirror
return currentPosition;
}
static Quaternion InterpolateRotation(DataPoint start, DataPoint goal, Quaternion defaultRotation)
Quaternion InterpolateRotation(DataPoint start, DataPoint goal, Quaternion defaultRotation)
{
if (start != null)
if (!interpolateRotation)
{
return goal.localRotation;
}
else if (start != null)
{
float t = CurrentInterpolationFactor(start, goal);
return Quaternion.Slerp(start.localRotation, goal.localRotation, t);
@ -296,7 +308,15 @@ namespace Mirror
Vector3 InterpolateScale(DataPoint start, DataPoint goal, Vector3 currentScale)
{
if (start != null && interpolateScale)
if (!syncScale)
{
return currentScale;
}
else if (!interpolateScale)
{
return goal.localScale;
}
else if (interpolateScale && start != null )
{
float t = CurrentInterpolationFactor(start, goal);
return Vector3.Lerp(start.localScale, goal.localScale, t);

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -7,7 +7,7 @@ namespace Mirror
/// <para>There must be a NetworkTransform on the root object of the hierarchy. There can be multiple NetworkTransformChild components on an object. This does not use physics for synchronization, it simply synchronizes the localPosition and localRotation of the child transform and lerps towards the received values.</para>
/// </summary>
[AddComponentMenu("Network/NetworkTransformChild")]
[HelpURL("https://mirror-networking.com/docs/Articles/Components/NetworkTransformChild.html")]
[HelpURL("https://mirror-networking.gitbook.io/docs/components/network-transform-child")]
public class NetworkTransformChild : NetworkTransformBase
{
[Header("Target")]

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -3,6 +3,6 @@ guid: 2539267b6934a4026a505690a1e1eda2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 62c8dc5bb12bbc6428bb66ccbac57000
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -3,6 +3,6 @@ guid: 4d97731cd74ac8b4b8aad808548ef9cd
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

View file

@ -6,6 +6,6 @@ MonoImporter:
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
userData: ''
assetBundleName: ''
assetBundleVariant: ''

Some files were not shown because too many files have changed in this diff Show more