diff --git a/LoadBalancerProject-DONT-IMPORT-INTO-UNITY/LRM_LoadBalancer/DataContainer.cs b/LoadBalancerProject-DONT-IMPORT-INTO-UNITY/LRM_LoadBalancer/DataContainer.cs index 06cd016..71217b1 100644 --- a/LoadBalancerProject-DONT-IMPORT-INTO-UNITY/LRM_LoadBalancer/DataContainer.cs +++ b/LoadBalancerProject-DONT-IMPORT-INTO-UNITY/LRM_LoadBalancer/DataContainer.cs @@ -8,10 +8,10 @@ namespace LightReflectiveMirror.LoadBalancing [Serializable] public struct RelayServerInfo { - public int ConnectedClients; - public int RoomCount; - public int PublicRoomCount; - public TimeSpan Uptime; + public int connectedClients; + public int roomCount; + public int publicRoomCount; + public TimeSpan uptime; [JsonIgnore] public List serversConnectedToRelay; @@ -20,21 +20,22 @@ namespace LightReflectiveMirror.LoadBalancing [Serializable] internal struct LoadBalancerStats { - public int NodeCount; - public TimeSpan Uptime; + public int nodeCount; + public TimeSpan uptime; public long CCU; - public long TotalServerCount; + public long totalServerCount; } // container for relay address info [JsonObject(MemberSerialization.OptOut)] public struct RelayAddress { - public ushort Port; - public ushort EndpointPort; - public string Address; + public ushort port; + public ushort endpointPort; + public string address; + public LRMRegions serverRegion; [JsonIgnore] - public string EndpointAddress; + public string endpointAddress; } [Serializable] @@ -50,4 +51,6 @@ namespace LightReflectiveMirror.LoadBalancing public RelayAddress relayInfo; } + + public enum LRMRegions { Any, NorthAmerica, SouthAmerica, Europe, Asia, Africa, Oceania } } diff --git a/LoadBalancerProject-DONT-IMPORT-INTO-UNITY/LRM_LoadBalancer/Endpoint.cs b/LoadBalancerProject-DONT-IMPORT-INTO-UNITY/LRM_LoadBalancer/Endpoint.cs index a1ef00d..6b0ee94 100644 --- a/LoadBalancerProject-DONT-IMPORT-INTO-UNITY/LRM_LoadBalancer/Endpoint.cs +++ b/LoadBalancerProject-DONT-IMPORT-INTO-UNITY/LRM_LoadBalancer/Endpoint.cs @@ -18,16 +18,30 @@ namespace LightReflectiveMirror.LoadBalancing [RestResource] public class Endpoint { - public static string cachedServerList = "[]"; + public static string allCachedServers = "[]"; + public static string NorthAmericaCachedServers = "[]"; + public static string SouthAmericaCachedServers = "[]"; + public static string EuropeCachedServers = "[]"; + public static string AsiaCachedServers = "[]"; + public static string AfricaCachedServers = "[]"; + public static string OceaniaCachedServers = "[]"; + + private static List northAmericaServers = new(); + private static List southAmericaServers = new(); + private static List europeServers = new(); + private static List africaServers = new(); + private static List asiaServers = new(); + private static List oceaniaServers = new(); + private static List allServers = new(); private LoadBalancerStats _stats { get => new() { - NodeCount = Program.instance.availableRelayServers.Count, - Uptime = DateTime.Now - Program.startupTime, + nodeCount = Program.instance.availableRelayServers.Count, + uptime = DateTime.Now - Program.startupTime, CCU = Program.instance.GetTotalCCU(), - TotalServerCount = Program.instance.GetTotalServers(), + totalServerCount = Program.instance.GetTotalServers(), }; } @@ -45,12 +59,14 @@ namespace LightReflectiveMirror.LoadBalancing string endpointPort = req.Headers["x-EndpointPort"]; string gamePort = req.Headers["x-GamePort"]; string publicIP = req.Headers["x-PIP"]; + string region = req.Headers["x-Region"]; + int regionId = 1; string address = context.Request.RemoteEndPoint.Address.ToString(); Logger.WriteLogMessage("Received auth req [" + receivedAuthKey + "] == [" + Program.conf.AuthKey + "]"); // if server is authenticated - if (receivedAuthKey != null && address != null && endpointPort != null && gamePort != null && receivedAuthKey == Program.conf.AuthKey) + if (receivedAuthKey != null && region != null && int.TryParse(region, out regionId) && address != null && endpointPort != null && gamePort != null && receivedAuthKey == Program.conf.AuthKey) { Logger.WriteLogMessage($"Server accepted: {address}:{gamePort}"); @@ -58,7 +74,7 @@ namespace LightReflectiveMirror.LoadBalancing { var _gamePort = Convert.ToUInt16(gamePort); var _endpointPort = Convert.ToUInt16(endpointPort); - await Program.instance.AddServer(address, _gamePort, _endpointPort, publicIP); + await Program.instance.AddServer(address, _gamePort, _endpointPort, publicIP, regionId); } catch { @@ -82,20 +98,66 @@ namespace LightReflectiveMirror.LoadBalancing // Dont allow unauthorizated access waste computing resources. string auth = context.Request.Headers["Authorization"]; - if(!string.IsNullOrEmpty(auth) && auth == Program.conf.AuthKey) + if (!string.IsNullOrEmpty(auth) && auth == Program.conf.AuthKey) { var relays = Program.instance.availableRelayServers.ToList(); - List masterList = new(); + ClearAllServersLists(); + List requestedRooms; - for(int i = 0; i < relays.Count; i++) + for (int i = 0; i < relays.Count; i++) { - masterList.AddRange(await Program.instance.RequestServerListFromNode(relays[i].Key.Address, relays[i].Key.EndpointPort)); + requestedRooms = await Program.instance.RequestServerListFromNode(relays[i].Key.address, relays[i].Key.endpointPort); + allServers.AddRange(requestedRooms); + + switch (relays[i].Key.serverRegion) + { + case (LRMRegions.NorthAmerica): + northAmericaServers.AddRange(requestedRooms); + break; + case (LRMRegions.SouthAmerica): + southAmericaServers.AddRange(requestedRooms); + break; + case (LRMRegions.Europe): + europeServers.AddRange(requestedRooms); + break; + case (LRMRegions.Africa): + africaServers.AddRange(requestedRooms); + break; + case (LRMRegions.Asia): + asiaServers.AddRange(requestedRooms); + break; + case (LRMRegions.Oceania): + oceaniaServers.AddRange(requestedRooms); + break; + } } - cachedServerList = JsonConvert.SerializeObject(masterList); + CacheAllServers(); } } + void CacheAllServers() + { + allCachedServers = JsonConvert.SerializeObject(allServers); + NorthAmericaCachedServers = JsonConvert.SerializeObject(northAmericaServers); + SouthAmericaCachedServers = JsonConvert.SerializeObject(southAmericaServers); + EuropeCachedServers = JsonConvert.SerializeObject(europeServers); + AsiaCachedServers = JsonConvert.SerializeObject(asiaServers); + AfricaCachedServers = JsonConvert.SerializeObject(africaServers); + OceaniaCachedServers = JsonConvert.SerializeObject(oceaniaServers); + } + + void ClearAllServersLists() + { + northAmericaServers.Clear(); + southAmericaServers.Clear(); + europeServers.Clear(); + asiaServers.Clear(); + africaServers.Clear(); + oceaniaServers.Clear(); + allServers.Clear(); + } + /// /// Hooks into from unity side, client will call this to /// find the least populated server to join @@ -119,7 +181,7 @@ namespace LightReflectiveMirror.LoadBalancing for (int i = 0; i < servers.Count; i++) { - if (servers[i].Value.ConnectedClients < lowest.Value.ConnectedClients) + if (servers[i].Value.connectedClients < lowest.Value.connectedClients) { lowest = servers[i]; } @@ -127,7 +189,7 @@ namespace LightReflectiveMirror.LoadBalancing // respond with the server ip // if the string is still dummy then theres no servers - if (lowest.Key.Address != "Dummy") + if (lowest.Key.address != "Dummy") { await context.Response.SendResponseAsync(JsonConvert.SerializeObject(lowest.Key)); } @@ -145,7 +207,40 @@ namespace LightReflectiveMirror.LoadBalancing [RestRoute("Get", "/api/masterlist/")] public async Task GetMasterServerList(IHttpContext context) { - await context.Response.SendResponseAsync(cachedServerList); + string region = context.Request.Headers["x-Region"]; + + if(int.TryParse(region, out int regionID)) + { + switch ((LRMRegions)regionID) + { + case LRMRegions.Any: + await context.Response.SendResponseAsync(allCachedServers); + break; + case LRMRegions.NorthAmerica: + await context.Response.SendResponseAsync(NorthAmericaCachedServers); + break; + case LRMRegions.SouthAmerica: + await context.Response.SendResponseAsync(SouthAmericaCachedServers); + break; + case LRMRegions.Europe: + await context.Response.SendResponseAsync(EuropeCachedServers); + break; + case LRMRegions.Africa: + await context.Response.SendResponseAsync(AfricaCachedServers); + break; + case LRMRegions.Asia: + await context.Response.SendResponseAsync(AsiaCachedServers); + break; + case LRMRegions.Oceania: + await context.Response.SendResponseAsync(OceaniaCachedServers); + break; + } + + return; + } + + // They didnt submit a region header, just give them all servers as they probably are viewing in browser. + await context.Response.SendResponseAsync(allCachedServers); } /// @@ -218,7 +313,7 @@ namespace LightReflectiveMirror.LoadBalancing bool hasLocal = false; - for(int i = 0; i < bindableIPv4Addresses.Count; i++) + for (int i = 0; i < bindableIPv4Addresses.Count; i++) { if (bindableIPv4Addresses[i] == "127.0.0.1") hasLocal = true; @@ -233,4 +328,4 @@ namespace LightReflectiveMirror.LoadBalancing } -} +} \ No newline at end of file diff --git a/LoadBalancerProject-DONT-IMPORT-INTO-UNITY/LRM_LoadBalancer/Program.cs b/LoadBalancerProject-DONT-IMPORT-INTO-UNITY/LRM_LoadBalancer/Program.cs index f80ba0f..38ec578 100644 --- a/LoadBalancerProject-DONT-IMPORT-INTO-UNITY/LRM_LoadBalancer/Program.cs +++ b/LoadBalancerProject-DONT-IMPORT-INTO-UNITY/LRM_LoadBalancer/Program.cs @@ -71,9 +71,9 @@ namespace LightReflectiveMirror.LoadBalancing /// /// /// - public async Task AddServer(string serverIP, ushort port, ushort endpointPort, string publicIP) + public async Task AddServer(string serverIP, ushort port, ushort endpointPort, string publicIP, int regionId) { - var relayAddr = new RelayAddress { Port = port, EndpointPort = endpointPort, Address = publicIP, EndpointAddress = serverIP }; + var relayAddr = new RelayAddress { port = port, endpointPort = endpointPort, address = publicIP, endpointAddress = serverIP.Trim(), serverRegion = (LRMRegions)regionId }; if (availableRelayServers.ContainsKey(relayAddr)) { @@ -187,13 +187,14 @@ namespace LightReflectiveMirror.LoadBalancing for (int i = 0; i < keys.Count; i++) { - if(!await HealthCheckNode(keys[i].EndpointAddress, keys[i].EndpointPort)) + if(!await HealthCheckNode(keys[i].endpointAddress, keys[i].endpointPort)) { - Logger.ForceLogMessage($"Server {keys[i].Address}:{keys[i].Port} failed a health check, removing from load balancer.", ConsoleColor.Red); + Logger.ForceLogMessage($"Server {keys[i].address}:{keys[i].port} failed a health check, removing from load balancer.", ConsoleColor.Red); availableRelayServers.Remove(keys[i]); } } + GC.Collect(); await Task.Delay(_pingDelay); } } @@ -229,5 +230,4 @@ namespace LightReflectiveMirror.LoadBalancing Logger.ForceLogMessage(load, ConsoleColor.Cyan); } } - } diff --git a/ServerProject-DONT-IMPORT-INTO-UNITY/LRM/Config.cs b/ServerProject-DONT-IMPORT-INTO-UNITY/LRM/Config.cs index 7136368..145f5c1 100644 --- a/ServerProject-DONT-IMPORT-INTO-UNITY/LRM/Config.cs +++ b/ServerProject-DONT-IMPORT-INTO-UNITY/LRM/Config.cs @@ -35,5 +35,6 @@ namespace LightReflectiveMirror public string LoadBalancerAuthKey = "AuthKey"; public string LoadBalancerAddress = "127.0.0.1"; public ushort LoadBalancerPort = 7070; + public LRMRegions LoadBalancerRegion = LRMRegions.NorthAmerica; } } diff --git a/ServerProject-DONT-IMPORT-INTO-UNITY/LRM/LRM.csproj b/ServerProject-DONT-IMPORT-INTO-UNITY/LRM/LRM.csproj index 8ddfbee..73fbd6f 100644 --- a/ServerProject-DONT-IMPORT-INTO-UNITY/LRM/LRM.csproj +++ b/ServerProject-DONT-IMPORT-INTO-UNITY/LRM/LRM.csproj @@ -22,4 +22,19 @@ + + + True + True + Resources.resx + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + diff --git a/ServerProject-DONT-IMPORT-INTO-UNITY/LRM/Program/Program.cs b/ServerProject-DONT-IMPORT-INTO-UNITY/LRM/Program/Program.cs index 695f86a..a7a9152 100644 --- a/ServerProject-DONT-IMPORT-INTO-UNITY/LRM/Program/Program.cs +++ b/ServerProject-DONT-IMPORT-INTO-UNITY/LRM/Program/Program.cs @@ -225,13 +225,14 @@ namespace LightReflectiveMirror var uri = new Uri($"http://{conf.LoadBalancerAddress}:{conf.LoadBalancerPort}/api/auth"); string endpointPort = conf.EndpointPort.ToString(); - string gamePort = 7777.ToString(); + string gamePort = "7777"; HttpWebRequest authReq = (HttpWebRequest)WebRequest.Create(uri); authReq.Headers.Add("Authorization", conf.LoadBalancerAuthKey); authReq.Headers.Add("x-EndpointPort", endpointPort); authReq.Headers.Add("x-GamePort", gamePort); authReq.Headers.Add("x-PIP", publicIP); // Public IP + authReq.Headers.Add("x-Region", ((int)conf.LoadBalancerRegion).ToString()); var res = await authReq.GetResponseAsync(); diff --git a/ServerProject-DONT-IMPORT-INTO-UNITY/LRM/Program/ProgramVariables.cs b/ServerProject-DONT-IMPORT-INTO-UNITY/LRM/Program/ProgramVariables.cs index 7376991..9addd44 100644 --- a/ServerProject-DONT-IMPORT-INTO-UNITY/LRM/Program/ProgramVariables.cs +++ b/ServerProject-DONT-IMPORT-INTO-UNITY/LRM/Program/ProgramVariables.cs @@ -34,4 +34,6 @@ namespace LightReflectiveMirror private readonly string CONFIG_PATH = System.Environment.GetEnvironmentVariable("LRM_CONFIG_PATH") ?? "config.json"; } + + public enum LRMRegions { Any, NorthAmerica, SouthAmerica, Europe, Asia, Africa, Oceania } } diff --git a/ServerProject-DONT-IMPORT-INTO-UNITY/LRM/RelayHandler/RelayHandlerRoomMethods.cs b/ServerProject-DONT-IMPORT-INTO-UNITY/LRM/RelayHandler/RelayHandlerRoomMethods.cs index 6fe602f..e87c60f 100644 --- a/ServerProject-DONT-IMPORT-INTO-UNITY/LRM/RelayHandler/RelayHandlerRoomMethods.cs +++ b/ServerProject-DONT-IMPORT-INTO-UNITY/LRM/RelayHandler/RelayHandlerRoomMethods.cs @@ -132,10 +132,10 @@ namespace LightReflectiveMirror serverData = serverData, clients = new List(), - // hard coded for now REMEMBER TO UN-HARDCODE RETARD + // hard coded for now REMEMBER TO UN-HARDCODE // this is needed for load balancer to know which server this room // belongs to - relayInfo = new RelayAddress { Address = Program.publicIP, Port = 7777, EndpointPort = Program.conf.EndpointPort }, + relayInfo = new RelayAddress { address = Program.publicIP, port = 7777, endpointPort = Program.conf.EndpointPort }, serverId = GetRandomServerID(), hostIP = hostIP, diff --git a/ServerProject-DONT-IMPORT-INTO-UNITY/LRM/Room.cs b/ServerProject-DONT-IMPORT-INTO-UNITY/LRM/Room.cs index 48acb38..aff389e 100644 --- a/ServerProject-DONT-IMPORT-INTO-UNITY/LRM/Room.cs +++ b/ServerProject-DONT-IMPORT-INTO-UNITY/LRM/Room.cs @@ -33,8 +33,8 @@ namespace LightReflectiveMirror [Serializable] public struct RelayAddress { - public ushort Port; - public ushort EndpointPort; - public string Address; + public ushort port; + public ushort endpointPort; + public string address; } } diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/LRM/Editor/LRMInspector.cs b/UnityProject/Assets/Mirror/Runtime/Transport/LRM/Editor/LRMInspector.cs index 8b05a26..feb6f91 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/LRM/Editor/LRMInspector.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transport/LRM/Editor/LRMInspector.cs @@ -152,22 +152,23 @@ namespace LightReflectiveMirror } } } - else if(lrm.NATPunchtroughPort < 0) + else if (lrm.NATPunchtroughPort < 0) { // NAT Punchthrough configuration. EditorGUILayout.HelpBox("Do you wish to use NAT Punchthrough? This can reduce load by up to 80% on your LRM nodes, but exposes players IP's to other players.", MessageType.None); - if(GUILayout.Button("Use NAT Punchthrough")) + if (GUILayout.Button("Use NAT Punchthrough")) { lrm.NATPunchtroughPort = 1; lrm.useNATPunch = true; lrm.gameObject.AddComponent(); } - if(GUILayout.Button("Do NOT use NAT Punchthrough")) + if (GUILayout.Button("Do NOT use NAT Punchthrough")) lrm.NATPunchtroughPort = 1; - }else if(directModule != null && directModule.directConnectTransport == null) + } + else if (directModule != null && directModule.directConnectTransport == null) { // NAT Punchthrough setup. EditorGUILayout.HelpBox("To use direct connecting, we need a transport to communicate with the other clients. Please select a transport to use.", MessageType.None); @@ -217,7 +218,7 @@ namespace LightReflectiveMirror break; case 1: // NAT punch tab. - if(directModule == null) + if (directModule == null) { EditorGUILayout.HelpBox("If you wish to use NAT punch, you will need to add a \"Direct Connect Module\" to this gameobject.", MessageType.Info); } @@ -230,8 +231,14 @@ namespace LightReflectiveMirror case 2: // Load balancer tab lrm.useLoadBalancer = EditorGUILayout.Toggle("Use Load Balancer", lrm.useLoadBalancer); + if (!lrm.useLoadBalancer) + GUI.enabled = false; lrm.loadBalancerAddress = EditorGUILayout.TextField("Load Balancer Address", lrm.loadBalancerAddress); lrm.loadBalancerPort = (ushort)Mathf.Clamp(EditorGUILayout.IntField("Load Balancer Port", lrm.loadBalancerPort), ushort.MinValue, ushort.MaxValue); + lrm.region = (LRMRegions)EditorGUILayout.EnumPopup("Node Region", lrm.region); + if (!lrm.useLoadBalancer) + GUI.enabled = true; + break; case 3: // Other tab... diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/LRM/LRMTransport/LRMTransportRequests.cs b/UnityProject/Assets/Mirror/Runtime/Transport/LRM/LRMTransport/LRMTransportRequests.cs index 453aa55..7a6164e 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/LRM/LRMTransport/LRMTransportRequests.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transport/LRM/LRMTransport/LRMTransportRequests.cs @@ -148,6 +148,7 @@ namespace LightReflectiveMirror using (UnityWebRequest webRequest = UnityWebRequest.Get(uri)) { + webRequest.SetRequestHeader("x-Region", ((int)region).ToString()); // Request and wait for the desired page. yield return webRequest.SendWebRequest(); var result = webRequest.downloadHandler.text; diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/LRM/LRMTransport/LRMTransportVariables.cs b/UnityProject/Assets/Mirror/Runtime/Transport/LRM/LRMTransport/LRMTransportVariables.cs index 02338b5..70b0042 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/LRM/LRMTransport/LRMTransportVariables.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transport/LRM/LRMTransport/LRMTransportVariables.cs @@ -42,6 +42,7 @@ namespace LightReflectiveMirror private LRMDirectConnectModule _directConnectModule; + public LRMRegions region = LRMRegions.NorthAmerica; private byte[] _clientSendBuffer; private bool _connectedToRelay = false; private bool _isClient = false; @@ -61,4 +62,6 @@ namespace LightReflectiveMirror private BiDictionary _connectedRelayClients = new BiDictionary(); private BiDictionary _connectedDirectClients = new BiDictionary(); } + + public enum LRMRegions { Any, NorthAmerica, SouthAmerica, Europe, Asia, Africa, Oceania } } \ No newline at end of file