Skip to content

Commit

Permalink
Merge pull request #76 from BeatTogether/feat/supported-versions
Browse files Browse the repository at this point in the history
Add version ranged lobbies
  • Loading branch information
michael-r-elp authored Feb 8, 2025
2 parents 7330757 + e405ad6 commit 59fbd68
Show file tree
Hide file tree
Showing 22 changed files with 207 additions and 80 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -351,4 +351,4 @@ MigrationBackup/

out/
run/
/BeatTogether.MasterServer.Kernel/Properties/launchSettings.json
/BeatTogether.MasterServer/appsettings.LocalDevelopment.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="BeatTogether.Core" Version="1.1.0" />
<PackageReference Include="BeatTogether.Core" Version="1.2.0" />
<PackageReference Include="BeatTogether.Extensions.Serilog" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.29" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
using System;
using System.Collections.Generic;
using BeatTogether.Core.Enums;
using BeatTogether.Core.Models;
using BeatTogether.MasterServer.Api.Util;

namespace BeatTogether.MasterServer.Api.Configuration
{
public class ApiServerConfiguration
public sealed class ApiServerConfiguration
{
public int SessionTimeToLive { get; set; } = 180;
public bool AuthenticateClients { get; set; } = true;
public HashSet<VersionRange> VersionRanges { get; set; } = new();
public HashSet<Platform> AuthedClients { get; set; } = new();
}
}
13 changes: 10 additions & 3 deletions BeatTogether.MasterServer.Api/Extensions/HostBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
using BeatTogether.MasterServer.Api.Implementations;
using BeatTogether.MasterServer.Api.Implimentations;
using BeatTogether.MasterServer.Kernel.Implementations.Providers;
using BinaryRecords.Extensions;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Rewrite;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

Expand Down Expand Up @@ -38,9 +40,14 @@ public static IHostBuilder UseMasterServerApi(this IHostBuilder hostBuilder) =>
)
.Configure(applicationBuilder =>
applicationBuilder
.UseRouting()
.Use((context, next) =>
{
context.Response.Headers.Add("X-Robots-Tag", "noindex, nofollow"); // Tell everyone that we don't want to be indexed
return next(context);
})
.UseRouting()
.UseEndpoints(endPointRouteBuilder => endPointRouteBuilder.MapControllers())
)
);
)
);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using BeatTogether.MasterServer.Messaging.Enums;
using BeatTogether.MasterServer.Messaging.Models;
Expand All @@ -10,8 +11,10 @@
using BeatTogether.MasterServer.Api.Abstractions;
using BeatTogether.MasterServer.Api.Util;
using System.Text;
using BeatTogether.Core.Models;
using BeatTogether.MasterServer.Domain.Models;
using BeatTogether.MasterServer.Api.Abstractions.Providers;
using BeatTogether.MasterServer.Api.Configuration;

namespace BeatTogether.MasterServer.Api.HttpControllers
{
Expand All @@ -30,21 +33,25 @@ public class GetMultiplayerInstanceController : Controller

private readonly ILayer2 _layer2;

public GetMultiplayerInstanceController(
private readonly ApiServerConfiguration _apiServerConfiguration;

public GetMultiplayerInstanceController(
IMasterServerSessionService sessionService,
ILayer2 layer2,
IServerCodeProvider serverCodeProvider,
ISecretProvider secretProvider,
IUserAuthenticator userAuthenticator)
IUserAuthenticator userAuthenticator,
ApiServerConfiguration configuration)
{
_layer2 = layer2;
_sessionService = sessionService;
_serverCodeProvider = serverCodeProvider;
_secretProvider = secretProvider;
_userAuthenticator = userAuthenticator;


_logger = Log.ForContext<GetMultiplayerInstanceController>();
_apiServerConfiguration = configuration;

_logger = Log.ForContext<GetMultiplayerInstanceController>();
}

/// <summary>
Expand All @@ -57,8 +64,6 @@ public async Task<IActionResult> GetMultiplayerInstance([FromBody] GetMultiplaye
var response = new GetMultiplayerInstanceResponse();
response.AddRequestContext(request);

// TODO Validate game client version supported range?

if (HttpContext.Connection.RemoteIpAddress is null)
{
_logger.Warning("Auth failure: Missing IP address from HTTP request context");
Expand Down Expand Up @@ -104,8 +109,25 @@ public async Task<IActionResult> GetMultiplayerInstance([FromBody] GetMultiplaye
//if(session == null) { _logger.Information("Player Session is null"); }
response.AddSessionContext(session.PlayerSessionId);

//Player authed and has a session, now get them a server.
bool isQuickplay = string.IsNullOrEmpty(request.PrivateGameCode) && string.IsNullOrEmpty(request.PrivateGameSecret); //Quickplay is true if there is no code and no secret
// Get version range of player
VersionRange supportedRange = VersionRange.FindVersionRange(_apiServerConfiguration.VersionRanges.ToList(), session.PlayerClientVersion!);
if (supportedRange == null)
{
// Version not supported at all according to config
_logger.Error($"Could not find matching version range for client version: {session.PlayerClientVersion}");
response.ErrorCode = MultiplayerPlacementErrorCode.GameVersionUnknown;
return new JsonResult(response);
}

//if (supportedRange != null) // TODO: Should we just set to exact version match if not known?
// supportedRange = new VersionRange
// { MinVersion = session.PlayerClientVersion.ToString(), MaxVersion = session.PlayerClientVersion.ToString() };

_logger.Debug(
$"Found version range MinVersion: '{supportedRange.MinVersion}' MaxVersion: '{supportedRange.MaxVersion}' for client version '{session.PlayerClientVersion}'");

//Player authed and has a session, now get them a server.
bool isQuickplay = string.IsNullOrEmpty(request.PrivateGameCode) && string.IsNullOrEmpty(request.PrivateGameSecret); //Quickplay is true if there is no code and no secret
IServerInstance server = null;
if (!isQuickplay)
{
Expand All @@ -123,7 +145,8 @@ public async Task<IActionResult> GetMultiplayerInstance([FromBody] GetMultiplaye
(Core.Enums.GameplayServerControlSettings)request.GameplayServerConfiguration.GameplayServerControlSettings,
(Core.Enums.BeatmapDifficultyMask)request.BeatmapLevelSelectionMask.BeatmapDifficultyMask,
(Core.Enums.GameplayModifiersMask)request.BeatmapLevelSelectionMask.GameplayModifiersMask,
request.BeatmapLevelSelectionMask.SongPackMasks);
request.BeatmapLevelSelectionMask.SongPackMasks,
supportedRange);
}

//If the server is still null, then make new server
Expand All @@ -144,13 +167,13 @@ public async Task<IActionResult> GetMultiplayerInstance([FromBody] GetMultiplaye
string secret = request.PrivateGameSecret;
string managerId = FixedServerUserId;
if (!isQuickplay)
managerId = session.HashedUserId;//sets the manager to the player who is requesting
managerId = session.HashedUserId; //sets the manager to the player who is requesting
else
secret = _secretProvider.GetSecret();

string ServerName = string.Empty;
if (isQuickplay)
ServerName = "BeatTogether Quickplay: " + ((Core.Enums.BeatmapDifficultyMask)request.BeatmapLevelSelectionMask.BeatmapDifficultyMask).ToString();
ServerName = "BeatTogether Quickplay: " + ((Core.Enums.BeatmapDifficultyMask)request.BeatmapLevelSelectionMask.BeatmapDifficultyMask);
else if (request.ExtraServerConfiguration != null && request.ExtraServerConfiguration.ServerName != null)
{
ServerName = request.ExtraServerConfiguration.ServerName;
Expand Down Expand Up @@ -184,7 +207,8 @@ public async Task<IActionResult> GetMultiplayerInstance([FromBody] GetMultiplaye
ServerStartJoinTimeout = 10000L,
//ServerStartJoinTimeout = request.ExtraServerConfiguration.Timeout ?? 10, //Dont allow everyone to do this as -1 means infinite server online time, server wont turn off when a player leaves
PermanentManager = request.ExtraServerConfiguration != null ? request.ExtraServerConfiguration.PermenantManger ?? true : true,
};
SupportedVersionRange = supportedRange
};
//Missing values from the server instance such as endpoint, will be added from within the CreateInstance function below.
if (!await _layer2.CreateInstance(server))
{
Expand All @@ -203,7 +227,29 @@ public async Task<IActionResult> GetMultiplayerInstance([FromBody] GetMultiplaye
return new JsonResult(response);
}

_logger.Information("Player session data from player: " + session.HashedUserId + " Is being sent to node: " + server.InstanceEndPoint + ", Server secret: " + server.Secret + ", Player count before join: " + server.PlayerHashes.Count);
// Checks if the joining players version is witin the supported range of the lobby
switch (VersionRange.CheckVersionRange(server.SupportedVersionRange, session.PlayerClientVersion))
{
case VersionRange.VersionStatus.Ok:
break;
case VersionRange.VersionStatus.TooHigh:
_logger.Warning($"Player '{session.HashedUserId}' on version '{session.PlayerClientVersion}' cannot join lobby with range {server.SupportedVersionRange.MinVersion} - {server.SupportedVersionRange.MaxVersion} reason: Game Version Too New");
response.ErrorCode = MultiplayerPlacementErrorCode.GameVersionTooNew;
return new JsonResult(response);
case VersionRange.VersionStatus.TooLow:
_logger.Warning($"Player '{session.HashedUserId}' on version '{session.PlayerClientVersion}' cannot join lobby with range {server.SupportedVersionRange.MinVersion} - {server.SupportedVersionRange.MaxVersion} reason: Game Version Too Old");
response.ErrorCode = MultiplayerPlacementErrorCode.GameVersionTooOld;
return new JsonResult(response);
}
//if (!VersionRange.VersionRangeSatisfies(server.SupportedVersionRange,
// session.PlayerClientVersion.ToString()))
//{
// _logger.Warning($"Player '{session.HashedUserId}' on version '{session.PlayerClientVersion}' cannot join lobby with range {server.SupportedVersionRange.MinVersion} - {server.SupportedVersionRange.MaxVersion}");
// response.ErrorCode = MultiplayerPlacementErrorCode.LobbyHostVersionMismatch;
// return new JsonResult(response);
//}

_logger.Information("Player session data from player: " + session.HashedUserId + " Is being sent to node: " + server.InstanceEndPoint + ", Server secret: " + server.Secret + ", Player count before join: " + server.PlayerHashes.Count);

if (!await _layer2.SetPlayerSessionData(server.Secret, session))
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Text.Json;
Expand Down Expand Up @@ -146,14 +147,8 @@ public async Task<bool> TryAuthenticateUserWithPlatform(MasterServerSession sess

private bool GetPlatformRequiresAuth(Platform platform)
{
return platform switch
{
Platform.Steam => true,
Platform.OculusQuest => true,
Platform.Oculus => true,
Platform.Pico => false, // TODO: Pico auth, if possible
_ => false,
};
_logger.Debug("Authed Platforms: " + string.Join(", ", _apiServerConfiguration.AuthedClients));
return _apiServerConfiguration.AuthedClients.Contains(platform);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Net;
using System.Threading.Tasks;
using BeatTogether.Core.Enums;
using BeatTogether.Core.Models;
using BeatTogether.MasterServer.Domain.Models;

namespace BeatTogether.MasterServer.Data.Abstractions.Repositories
Expand All @@ -10,14 +11,15 @@ public interface IServerRepository
Task<Server> GetServer(string secret);
Task<Server> GetServerByCode(string code);
Task<Server> GetAvailablePublicServer(
InvitePolicy invitePolicy,
GameplayServerMode serverMode,
SongSelectionMode songMode,
GameplayServerControlSettings serverControlSettings,
BeatmapDifficultyMask difficultyMask,
GameplayModifiersMask modifiersMask,
string SongPackMasks);
Task<bool> UpdateServer(string secret, Server server);
InvitePolicy invitePolicy,
GameplayServerMode serverMode,
SongSelectionMode songMode,
GameplayServerControlSettings serverControlSettings,
BeatmapDifficultyMask difficultyMask,
GameplayModifiersMask modifiersMask,
string SongPackMasks,
VersionRange versionRange);
Task<bool> UpdateServer(string secret, Server server);
Task<string[]> GetPublicServerSecrets();
Task<string[]> GetPublicServerCodes();
Task<Server[]> GetPublicServerList();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using BeatTogether.MasterServer.Data.Abstractions.Repositories;
using BeatTogether.Core.Enums;
using BeatTogether.Core.Models;
using BeatTogether.MasterServer.Domain.Models;

namespace BeatTogether.MasterServer.Data.Implementations.Repositories
Expand Down Expand Up @@ -96,28 +98,29 @@ public Task<Server> GetServerByCode(string code)
}

public Task<Server> GetAvailablePublicServer(
InvitePolicy invitePolicy,
GameplayServerMode serverMode,
SongSelectionMode songMode,
GameplayServerControlSettings serverControlSettings,
BeatmapDifficultyMask difficultyMask,
GameplayModifiersMask modifiersMask,
string SongPackMasks)
{
if (!_servers.Any())
return Task.FromResult<Server>(null);
//Search for public server that fits the filter
var publicServers = _servers.Values.Where(server =>
server.GameplayServerConfiguration.DiscoveryPolicy == DiscoveryPolicy.Public &&
server.GameplayServerConfiguration.InvitePolicy == invitePolicy &&
server.GameplayServerConfiguration.GameplayServerMode == serverMode &&
server.GameplayServerConfiguration.SongSelectionMode == songMode &&
server.GameplayServerConfiguration.GameplayServerControlSettings == serverControlSettings &&
server.BeatmapDifficultyMask == difficultyMask &&
server.GameplayModifiersMask == modifiersMask &&
server.SongPackMasks == SongPackMasks &&
InvitePolicy invitePolicy,
GameplayServerMode serverMode,
SongSelectionMode songMode,
GameplayServerControlSettings serverControlSettings,
BeatmapDifficultyMask difficultyMask,
GameplayModifiersMask modifiersMask,
string SongPackMasks,
VersionRange versionRange)
{
if (!_servers.Any())
return Task.FromResult<Server>(null);
var publicServers = _servers.Values.Where(server =>
server.GameplayServerConfiguration.DiscoveryPolicy == DiscoveryPolicy.Public &&
server.GameplayServerConfiguration.InvitePolicy == invitePolicy &&
server.GameplayServerConfiguration.GameplayServerMode == serverMode &&
server.GameplayServerConfiguration.SongSelectionMode == songMode &&
server.GameplayServerConfiguration.GameplayServerControlSettings == serverControlSettings &&
server.BeatmapDifficultyMask == difficultyMask &&
server.GameplayModifiersMask == modifiersMask &&
server.SongPackMasks == SongPackMasks &&
server.SupportedVersionRange == versionRange &&
server.CurrentPlayerCount <= server.GameplayServerConfiguration.MaxPlayerCount
);
);
if (!publicServers.Any())
return Task.FromResult<Server>(null);
var server = publicServers.First();
Expand All @@ -132,7 +135,8 @@ public Task<Server> GetAvailablePublicServer(
return Task.FromResult(server);
}

public Task<bool> AddServer(Server server)

public Task<bool> AddServer(Server server)
{
if (!_servers.TryAdd(server.Secret, server))
return Task.FromResult(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ public async Task<Server> GetAvailablePublicServer(InvitePolicy invitePolicy,
GameplayServerControlSettings serverControlSettings,
BeatmapDifficultyMask difficultyMask,
GameplayModifiersMask modifiersMask,
string SongPackMasks)
string SongPackMasks,
VersionRange versionRange)
{
var database = _connectionMultiplexer.GetDatabase();
var redisValues = await database.SortedSetRangeByScoreAsync(RedisKeys.PublicServersByPlayerCount, take: 1);
Expand Down Expand Up @@ -132,6 +133,7 @@ public async Task<bool> AddServer(Server server)
currentPlayerCount = (RedisValue)server.CurrentPlayerCount,
//random = (RedisValue)server.Random,
//publicKey = (RedisValue)server.PublicKey
//SupportedVersionRange = (RedisValue)()
},
flags: CommandFlags.DemandMaster
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</PropertyGroup>

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

</Project>
5 changes: 4 additions & 1 deletion BeatTogether.MasterServer.Domain/Models/Server.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Net;
using BeatTogether.Core.Abstractions;
using BeatTogether.Core.Enums;
Expand All @@ -23,6 +24,8 @@ public Server() { }
public HashSet<string> PlayerHashes { get; set; } = new();

public string ManagerId { get; set; }

public VersionRange SupportedVersionRange { get; set; }
public bool PermanentManager { get; set; }
public long ServerStartJoinTimeout { get; set; }
public bool NeverCloseServer { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

<ItemGroup>
<PackageReference Include="Autobus.Abstractions" Version="0.1.5" />
<PackageReference Include="BeatTogether.Core" Version="1.0.3" />
<PackageReference Include="BeatTogether.Core" Version="1.2.0" />
</ItemGroup>

</Project>
Loading

0 comments on commit 59fbd68

Please sign in to comment.