mirror of
https://github.com/Impostor/Impostor.git
synced 2026-10-20 01:46:11 +00:00
Added server redirection using redis
This commit is contained in:
1 parent
7b6c55c78e
commit
f2a23d5bbb
27 files changed
+451
-61
No files matched your search
@@ -19,7 +19,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: 3.1.301
|
||||
dotnet-version: 3.1.402
|
||||
- name: Install dependencies
|
||||
run: dotnet restore ./src
|
||||
- name: Build Impostor.Client
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: 3.1.301
|
||||
dotnet-version: 3.1.402
|
||||
- name: Install dependencies
|
||||
run: dotnet restore ./src
|
||||
- name: Build Impostor.Client
|
||||
@@ -35,13 +35,3 @@ jobs:
|
||||
run: dotnet build --no-restore -c Release -f netcoreapp3.1 ./src/Impostor.Server/Impostor.Server.csproj /p:PublishTrimmed=false
|
||||
- name: Test
|
||||
run: dotnet test --no-restore -v normal -f netcoreapp3.1 ./src
|
||||
deploy:
|
||||
name: Deploy
|
||||
runs-on: windows-latest
|
||||
needs: build
|
||||
if: github.event_name == 'release'
|
||||
steps:
|
||||
- name: Publish Impostor.Server (win-x64)
|
||||
run: dotnet publish -c release -o /build/linux-x64 -f netcoreapp3.1 -r win-x64 --self-contained --no-restore ./src/Impostor.Server/Impostor.Server.csproj
|
||||
- name: Publish Impostor.Server (linux-x64)
|
||||
run: dotnet publish -c release -o /build/linux-x64 -f netcoreapp3.1 -r linux-x64 --self-contained --no-restore ./src/Impostor.Server/Impostor.Server.csproj
|
||||
@@ -1,3 +1,5 @@
|
||||
config.*.json
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
|
||||
@@ -8,5 +8,8 @@
|
||||
|
||||
public const string Destroyed = "The game you tried to join is being destroyed. " +
|
||||
"Please create a new game.";
|
||||
|
||||
public const string NotImplemented = "Game listing has not been implemented in Impostor yet for servers " +
|
||||
"running in server redirection mode.";
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
namespace Impostor.Server.Data
|
||||
{
|
||||
public class ServerConfig
|
||||
internal class ServerConfig
|
||||
{
|
||||
public const string Section = "Server";
|
||||
|
||||
public string PublicIp { get; set; } = "127.0.0.1";
|
||||
public ushort PublicPort { get; set; } = 22023;
|
||||
public string ListenIp { get; set; } = "127.0.0.1";
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Impostor.Server.Data
|
||||
{
|
||||
internal class ServerRedirectorConfig
|
||||
{
|
||||
public const string Section = "ServerRedirector";
|
||||
|
||||
public bool Enabled { get; set; }
|
||||
public bool Master { get; set; }
|
||||
public string Redis { get; set; }
|
||||
public List<ServerRedirectorNode> Nodes { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Impostor.Server.Data
|
||||
{
|
||||
internal class ServerRedirectorNode
|
||||
{
|
||||
public string Ip { get; set; }
|
||||
public ushort Port { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="3.1.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.8" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="3.1.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
|
||||
@@ -27,6 +28,9 @@
|
||||
<None Update="config.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="config.*.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Hazel;
|
||||
using Impostor.Server.Data;
|
||||
using Impostor.Server.Net.Manager;
|
||||
@@ -10,7 +10,7 @@ using ILogger = Serilog.ILogger;
|
||||
|
||||
namespace Impostor.Server.Net
|
||||
{
|
||||
public class Client
|
||||
internal class Client
|
||||
{
|
||||
private static readonly ILogger Logger = Log.ForContext<Client>();
|
||||
|
||||
|
||||
@@ -1,26 +1,29 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Hazel;
|
||||
using Impostor.Server.Exceptions;
|
||||
using Serilog;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Impostor.Server.Net.Manager
|
||||
{
|
||||
public class ClientManager
|
||||
internal class ClientManager : IClientManager
|
||||
{
|
||||
private static readonly ILogger Logger = Log.ForContext<ClientManager>();
|
||||
|
||||
private readonly ILogger<ClientManager> _clientManager;
|
||||
private readonly GameManager _gameManager;
|
||||
private readonly ConcurrentDictionary<int, Client> _clients;
|
||||
private readonly object _idLock;
|
||||
private int _idLast;
|
||||
|
||||
public ClientManager()
|
||||
public ClientManager(ILogger<ClientManager> clientManager, GameManager gameManager)
|
||||
{
|
||||
_clientManager = clientManager;
|
||||
_gameManager = gameManager;
|
||||
_clients = new ConcurrentDictionary<int, Client>();
|
||||
_idLock = new object();
|
||||
_idLast = 0;
|
||||
}
|
||||
|
||||
// No idea what a good way for this is.
|
||||
public int NextId()
|
||||
private int NextId()
|
||||
{
|
||||
lock (_idLock)
|
||||
{
|
||||
@@ -35,7 +38,7 @@ namespace Impostor.Server.Net.Manager
|
||||
_idLast = 0;
|
||||
}
|
||||
|
||||
if (_clients.ContainsKey(_idLast))
|
||||
if (_clients.ContainsKey(result))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -47,17 +50,17 @@ namespace Impostor.Server.Net.Manager
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(Client client)
|
||||
public void Create(string name, Connection connection)
|
||||
{
|
||||
Logger.Information("Client connected.");
|
||||
|
||||
_clients.TryAdd(client.Id, client);
|
||||
var clientId = NextId();
|
||||
|
||||
_clientManager.LogInformation("Client connected.");
|
||||
_clients.TryAdd(clientId, new Client(this, _gameManager, clientId, name, connection));
|
||||
}
|
||||
|
||||
public void Remove(Client client)
|
||||
{
|
||||
Logger.Information("Client disconnected.");
|
||||
|
||||
_clientManager.LogInformation("Client disconnected.");
|
||||
_clients.TryRemove(client.Id, out _);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Impostor.Server.Data;
|
||||
using Impostor.Server.Net.Redirector;
|
||||
using Impostor.Server.Net.State;
|
||||
using Impostor.Shared.Innersloth;
|
||||
using Impostor.Shared.Innersloth.Data;
|
||||
@@ -11,26 +12,33 @@ using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Impostor.Server.Net.Manager
|
||||
{
|
||||
public class GameManager
|
||||
internal class GameManager
|
||||
{
|
||||
private readonly ILogger<GameManager> _logger;
|
||||
private readonly INodeProvider _nodeProvider;
|
||||
private readonly IPEndPoint _publicIp;
|
||||
private readonly ConcurrentDictionary<int, Game> _games;
|
||||
|
||||
public GameManager(ILogger<GameManager> logger, IOptions<ServerConfig> config)
|
||||
public GameManager(ILogger<GameManager> logger, IOptions<ServerConfig> config, INodeProvider nodeProvider)
|
||||
{
|
||||
_logger = logger;
|
||||
_nodeProvider = nodeProvider;
|
||||
_publicIp = new IPEndPoint(IPAddress.Parse(config.Value.PublicIp), config.Value.PublicPort);
|
||||
_games = new ConcurrentDictionary<int, Game>();
|
||||
}
|
||||
|
||||
public Game Create(GameOptionsData options)
|
||||
{
|
||||
// TODO: Prevent duplicates when using server redirector using INodeProvider.
|
||||
|
||||
var gameCode = GameCode.GenerateCode(6);
|
||||
var game = new Game(this, _publicIp, gameCode, options);
|
||||
var gameCodeStr = GameCode.IntToGameName(gameCode);
|
||||
var game = new Game(this, _nodeProvider, _publicIp, gameCode, options);
|
||||
|
||||
if (_games.TryAdd(gameCode, game))
|
||||
if (_nodeProvider.Find(gameCodeStr) == null &&
|
||||
_games.TryAdd(gameCode, game))
|
||||
{
|
||||
_nodeProvider.Save(gameCodeStr, _publicIp);
|
||||
_logger.LogDebug("Created game with code {0} ({1}).", game.CodeStr, gameCode);
|
||||
return game;
|
||||
}
|
||||
@@ -53,7 +61,7 @@ namespace Impostor.Server.Net.Manager
|
||||
foreach (var (code, game) in _games.Where(x =>
|
||||
x.Value.IsPublic &&
|
||||
x.Value.GameState == GameStates.NotStarted &&
|
||||
x.Value.PlayerCount < 10))
|
||||
x.Value.PlayerCount < 10)) // TODO: Do "< x.Value.Options.MaxPlayers" when GameData packets are done.
|
||||
{
|
||||
// Check for options.
|
||||
// TODO: Re-enable map filter when GameData packets are done.
|
||||
@@ -77,7 +85,8 @@ namespace Impostor.Server.Net.Manager
|
||||
|
||||
public void Remove(int gameCode)
|
||||
{
|
||||
_logger.LogDebug("Remove game with code {0} ({1}).", GameCode.IntToGameName(gameCode), gameCode);
|
||||
_logger.LogDebug("Remove game with code {0} ({1}).", GameCode.IntToGameName(gameCode), gameCode);
|
||||
_nodeProvider.Remove(GameCode.IntToGameName(gameCode));
|
||||
_games.TryRemove(gameCode, out _);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
using Hazel;
|
||||
|
||||
namespace Impostor.Server.Net.Manager
|
||||
{
|
||||
internal interface IClientManager
|
||||
{
|
||||
void Create(string name, Connection connection);
|
||||
}
|
||||
}
|
||||
@@ -10,21 +10,22 @@ using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Impostor.Server.Net
|
||||
{
|
||||
public class Matchmaker
|
||||
internal class Matchmaker
|
||||
{
|
||||
private readonly ILogger<Matchmaker> _logger;
|
||||
private readonly ServerConfig _config;
|
||||
private readonly GameManager _gameManager;
|
||||
private readonly ClientManager _clientManager;
|
||||
private readonly ServerConfig _serverConfig;
|
||||
private readonly IClientManager _clientManager;
|
||||
private readonly UdpConnectionListener _connection;
|
||||
|
||||
public Matchmaker(ILogger<Matchmaker> logger, IOptions<ServerConfig> configOptions, GameManager gameManager)
|
||||
public Matchmaker(
|
||||
ILogger<Matchmaker> logger,
|
||||
IOptions<ServerConfig> serverConfig,
|
||||
IClientManager clientManager)
|
||||
{
|
||||
_logger = logger;
|
||||
_config = configOptions.Value;
|
||||
_gameManager = gameManager;
|
||||
_clientManager = new ClientManager();
|
||||
_connection = new UdpConnectionListener(new IPEndPoint(IPAddress.Parse(_config.ListenIp), _config.ListenPort), IPMode.IPv4, s =>
|
||||
_serverConfig = serverConfig.Value;
|
||||
_clientManager = clientManager;
|
||||
_connection = new UdpConnectionListener(new IPEndPoint(IPAddress.Parse(_serverConfig.ListenIp), _serverConfig.ListenPort), IPMode.IPv4, s =>
|
||||
{
|
||||
_logger.LogWarning("Log from Hazel: {0}", s);
|
||||
});
|
||||
@@ -52,8 +53,8 @@ namespace Impostor.Server.Net
|
||||
return;
|
||||
}
|
||||
|
||||
// Register client.
|
||||
_clientManager.Add(new Client(_clientManager, _gameManager, _clientManager.NextId(), clientName, e.Connection));
|
||||
// Create client.
|
||||
_clientManager.Create(clientName, e.Connection);
|
||||
}
|
||||
|
||||
public void Start()
|
||||
|
||||
@@ -1,27 +1,47 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Server.Data;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Impostor.Server.Net
|
||||
{
|
||||
public class MatchmakerService : IHostedService
|
||||
internal class MatchmakerService : IHostedService
|
||||
{
|
||||
private readonly ILogger<MatchmakerService> _logger;
|
||||
private readonly ServerConfig _serverConfig;
|
||||
private readonly ServerRedirectorConfig _redirectorConfig;
|
||||
private readonly Matchmaker _matchmaker;
|
||||
|
||||
public MatchmakerService(ILogger<MatchmakerService> logger, Matchmaker matchmaker)
|
||||
public MatchmakerService(
|
||||
ILogger<MatchmakerService> logger,
|
||||
IOptions<ServerConfig> serverConfig,
|
||||
IOptions<ServerRedirectorConfig> redirectorConfig,
|
||||
Matchmaker matchmaker)
|
||||
{
|
||||
_logger = logger;
|
||||
_serverConfig = serverConfig.Value;
|
||||
_redirectorConfig = redirectorConfig.Value;
|
||||
_matchmaker = matchmaker;
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_matchmaker.Start();
|
||||
_logger.LogInformation("Matchmaker is running on {0}:{1}.",
|
||||
|
||||
_logger.LogInformation("Matchmaker is listening on {0}:{1}, the public server ip is {2}:{3}.",
|
||||
_matchmaker.EndPoint.Address,
|
||||
_matchmaker.EndPoint.Port);
|
||||
_matchmaker.EndPoint.Port,
|
||||
_serverConfig.PublicIp,
|
||||
_serverConfig.PublicPort);
|
||||
|
||||
if (_redirectorConfig.Enabled)
|
||||
{
|
||||
_logger.LogWarning(_redirectorConfig.Master
|
||||
? "Server redirection is enabled as master, this instance will redirect clients to other nodes."
|
||||
: "Server redirection is enabled as node, this instance will accept clients.");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.Net;
|
||||
using Hazel;
|
||||
|
||||
namespace Impostor.Server.Net.Messages
|
||||
{
|
||||
internal static class Message13Redirect
|
||||
{
|
||||
public static void Serialize(MessageWriter writer, bool clear, IPEndPoint ipEndPoint)
|
||||
{
|
||||
if (clear)
|
||||
{
|
||||
writer.Clear(SendOption.Reliable);
|
||||
}
|
||||
|
||||
writer.StartMessage(MessageFlags.Redirect);
|
||||
writer.Write(ipEndPoint.Address.GetAddressBytes());
|
||||
writer.Write((ushort) ipEndPoint.Port);
|
||||
writer.EndMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Collections.Generic;
|
||||
using Hazel;
|
||||
using Impostor.Server.Net.Manager;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Impostor.Server.Net.Redirector
|
||||
{
|
||||
internal class ClientManagerRedirector : IClientManager
|
||||
{
|
||||
private readonly ILogger<ClientManagerRedirector> _logger;
|
||||
private readonly INodeProvider _nodeProvider;
|
||||
private readonly HashSet<ClientRedirector> _clients;
|
||||
|
||||
public ClientManagerRedirector(ILogger<ClientManagerRedirector> logger, INodeProvider nodeProvider)
|
||||
{
|
||||
_logger = logger;
|
||||
_nodeProvider = nodeProvider;
|
||||
_clients = new HashSet<ClientRedirector>();
|
||||
}
|
||||
|
||||
public void Create(string name, Connection connection)
|
||||
{
|
||||
_logger.LogInformation("Client connected.");
|
||||
_clients.Add(new ClientRedirector(name, connection, this, _nodeProvider));
|
||||
}
|
||||
|
||||
public void Remove(ClientRedirector client)
|
||||
{
|
||||
_logger.LogInformation("Client disconnected.");
|
||||
_clients.Remove(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
using System;
|
||||
using Hazel;
|
||||
using Impostor.Server.Data;
|
||||
using Impostor.Server.Net.Messages;
|
||||
using Impostor.Shared.Innersloth;
|
||||
using Impostor.Shared.Innersloth.Data;
|
||||
using Serilog;
|
||||
using ILogger = Serilog.ILogger;
|
||||
|
||||
namespace Impostor.Server.Net.Redirector
|
||||
{
|
||||
internal class ClientRedirector
|
||||
{
|
||||
private static readonly ILogger Logger = Log.ForContext<ClientRedirector>();
|
||||
|
||||
private readonly string _name;
|
||||
private readonly Connection _connection;
|
||||
private readonly ClientManagerRedirector _clientManager;
|
||||
private readonly INodeProvider _nodeProvider;
|
||||
|
||||
public ClientRedirector(string name, Connection connection, ClientManagerRedirector clientManager, INodeProvider nodeProvider)
|
||||
{
|
||||
_name = name;
|
||||
_connection = connection;
|
||||
_connection.DataReceived += OnDataReceived;
|
||||
_connection.Disconnected += OnDisconnected;
|
||||
_clientManager = clientManager;
|
||||
_nodeProvider = nodeProvider;
|
||||
}
|
||||
|
||||
private void OnDataReceived(DataReceivedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (e.Message.Position >= e.Message.Length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
OnMessageReceived(e.Message.ReadMessage());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex, "Exception caught in client data handler.");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMessageReceived(MessageReader message)
|
||||
{
|
||||
var flag = message.Tag;
|
||||
|
||||
Logger.Verbose("Server got {0}.", flag);
|
||||
|
||||
switch (flag)
|
||||
{
|
||||
case MessageFlags.HostGame:
|
||||
{
|
||||
using (var packet = MessageWriter.Get(SendOption.Reliable))
|
||||
{
|
||||
Message13Redirect.Serialize(packet, false, _nodeProvider.Get());
|
||||
_connection.Send(packet);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MessageFlags.JoinGame:
|
||||
{
|
||||
Message01JoinGame.Deserialize(message,
|
||||
out var gameCode,
|
||||
out var unknown);
|
||||
|
||||
using (var packet = MessageWriter.Get(SendOption.Reliable))
|
||||
{
|
||||
var endpoint = _nodeProvider.Find(GameCode.IntToGameName(gameCode));
|
||||
if (endpoint == null)
|
||||
{
|
||||
Message01JoinGame.SerializeError(packet, false, DisconnectReason.GameMissing);
|
||||
}
|
||||
else
|
||||
{
|
||||
Message13Redirect.Serialize(packet, false, endpoint);
|
||||
}
|
||||
|
||||
_connection.Send(packet);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MessageFlags.GetGameListV2:
|
||||
{
|
||||
// TODO: Implement.
|
||||
using (var packet = MessageWriter.Get(SendOption.Reliable))
|
||||
{
|
||||
Message01JoinGame.SerializeError(packet, false, DisconnectReason.Custom, DisconnectMessages.NotImplemented);
|
||||
_connection.Send(packet);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
Logger.Warning("Received unsupported message flag on the redirector ({0}).", flag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisconnected(object sender, DisconnectedEventArgs e)
|
||||
{
|
||||
_clientManager.Remove(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.Net;
|
||||
|
||||
namespace Impostor.Server.Net.Redirector
|
||||
{
|
||||
internal interface INodeProvider
|
||||
{
|
||||
IPEndPoint Get();
|
||||
IPEndPoint Find(string gameCode);
|
||||
void Save(string gameCode, IPEndPoint endPoint);
|
||||
void Remove(string gameCode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
namespace Impostor.Server.Net.Redirector
|
||||
{
|
||||
public class NodeProviderNoOp : INodeProvider
|
||||
{
|
||||
public IPEndPoint Get()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IPEndPoint Find(string gameCode)
|
||||
{
|
||||
// Do nothing.
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Save(string gameCode, IPEndPoint endPoint)
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
public void Remove(string gameCode)
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using Impostor.Server.Data;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Impostor.Server.Net.Redirector
|
||||
{
|
||||
internal class NodeProviderRedis : INodeProvider
|
||||
{
|
||||
private readonly IDistributedCache _cache;
|
||||
private readonly List<IPEndPoint> _nodes;
|
||||
private readonly object _lock;
|
||||
private int _currentIndex;
|
||||
|
||||
public NodeProviderRedis(IOptions<ServerRedirectorConfig> redirectorConfig, IDistributedCache cache)
|
||||
{
|
||||
_cache = cache;
|
||||
_nodes = new List<IPEndPoint>();
|
||||
_lock = new object();
|
||||
|
||||
foreach (var node in redirectorConfig.Value.Nodes)
|
||||
{
|
||||
_nodes.Add(new IPEndPoint(IPAddress.Parse(node.Ip), node.Port));
|
||||
}
|
||||
}
|
||||
|
||||
public IPEndPoint Get()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var node = _nodes[_currentIndex++];
|
||||
|
||||
if (_currentIndex == _nodes.Count)
|
||||
{
|
||||
_currentIndex = 0;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
public IPEndPoint Find(string gameCode)
|
||||
{
|
||||
var entry = _cache.GetString(gameCode);
|
||||
if (entry == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return IPEndPoint.Parse(entry);
|
||||
}
|
||||
|
||||
public void Save(string gameCode, IPEndPoint endPoint)
|
||||
{
|
||||
_cache.SetString(gameCode, endPoint.ToString(), new DistributedCacheEntryOptions
|
||||
{
|
||||
SlidingExpiration = TimeSpan.FromHours(1)
|
||||
});
|
||||
}
|
||||
|
||||
public void Remove(string gameCode)
|
||||
{
|
||||
_cache.Remove(gameCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using Impostor.Shared.Innersloth;
|
||||
|
||||
namespace Impostor.Server.Net.State
|
||||
{
|
||||
public partial class ClientPlayer
|
||||
internal partial class ClientPlayer
|
||||
{
|
||||
/// <summary>
|
||||
/// Triggered when the connected client requests the game listing.
|
||||
|
||||
@@ -5,7 +5,7 @@ using Impostor.Shared.Innersloth.Data;
|
||||
|
||||
namespace Impostor.Server.Net.State
|
||||
{
|
||||
public partial class ClientPlayer
|
||||
internal partial class ClientPlayer
|
||||
{
|
||||
private readonly GameManager _gameManager;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ using Impostor.Shared.Innersloth.Data;
|
||||
|
||||
namespace Impostor.Server.Net.State
|
||||
{
|
||||
public partial class Game
|
||||
internal partial class Game
|
||||
{
|
||||
public void HandleStartGame(MessageReader message)
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ using Impostor.Shared.Innersloth.Data;
|
||||
|
||||
namespace Impostor.Server.Net.State
|
||||
{
|
||||
public partial class Game
|
||||
internal partial class Game
|
||||
{
|
||||
private void WriteRemovePlayerMessage(MessageWriter message, bool clear, int playerId, DisconnectReason reason)
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Net;
|
||||
using Hazel;
|
||||
using Impostor.Server.Net.Manager;
|
||||
using Impostor.Server.Net.Messages;
|
||||
using Impostor.Server.Net.Redirector;
|
||||
using Impostor.Shared.Innersloth;
|
||||
using Impostor.Shared.Innersloth.Data;
|
||||
using Serilog;
|
||||
@@ -12,17 +13,19 @@ using ILogger = Serilog.ILogger;
|
||||
|
||||
namespace Impostor.Server.Net.State
|
||||
{
|
||||
public partial class Game
|
||||
internal partial class Game
|
||||
{
|
||||
private static readonly ILogger Logger = Log.ForContext<Game>();
|
||||
|
||||
private readonly GameManager _gameManager;
|
||||
private readonly INodeProvider _nodeProvider;
|
||||
private readonly ConcurrentDictionary<int, ClientPlayer> _players;
|
||||
private readonly HashSet<IPAddress> _bannedIps;
|
||||
|
||||
public Game(GameManager gameManager, IPEndPoint publicIp, int code, GameOptionsData options)
|
||||
public Game(GameManager gameManager, INodeProvider nodeProvider, IPEndPoint publicIp, int code, GameOptionsData options)
|
||||
{
|
||||
_gameManager = gameManager;
|
||||
_nodeProvider = nodeProvider;
|
||||
_players = new ConcurrentDictionary<int, ClientPlayer>();
|
||||
_bannedIps = new HashSet<IPAddress>();
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
using Impostor.Server.Data;
|
||||
using Impostor.Server.Net;
|
||||
using Impostor.Server.Net.Manager;
|
||||
using Impostor.Server.Net.Redirector;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace Impostor.Server
|
||||
{
|
||||
@@ -19,9 +19,8 @@ namespace Impostor.Server
|
||||
.MinimumLevel.Verbose()
|
||||
#else
|
||||
.MinimumLevel.Information()
|
||||
|
||||
#endif
|
||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
|
||||
#endif
|
||||
.Enrich.FromLogContext()
|
||||
.WriteTo.Console()
|
||||
.CreateLogger();
|
||||
@@ -48,14 +47,43 @@ namespace Impostor.Server
|
||||
.ConfigureAppConfiguration(builder =>
|
||||
{
|
||||
builder.AddJsonFile("config.json", true);
|
||||
builder.AddJsonFile("config.Development.json", true);
|
||||
builder.AddEnvironmentVariables(prefix: "IMPOSTOR_");
|
||||
builder.AddCommandLine(args);
|
||||
})
|
||||
.ConfigureServices((host, services) =>
|
||||
{
|
||||
services.Configure<ServerConfig>(host.Configuration.GetSection("Server"));
|
||||
var redirector = host.Configuration
|
||||
.GetSection(ServerRedirectorConfig.Section)
|
||||
.Get<ServerRedirectorConfig>();
|
||||
|
||||
services.Configure<ServerConfig>(host.Configuration.GetSection(ServerConfig.Section));
|
||||
services.Configure<ServerRedirectorConfig>(host.Configuration.GetSection(ServerRedirectorConfig.Section));
|
||||
|
||||
services.AddSingleton<GameManager>();
|
||||
if (redirector.Enabled)
|
||||
{
|
||||
services.AddSingleton<INodeProvider, NodeProviderRedis>();
|
||||
services.AddStackExchangeRedisCache(options =>
|
||||
{
|
||||
options.Configuration = redirector.Redis;
|
||||
options.InstanceName = "ImpostorRedis";
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddSingleton<INodeProvider, NodeProviderNoOp>();
|
||||
}
|
||||
|
||||
if (redirector.Enabled && redirector.Master)
|
||||
{
|
||||
services.AddSingleton<IClientManager, ClientManagerRedirector>();
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddSingleton<IClientManager, ClientManager>();
|
||||
services.AddSingleton<GameManager>();
|
||||
}
|
||||
|
||||
services.AddSingleton<Matchmaker>();
|
||||
|
||||
services.AddHostedService<MatchmakerService>();
|
||||
|
||||
@@ -4,5 +4,20 @@
|
||||
"PublicPort": 22023,
|
||||
"ListenIp": "0.0.0.0",
|
||||
"ListenPort": 22023
|
||||
},
|
||||
"ServerRedirector": {
|
||||
"Enabled": false,
|
||||
"Master": true,
|
||||
"Redis": "127.0.0.1:6379",
|
||||
"Nodes": [
|
||||
{
|
||||
"Ip": "127.0.0.1",
|
||||
"Port": 22024
|
||||
},
|
||||
{
|
||||
"Ip": "127.0.0.1",
|
||||
"Port": 22025
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user