Skip to content

Commit 59fbd68

Browse files
Merge pull request #76 from BeatTogether/feat/supported-versions
Add version ranged lobbies
2 parents 7330757 + e405ad6 commit 59fbd68

22 files changed

Lines changed: 207 additions & 80 deletions

File tree

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,4 +351,4 @@ MigrationBackup/
351351

352352
out/
353353
run/
354-
/BeatTogether.MasterServer.Kernel/Properties/launchSettings.json
354+
/BeatTogether.MasterServer/appsettings.LocalDevelopment.json

BeatTogether.MasterServer.Api/BeatTogether.MasterServer.Api.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
</ItemGroup>
1818

1919
<ItemGroup>
20-
<PackageReference Include="BeatTogether.Core" Version="1.1.0" />
20+
<PackageReference Include="BeatTogether.Core" Version="1.2.0" />
2121
<PackageReference Include="BeatTogether.Extensions.Serilog" Version="2.1.0" />
2222
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.29" />
2323
</ItemGroup>
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
using System;
2+
using System.Collections.Generic;
3+
using BeatTogether.Core.Enums;
4+
using BeatTogether.Core.Models;
5+
using BeatTogether.MasterServer.Api.Util;
26

37
namespace BeatTogether.MasterServer.Api.Configuration
48
{
5-
public class ApiServerConfiguration
9+
public sealed class ApiServerConfiguration
610
{
711
public int SessionTimeToLive { get; set; } = 180;
812
public bool AuthenticateClients { get; set; } = true;
13+
public HashSet<VersionRange> VersionRanges { get; set; } = new();
14+
public HashSet<Platform> AuthedClients { get; set; } = new();
915
}
1016
}

BeatTogether.MasterServer.Api/Extensions/HostBuilderExtensions.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
using BeatTogether.MasterServer.Api.Implementations;
88
using BeatTogether.MasterServer.Api.Implimentations;
99
using BeatTogether.MasterServer.Kernel.Implementations.Providers;
10+
using BinaryRecords.Extensions;
1011
using Microsoft.AspNetCore.Builder;
1112
using Microsoft.AspNetCore.Hosting;
13+
using Microsoft.AspNetCore.Rewrite;
1214
using Microsoft.Extensions.DependencyInjection;
1315
using Microsoft.Extensions.Hosting;
1416

@@ -38,9 +40,14 @@ public static IHostBuilder UseMasterServerApi(this IHostBuilder hostBuilder) =>
3840
)
3941
.Configure(applicationBuilder =>
4042
applicationBuilder
41-
.UseRouting()
43+
.Use((context, next) =>
44+
{
45+
context.Response.Headers.Add("X-Robots-Tag", "noindex, nofollow"); // Tell everyone that we don't want to be indexed
46+
return next(context);
47+
})
48+
.UseRouting()
4249
.UseEndpoints(endPointRouteBuilder => endPointRouteBuilder.MapControllers())
43-
)
44-
);
50+
)
51+
);
4552
}
4653
}

BeatTogether.MasterServer.Api/HttpControllers/GetMultiplayerInstanceController.cs

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Linq;
23
using System.Threading.Tasks;
34
using BeatTogether.MasterServer.Messaging.Enums;
45
using BeatTogether.MasterServer.Messaging.Models;
@@ -10,8 +11,10 @@
1011
using BeatTogether.MasterServer.Api.Abstractions;
1112
using BeatTogether.MasterServer.Api.Util;
1213
using System.Text;
14+
using BeatTogether.Core.Models;
1315
using BeatTogether.MasterServer.Domain.Models;
1416
using BeatTogether.MasterServer.Api.Abstractions.Providers;
17+
using BeatTogether.MasterServer.Api.Configuration;
1518

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

3134
private readonly ILayer2 _layer2;
3235

33-
public GetMultiplayerInstanceController(
36+
private readonly ApiServerConfiguration _apiServerConfiguration;
37+
38+
public GetMultiplayerInstanceController(
3439
IMasterServerSessionService sessionService,
3540
ILayer2 layer2,
3641
IServerCodeProvider serverCodeProvider,
3742
ISecretProvider secretProvider,
38-
IUserAuthenticator userAuthenticator)
43+
IUserAuthenticator userAuthenticator,
44+
ApiServerConfiguration configuration)
3945
{
4046
_layer2 = layer2;
4147
_sessionService = sessionService;
4248
_serverCodeProvider = serverCodeProvider;
4349
_secretProvider = secretProvider;
4450
_userAuthenticator = userAuthenticator;
4551

46-
47-
_logger = Log.ForContext<GetMultiplayerInstanceController>();
52+
_apiServerConfiguration = configuration;
53+
54+
_logger = Log.ForContext<GetMultiplayerInstanceController>();
4855
}
4956

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

60-
// TODO Validate game client version supported range?
61-
6267
if (HttpContext.Connection.RemoteIpAddress is null)
6368
{
6469
_logger.Warning("Auth failure: Missing IP address from HTTP request context");
@@ -104,8 +109,25 @@ public async Task<IActionResult> GetMultiplayerInstance([FromBody] GetMultiplaye
104109
//if(session == null) { _logger.Information("Player Session is null"); }
105110
response.AddSessionContext(session.PlayerSessionId);
106111

107-
//Player authed and has a session, now get them a server.
108-
bool isQuickplay = string.IsNullOrEmpty(request.PrivateGameCode) && string.IsNullOrEmpty(request.PrivateGameSecret); //Quickplay is true if there is no code and no secret
112+
// Get version range of player
113+
VersionRange supportedRange = VersionRange.FindVersionRange(_apiServerConfiguration.VersionRanges.ToList(), session.PlayerClientVersion!);
114+
if (supportedRange == null)
115+
{
116+
// Version not supported at all according to config
117+
_logger.Error($"Could not find matching version range for client version: {session.PlayerClientVersion}");
118+
response.ErrorCode = MultiplayerPlacementErrorCode.GameVersionUnknown;
119+
return new JsonResult(response);
120+
}
121+
122+
//if (supportedRange != null) // TODO: Should we just set to exact version match if not known?
123+
// supportedRange = new VersionRange
124+
// { MinVersion = session.PlayerClientVersion.ToString(), MaxVersion = session.PlayerClientVersion.ToString() };
125+
126+
_logger.Debug(
127+
$"Found version range MinVersion: '{supportedRange.MinVersion}' MaxVersion: '{supportedRange.MaxVersion}' for client version '{session.PlayerClientVersion}'");
128+
129+
//Player authed and has a session, now get them a server.
130+
bool isQuickplay = string.IsNullOrEmpty(request.PrivateGameCode) && string.IsNullOrEmpty(request.PrivateGameSecret); //Quickplay is true if there is no code and no secret
109131
IServerInstance server = null;
110132
if (!isQuickplay)
111133
{
@@ -123,7 +145,8 @@ public async Task<IActionResult> GetMultiplayerInstance([FromBody] GetMultiplaye
123145
(Core.Enums.GameplayServerControlSettings)request.GameplayServerConfiguration.GameplayServerControlSettings,
124146
(Core.Enums.BeatmapDifficultyMask)request.BeatmapLevelSelectionMask.BeatmapDifficultyMask,
125147
(Core.Enums.GameplayModifiersMask)request.BeatmapLevelSelectionMask.GameplayModifiersMask,
126-
request.BeatmapLevelSelectionMask.SongPackMasks);
148+
request.BeatmapLevelSelectionMask.SongPackMasks,
149+
supportedRange);
127150
}
128151

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

151174
string ServerName = string.Empty;
152175
if (isQuickplay)
153-
ServerName = "BeatTogether Quickplay: " + ((Core.Enums.BeatmapDifficultyMask)request.BeatmapLevelSelectionMask.BeatmapDifficultyMask).ToString();
176+
ServerName = "BeatTogether Quickplay: " + ((Core.Enums.BeatmapDifficultyMask)request.BeatmapLevelSelectionMask.BeatmapDifficultyMask);
154177
else if (request.ExtraServerConfiguration != null && request.ExtraServerConfiguration.ServerName != null)
155178
{
156179
ServerName = request.ExtraServerConfiguration.ServerName;
@@ -184,7 +207,8 @@ public async Task<IActionResult> GetMultiplayerInstance([FromBody] GetMultiplaye
184207
ServerStartJoinTimeout = 10000L,
185208
//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
186209
PermanentManager = request.ExtraServerConfiguration != null ? request.ExtraServerConfiguration.PermenantManger ?? true : true,
187-
};
210+
SupportedVersionRange = supportedRange
211+
};
188212
//Missing values from the server instance such as endpoint, will be added from within the CreateInstance function below.
189213
if (!await _layer2.CreateInstance(server))
190214
{
@@ -203,7 +227,29 @@ public async Task<IActionResult> GetMultiplayerInstance([FromBody] GetMultiplaye
203227
return new JsonResult(response);
204228
}
205229

206-
_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);
230+
// Checks if the joining players version is witin the supported range of the lobby
231+
switch (VersionRange.CheckVersionRange(server.SupportedVersionRange, session.PlayerClientVersion))
232+
{
233+
case VersionRange.VersionStatus.Ok:
234+
break;
235+
case VersionRange.VersionStatus.TooHigh:
236+
_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");
237+
response.ErrorCode = MultiplayerPlacementErrorCode.GameVersionTooNew;
238+
return new JsonResult(response);
239+
case VersionRange.VersionStatus.TooLow:
240+
_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");
241+
response.ErrorCode = MultiplayerPlacementErrorCode.GameVersionTooOld;
242+
return new JsonResult(response);
243+
}
244+
//if (!VersionRange.VersionRangeSatisfies(server.SupportedVersionRange,
245+
// session.PlayerClientVersion.ToString()))
246+
//{
247+
// _logger.Warning($"Player '{session.HashedUserId}' on version '{session.PlayerClientVersion}' cannot join lobby with range {server.SupportedVersionRange.MinVersion} - {server.SupportedVersionRange.MaxVersion}");
248+
// response.ErrorCode = MultiplayerPlacementErrorCode.LobbyHostVersionMismatch;
249+
// return new JsonResult(response);
250+
//}
251+
252+
_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);
207253

208254
if (!await _layer2.SetPlayerSessionData(server.Secret, session))
209255
{

BeatTogether.MasterServer.Api/Implimentations/UserAuthenticator.cs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Linq;
23
using System.Net.Http;
34
using System.Text;
45
using System.Text.Json;
@@ -146,14 +147,8 @@ public async Task<bool> TryAuthenticateUserWithPlatform(MasterServerSession sess
146147

147148
private bool GetPlatformRequiresAuth(Platform platform)
148149
{
149-
return platform switch
150-
{
151-
Platform.Steam => true,
152-
Platform.OculusQuest => true,
153-
Platform.Oculus => true,
154-
Platform.Pico => false, // TODO: Pico auth, if possible
155-
_ => false,
156-
};
150+
_logger.Debug("Authed Platforms: " + string.Join(", ", _apiServerConfiguration.AuthedClients));
151+
return _apiServerConfiguration.AuthedClients.Contains(platform);
157152
}
158153
}
159154
}

BeatTogether.MasterServer.Data/Abstractions/Repositories/IServerRepository.cs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Net;
22
using System.Threading.Tasks;
33
using BeatTogether.Core.Enums;
4+
using BeatTogether.Core.Models;
45
using BeatTogether.MasterServer.Domain.Models;
56

67
namespace BeatTogether.MasterServer.Data.Abstractions.Repositories
@@ -10,14 +11,15 @@ public interface IServerRepository
1011
Task<Server> GetServer(string secret);
1112
Task<Server> GetServerByCode(string code);
1213
Task<Server> GetAvailablePublicServer(
13-
InvitePolicy invitePolicy,
14-
GameplayServerMode serverMode,
15-
SongSelectionMode songMode,
16-
GameplayServerControlSettings serverControlSettings,
17-
BeatmapDifficultyMask difficultyMask,
18-
GameplayModifiersMask modifiersMask,
19-
string SongPackMasks);
20-
Task<bool> UpdateServer(string secret, Server server);
14+
InvitePolicy invitePolicy,
15+
GameplayServerMode serverMode,
16+
SongSelectionMode songMode,
17+
GameplayServerControlSettings serverControlSettings,
18+
BeatmapDifficultyMask difficultyMask,
19+
GameplayModifiersMask modifiersMask,
20+
string SongPackMasks,
21+
VersionRange versionRange);
22+
Task<bool> UpdateServer(string secret, Server server);
2123
Task<string[]> GetPublicServerSecrets();
2224
Task<string[]> GetPublicServerCodes();
2325
Task<Server[]> GetPublicServerList();

BeatTogether.MasterServer.Data/Implementations/Repositories/MemoryServerRepository.cs

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
using System.Collections.Concurrent;
1+
using System;
2+
using System.Collections.Concurrent;
23
using System.Collections.Generic;
34
using System.Linq;
45
using System.Net;
56
using System.Threading;
67
using System.Threading.Tasks;
78
using BeatTogether.MasterServer.Data.Abstractions.Repositories;
89
using BeatTogether.Core.Enums;
10+
using BeatTogether.Core.Models;
911
using BeatTogether.MasterServer.Domain.Models;
1012

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

98100
public Task<Server> GetAvailablePublicServer(
99-
InvitePolicy invitePolicy,
100-
GameplayServerMode serverMode,
101-
SongSelectionMode songMode,
102-
GameplayServerControlSettings serverControlSettings,
103-
BeatmapDifficultyMask difficultyMask,
104-
GameplayModifiersMask modifiersMask,
105-
string SongPackMasks)
106-
{
107-
if (!_servers.Any())
108-
return Task.FromResult<Server>(null);
109-
//Search for public server that fits the filter
110-
var publicServers = _servers.Values.Where(server =>
111-
server.GameplayServerConfiguration.DiscoveryPolicy == DiscoveryPolicy.Public &&
112-
server.GameplayServerConfiguration.InvitePolicy == invitePolicy &&
113-
server.GameplayServerConfiguration.GameplayServerMode == serverMode &&
114-
server.GameplayServerConfiguration.SongSelectionMode == songMode &&
115-
server.GameplayServerConfiguration.GameplayServerControlSettings == serverControlSettings &&
116-
server.BeatmapDifficultyMask == difficultyMask &&
117-
server.GameplayModifiersMask == modifiersMask &&
118-
server.SongPackMasks == SongPackMasks &&
101+
InvitePolicy invitePolicy,
102+
GameplayServerMode serverMode,
103+
SongSelectionMode songMode,
104+
GameplayServerControlSettings serverControlSettings,
105+
BeatmapDifficultyMask difficultyMask,
106+
GameplayModifiersMask modifiersMask,
107+
string SongPackMasks,
108+
VersionRange versionRange)
109+
{
110+
if (!_servers.Any())
111+
return Task.FromResult<Server>(null);
112+
var publicServers = _servers.Values.Where(server =>
113+
server.GameplayServerConfiguration.DiscoveryPolicy == DiscoveryPolicy.Public &&
114+
server.GameplayServerConfiguration.InvitePolicy == invitePolicy &&
115+
server.GameplayServerConfiguration.GameplayServerMode == serverMode &&
116+
server.GameplayServerConfiguration.SongSelectionMode == songMode &&
117+
server.GameplayServerConfiguration.GameplayServerControlSettings == serverControlSettings &&
118+
server.BeatmapDifficultyMask == difficultyMask &&
119+
server.GameplayModifiersMask == modifiersMask &&
120+
server.SongPackMasks == SongPackMasks &&
121+
server.SupportedVersionRange == versionRange &&
119122
server.CurrentPlayerCount <= server.GameplayServerConfiguration.MaxPlayerCount
120-
);
123+
);
121124
if (!publicServers.Any())
122125
return Task.FromResult<Server>(null);
123126
var server = publicServers.First();
@@ -132,7 +135,8 @@ public Task<Server> GetAvailablePublicServer(
132135
return Task.FromResult(server);
133136
}
134137

135-
public Task<bool> AddServer(Server server)
138+
139+
public Task<bool> AddServer(Server server)
136140
{
137141
if (!_servers.TryAdd(server.Secret, server))
138142
return Task.FromResult(false);

BeatTogether.MasterServer.Data/Implementations/Repositories/ServerRepository.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ public async Task<Server> GetAvailablePublicServer(InvitePolicy invitePolicy,
9797
GameplayServerControlSettings serverControlSettings,
9898
BeatmapDifficultyMask difficultyMask,
9999
GameplayModifiersMask modifiersMask,
100-
string SongPackMasks)
100+
string SongPackMasks,
101+
VersionRange versionRange)
101102
{
102103
var database = _connectionMultiplexer.GetDatabase();
103104
var redisValues = await database.SortedSetRangeByScoreAsync(RedisKeys.PublicServersByPlayerCount, take: 1);
@@ -132,6 +133,7 @@ public async Task<bool> AddServer(Server server)
132133
currentPlayerCount = (RedisValue)server.CurrentPlayerCount,
133134
//random = (RedisValue)server.Random,
134135
//publicKey = (RedisValue)server.PublicKey
136+
//SupportedVersionRange = (RedisValue)()
135137
},
136138
flags: CommandFlags.DemandMaster
137139
);

BeatTogether.MasterServer.Domain/BeatTogether.MasterServer.Domain.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
</PropertyGroup>
77

88
<ItemGroup>
9-
<PackageReference Include="BeatTogether.Core" Version="1.0.3" />
9+
<PackageReference Include="BeatTogether.Core" Version="1.2.0" />
1010
</ItemGroup>
1111

1212
</Project>

0 commit comments

Comments
 (0)