Master List stuff

This commit is contained in:
cxxpxr 2021-04-05 23:00:41 -04:00
parent bfa2785e22
commit f626321dc6
6 changed files with 164 additions and 21 deletions

View file

@ -1,4 +1,4 @@
using Grapevine; using Grapevine;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -33,7 +33,7 @@ namespace LightReflectiveMirror.LoadBalancing
string address = context.Request.RemoteEndPoint.Address.ToString(); string address = context.Request.RemoteEndPoint.Address.ToString();
Console.WriteLine("Received auth req [" + receivedAuthKey + "] == [" + Program.conf.AuthKey+"]"); Console.WriteLine("Received auth req [" + receivedAuthKey + "] == [" + Program.conf.AuthKey + "]");
// if server is authenticated // if server is authenticated
if (receivedAuthKey != null && address != null && endpointPort != null && gamePort != null && receivedAuthKey == Program.conf.AuthKey) if (receivedAuthKey != null && address != null && endpointPort != null && gamePort != null && receivedAuthKey == Program.conf.AuthKey)
@ -62,13 +62,13 @@ namespace LightReflectiveMirror.LoadBalancing
// collection being modified while iterating. // collection being modified while iterating.
var servers = Program.instance.availableRelayServers.ToList(); var servers = Program.instance.availableRelayServers.ToList();
if(servers.Count == 0) if (servers.Count == 0)
{ {
await context.Response.SendResponseAsync(HttpStatusCode.ServiceUnavailable); await context.Response.SendResponseAsync(HttpStatusCode.ServiceUnavailable);
return; return;
} }
KeyValuePair<RelayAddress, RelayStats> lowest = new(new RelayAddress { Address = "Dummy" }, new RelayStats { ConnectedClients = int.MaxValue }); KeyValuePair<RelayAddress, RelayServerInfo> lowest = new(new RelayAddress { Address = "Dummy" }, new RelayServerInfo { ConnectedClients = int.MaxValue });
for (int i = 0; i < servers.Count; i++) for (int i = 0; i < servers.Count; i++)
{ {
@ -85,7 +85,7 @@ namespace LightReflectiveMirror.LoadBalancing
// ping server to ensure its online. // ping server to ensure its online.
var chosenServer = await Program.instance.ManualPingServer(lowest.Key.Address, lowest.Key.EndpointPort); var chosenServer = await Program.instance.ManualPingServer(lowest.Key.Address, lowest.Key.EndpointPort);
if(chosenServer.HasValue) if (chosenServer.HasValue)
await context.Response.SendResponseAsync(JsonConvert.SerializeObject(lowest.Key)); await context.Response.SendResponseAsync(JsonConvert.SerializeObject(lowest.Key));
else else
await context.Response.SendResponseAsync(HttpStatusCode.BadGateway); await context.Response.SendResponseAsync(HttpStatusCode.BadGateway);
@ -95,7 +95,39 @@ namespace LightReflectiveMirror.LoadBalancing
await context.Response.SendResponseAsync(HttpStatusCode.InternalServerError); await context.Response.SendResponseAsync(HttpStatusCode.InternalServerError);
} }
} }
/// <summary>
/// Returns all the servers on all the relay nodes.
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
[RestRoute("Get", "/api/masterlist/")]
public async Task GetMasterServerList(IHttpContext context)
{
var relays = Program.instance.availableRelayServers.ToList();
List<Room> masterList = new();
foreach (var relay in relays)
{
var serversOnRelay = await Program.instance.GetServerListFromIndividualRelay(relay.Key.Address, relay.Key.EndpointPort);
if(serversOnRelay != null)
{
masterList.AddRange(serversOnRelay);
} }
else { continue; }
}
// we have servers, send em!
if (masterList.Any())
await context.Response.SendResponseAsync(JsonConvert.SerializeObject(masterList));
// no servers or maybe no relays, fuck you
else
await context.Response.SendResponseAsync(HttpStatusCode.NoContent);
}
}
#region Startup
public class EndpointServer public class EndpointServer
{ {
@ -144,5 +176,8 @@ namespace LightReflectiveMirror.LoadBalancing
return null; return null;
} }
#endregion
} }
} }

View file

@ -14,7 +14,7 @@ namespace LightReflectiveMirror.LoadBalancing
/// Keeps track of all available relays. /// Keeps track of all available relays.
/// Key is server address, value is CCU. /// Key is server address, value is CCU.
/// </summary> /// </summary>
public Dictionary<RelayAddress, RelayStats> availableRelayServers = new(); public Dictionary<RelayAddress, RelayServerInfo> availableRelayServers = new();
private int _pingDelay = 10000; private int _pingDelay = 10000;
const string API_PATH = "/api/stats"; const string API_PATH = "/api/stats";
@ -64,7 +64,7 @@ namespace LightReflectiveMirror.LoadBalancing
availableRelayServers.Add(new RelayAddress { Port = port, EndpointPort = endpointPort, Address = serverIP }, stats.Value); availableRelayServers.Add(new RelayAddress { Port = port, EndpointPort = endpointPort, Address = serverIP }, stats.Value);
} }
public async Task<RelayStats?> ManualPingServer(string serverIP, ushort port) public async Task<RelayServerInfo?> ManualPingServer(string serverIP, ushort port)
{ {
using (WebClient wc = new WebClient()) using (WebClient wc = new WebClient())
{ {
@ -72,7 +72,7 @@ namespace LightReflectiveMirror.LoadBalancing
{ {
string receivedStats = await wc.DownloadStringTaskAsync($"http://{serverIP}:{port}{API_PATH}"); string receivedStats = await wc.DownloadStringTaskAsync($"http://{serverIP}:{port}{API_PATH}");
return JsonConvert.DeserializeObject<RelayStats>(receivedStats); return JsonConvert.DeserializeObject<RelayServerInfo>(receivedStats);
} }
catch(Exception e) catch(Exception e)
{ {
@ -82,6 +82,23 @@ namespace LightReflectiveMirror.LoadBalancing
} }
} }
public async Task<List<Room>> GetServerListFromIndividualRelay(string serverIP, ushort port)
{
using (WebClient wc = new WebClient())
{
try
{
string receivedStats = await wc.DownloadStringTaskAsync($"http://{serverIP}:{port}/api/servers");
return JsonConvert.DeserializeObject<List<Room>>(receivedStats);
}
catch (Exception e)
{
// Server failed to respond
return null;
}
}
}
async Task PingServers() async Task PingServers()
{ {
while (true) while (true)
@ -100,7 +117,7 @@ namespace LightReflectiveMirror.LoadBalancing
try try
{ {
var serverStats = wc.DownloadString(url); var serverStats = wc.DownloadString(url);
var deserializedData = JsonConvert.DeserializeObject<RelayStats>(serverStats); var deserializedData = JsonConvert.DeserializeObject<RelayServerInfo>(serverStats);
WriteLogMessage("Server " + keys[i].Address + " still exists, keeping in collection."); WriteLogMessage("Server " + keys[i].Address + " still exists, keeping in collection.");
@ -165,8 +182,9 @@ namespace LightReflectiveMirror.LoadBalancing
} }
// for stats
[Serializable] [Serializable]
public struct RelayStats public struct RelayServerInfo
{ {
public int ConnectedClients; public int ConnectedClients;
public int RoomCount; public int RoomCount;
@ -174,6 +192,7 @@ namespace LightReflectiveMirror.LoadBalancing
public TimeSpan Uptime; public TimeSpan Uptime;
} }
// container for relay address info
[Serializable] [Serializable]
public struct RelayAddress public struct RelayAddress
{ {
@ -182,4 +201,19 @@ namespace LightReflectiveMirror.LoadBalancing
public string Address; public string Address;
} }
// fuck you
[Serializable]
public struct Room
{
public int serverId;
public int hostId;
public string serverName;
public string serverData;
public bool isPublic;
public int maxPlayers;
public List<int> clients;
public RelayAddress relayInfo;
}
} }

View file

@ -26,7 +26,7 @@ namespace LightReflectiveMirror
private MethodInfo _lateUpdateMethod; private MethodInfo _lateUpdateMethod;
private DateTime _startupTime; private DateTime _startupTime;
public static string publicIP;
private List<int> _currentConnections = new List<int>(); private List<int> _currentConnections = new List<int>();
public Dictionary<int, IPEndPoint> NATConnections = new Dictionary<int, IPEndPoint>(); public Dictionary<int, IPEndPoint> NATConnections = new Dictionary<int, IPEndPoint>();
private BiDictionary<int, string> _pendingNATPunches = new BiDictionary<int, string>(); private BiDictionary<int, string> _pendingNATPunches = new BiDictionary<int, string>();
@ -51,6 +51,7 @@ namespace LightReflectiveMirror
WriteTitle(); WriteTitle();
instance = this; instance = this;
_startupTime = DateTime.Now; _startupTime = DateTime.Now;
publicIP = new WebClient().DownloadString("http://icanhazip.com").Replace("\\r\\n", "").Replace("\\n", "").Trim();
if (!File.Exists(CONFIG_PATH)) if (!File.Exists(CONFIG_PATH))
{ {

View file

@ -233,9 +233,7 @@ namespace LightReflectiveMirror
void CreateRoom(int clientId, int maxPlayers, string serverName, bool isPublic, string serverData, bool useDirectConnect, string hostLocalIP, bool useNatPunch, int port) void CreateRoom(int clientId, int maxPlayers, string serverName, bool isPublic, string serverData, bool useDirectConnect, string hostLocalIP, bool useNatPunch, int port)
{ {
LeaveRoom(clientId); LeaveRoom(clientId);
Program.instance.NATConnections.TryGetValue(clientId, out IPEndPoint hostIP);
IPEndPoint hostIP = null;
Program.instance.NATConnections.TryGetValue(clientId, out hostIP);
Room room = new Room Room room = new Room
{ {
@ -245,6 +243,12 @@ namespace LightReflectiveMirror
isPublic = isPublic, isPublic = isPublic,
serverData = serverData, serverData = serverData,
clients = new List<int>(), clients = new List<int>(),
// hard coded for now REMEMBER TO UN-HARDCODE RETARD
// 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 },
serverId = GetRandomServerID(), serverId = GetRandomServerID(),
hostIP = hostIP, hostIP = hostIP,
hostLocalIP = hostLocalIP, hostLocalIP = hostLocalIP,

View file

@ -1,4 +1,5 @@
using Newtonsoft.Json; using Newtonsoft.Json;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
@ -14,6 +15,9 @@ namespace LightReflectiveMirror
public bool isPublic; public bool isPublic;
public int maxPlayers; public int maxPlayers;
public List<int> clients; public List<int> clients;
public RelayAddress relayInfo;
[JsonIgnore] [JsonIgnore]
public bool supportsDirectConnect = false; public bool supportsDirectConnect = false;
[JsonIgnore] [JsonIgnore]
@ -25,4 +29,12 @@ namespace LightReflectiveMirror
[JsonIgnore] [JsonIgnore]
public int port; public int port;
} }
[Serializable]
public struct RelayAddress
{
public ushort Port;
public ushort EndpointPort;
public string Address;
}
} }

View file

@ -44,7 +44,7 @@ namespace LightReflectiveMirror
[Header("Server List")] [Header("Server List")]
public UnityEvent serverListUpdated; public UnityEvent serverListUpdated;
public List<RelayServerInfo> relayServerList { private set; get; } = new List<RelayServerInfo>(); public List<Room> relayServerList { private set; get; } = new List<Room>();
[Header("Server Information")] [Header("Server Information")]
public int serverId = -1; public int serverId = -1;
@ -471,6 +471,8 @@ namespace LightReflectiveMirror
} }
IEnumerator GetServerList() IEnumerator GetServerList()
{
if (!useLoadBalancer)
{ {
string uri = $"http://{serverIP}:{endpointServerPort}/api/compressed/servers"; string uri = $"http://{serverIP}:{endpointServerPort}/api/compressed/servers";
@ -491,7 +493,7 @@ namespace LightReflectiveMirror
case UnityWebRequest.Result.Success: case UnityWebRequest.Result.Success:
relayServerList?.Clear(); relayServerList?.Clear();
relayServerList = JsonConvert.DeserializeObject<List<RelayServerInfo>>(result.Decompress()); relayServerList = JsonConvert.DeserializeObject<List<Room>>(result.Decompress());
serverListUpdated?.Invoke(); serverListUpdated?.Invoke();
break; break;
} }
@ -509,6 +511,58 @@ namespace LightReflectiveMirror
#endif #endif
} }
} }
else // get master list from load balancer
{
yield return StartCoroutine(RetrieveMasterServerListFromLoadBalancer());
}
}
/// <summary>
/// Gets master list from the LB.
/// This can be optimized but for now it is it's
/// own separate method, so i can understand wtf is going on :D
/// </summary>
/// <returns></returns>
IEnumerator RetrieveMasterServerListFromLoadBalancer()
{
string uri = $"http://{loadBalancerAddress}:{loadBalancerPort}/api/masterlist/";
using (UnityWebRequest webRequest = UnityWebRequest.Get(uri))
{
// Request and wait for the desired page.
yield return webRequest.SendWebRequest();
var result = webRequest.downloadHandler.text;
#if UNITY_2020_1_OR_NEWER
switch (webRequest.result)
{
case UnityWebRequest.Result.ConnectionError:
case UnityWebRequest.Result.DataProcessingError:
case UnityWebRequest.Result.ProtocolError:
Debug.LogWarning("LRM | Network Error while retreiving the server list!");
break;
case UnityWebRequest.Result.Success:
relayServerList?.Clear();
relayServerList = JsonConvert.DeserializeObject<List<Room>>(result);
serverListUpdated?.Invoke();
break;
}
#else
if (webRequest.isNetworkError || webRequest.isHttpError)
{
Debug.LogWarning("LRM | Network Error while retreiving the server list!");
}
else
{
relayServerList?.Clear();
relayServerList = JsonConvert.DeserializeObject<List<RelayServerInfo>>(result);
serverListUpdated?.Invoke();
}
#endif
}
}
public void UpdateRoomInfo(string newServerName = null, string newServerData = null, bool? newServerIsPublic = null, int? newPlayerCap = null) public void UpdateRoomInfo(string newServerName = null, string newServerData = null, bool? newServerIsPublic = null, int? newPlayerCap = null)
{ {
@ -842,13 +896,15 @@ namespace LightReflectiveMirror
} }
[Serializable] [Serializable]
public struct RelayServerInfo public struct Room
{ {
public string serverName; public string serverName;
public int currentPlayers; public int currentPlayers;
public int maxPlayers; public int maxPlayers;
public int serverId; public int serverId;
public string serverData; public string serverData;
public RelayAddress relayInfo;
} }
[Serializable] [Serializable]
@ -858,4 +914,5 @@ namespace LightReflectiveMirror
public ushort EndpointPort; public ushort EndpointPort;
public string Address; public string Address;
} }
} }