mirror of
https://github.com/Impostor/Impostor.git
synced 2026-10-19 15:51:42 +00:00
Add an alternative NodeLocator that doesn't rely on Redis for smaller multi-server setups with a single Master.
This commit is contained in:
1 parent
5284f0c2fe
commit
2f58fc0671
5 files changed
+156
-8
No files matched your search
@@ -3,3 +3,4 @@
|
||||
################################################################################
|
||||
|
||||
/.vscode/tasks.json
|
||||
/.vscode/launch.json
|
||||
@@ -8,7 +8,9 @@ namespace Impostor.Server.Data
|
||||
|
||||
public bool Enabled { get; set; }
|
||||
public bool Master { get; set; }
|
||||
public bool UseRedis { get; set; }
|
||||
public string Redis { get; set; }
|
||||
public string UDPMasterEndpoint { get; set; }
|
||||
public List<ServerRedirectorNode> Nodes { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
using Impostor.Server.Data;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Impostor.Server.Net.Redirector
|
||||
{
|
||||
public class NodeLocatorUDPSockets : INodeLocator, IDisposable
|
||||
{
|
||||
readonly UdpClient client;
|
||||
private bool disposedValue;
|
||||
|
||||
private class AvailableNode {
|
||||
public IPEndPoint Endpoint { get; set; }
|
||||
public DateTime LastUpdated { get; set; }
|
||||
public bool Expired => LastUpdated < DateTime.Now.AddHours(-1);
|
||||
}
|
||||
|
||||
private Dictionary<string, AvailableNode> AvailableNodes;
|
||||
|
||||
internal NodeLocatorUDPSockets(IOptions<ServerRedirectorConfig> config)
|
||||
{
|
||||
if (!Uri.TryCreate(config.Value.UDPMasterEndpoint, UriKind.Absolute, out Uri ServerAddress) || ServerAddress.Scheme.ToLower() != "udp")
|
||||
{
|
||||
throw new Exception($"UDPMasterEndpoint has an invalid value of '{config.Value.UDPMasterEndpoint}'. Expected udp://ip:port/ schema.");
|
||||
}
|
||||
|
||||
if (config.Value.Master)
|
||||
{
|
||||
if (!IPAddress.TryParse(ServerAddress.Host, out var LocalIPBinding)) {
|
||||
throw new Exception("$UDPMasterEndpoint must use an IP address rather than a hostname when acting as a master.");
|
||||
}
|
||||
|
||||
client = new UdpClient(new IPEndPoint(LocalIPBinding, ServerAddress.Port));
|
||||
AvailableNodes = new Dictionary<string, AvailableNode>();
|
||||
Task.Run(HandleClientPackets);
|
||||
}
|
||||
else
|
||||
{
|
||||
client = new UdpClient(ServerAddress.Host, ServerAddress.Port);
|
||||
}
|
||||
}
|
||||
|
||||
public IPEndPoint Find(string gameCode)
|
||||
{
|
||||
lock (AvailableNodes)
|
||||
{
|
||||
return AvailableNodes.ContainsKey(gameCode) ? AvailableNodes[gameCode].Endpoint : null;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleClientPackets()
|
||||
{
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var data = client.ReceiveAsync().GetAwaiter().GetResult();
|
||||
//TODO: Log that we got an update from the given remote EP.
|
||||
string message = Encoding.UTF8.GetString(data.Buffer);
|
||||
var parts = message.Split(',', 2);
|
||||
if (parts.Length != 2) { continue; }
|
||||
|
||||
if (!IPEndPoint.TryParse(parts[1], out var Endpoint)) { continue; }
|
||||
|
||||
HandleUpdate(parts[0], Endpoint);
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
return; //Bail out and don't try anything else.
|
||||
}
|
||||
catch
|
||||
{
|
||||
//Something else went wrong but we're not shutting down, give up and try again.
|
||||
if (!disposedValue) { Task.Run(HandleClientPackets); }
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleUpdate(string gameCode, IPEndPoint endpoint)
|
||||
{
|
||||
lock (AvailableNodes)
|
||||
{
|
||||
var node = AvailableNodes.ContainsKey(gameCode) ? AvailableNodes[gameCode] : new AvailableNode();
|
||||
node.Endpoint = endpoint;
|
||||
node.LastUpdated = DateTime.Now;
|
||||
AvailableNodes[gameCode] = node;
|
||||
|
||||
var KeysToRemove = AvailableNodes.Where(kvp => kvp.Value.Expired).Select(kvp => kvp.Key).ToList();
|
||||
KeysToRemove.ForEach(n => AvailableNodes.Remove(n));
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(string gameCode)
|
||||
{
|
||||
lock (AvailableNodes)
|
||||
{
|
||||
AvailableNodes.Remove(gameCode);
|
||||
}
|
||||
}
|
||||
|
||||
public void Save(string gameCode, IPEndPoint endPoint)
|
||||
{
|
||||
byte[] data = Encoding.UTF8.GetBytes($"{endPoint},{gameCode}");
|
||||
client.Send(data, data.Length);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
client.Close();
|
||||
client.Dispose();
|
||||
}
|
||||
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,18 +66,25 @@ namespace Impostor.Server
|
||||
{
|
||||
// When joining a game, it retrieves the game server ip from redis.
|
||||
// When a game has been created on this node, it stores the game code with its ip in redis.
|
||||
services.AddSingleton<INodeLocator, NodeLocatorRedis>();
|
||||
if (redirector.UseRedis)
|
||||
{
|
||||
services.AddSingleton<INodeLocator, NodeLocatorRedis>();
|
||||
|
||||
// Dependency for the NodeLocatorRedis.
|
||||
services.AddStackExchangeRedisCache(options =>
|
||||
{
|
||||
options.Configuration = redirector.Redis;
|
||||
options.InstanceName = "ImpostorRedis";
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddSingleton<INodeLocator, NodeLocatorUDPSockets>();
|
||||
}
|
||||
|
||||
// Use the configuration as source for the list of nodes to provide
|
||||
// when creating a game.
|
||||
services.AddSingleton<INodeProvider, NodeProviderConfig>();
|
||||
|
||||
// Dependency for the NodeLocatorRedis.
|
||||
services.AddStackExchangeRedisCache(options =>
|
||||
{
|
||||
options.Configuration = redirector.Redis;
|
||||
options.InstanceName = "ImpostorRedis";
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
"ServerRedirector": {
|
||||
"Enabled": false,
|
||||
"Master": true,
|
||||
"UseRedis": true,
|
||||
"Redis": "127.0.0.1:6379",
|
||||
"UDPMasterEndpoint": "127.0.0.1:32320",
|
||||
"Nodes": [
|
||||
{
|
||||
"Ip": "127.0.0.1",
|
||||
|
||||
Reference in new issue
Block a user