diff --git a/Refresh.GameServer/Endpoints/Game/UserEndpoints.cs b/Refresh.GameServer/Endpoints/Game/UserEndpoints.cs
index a06f56de..f306c51c 100644
--- a/Refresh.GameServer/Endpoints/Game/UserEndpoints.cs
+++ b/Refresh.GameServer/Endpoints/Game/UserEndpoints.cs
@@ -169,14 +169,14 @@ public SerializedUserList GetMultipleUsers(RequestContext context, GameDatabaseC
/// The string shown in-game.
[GameEndpoint("filter", Method.Post)]
[AllowEmptyBody]
- public string Filter(RequestContext context, CommandService command, string body, GameUser user)
+ public string Filter(RequestContext context, CommandService commandService, string body, GameUser user, GameDatabaseContext database)
{
Debug.Assert(user != null);
Debug.Assert(body != null);
//TODO: Add filtering
- if (command.IsPublishing(user.UserId))
+ if (commandService.IsPublishing(user.UserId))
{
context.Logger.LogInfo(BunkumContext.Filter, $"Publish filter {body}");
}
@@ -186,9 +186,11 @@ public string Filter(RequestContext context, CommandService command, string body
try
{
- Command parsedCommand = command.ParseCommand(body);
+ Command command = commandService.ParseCommand(body);
- context.Logger.LogInfo(BunkumContext.Commands, $"User used command \"{parsedCommand.Name}\" with args \"{parsedCommand.Arguments}\"");
+ context.Logger.LogInfo(BunkumContext.Commands, $"User used command \"{command.Name}\" with args \"{command.Arguments}\"");
+
+ commandService.HandleCommand(command, database, user);
}
catch
{
diff --git a/Refresh.GameServer/RefreshGameServer.cs b/Refresh.GameServer/RefreshGameServer.cs
index 915dd8a4..405183b8 100644
--- a/Refresh.GameServer/RefreshGameServer.cs
+++ b/Refresh.GameServer/RefreshGameServer.cs
@@ -120,8 +120,8 @@ protected virtual void SetupServices()
this._server.AddRateLimitService(new RateLimitSettings(60, 400, 30, "global"));
this._server.AddService();
this._server.AddService();
- this._server.AddService();
this._server.AddService();
+ this._server.AddService();
this._server.AddService();
this._server.AddService();
this._server.AddAutoDiscover(serverBrand: "Refresh",
diff --git a/Refresh.GameServer/Services/CommandService.cs b/Refresh.GameServer/Services/CommandService.cs
index e7002bd4..9be9b848 100644
--- a/Refresh.GameServer/Services/CommandService.cs
+++ b/Refresh.GameServer/Services/CommandService.cs
@@ -3,13 +3,19 @@
using JetBrains.Annotations;
using MongoDB.Bson;
using NotEnoughLogs;
+using Refresh.GameServer.Database;
using Refresh.GameServer.Types.Commands;
+using Refresh.GameServer.Types.UserData;
namespace Refresh.GameServer.Services;
public class CommandService : EndpointService
{
- public CommandService(LoggerContainer logger) : base(logger) {}
+ private readonly MatchService _match;
+
+ public CommandService(LoggerContainer logger, MatchService match) : base(logger) {
+ this._match = match;
+ }
private readonly HashSet _usersPublishing = new();
@@ -67,4 +73,31 @@ public Command ParseCommand(string str)
return new Command(str[1..idx], str[(idx + 1)..]);
}
+
+ public void HandleCommand(Command command, GameDatabaseContext database, GameUser user)
+ {
+ switch (command.Name)
+ {
+ case "forcematch": {
+ if (command.Arguments == null)
+ {
+ throw new Exception("User not provided for force match command");
+ }
+
+ GameUser? target = database.GetUserByUsername(command.Arguments);
+
+ if (target != null)
+ {
+ this._match.SetForceMatch(user.UserId, target.UserId);
+ }
+
+ break;
+ }
+ case "clearforcematch": {
+ this._match.ClearForceMatch(user.UserId);
+
+ break;
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/Refresh.GameServer/Services/MatchService.cs b/Refresh.GameServer/Services/MatchService.cs
index d68d3906..136561c4 100644
--- a/Refresh.GameServer/Services/MatchService.cs
+++ b/Refresh.GameServer/Services/MatchService.cs
@@ -3,6 +3,7 @@
using NotEnoughLogs;
using System.Reflection;
using Bunkum.HttpServer.Responses;
+using MongoDB.Bson;
using Refresh.GameServer.Authentication;
using Refresh.GameServer.Database;
using Refresh.GameServer.Types.Matching;
@@ -16,6 +17,8 @@ public partial class MatchService : EndpointService
private readonly List _matchMethods = new();
private readonly List _rooms = new();
+
+ private readonly Dictionary _forceMatches = new();
public IEnumerable Rooms
{
@@ -122,4 +125,24 @@ public Response ExecuteMethod(string methodStr, SerializedRoomData roomData, Gam
return method.Execute(this, this.Logger, database, user, token, roomData);
}
+
+ public void SetForceMatch(ObjectId user, ObjectId target)
+ {
+ this._forceMatches[user] = target;
+ }
+
+ public ObjectId? GetForceMatch(ObjectId user)
+ {
+ if (this._forceMatches.TryGetValue(user, out ObjectId target))
+ {
+ return target;
+ }
+
+ return null;
+ }
+
+ public void ClearForceMatch(ObjectId user)
+ {
+ this._forceMatches.Remove(user);
+ }
}
\ No newline at end of file
diff --git a/Refresh.GameServer/Types/Matching/MatchMethods/FindRoomMethod.cs b/Refresh.GameServer/Types/Matching/MatchMethods/FindRoomMethod.cs
index ab01af8d..f565e6a4 100644
--- a/Refresh.GameServer/Types/Matching/MatchMethods/FindRoomMethod.cs
+++ b/Refresh.GameServer/Types/Matching/MatchMethods/FindRoomMethod.cs
@@ -1,6 +1,7 @@
using Bunkum.CustomHttpListener.Parsing;
using Bunkum.HttpServer;
using Bunkum.HttpServer.Responses;
+using MongoDB.Bson;
using NotEnoughLogs;
using Refresh.GameServer.Authentication;
using Refresh.GameServer.Database;
@@ -42,17 +43,33 @@ public Response Execute(MatchService service, LoggerContainer log
(levelId == null || r.LevelId == levelId))
.OrderByDescending(r => r.RoomMood)
.ToList();
-
+
//When a user is behind a Strict NAT layer, we can only connect them to players with Open NAT types
if (body.NatType != null && body.NatType[0] == NatType.Strict)
{
rooms = rooms.Where(r => r.NatType == NatType.Open).ToList();
}
+
+ ObjectId? forceMatch = service.GetForceMatch(user.UserId);
+
+ //If the user has a forced match
+ if (forceMatch != null)
+ {
+ //Filter the rooms to only the rooms that contain the player we are wanting to force match to
+ rooms = rooms.Where(r => r.PlayerIds.Any(player => player.Id != null && player.Id == forceMatch.Value)).ToList();
+ }
if (rooms.Count <= 0)
{
return NotFound; // TODO: update this response, shouldn't be 404
}
+
+ //If the user has a forced match and we found a room
+ if (forceMatch != null)
+ {
+ //Clear the user's force match
+ service.ClearForceMatch(user.UserId);
+ }
GameRoom room = rooms[Random.Shared.Next(0, rooms.Count)];
diff --git a/Refresh.GameServer/Types/Matching/MatchMethods/UpdatePlayersInRoomMethod.cs b/Refresh.GameServer/Types/Matching/MatchMethods/UpdatePlayersInRoomMethod.cs
index b4ea7d35..b9fe2245 100644
--- a/Refresh.GameServer/Types/Matching/MatchMethods/UpdatePlayersInRoomMethod.cs
+++ b/Refresh.GameServer/Types/Matching/MatchMethods/UpdatePlayersInRoomMethod.cs
@@ -18,7 +18,7 @@ public Response Execute(MatchService service, LoggerContainer log
SerializedRoomData body)
{
if (body.Players == null) return BadRequest;
- GameRoom room = service.GetOrCreateRoomByPlayer(user, token.TokenPlatform, token.TokenGame, body.NatType[0]);
+ GameRoom room = service.GetOrCreateRoomByPlayer(user, token.TokenPlatform, token.TokenGame, body.NatType == null ? NatType.Open : body.NatType[0]);
room.LastContact = DateTimeOffset.Now;
diff --git a/Refresh.GameServer/Types/Matching/MatchMethods/UpdateRoomDataMethod.cs b/Refresh.GameServer/Types/Matching/MatchMethods/UpdateRoomDataMethod.cs
index ed39476f..141b3346 100644
--- a/Refresh.GameServer/Types/Matching/MatchMethods/UpdateRoomDataMethod.cs
+++ b/Refresh.GameServer/Types/Matching/MatchMethods/UpdateRoomDataMethod.cs
@@ -15,7 +15,7 @@ public class UpdateRoomDataMethod : IMatchMethod
public Response Execute(MatchService service, LoggerContainer logger,
GameDatabaseContext database, GameUser user, Token token, SerializedRoomData body)
{
- GameRoom room = service.GetOrCreateRoomByPlayer(user, token.TokenPlatform, token.TokenGame, body.NatType[0]);
+ GameRoom room = service.GetOrCreateRoomByPlayer(user, token.TokenPlatform, token.TokenGame, body.NatType == null ? NatType.Open : body.NatType[0]);
if (room.HostId.Id != user.UserId) return Unauthorized;
room.LastContact = DateTimeOffset.Now;
diff --git a/RefreshTests.GameServer/Tests/Commands/CommandParseTests.cs b/RefreshTests.GameServer/Tests/Commands/CommandParseTests.cs
index 54ed19cc..223437b0 100644
--- a/RefreshTests.GameServer/Tests/Commands/CommandParseTests.cs
+++ b/RefreshTests.GameServer/Tests/Commands/CommandParseTests.cs
@@ -10,8 +10,9 @@ public class CommandParseTests : GameServerTest
[Test]
public void ParsingTest()
{
- CommandService service = new(new LoggerContainer());
-
+ LoggerContainer logger = new();
+ CommandService service = new(logger, new MatchService(logger));
+
Assert.That(service.ParseCommand("/parse test"), Is.EqualTo(new Command("parse", "test")));
Assert.That(service.ParseCommand("/noargs"), Is.EqualTo(new Command("noargs", null)));
Assert.That(service.ParseCommand("/noargs "), Is.EqualTo(new Command("noargs", null)));
@@ -20,16 +21,18 @@ public void ParsingTest()
[Test]
public void NoSlashThrows()
{
- CommandService service = new(new LoggerContainer());
-
+ LoggerContainer logger = new();
+ CommandService service = new(logger, new MatchService(logger));
+
Assert.That(() => service.ParseCommand("parse test"), Throws.Exception);
}
[Test]
public void BlankCommandThrows()
{
- CommandService service = new(new LoggerContainer());
-
+ LoggerContainer logger = new();
+ CommandService service = new(logger, new MatchService(logger));
+
Assert.That(() => service.ParseCommand("/ test"), Throws.Exception);
}
}