Skip to content

Commit 69a4116

Browse files
dartasenMeasurity
andauthored
Command rework (SubnauticaNitrox#1047)
* Rework abstraction of server Command * Args dynamic read * Cleanup * Code style cleanup * Made it work again * Renamed Perform method in commands to Execute * Added whois cmd + Crop help command IG for readability * DisableAutoSave persisted in config + more cleanup * some review changes * Removed unused DefaultValue * Used subclass for command args so that command class' state doesn't change * Cleaned up some commands implementations * Changed GetOverflowArgs to GetTillEnd * more cleanup Co-authored-by: Measurity <measuring.infinity@gmail.com>
1 parent d573efc commit 69a4116

37 files changed

Lines changed: 684 additions & 370 deletions

NitroxModel/Server/ServerConfig.cs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,31 @@ namespace NitroxModel.Server
77
{
88
public class ServerConfig
99
{
10-
private readonly ServerConfigItem<bool> disableConsoleSetting;
10+
private readonly ServerConfigItem<bool> disableConsoleSetting, disableAutoSaveSetting;
1111
private readonly ServerConfigItem<int> portSetting, saveIntervalSetting, maxConnectionsSetting;
1212
private readonly ServerConfigItem<string> saveNameSetting, serverPasswordSetting, adminPasswordSetting, gameModeSetting;
1313
private readonly ServerConfigItem<float> oxygenSetting, maxOxygenSetting, healthSetting, foodSetting, waterSetting, infectionSetting;
1414

15-
public ServerConfig(): this(
15+
public ServerConfig() : this(
1616
port: 1100,
17-
saveinterval: 60000,
17+
saveinterval: 120000,
1818
maxconnection: 100,
1919
disableconsole: false,
20+
disableautosave: false,
2021
savename: "world",
2122
serverpassword: string.Empty,
2223
adminpassword: StringHelper.GenerateRandomString(12),
2324
gamemodeSetting: ServerGameMode.SURVIVAL
24-
) { }
25+
)
26+
{ }
2527

26-
public ServerConfig(int port, int saveinterval, int maxconnection, bool disableconsole, string savename, string serverpassword, string adminpassword, ServerGameMode gamemodeSetting)
28+
public ServerConfig(int port, int saveinterval, int maxconnection, bool disableconsole, bool disableautosave, string savename, string serverpassword, string adminpassword, ServerGameMode gamemodeSetting)
2729
{
2830
portSetting = new ServerConfigItem<int>("Port", port);
2931
saveIntervalSetting = new ServerConfigItem<int>("SaveInterval", saveinterval);
3032
maxConnectionsSetting = new ServerConfigItem<int>("MaxConnections", maxconnection);
3133
disableConsoleSetting = new ServerConfigItem<bool>("DisableConsole", disableconsole);
34+
disableAutoSaveSetting = new ServerConfigItem<bool>("DisableAutoSave", disableautosave);
3235
saveNameSetting = new ServerConfigItem<string>("SaveName", savename);
3336
serverPasswordSetting = new ServerConfigItem<string>("ServerPassword", serverpassword);
3437
adminPasswordSetting = new ServerConfigItem<string>("AdminPassword", adminpassword);
@@ -97,6 +100,19 @@ public bool DisableConsole
97100
{
98101
disableConsoleSetting.Value = value;
99102
}
103+
}
104+
105+
public bool DisableAutoSave
106+
{
107+
get
108+
{
109+
return disableAutoSaveSetting.Value;
110+
}
111+
112+
set
113+
{
114+
disableAutoSaveSetting.Value = value;
115+
}
100116
}
101117

102118
public string SaveName
@@ -156,6 +172,13 @@ public string GameMode
156172
}
157173
}
158174

175+
public ServerGameMode GameModeEnum
176+
{
177+
set
178+
{
179+
GameMode = value.GetAttribute<DescriptionAttribute>().Description.ToString();
180+
}
181+
}
159182

160183
public PlayerStatsData DefaultPlayerStats => new PlayerStatsData(oxygenSetting.Value, maxOxygenSetting.Value, healthSetting.Value, foodSetting.Value, waterSetting.Value, infectionSetting.Value);
161184
#endregion

NitroxServer-Subnautica/App.config

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
<appSettings>
44
<add key="MaxConnections" value="100" />
55
<add key="Port" value="11000" />
6-
<add key="SaveInterval" value="60000" />
6+
<add key="SaveInterval" value="120000" />
77
<add key="GameMode" value="Survival" />
88
<add key="DisableConsole" value="false" />
9+
<add key="DisableAutoSave" value="false" />
910
<add key="ServerPassword" value="" />
1011
<add key="AdminPassword" value="" />
1112
<add key="SaveName" value="world" />

NitroxServer-Subnautica/SubnauticaServerAutoFacRegistrar.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ public override void RegisterDependencies(ContainerBuilder containerBuilder)
4343
[TechType.CrashHome.Model()] = new CrashFishBootstrapper(),
4444
[TechType.Reefback.Model()] = new ReefbackBootstrapper()
4545
}).SingleInstance();
46-
4746
}
4847
}
4948
}

NitroxServer/Communication/Packets/Processors/ServerCommandProcessor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public ServerCommandProcessor(ConsoleCommandProcessor cmdProcessor)
1717

1818
public override void Process(ServerCommand packet, Player player)
1919
{
20-
Log.Info($"{player.Name} issued server command: /{packet.Cmd}");
20+
Log.Info($"{player.Name} issued command: /{packet.Cmd}");
2121
cmdProcessor.ProcessCommand(packet.Cmd, Optional.Of(player), player.Permissions);
2222
}
2323
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
using System.Linq;
2+
using NitroxModel.DataStructures.Util;
3+
4+
namespace NitroxServer.ConsoleCommands.Abstract
5+
{
6+
public abstract partial class Command
7+
{
8+
public class CallArgs
9+
{
10+
public CallArgs(Command command, Optional<Player> sender, string[] args)
11+
{
12+
Command = command;
13+
Sender = sender;
14+
Args = args;
15+
}
16+
17+
public Command Command { get; }
18+
public string[] Args { get; }
19+
public Optional<Player> Sender { get; }
20+
21+
public string SenderName => Sender.HasValue ? Sender.Value.Name : "SERVER";
22+
23+
public bool Valid(int index)
24+
{
25+
return index < Args.Length && index >= 0 && Args.Length != 0;
26+
}
27+
28+
public string Get(int index)
29+
{
30+
return Get<string>(index);
31+
}
32+
33+
public string GetTillEnd(int startIndex = 0)
34+
{
35+
// TODO: Proper argument capture/parse instead of this argument join hack
36+
if (Args?.Length > 0)
37+
{
38+
return string.Join(" ", Args.Skip(startIndex));
39+
}
40+
41+
return string.Empty;
42+
}
43+
44+
public T Get<T>(int index)
45+
{
46+
IParameter<object> param = Command.Parameters[index];
47+
string arg = Valid(index) ? Args[index] : null;
48+
49+
if (arg == null)
50+
{
51+
return default(T);
52+
}
53+
if (typeof(T) == typeof(string))
54+
{
55+
return (T)(object)arg;
56+
}
57+
58+
return (T)param.Read(arg);
59+
}
60+
}
61+
}
62+
}
Lines changed: 86 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,110 @@
1-
using NitroxModel.DataStructures.Util;
2-
using NitroxModel.Helper;
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Collections.ObjectModel;
4+
using System.Linq;
5+
using System.Text;
36
using NitroxModel.DataStructures.GameLogic;
4-
using NitroxModel.Packets;
7+
using NitroxModel.DataStructures.Util;
8+
using NitroxModel.Helper;
59
using NitroxModel.Logger;
6-
using System.Text;
10+
using NitroxModel.Packets;
711

812
namespace NitroxServer.ConsoleCommands.Abstract
913
{
10-
public abstract class Command
14+
public abstract partial class Command
1115
{
12-
public string Name { get; protected set; }
13-
public string[] Alias { get; protected set; }
14-
public string Description { get; protected set; }
15-
public string ArgsDescription { get; protected set; }
16-
public Perms RequiredPermLevel { get; protected set; }
16+
private readonly List<string> aliases;
17+
private int optional, required;
1718

18-
protected Command(string name, Perms requiredPermLevel, string argsDescription, string description) : this(name, requiredPermLevel, argsDescription, "", null)
19-
{
20-
ArgsDescription = argsDescription;
21-
Name = name;
22-
Description = description;
23-
}
19+
public ReadOnlyCollection<string> Aliases => aliases.AsReadOnly();
20+
21+
public string Name { get; }
22+
public string Description { get; }
23+
public Perms RequiredPermLevel { get; }
24+
public bool AllowedArgOverflow { get; }
25+
public List<IParameter<object>> Parameters { get; }
2426

25-
protected Command(string name, Perms requiredPermLevel, string argsDescription, string description, string[] alias)
27+
protected Command(string name, Perms perm, string description, bool allowedArgOveflow = false)
2628
{
2729
Validate.NotNull(name);
28-
Validate.NotNull(argsDescription);
29-
Validate.NotNull(description);
3030

3131
Name = name;
32-
Description = string.IsNullOrEmpty(description) ? "No description" : description;
33-
ArgsDescription = argsDescription;
34-
RequiredPermLevel = requiredPermLevel;
35-
Alias = alias ?? new string[0];
32+
RequiredPermLevel = perm;
33+
aliases = new List<string>();
34+
Parameters = new List<IParameter<object>>();
35+
AllowedArgOverflow = allowedArgOveflow;
36+
Description = string.IsNullOrEmpty(description) ? "No description provided" : description;
3637
}
3738

38-
public abstract void RunCommand(string[] args, Optional<Player> sender);
39+
protected abstract void Execute(CallArgs args);
3940

40-
public abstract bool VerifyArgs(string[] args);
41-
42-
public override string ToString()
41+
public void TryExecute(Optional<Player> sender, string[] args)
4342
{
44-
return $"{nameof(Name)}: {Name}, {nameof(Description)}: {Description}, {nameof(ArgsDescription)}: {ArgsDescription}, {nameof(Alias)}: [{string.Join(", ", Alias)}]";
43+
if (args.Length < required)
44+
{
45+
SendMessage(sender, $"Error: Invalid Parameters\nUsage: {ToHelpText(true)}");
46+
return;
47+
}
48+
49+
if (!AllowedArgOverflow && args.Length > optional + required)
50+
{
51+
SendMessage(sender, $"Error: Too many Parameters\nUsage: {ToHelpText(true)}");
52+
return;
53+
}
54+
55+
try
56+
{
57+
Execute(new CallArgs(this, sender, args));
58+
}
59+
catch (ArgumentException e)
60+
{
61+
SendMessage(sender, $"Error: {e.Message}");
62+
}
63+
catch (Exception e)
64+
{
65+
Log.Error("Fatal error while trying to execute the command", e);
66+
}
4567
}
4668

47-
public string ToHelpText()
69+
public string ToHelpText(bool cropText = false)
4870
{
4971
StringBuilder cmd = new StringBuilder(Name);
72+
if (Aliases?.Count > 0)
73+
{
74+
cmd.AppendFormat("/{0}", string.Join("/", Aliases));
75+
}
76+
cmd.AppendFormat(" {0}", string.Join(" ", Parameters));
77+
return cropText ? $"{cmd}" : $"{cmd,-32} - {Description}";
78+
}
79+
80+
protected void AddParameter<T>(T param) where T : IParameter<object>
81+
{
82+
Validate.NotNull(param as object);
5083

51-
if (Alias.Length > 0)
84+
Parameters.Add(param);
85+
if (param.IsRequired)
5286
{
53-
cmd.AppendFormat("/{0}", string.Join("/", Alias));
87+
required++;
5488
}
55-
cmd.AppendFormat(" {0}", ArgsDescription);
89+
else
90+
{
91+
optional++;
92+
}
93+
}
5694

57-
return $"{cmd,-35} - {Description}";
95+
protected void AddAlias(params string[] alias)
96+
{
97+
aliases.AddRange(alias);
5898
}
5999

100+
protected void AddAlias(string alias)
101+
{
102+
aliases.Add(alias);
103+
}
104+
105+
/// <summary>
106+
/// Send a message to an existing player
107+
/// </summary>
60108
public void SendMessageToPlayer(Optional<Player> player, string message)
61109
{
62110
if (player.HasValue)
@@ -65,10 +113,13 @@ public void SendMessageToPlayer(Optional<Player> player, string message)
65113
}
66114
}
67115

68-
public void Notify(Optional<Player> player, string message)
116+
/// <summary>
117+
/// Send a message to an existing player and logs it in the console
118+
/// </summary>
119+
public void SendMessage(Optional<Player> player, string message)
69120
{
70-
Log.Info(message);
71121
SendMessageToPlayer(player, message);
122+
Log.Info(message);
72123
}
73124
}
74125
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using NitroxModel.Helper;
2+
3+
namespace NitroxServer.ConsoleCommands.Abstract
4+
{
5+
public abstract class Parameter<T> : IParameter<T>
6+
{
7+
public bool IsRequired { get; }
8+
public string Name { get; }
9+
10+
protected Parameter(string name, bool isRequired)
11+
{
12+
Validate.IsFalse(string.IsNullOrEmpty(name));
13+
14+
Name = name;
15+
IsRequired = isRequired;
16+
}
17+
18+
public abstract bool IsValid(string arg);
19+
public abstract T Read(string arg);
20+
21+
public override string ToString()
22+
{
23+
return $"{(IsRequired ? '{' : '[')}{Name}{(IsRequired ? '}' : ']')}";
24+
}
25+
}
26+
27+
public interface IParameter<out T>
28+
{
29+
bool IsRequired { get; }
30+
string Name { get; }
31+
32+
bool IsValid(string arg);
33+
T Read(string arg);
34+
}
35+
}

0 commit comments

Comments
 (0)