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; /// /// optional /// public string displayName; /// /// Uri string of the ip and port of the server. /// The ip is calculated by the request to the API /// This is returns from the api, any incoming address fields will be ignored /// public string address; /// /// Can be used to set custom uri /// optional /// public string customAddress; /// /// Array of custom data, use SetCustomData to set values /// optional /// public KeyValue[] customData; /// /// Uri from address field /// /// public Uri GetServerUri() => new Uri(address); /// /// Uri from customAddress field /// /// public Uri GetCustomUri() => new Uri(customAddress); /// /// Updates the customData array /// /// public void SetCustomData(Dictionary 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 { /// /// optional /// public int playerCount; /// /// optional /// public int maxPlayerCount; /// /// optional /// public string displayName; /// /// Array of custom data, use SetCustomData to set values /// optional /// public KeyValue[] customData; public void SetCustomData(Dictionary 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 ToDictionary(this KeyValue[] keyValues) { return keyValues.ToDictionary(x => x.key, x => x.value); } public static KeyValue[] ToKeyValueArray(this Dictionary 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); } } } }