Skip to content

Commit

Permalink
Implement command parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
Beyley committed Sep 2, 2023
1 parent 7cf6033 commit 62378af
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 44 deletions.
8 changes: 4 additions & 4 deletions Refresh.GameServer/Endpoints/Game/Levels/PublishEndpoints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ private static bool VerifyLevel(GameLevelRequest body, GameUser user, LoggerCont

[GameEndpoint("startPublish", ContentType.Xml, Method.Post)]
[NullStatusCode(BadRequest)]
public SerializedLevelResources? StartPublish(RequestContext context, GameUser user, GameDatabaseContext database, GameLevelRequest body, PublishTrackingService publishTracking, IDataStore dataStore, LoggerContainer<BunkumContext> logger)
public SerializedLevelResources? StartPublish(RequestContext context, GameUser user, GameDatabaseContext database, GameLevelRequest body, CommandService command, IDataStore dataStore, LoggerContainer<BunkumContext> logger)
{
//If verifying the request fails, return null
if (!VerifyLevel(body, user, logger)) return null;
Expand All @@ -60,7 +60,7 @@ private static bool VerifyLevel(GameLevelRequest body, GameUser user, LoggerCont
if (hashes.Any(hash => hash.Length != 40)) return null;

//Mark the user as publishing
publishTracking.StartTracking(user.UserId);
command.StartPublishing(user.UserId);

return new SerializedLevelResources
{
Expand All @@ -69,7 +69,7 @@ private static bool VerifyLevel(GameLevelRequest body, GameUser user, LoggerCont
}

[GameEndpoint("publish", ContentType.Xml, Method.Post)]
public Response PublishLevel(RequestContext context, GameUser user, Token token, GameDatabaseContext database, GameLevelRequest body, PublishTrackingService publishTracking, IDataStore dataStore, LoggerContainer<BunkumContext> logger)
public Response PublishLevel(RequestContext context, GameUser user, Token token, GameDatabaseContext database, GameLevelRequest body, CommandService command, IDataStore dataStore, LoggerContainer<BunkumContext> logger)
{
//If verifying the request fails, return null
if (!VerifyLevel(body, user, logger)) return BadRequest;
Expand Down Expand Up @@ -99,7 +99,7 @@ public Response PublishLevel(RequestContext context, GameUser user, Token token,
}

//Mark the user as no longer publishing
publishTracking.StopTracking(user.UserId);
command.StopPublishing(user.UserId);

level.Publisher = user;

Expand Down
25 changes: 22 additions & 3 deletions Refresh.GameServer/Endpoints/Game/UserEndpoints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Refresh.GameServer.Database;
using Refresh.GameServer.Endpoints.Game.DataTypes.Response;
using Refresh.GameServer.Services;
using Refresh.GameServer.Types.Commands;
using Refresh.GameServer.Types.Lists;
using Refresh.GameServer.Types.Roles;
using Refresh.GameServer.Types.UserData;
Expand Down Expand Up @@ -168,15 +169,33 @@ public SerializedUserList GetMultipleUsers(RequestContext context, GameDatabaseC
/// <returns>The string shown in-game.</returns>
[GameEndpoint("filter", Method.Post)]
[AllowEmptyBody]
public string Filter(RequestContext context, string body, GameUser user)
public string Filter(RequestContext context, CommandService command, string body, GameUser user)
{
Debug.Assert(user != null);
Debug.Assert(body != null);

context.Logger.LogInfo(BunkumContext.Filter, $"<{user}>: {body}");

//TODO: Add filtering

if (command.IsPublishing(user.UserId))
{
context.Logger.LogInfo(BunkumContext.Filter, $"Publish filter {body}");
}
else
{
context.Logger.LogInfo(BunkumContext.Filter, $"<{user}>: {body}");

try
{
Command parsedCommand = command.ParseCommand(body);

context.Logger.LogInfo(BunkumContext.Commands, $"User used command \"{parsedCommand.Name}\" with args \"{parsedCommand.Arguments}\"");
}
catch
{
//do nothing
}
}

return body;
}
}
2 changes: 1 addition & 1 deletion Refresh.GameServer/Refresh.GameServer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
</ItemGroup>

<ItemGroup Condition="'$(Configuration)'!='DebugLocalBunkum'">
<PackageReference Include="Bunkum" Version="3.3.17" />
<PackageReference Include="Bunkum" Version="3.3.18" />
<PackageReference Include="Bunkum.RealmDatabase" Version="3.1.5" />
</ItemGroup>

Expand Down
2 changes: 1 addition & 1 deletion Refresh.GameServer/RefreshGameServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ protected virtual void SetupServices()
this._server.AddRateLimitService(new RateLimitSettings(60, 400, 30, "global"));
this._server.AddService<CategoryService>();
this._server.AddService<FriendStorageService>();
this._server.AddService<PublishTrackingService>();
this._server.AddService<CommandService>();
this._server.AddService<MatchService>();
this._server.AddService<ImportService>();
this._server.AddService<DocumentationService>();
Expand Down
70 changes: 70 additions & 0 deletions Refresh.GameServer/Services/CommandService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using Bunkum.HttpServer;
using Bunkum.HttpServer.Services;
using JetBrains.Annotations;
using MongoDB.Bson;
using NotEnoughLogs;
using Refresh.GameServer.Types.Commands;

namespace Refresh.GameServer.Services;

public class CommandService : EndpointService
{
public CommandService(LoggerContainer<BunkumContext> logger) : base(logger) {}

private readonly HashSet<ObjectId> _usersPublishing = new();

/// <summary>
/// Start tracking the user, eg. they started publishing
/// </summary>
/// <param name="id">The user ID</param>
public void StartPublishing(ObjectId id)
{
//Unconditionally add the user to the set
_ = this._usersPublishing.Add(id);
}

/// <summary>
/// Stop tracking the user, eg. they stopped publishing
/// </summary>
/// <param name="id">The user ID</param>
public void StopPublishing(ObjectId id)
{
//Unconditionally remove the user from the set
_ = this._usersPublishing.Remove(id);
}

public bool IsPublishing(ObjectId id) => this._usersPublishing.Contains(id);

/// <summary>
/// Parse a command string into a command object
/// </summary>
/// <param name="str">Command string</param>
/// <returns>Parsed command</returns>
/// <exception cref="FormatException">When the command is in an invalid format</exception>
[Pure]
public Command ParseCommand(string str)
{
//Ensure the command string starts with a slash
if (str[0] != '/')
{
throw new FormatException("Commands must start with `/`");
}

int idx = str.IndexOf(" ", StringComparison.Ordinal);

//If idx is 1, the command name is blank
// ReSharper disable once ConvertIfStatementToSwitchStatement
if (idx == 1)
{
throw new FormatException("Blank command name");
}

//If theres no space after, or if the space is the last character, then there are no arguments
if (idx == -1 || idx == str.Length - 1)
{
return new Command(idx == str.Length - 1 ? str[1..idx] : str[1..], null);
}

return new Command(str[1..idx], str[(idx + 1)..]);
}
}
35 changes: 0 additions & 35 deletions Refresh.GameServer/Services/PublishTrackingService.cs

This file was deleted.

3 changes: 3 additions & 0 deletions Refresh.GameServer/Types/Commands/Command.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace Refresh.GameServer.Types.Commands;

public record Command(string Name, string? Arguments);
35 changes: 35 additions & 0 deletions RefreshTests.GameServer/Tests/Commands/CommandParseTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using Bunkum.HttpServer;
using NotEnoughLogs;
using Refresh.GameServer.Services;
using Refresh.GameServer.Types.Commands;

namespace RefreshTests.GameServer.Tests.Commands;

public class CommandParseTests : GameServerTest
{
[Test]
public void ParsingTest()
{
CommandService service = new(new LoggerContainer<BunkumContext>());

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)));
}

[Test]
public void NoSlashThrows()
{
CommandService service = new(new LoggerContainer<BunkumContext>());

Assert.That(() => service.ParseCommand("parse test"), Throws.Exception);
}

[Test]
public void BlankCommandThrows()
{
CommandService service = new(new LoggerContainer<BunkumContext>());

Assert.That(() => service.ParseCommand("/ test"), Throws.Exception);
}
}

0 comments on commit 62378af

Please sign in to comment.