Skip to content

Commit

Permalink
add server management module with set password and slots commands (#264)
Browse files Browse the repository at this point in the history
  • Loading branch information
snixtho authored Aug 4, 2024
1 parent 9915696 commit 333ba06
Show file tree
Hide file tree
Showing 25 changed files with 426 additions and 16 deletions.
15 changes: 15 additions & 0 deletions EvoSC.sln
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeamSettingsModule.Tests",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ForceTeamModule.Tests", "tests\Modules\ForceTeamModule.Tests\ForceTeamModule.Tests.csproj", "{DE9532E8-F0ED-400B-9592-AF8F74BC83EB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServerManagementModule", "src/Modules/ServerManagementModule/ServerManagementModule.csproj", "{AAE1AC39-4C4B-48A9-8B7B-08D2CD461FC1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServerManagementModule.Tests", "tests\Modules\ServerManagementModule.Tests\ServerManagementModule.Tests.csproj", "{F8C7FE5E-B389-4BA8-B0DF-6D9D0A9B0949}"
EndProject




Expand Down Expand Up @@ -366,6 +371,14 @@ Global
{DE9532E8-F0ED-400B-9592-AF8F74BC83EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DE9532E8-F0ED-400B-9592-AF8F74BC83EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DE9532E8-F0ED-400B-9592-AF8F74BC83EB}.Release|Any CPU.Build.0 = Release|Any CPU
{AAE1AC39-4C4B-48A9-8B7B-08D2CD461FC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AAE1AC39-4C4B-48A9-8B7B-08D2CD461FC1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AAE1AC39-4C4B-48A9-8B7B-08D2CD461FC1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AAE1AC39-4C4B-48A9-8B7B-08D2CD461FC1}.Release|Any CPU.Build.0 = Release|Any CPU
{F8C7FE5E-B389-4BA8-B0DF-6D9D0A9B0949}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F8C7FE5E-B389-4BA8-B0DF-6D9D0A9B0949}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F8C7FE5E-B389-4BA8-B0DF-6D9D0A9B0949}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F8C7FE5E-B389-4BA8-B0DF-6D9D0A9B0949}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -424,5 +437,7 @@ Global
{C10A11E5-4AD8-4229-81F1-57D7DC364E27} = {DC47658A-F421-4BA4-B617-090A7DFB3900}
{B1745099-0081-4443-BF79-26A888765E16} = {6D75D6A2-6ECD-4DE4-96C5-CAD7D134407A}
{DE9532E8-F0ED-400B-9592-AF8F74BC83EB} = {6D75D6A2-6ECD-4DE4-96C5-CAD7D134407A}
{AAE1AC39-4C4B-48A9-8B7B-08D2CD461FC1} = {DC47658A-F421-4BA4-B617-090A7DFB3900}
{F8C7FE5E-B389-4BA8-B0DF-6D9D0A9B0949} = {6D75D6A2-6ECD-4DE4-96C5-CAD7D134407A}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
using EvoSC.Commands.Interfaces;
using EvoSC.Common.Interfaces;
using EvoSC.Common.Interfaces.Controllers;
using EvoSC.Common.Interfaces.Models;
using GbxRemoteNet.Interfaces;
using Moq;

namespace EvoSC.Testing.Controllers;

public class CommandInteractionControllerTestBase<TController> : ControllerMock<TController, ICommandInteractionContext>
where TController : class, IController
{
protected readonly (Mock<IServerClient> Client, Mock<IGbxRemoteClient> Remote) Server =
Mocking.NewServerClientMock();

/// <summary>
/// Initialize this controller mock.
/// </summary>
Expand All @@ -15,7 +21,7 @@ public class CommandInteractionControllerTestBase<TController> : ControllerMock<
protected void InitMock(IOnlinePlayer actor, params object[] services)
{
base.InitMock(services);
this.SetupMock(actor);
this.SetupMock(Server.Client, actor);
}
}

14 changes: 8 additions & 6 deletions src/EvoSC.Testing/Mocking.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@ public static ControllerContextMock<TContext> NewControllerContextMock<TContext>
/// <param name="actor">The mocked actor that triggered this action.</param>
/// <returns></returns>
public static ControllerContextMock<IPlayerInteractionContext> SetupMock(
this ControllerContextMock<IPlayerInteractionContext> mock, IOnlinePlayer actor)
this ControllerContextMock<IPlayerInteractionContext> mock, Mock<IServerClient> serverClient, IOnlinePlayer actor)
{
mock.Context.Setup(c => c.Player).Returns(actor);
mock.Context.Setup(c => c.Server).Returns(serverClient.Object);
mock.Context.Object.AuditEvent.CausedBy(actor);

return mock;
Expand All @@ -50,8 +51,8 @@ public static ControllerContextMock<IPlayerInteractionContext> SetupMock(
/// <param name="actor">The mocked actor that triggered this action.</param>
/// <returns></returns>
public static ControllerContextMock<IPlayerInteractionContext>
NewPlayerInteractionContextMock(IOnlinePlayer actor) =>
new ControllerContextMock<IPlayerInteractionContext>().SetupMock(actor);
NewPlayerInteractionContextMock(Mock<IServerClient> serverClient, IOnlinePlayer actor) =>
new ControllerContextMock<IPlayerInteractionContext>().SetupMock(serverClient, actor);

/// <summary>
/// Set up a command interaction context mock.
Expand All @@ -60,9 +61,10 @@ public static ControllerContextMock<IPlayerInteractionContext>
/// <param name="actor">The mocked actor that triggered this command.</param>
/// <returns></returns>
public static ControllerContextMock<ICommandInteractionContext> SetupMock(
this ControllerContextMock<ICommandInteractionContext> mock, IOnlinePlayer actor)
this ControllerContextMock<ICommandInteractionContext> mock, Mock<IServerClient> serverClient, IOnlinePlayer actor)
{
mock.Context.Setup(c => c.Player).Returns(actor);
mock.Context.Setup(c => c.Server).Returns(serverClient.Object);
mock.Context.Object.AuditEvent.CausedBy(actor);

return mock;
Expand All @@ -74,8 +76,8 @@ public static ControllerContextMock<ICommandInteractionContext> SetupMock(
/// <param name="actor">The mocked actor that triggered this command.</param>
/// <returns></returns>
public static ControllerContextMock<ICommandInteractionContext>
NewCommandInteractionContextMock(IOnlinePlayer actor) =>
new ControllerContextMock<ICommandInteractionContext>().SetupMock(actor);
NewCommandInteractionContextMock(Mock<IServerClient> serverClient, IOnlinePlayer actor) =>
new ControllerContextMock<ICommandInteractionContext>().SetupMock(serverClient, actor);

/// <summary>
/// Set up a new Manialink context mock.
Expand Down
1 change: 1 addition & 0 deletions src/EvoSC/EvoSC.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
<ProjectReference Include="..\Modules\MapsModule\MapsModule.csproj" />
<ProjectReference Include="..\Modules\MatchManagerModule\MatchManagerModule.csproj" />
<ProjectReference Include="..\Modules\ASayModule\ASayModule.csproj" />
<ProjectReference Include="..\Modules\ServerManagementModule\ServerManagementModule.csproj" />
<ProjectReference Include="..\Modules\SpectatorTargetInfoModule\SpectatorTargetInfoModule.csproj" />
<ProjectReference Include="..\Modules\TeamSettingsModule\TeamSettingsModule.csproj" />
<ProjectReference Include="..\Modules\WorldRecordModule\WorldRecordModule.csproj" />
Expand Down
4 changes: 3 additions & 1 deletion src/EvoSC/InternalModules.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using EvoSC.Modules.Official.Player;
using EvoSC.Modules.Official.PlayerRecords;
using EvoSC.Modules.Official.Scoreboard;
using EvoSC.Modules.Official.ServerManagementModule;
using EvoSC.Modules.Official.SetName;
using EvoSC.Modules.Official.SpectatorTargetInfoModule;
using EvoSC.Modules.Official.TeamSettingsModule;
Expand Down Expand Up @@ -55,7 +56,8 @@ public static class InternalModules
typeof(MapListModule),
typeof(LocalRecordsModule),
typeof(ForceTeamModule),
typeof(TeamSettingsModule)
typeof(TeamSettingsModule),
typeof(ServerManagementModule)
];

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using EvoSC.Commands.Attributes;
using EvoSC.Commands.Interfaces;
using EvoSC.Common.Controllers;
using EvoSC.Common.Controllers.Attributes;
using EvoSC.Modules.Official.ServerManagementModule.Events;
using EvoSC.Modules.Official.ServerManagementModule.Interfaces;
using EvoSC.Modules.Official.ServerManagementModule.Permissions;

namespace EvoSC.Modules.Official.ServerManagementModule.Controllers;

[Controller]
public class ServerCommandsController(IServerManagementService serverManagementService) : EvoScController<ICommandInteractionContext>
{
[ChatCommand("setpassword", "Set the player and spectator password for joining the server.",
ServerManagementPermissions.SetPassword)]
[CommandAlias("/setpw", true)]
public async Task SetServerPasswordAsync(string password)
{
await serverManagementService.SetPasswordAsync(password);

Context.AuditEvent
.WithEventName(ServerManagementAuditEvents.PasswordSet)
.HavingProperties(new { password })
.Success();

await Context.Server.SuccessMessageAsync(Context.Player, "The password was changed.");
}

[ChatCommand("clearpassword", "Clear the server password.",
ServerManagementPermissions.SetPassword)]
[CommandAlias("/clearpw", true)]
public async Task ClearServerPasswordAsync()
{
await serverManagementService.SetPasswordAsync("");

Context.AuditEvent
.WithEventName(ServerManagementAuditEvents.PasswordSet)
.HavingProperties(new { password = "", cleared = true })
.Success();

await Context.Server.SuccessMessageAsync(Context.Player, "The password was cleared and removed.");
}

[ChatCommand("setmaxplayers", "Set maximum number of players that can join the server.",
ServerManagementPermissions.SetMaxSlots)]
[CommandAlias("/maxplayers", true)]
public async Task SetMaxPlayersAsync(int maxPlayers)
{
await serverManagementService.SetMaxPlayersAsync(maxPlayers);

Context.AuditEvent
.WithEventName(ServerManagementAuditEvents.MaxPlayersSet)
.HavingProperties(new { maxPlayers })
.Success();

await Context.Server.SuccessMessageAsync(Context.Player, $"Max players set to {maxPlayers}");
}

[ChatCommand("setmaxspectators", "Set the maximum number of spectators that can join the server.",
ServerManagementPermissions.SetMaxSlots)]
[CommandAlias("/maxspectators", true)]
public async Task SetMaxSpectatorsAsync(int maxSpectators)
{
await serverManagementService.SetMaxSpectatorsAsync(maxSpectators);

Context.AuditEvent
.WithEventName(ServerManagementAuditEvents.MaxSpectatorsSet)
.HavingProperties(new { maxSpectators })
.Success();

await Context.Server.SuccessMessageAsync(Context.Player, $"Max spectators set to {maxSpectators}");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using EvoSC.Common.Events.Arguments;

namespace EvoSC.Modules.Official.ServerManagementModule.Events.Args;

public class PasswordChangedEventArgs : EvoScEventArgs
{
public string NewPassword { get; init; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using EvoSC.Common.Events.Arguments;

namespace EvoSC.Modules.Official.ServerManagementModule.Events.Args;

public class PlayerSlotsChangedEventArgs : EvoScEventArgs
{
public int NewSlots { get; init; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace EvoSC.Modules.Official.ServerManagementModule.Events;

public enum ServerManagementAuditEvents
{
PasswordSet,
MaxPlayersSet,
MaxSpectatorsSet
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace EvoSC.Modules.Official.ServerManagementModule.Events;

public enum ServerManagementEvents
{
PasswordChanged,
MaxPlayersChanged,
MaxSpectatorsChanged
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace EvoSC.Modules.Official.ServerManagementModule.Interfaces;

public interface IServerManagementService
{
public Task SetPasswordAsync(string password);
public Task SetMaxPlayersAsync(int maxPlayers);
public Task SetMaxSpectatorsAsync(int maxSpectators);
}
19 changes: 19 additions & 0 deletions src/Modules/ServerManagementModule/Localization.resx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:element name="root" msdata:IsDataSet="true">
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using EvoSC.Common.Permissions.Attributes;

namespace EvoSC.Modules.Official.ServerManagementModule.Permissions;

[PermissionGroup]
public enum ServerManagementPermissions
{
SetPassword,
SetMaxSlots
}
6 changes: 6 additions & 0 deletions src/Modules/ServerManagementModule/ServerManagementModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using EvoSC.Modules.Attributes;

namespace EvoSC.Modules.Official.ServerManagementModule;

[Module(IsInternal = true)]
public class ServerManagementModule : EvoScModule;
25 changes: 25 additions & 0 deletions src/Modules/ServerManagementModule/ServerManagementModule.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>EvoSC.Modules.Official.ServerManagementModule</RootNamespace>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<AssemblyName>ServerManagementModule</AssemblyName>
<Title>Server Management</Title>
<Description>Commands and functions to manage server configuration such as password, player slots etc.</Description>
<Version>1.0.0</Version>
<Authors>Evo</Authors>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Templates\**\*" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Localization.resx" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../EvoSC.Modules.SourceGeneration/EvoSC.Modules.SourceGeneration.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="../../EvoSC.Modules/EvoSC.Modules.csproj" Private="true" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using EvoSC.Common.Interfaces;
using EvoSC.Common.Services.Attributes;
using EvoSC.Modules.Official.ServerManagementModule.Events;
using EvoSC.Modules.Official.ServerManagementModule.Events.Args;
using EvoSC.Modules.Official.ServerManagementModule.Interfaces;

namespace EvoSC.Modules.Official.ServerManagementModule.Services;

[Service]
public class ServerManagementService(IServerClient serverClient, IEventManager events) : IServerManagementService
{
public async Task SetPasswordAsync(string password)
{
await serverClient.Remote.SetServerPasswordAsync(password);
await serverClient.Remote.SetServerPasswordForSpectatorAsync(password);

await events.RaiseAsync(ServerManagementEvents.PasswordChanged,
new PasswordChangedEventArgs { NewPassword = password });
}

public async Task SetMaxPlayersAsync(int maxPlayers)
{
await serverClient.Remote.SetMaxPlayersAsync(maxPlayers);
await events.RaiseAsync(ServerManagementEvents.MaxPlayersChanged,
new PlayerSlotsChangedEventArgs() { NewSlots = maxPlayers });
}

public async Task SetMaxSpectatorsAsync(int maxSpectators)
{
await serverClient.Remote.SetMaxSpectatorsAsync(maxSpectators);
await events.RaiseAsync(ServerManagementEvents.MaxSpectatorsChanged,
new PlayerSlotsChangedEventArgs() { NewSlots = maxSpectators });
}
}
11 changes: 11 additions & 0 deletions src/Modules/ServerManagementModule/info.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[info]
# A unique name for this module, this is used as a identifier
name = "ServerManagementModule"
# The title of the module
title = "Server Management"
# A short description of what the module is and does
summary = "Commands and functions to manage server configuration such as password, player slots etc."
# The current version of this module, using SEMVER
version = "1.0.0"
# The name of the author that created this module
author = "Evo"
Loading

0 comments on commit 333ba06

Please sign in to comment.