fishbait/ServerProject-DONT-IMPORT-INTO-UNITY/LRM/Program/Program.cs

229 lines
9.7 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using LightReflectiveMirror.Endpoints;
using Mirror;
using Newtonsoft.Json;
namespace LightReflectiveMirror
{
partial class Program
{
public static void Main(string[] args) => new Program().MainAsync().GetAwaiter().GetResult();
public List<Room> GetRooms() => _relay.rooms;
public async Task MainAsync()
{
WriteTitle();
instance = this;
_startupTime = DateTime.Now;
publicIP = new WebClient().DownloadString("http://icanhazip.com").Replace("\\r\\n", "").Replace("\\n", "").Trim();
if (!File.Exists(CONFIG_PATH))
{
File.WriteAllText(CONFIG_PATH, JsonConvert.SerializeObject(new Config(), Formatting.Indented));
WriteLogMessage("A config.json file was generated. Please configure it to the proper settings and re-run!", ConsoleColor.Yellow);
Console.ReadKey();
Environment.Exit(0);
}
else
{
conf = JsonConvert.DeserializeObject<Config>(File.ReadAllText(CONFIG_PATH));
WriteLogMessage("Loading Assembly... ", ConsoleColor.White, true);
try
{
var asm = Assembly.LoadFile(Directory.GetCurrentDirectory() + @"\" + conf.TransportDLL);
WriteLogMessage($"OK", ConsoleColor.Green);
WriteLogMessage("\nLoading Transport Class... ", ConsoleColor.White, true);
transport = asm.CreateInstance(conf.TransportClass) as Transport;
if (transport != null)
{
var transportClass = asm.GetType(conf.TransportClass);
WriteLogMessage("OK", ConsoleColor.Green);
WriteLogMessage("\nLoading Transport Methods... ", ConsoleColor.White, true);
CheckMethods(transportClass);
WriteLogMessage("OK", ConsoleColor.Green);
WriteLogMessage("\nInvoking Transport Methods...");
if (_awakeMethod != null)
_awakeMethod.Invoke(transport, null);
if (_startMethod != null)
_startMethod.Invoke(transport, null);
WriteLogMessage("\nStarting Transport... ", ConsoleColor.White, true);
transport.OnServerError = (clientID, error) =>
{
WriteLogMessage($"Transport Error, Client: {clientID}, Error: {error}", ConsoleColor.Red);
};
transport.OnServerConnected = (clientID) =>
{
WriteLogMessage($"Transport Connected, Client: {clientID}", ConsoleColor.Cyan);
_currentConnections.Add(clientID);
_relay.ClientConnected(clientID);
if (conf.EnableNATPunchtroughServer)
{
string natID = Guid.NewGuid().ToString();
_pendingNATPunches.Add(clientID, natID);
_NATRequestPosition = 0;
_NATRequest.WriteByte(ref _NATRequestPosition, (byte)OpCodes.RequestNATConnection);
_NATRequest.WriteString(ref _NATRequestPosition, natID);
_NATRequest.WriteInt(ref _NATRequestPosition, conf.NATPunchtroughPort);
transport.ServerSend(clientID, 0, new ArraySegment<byte>(_NATRequest, 0, _NATRequestPosition));
}
};
_relay = new RelayHandler(transport.GetMaxPacketSize(0));
transport.OnServerDataReceived = _relay.HandleMessage;
transport.OnServerDisconnected = (clientID) =>
{
_currentConnections.Remove(clientID);
_relay.HandleDisconnect(clientID);
if(NATConnections.ContainsKey(clientID))
NATConnections.Remove(clientID);
if(_pendingNATPunches.TryGetByFirst(clientID, out _))
_pendingNATPunches.Remove(clientID);
};
transport.ServerStart();
WriteLogMessage("OK", ConsoleColor.Green);
if (conf.UseEndpoint)
{
WriteLogMessage("\nStarting Endpoint Service... ", ConsoleColor.White, true);
var endpointService = new EndpointServer();
if (endpointService.Start(conf.EndpointPort))
{
WriteLogMessage("OK", ConsoleColor.Green);
Endpoint.RoomsModified();
}
else
{
WriteLogMessage("FAILED\nPlease run as administrator or check if port is in use.", ConsoleColor.DarkRed);
}
}
if (conf.EnableNATPunchtroughServer)
{
WriteLogMessage("\nStarting NatPunchthrough Socket... ", ConsoleColor.White, true);
try
{
_punchServer = new UdpClient(conf.NATPunchtroughPort);
WriteLogMessage("OK\n", ConsoleColor.Green, true);
WriteLogMessage("\nStarting NatPunchthrough Thread... ", ConsoleColor.White, true);
var natThread = new Thread(new ThreadStart(RunNATPunchLoop));
try
{
natThread.Start();
}
catch(Exception e)
{
WriteLogMessage("FAILED\n" + e, ConsoleColor.DarkRed);
}
}
catch(Exception e)
{
WriteLogMessage("FAILED\nCheck if port is in use.", ConsoleColor.DarkRed, true);
Console.WriteLine(e);
}
}
}
else
{
WriteLogMessage("FAILED\nClass not found, make sure to included namespaces!", ConsoleColor.DarkRed);
Console.ReadKey();
Environment.Exit(0);
}
}
catch(Exception e)
{
WriteLogMessage("FAILED\nException: " + e, ConsoleColor.DarkRed);
Console.ReadKey();
Environment.Exit(0);
}
if(conf.UseLoadBalancer)
await RegisterSelfToLoadBalancer();
}
while (true)
{
if (_updateMethod != null) _updateMethod.Invoke(transport, null);
if (_lateUpdateMethod != null) _lateUpdateMethod.Invoke(transport, null);
_currentHeartbeatTimer++;
if(_currentHeartbeatTimer >= conf.UpdateHeartbeatInterval)
{
_currentHeartbeatTimer = 0;
for(int i = 0; i < _currentConnections.Count; i++)
transport.ServerSend(_currentConnections[i], 0, new ArraySegment<byte>(new byte[] { 200 }));
GC.Collect();
}
await Task.Delay(conf.UpdateLoopTime);
}
}
private async Task<bool> RegisterSelfToLoadBalancer()
{
try
{
// replace hard coded value for config value later
var uri = new Uri($"http://{conf.LoadBalancerAddress}:{conf.LoadBalancerPort}/api/auth");
string endpointPort = conf.EndpointPort.ToString();
string gamePort = 7777.ToString();
HttpWebRequest authReq = (HttpWebRequest)WebRequest.Create(uri);
authReq.Headers.Add("Auth", conf.LoadBalancerAuthKey);
authReq.Headers.Add("EndpointPort", endpointPort);
authReq.Headers.Add("GamePort", gamePort);
var res = await authReq.GetResponseAsync();
return true;
}
catch
{
// error adding or load balancer unavailable
WriteLogMessage("Error registering - Load Balancer probably timed out.", ConsoleColor.Red);
return false;
}
}
void CheckMethods(Type type)
{
_awakeMethod = type.GetMethod("Awake", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
_startMethod = type.GetMethod("Start", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
_updateMethod = type.GetMethod("Update", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
_lateUpdateMethod = type.GetMethod("LateUpdate", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
}
}
}