Skip to content

Commit

Permalink
First iteration of new stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
cubicgraphics committed May 6, 2024
1 parent a344bea commit 44d928b
Show file tree
Hide file tree
Showing 89 changed files with 932 additions and 787 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using BeatTogether.DedicatedServer.Kernel.Abstractions;
using BeatTogether.Core.Abstractions;

namespace BeatTogether.DedicatedServer.Instancing.Abstractions
{
public interface IInstanceFactory
{
public IDedicatedInstance? CreateInstance(
IServerInstance serverInstance);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using BeatTogether.Core.Enums;
using BeatTogether.DedicatedServer.Kernel.Abstractions;
using System.Diagnostics.CodeAnalysis;

namespace BeatTogether.DedicatedServer.Instancing.Abstractions
{
public interface IInstanceRegistry
{
public bool AddInstance(IDedicatedInstance instance);
public bool RemoveInstance(IDedicatedInstance instance);
public bool TryGetInstance(string secret, [MaybeNullWhen(false)] out IDedicatedInstance instance);
public bool TryGetInstanceByCode(string code, [MaybeNullWhen(false)] out IDedicatedInstance instance);
public bool TryGetAvailablePublicServer(InvitePolicy invitePolicy, GameplayServerMode serverMode, SongSelectionMode songMode, GameplayServerControlSettings serverControlSettings, BeatmapDifficultyMask difficultyMask, GameplayModifiersMask modifiersMask, string songPackMasks, [MaybeNullWhen(false)] out IDedicatedInstance instance);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace BeatTogether.DedicatedServer.Node.Abstractions
namespace BeatTogether.DedicatedServer.Instancing.Abstractions
{
public interface IPortAllocator
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>9</LangVersion>
<PackageIcon>icon.png</PackageIcon>
<Authors>BeatTogether Team</Authors>
<Company>BeatTogether</Company>
<RepositoryUrl>https://github.com/beattogether/BeatTogether.DedicatedServer</RepositoryUrl>
<Version>1.0.0</Version>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BeatTogether.Core" Version="1.0.3" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\BeatTogether.DedicatedServer.Kernel\BeatTogether.DedicatedServer.Kernel.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;

namespace BeatTogether.DedicatedServer.Instancing.Configuration
{
public sealed class InstancingConfiguration
{
public string HostName { get; set; } = "192.168.0.21";
public int BasePort { get; set; } = 30000;
public int MaximumSlots { get; set; } = 10000;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using BeatTogether.DedicatedServer.Instancing.Configuration;
using BeatTogether.DedicatedServer.Instancing.Abstractions;
using BeatTogether.Extensions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using BeatTogether.DedicatedServer.Kernel.Extensions;
using BeatTogether.Core.Abstractions;

namespace BeatTogether.DedicatedServer.Instancing.Extensions
{
public static class HostBuilderExtensions
{
public static IHostBuilder UseDedicatedServerInstancing(this IHostBuilder hostBuilder) =>
hostBuilder
.ConfigureAppConfiguration()
.UseSerilog()
.UseDedicatedInstances()
.ConfigureServices((hostBuilderContext, services) =>
services
.AddConfiguration<InstancingConfiguration>("Instancing")
.AddSingleton<IPortAllocator, PortAllocator>()
.AddSingleton<IInstanceRegistry, InstanceRegistry>()
.AddSingleton<IInstanceFactory, InstanceFactory>()
.AddSingleton<ILayer2, LayerService>()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using BeatTogether.Core.Abstractions;
using BeatTogether.Core.Enums;
using BeatTogether.Core.Models;
using BeatTogether.DedicatedServer.Kernel.Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;

namespace BeatTogether.DedicatedServer.Instancing.Implimentations
{
public class ServerInstance : IServerInstance
{
private readonly IDedicatedInstance _ServerInstance;

public ServerInstance(IDedicatedInstance serverInstance, IPEndPoint instanceEndPoint)
{
_ServerInstance = serverInstance;
InstanceEndPoint = instanceEndPoint;
}

public string ServerName { get => _ServerInstance._configuration.ServerName; set => throw new NotImplementedException(); }
public IPEndPoint InstanceEndPoint { get; set; }
public string Secret { get => _ServerInstance._configuration.Secret; set => throw new NotImplementedException(); }
public string Code { get => _ServerInstance._configuration.Code; set => throw new NotImplementedException(); }
public MultiplayerGameState GameState { get => (MultiplayerGameState)_ServerInstance.State; set => throw new NotImplementedException(); }
public BeatmapDifficultyMask BeatmapDifficultyMask { get => _ServerInstance._configuration.BeatmapDifficultyMask; set => throw new NotImplementedException(); }
public GameplayModifiersMask GameplayModifiersMask { get => _ServerInstance._configuration.GameplayModifiersMask; set => throw new NotImplementedException(); }
public string SongPackMasks { get => _ServerInstance._configuration.SongPacksMask; set => throw new NotImplementedException(); }
public GameplayServerConfiguration GameplayServerConfiguration { get => _ServerInstance._configuration.GameplayServerConfiguration; set => throw new NotImplementedException(); }
public HashSet<string> PlayerHashes { get => _ServerInstance.GetPlayerRegistry().Players.Select(p => p.HashedUserId).ToHashSet(); set => throw new NotImplementedException(); }
public string InstanceId { get => _ServerInstance._configuration.ServerId; set => throw new NotImplementedException(); }
public string ManagerId { get => _ServerInstance._configuration.ServerOwnerId; set => throw new NotImplementedException(); }
public bool PermanentManager { get => !string.IsNullOrEmpty(_ServerInstance._configuration.SetConstantManagerFromUserId); set => throw new NotImplementedException(); }
public long ServerStartJoinTimeout { get => _ServerInstance._configuration.DestroyInstanceTimeout; set => throw new NotImplementedException(); }
public bool NeverCloseServer { get => _ServerInstance._configuration.DestroyInstanceTimeout == -1; set => throw new NotImplementedException(); }
public long ResultScreenTime { get => _ServerInstance._configuration.CountdownConfig.ResultsScreenTime; set => throw new NotImplementedException(); }
public long BeatmapStartTime { get => _ServerInstance._configuration.CountdownConfig.BeatMapStartCountdownTime; set => throw new NotImplementedException(); }
public long PlayersReadyCountdownTime { get => _ServerInstance._configuration.CountdownConfig.CountdownTimePlayersReady; set => throw new NotImplementedException(); }
public bool AllowPerPlayerModifiers { get => _ServerInstance._configuration.AllowPerPlayerModifiers; set => throw new NotImplementedException(); }
public bool AllowPerPlayerDifficulties { get => _ServerInstance._configuration.AllowPerPlayerDifficulties; set => throw new NotImplementedException(); }
public bool AllowChroma { get => _ServerInstance._configuration.AllowChroma; set => throw new NotImplementedException(); }
public bool AllowME { get => _ServerInstance._configuration.AllowMappingExtensions; set => throw new NotImplementedException(); }
public bool AllowNE { get => _ServerInstance._configuration.AllowNoodleExtensions; set => throw new NotImplementedException(); }
}
}
104 changes: 104 additions & 0 deletions BeatTogether.DedicatedServer.Instancing/InstanceFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using System;
using BeatTogether.DedicatedServer.Kernel.Abstractions;
using BeatTogether.DedicatedServer.Kernel.Configuration;
using BeatTogether.Core.Enums;
using BeatTogether.DedicatedServer.Instancing.Abstractions;
using Microsoft.Extensions.DependencyInjection;
using BeatTogether.Core.Abstractions;
using System.Net;
using BeatTogether.DedicatedServer.Instancing.Configuration;
using BeatTogether.Core.ServerMessaging;
using BeatTogether.DedicatedServer.Instancing.Implimentations;
using Serilog;

namespace BeatTogether.DedicatedServer.Instancing
{
public sealed class InstanceFactory : IInstanceFactory
{
private readonly IInstanceRegistry _instanceRegistry;
private readonly IServiceProvider _serviceProvider;
private readonly IPortAllocator _portAllocator;
private readonly InstancingConfiguration _config;
private readonly ILayer1? _SendEventsLayer;
private readonly ILogger _logger = Log.ForContext<InstanceFactory>();

public InstanceFactory(
IInstanceRegistry instanceRegistry,
IServiceProvider serviceProvider,
IPortAllocator portAllocator,
InstancingConfiguration instancingConfiguration)
{
_instanceRegistry = instanceRegistry;
_serviceProvider = serviceProvider;
_portAllocator = portAllocator;
_config = instancingConfiguration;

_SendEventsLayer = _serviceProvider.GetService<ILayer1>();
}

public IDedicatedInstance? CreateInstance(IServerInstance serverInstance)
{
var Port = _portAllocator.AcquirePort();

if (!Port.HasValue)
return null;

var scope = _serviceProvider.CreateScope();

var instanceConfig = scope.ServiceProvider.GetRequiredService<InstanceConfiguration>();
instanceConfig.Port = (int)Port!;
instanceConfig.Secret = serverInstance.Secret;
instanceConfig.Code = serverInstance.Code;
instanceConfig.ServerId = serverInstance.InstanceId;
_logger.Information("Server ID: " + instanceConfig.ServerId);
instanceConfig.ServerOwnerId = serverInstance.ManagerId;
_logger.Information("Server owner ID: " + instanceConfig.ServerOwnerId);
instanceConfig.GameplayServerConfiguration = serverInstance.GameplayServerConfiguration;
instanceConfig.GameplayModifiersMask = serverInstance.GameplayModifiersMask;
instanceConfig.BeatmapDifficultyMask = serverInstance.BeatmapDifficultyMask;
instanceConfig.SongPacksMask = serverInstance.SongPackMasks;
instanceConfig.DestroyInstanceTimeout = serverInstance.ServerStartJoinTimeout;
instanceConfig.ServerName = serverInstance.ServerName;
instanceConfig.CountdownConfig.BeatMapStartCountdownTime = Math.Max(serverInstance.BeatmapStartTime, 0L);
instanceConfig.CountdownConfig.ResultsScreenTime = Math.Max(serverInstance.ResultScreenTime, 0L); //TODO convert the dedi logic to use long instead of float
instanceConfig.AllowChroma = serverInstance.AllowChroma;
instanceConfig.AllowMappingExtensions = serverInstance.AllowME;
instanceConfig.AllowNoodleExtensions = serverInstance.AllowNE;
instanceConfig.AllowPerPlayerDifficulties = serverInstance.AllowPerPlayerDifficulties;
instanceConfig.AllowPerPlayerModifiers = serverInstance.AllowPerPlayerModifiers;
if (serverInstance.PermanentManager)
instanceConfig.SetConstantManagerFromUserId = serverInstance.ManagerId;
instanceConfig.CountdownConfig.CountdownTimePlayersReady = Math.Max(serverInstance.PlayersReadyCountdownTime, 0L);
if (instanceConfig.CountdownConfig.CountdownTimePlayersReady == 0L)
instanceConfig.CountdownConfig.CountdownTimePlayersReady = instanceConfig.GameplayServerConfiguration.GameplayServerMode == GameplayServerMode.Managed ? 15000L : 30000L;
var instance = scope.ServiceProvider.GetRequiredService<IDedicatedInstance>();
if (!_instanceRegistry.AddInstance(instance))
{
return null;

}
instance.StopEvent += HandleStopEvent;

serverInstance.InstanceEndPoint = IPEndPoint.Parse($"{_config.HostName}:{instanceConfig.Port}");

//Subscribe to server events if the layer above allows this.
if(_SendEventsLayer != null)
{
instance.StopEvent += (dedi) => _SendEventsLayer.InstanceClosed(new ServerInstance(instance, serverInstance.InstanceEndPoint));
instance.PlayerConnectedEvent += (player) => _SendEventsLayer.PlayerJoined(new ServerInstance(instance, serverInstance.InstanceEndPoint), player);
instance.PlayerDisconnectedEvent += (player) => _SendEventsLayer.PlayerLeft(new ServerInstance(instance, serverInstance.InstanceEndPoint), player);
instance.PlayerDisconnectBeforeJoining += (a, b, c) => _SendEventsLayer.InstancePlayersChanged(new ServerInstance(instance, serverInstance.InstanceEndPoint));
instance.GameIsInLobby += (a, b) => _SendEventsLayer.InstanceStateChanged(new ServerInstance(instance, serverInstance.InstanceEndPoint));
instance.UpdateInstanceEvent += (dedi) => _SendEventsLayer.InstanceConfigChanged(new ServerInstance(instance, serverInstance.InstanceEndPoint));
}

return instance;
}

private void HandleStopEvent(IDedicatedInstance Instance)
{
_instanceRegistry.RemoveInstance(Instance);
_portAllocator.ReleasePort(Instance.Port);
}
}
}
60 changes: 60 additions & 0 deletions BeatTogether.DedicatedServer.Instancing/InstanceRegistry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using BeatTogether.DedicatedServer.Kernel.Abstractions;
using BeatTogether.DedicatedServer.Instancing.Abstractions;
using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using BeatTogether.Core.Enums;
using System.Linq;

namespace BeatTogether.DedicatedServer.Instancing
{
public sealed class InstanceRegistry : IInstanceRegistry
{
private readonly ConcurrentDictionary<string, IDedicatedInstance> _instances = new();
private readonly ConcurrentDictionary<string, IDedicatedInstance> _instancesByCode = new();

public bool AddInstance(IDedicatedInstance instance){
if (_instances.TryAdd(instance._configuration.Secret, instance)) {
if(_instancesByCode.TryAdd(instance._configuration.Code, instance))
return true;
_instances.TryRemove(instance._configuration.Secret, out _);
}
return false;
}

public bool RemoveInstance(IDedicatedInstance instance) => _instances.TryRemove(instance._configuration.Secret, out _) && _instancesByCode.TryRemove(instance._configuration.Code, out _);

public bool TryGetAvailablePublicServer(InvitePolicy invitePolicy, GameplayServerMode serverMode, SongSelectionMode songMode, GameplayServerControlSettings serverControlSettings, BeatmapDifficultyMask difficultyMask, GameplayModifiersMask modifiersMask, string songPackMasks, [MaybeNullWhen(false)] out IDedicatedInstance instance)
{
instance = null;
var AvaliableServers = _instances.Values.Where(s =>
s._configuration.GameplayServerConfiguration.InvitePolicy == invitePolicy &&
s._configuration.GameplayServerConfiguration.GameplayServerMode == serverMode &&
s._configuration.GameplayServerConfiguration.SongSelectionMode == songMode &&
s._configuration.GameplayServerConfiguration.GameplayServerControlSettings == serverControlSettings &&
s._configuration.BeatmapDifficultyMask == difficultyMask &&
s._configuration.GameplayModifiersMask == modifiersMask &&
s._configuration.SongPacksMask == songPackMasks
);
if (!AvaliableServers.Any())
return false;
var server = AvaliableServers.First();
foreach (var publicServer in AvaliableServers)
{
if ((publicServer.GetPlayerRegistry().GetPlayerCount() < publicServer._configuration.GameplayServerConfiguration.MaxPlayerCount && publicServer.GetPlayerRegistry().GetPlayerCount() > server.GetPlayerRegistry().GetPlayerCount()))
{
server = publicServer;
}
}
if (server.GetPlayerRegistry().GetPlayerCount() >= server._configuration.GameplayServerConfiguration.MaxPlayerCount)
return false;
instance = server;
return true;
}

public bool TryGetInstance(string secret, [MaybeNullWhen(false)] out IDedicatedInstance instance) =>
_instances.TryGetValue(secret, out instance);

public bool TryGetInstanceByCode(string code, [MaybeNullWhen(false)] out IDedicatedInstance instance) =>
_instancesByCode.TryGetValue(code, out instance);
}
}
Loading

0 comments on commit 44d928b

Please sign in to comment.