first commit
This commit is contained in:
64
NitroxServer/ConsoleCommands/Abstract/CallArgs.cs
Normal file
64
NitroxServer/ConsoleCommands/Abstract/CallArgs.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using NitroxModel.DataStructures.Util;
|
||||
|
||||
namespace NitroxServer.ConsoleCommands.Abstract
|
||||
{
|
||||
public abstract partial class Command
|
||||
{
|
||||
public ref struct CallArgs
|
||||
{
|
||||
public Command Command { get; }
|
||||
public Optional<Player> Sender { get; }
|
||||
public Span<string> Args { get; }
|
||||
|
||||
public bool IsConsole => !Sender.HasValue;
|
||||
public string SenderName => Sender.HasValue ? Sender.Value.Name : "SERVER";
|
||||
|
||||
public CallArgs(Command command, Optional<Player> sender, Span<string> args)
|
||||
{
|
||||
Command = command;
|
||||
Sender = sender;
|
||||
Args = args;
|
||||
}
|
||||
|
||||
public bool IsValid(int index)
|
||||
{
|
||||
return index < Args.Length && index >= 0 && Args.Length != 0;
|
||||
}
|
||||
|
||||
public string GetTillEnd(int startIndex = 0)
|
||||
{
|
||||
// TODO: Proper argument capture/parse instead of this argument join hack
|
||||
if (Args.Length > 0)
|
||||
{
|
||||
return string.Join(" ", Args.Slice(startIndex).ToArray());
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public string Get(int index)
|
||||
{
|
||||
return Get<string>(index);
|
||||
}
|
||||
|
||||
public T Get<T>(int index)
|
||||
{
|
||||
IParameter<object> param = Command.Parameters[index];
|
||||
string arg = IsValid(index) ? Args[index] : null;
|
||||
|
||||
if (arg == null)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
|
||||
if (typeof(T) == typeof(string))
|
||||
{
|
||||
return (T)(object)arg;
|
||||
}
|
||||
|
||||
return (T)param.Read(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
149
NitroxServer/ConsoleCommands/Abstract/Command.cs
Normal file
149
NitroxServer/ConsoleCommands/Abstract/Command.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NitroxModel.Core;
|
||||
using NitroxModel.DataStructures.GameLogic;
|
||||
using NitroxModel.DataStructures.Util;
|
||||
using NitroxModel.Helper;
|
||||
using NitroxModel.Packets;
|
||||
using NitroxServer.GameLogic;
|
||||
|
||||
namespace NitroxServer.ConsoleCommands.Abstract
|
||||
{
|
||||
public abstract partial class Command
|
||||
{
|
||||
private int optional, required;
|
||||
|
||||
public virtual IEnumerable<string> Aliases { get; }
|
||||
|
||||
public string Name { get; }
|
||||
public string Description { get; }
|
||||
public Perms RequiredPermLevel { get; }
|
||||
public PermsFlag Flags { get; }
|
||||
public bool AllowedArgOverflow { get; set; }
|
||||
public List<IParameter<object>> Parameters { get; }
|
||||
|
||||
protected Command(string name, Perms perms, PermsFlag flag, string description) : this(name, perms, description)
|
||||
{
|
||||
Flags = flag;
|
||||
}
|
||||
|
||||
protected Command(string name, Perms perms, string description)
|
||||
{
|
||||
Validate.NotNull(name);
|
||||
|
||||
Name = name;
|
||||
Flags = PermsFlag.NONE;
|
||||
RequiredPermLevel = perms;
|
||||
AllowedArgOverflow = false;
|
||||
Aliases = Array.Empty<string>();
|
||||
Parameters = new List<IParameter<object>>();
|
||||
Description = string.IsNullOrEmpty(description) ? "No description provided" : description;
|
||||
}
|
||||
|
||||
protected abstract void Execute(CallArgs args);
|
||||
|
||||
public void TryExecute(Optional<Player> sender, Span<string> args)
|
||||
{
|
||||
if (args.Length < required)
|
||||
{
|
||||
SendMessage(sender, $"Error: Invalid Parameters\nUsage: {ToHelpText(false, true)}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!AllowedArgOverflow && args.Length > optional + required)
|
||||
{
|
||||
SendMessage(sender, $"Error: Too many Parameters\nUsage: {ToHelpText(false, true)}");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Execute(new CallArgs(this, sender, args));
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
SendMessage(sender, $"Error: {ex.Message}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Fatal error while trying to execute the command");
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanExecute(Perms treshold)
|
||||
{
|
||||
return RequiredPermLevel <= treshold;
|
||||
}
|
||||
|
||||
public string ToHelpText(bool singleCommand, bool cropText = false)
|
||||
{
|
||||
StringBuilder cmd = new(Name);
|
||||
|
||||
if (Aliases.Any())
|
||||
{
|
||||
cmd.AppendFormat("/{0}", string.Join("/", Aliases));
|
||||
}
|
||||
|
||||
cmd.AppendFormat(" {0}", string.Join(" ", Parameters));
|
||||
|
||||
if (singleCommand)
|
||||
{
|
||||
string parameterPreText = Parameters.Count == 0 ? "" : Environment.NewLine;
|
||||
string parameterText = $"{parameterPreText}{string.Join("\n", Parameters.Select(p => $"{p,-47} - {p.GetDescription()}"))}";
|
||||
|
||||
return cropText ? $"{cmd}" : $"{cmd,-32} - {Description} {parameterText}";
|
||||
}
|
||||
return cropText ? $"{cmd}" : $"{cmd,-32} - {Description}";
|
||||
}
|
||||
|
||||
protected void AddParameter<T>(T param) where T : IParameter<object>
|
||||
{
|
||||
Validate.NotNull(param as object);
|
||||
Parameters.Add(param);
|
||||
|
||||
if (param.IsRequired)
|
||||
{
|
||||
required++;
|
||||
}
|
||||
else
|
||||
{
|
||||
optional++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to an existing player
|
||||
/// </summary>
|
||||
public static void SendMessageToPlayer(Optional<Player> player, string message)
|
||||
{
|
||||
if (player.HasValue)
|
||||
{
|
||||
player.Value.SendPacket(new ChatMessage(ChatMessage.SERVER_ID, message));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to an existing player and logs it in the console
|
||||
/// </summary>
|
||||
public static void SendMessage(Optional<Player> player, string message)
|
||||
{
|
||||
SendMessageToPlayer(player, message);
|
||||
if (!player.HasValue)
|
||||
{
|
||||
Log.Info(message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to all connected players
|
||||
/// </summary>
|
||||
public static void SendMessageToAllPlayers(string message)
|
||||
{
|
||||
PlayerManager playerManager = NitroxServiceLocator.LocateService<PlayerManager>();
|
||||
playerManager.SendPacketToAllPlayers(new ChatMessage(ChatMessage.SERVER_ID, message));
|
||||
Log.Info($"[BROADCAST] {message}");
|
||||
}
|
||||
}
|
||||
}
|
43
NitroxServer/ConsoleCommands/Abstract/Parameter.cs
Normal file
43
NitroxServer/ConsoleCommands/Abstract/Parameter.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using NitroxModel.Helper;
|
||||
|
||||
namespace NitroxServer.ConsoleCommands.Abstract
|
||||
{
|
||||
public abstract class Parameter<T> : IParameter<T>
|
||||
{
|
||||
public bool IsRequired { get; }
|
||||
public string Name { get; }
|
||||
private string Description { get; }
|
||||
|
||||
protected Parameter(string name, bool isRequired, string description)
|
||||
{
|
||||
Validate.IsFalse(string.IsNullOrEmpty(name));
|
||||
|
||||
Name = name;
|
||||
IsRequired = isRequired;
|
||||
Description = description;
|
||||
}
|
||||
|
||||
public abstract bool IsValid(string arg);
|
||||
public abstract T Read(string arg);
|
||||
|
||||
public virtual string GetDescription()
|
||||
{
|
||||
return Description;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{(IsRequired ? '{' : '[')}{Name}{(IsRequired ? '}' : ']')}";
|
||||
}
|
||||
}
|
||||
|
||||
public interface IParameter<out T>
|
||||
{
|
||||
bool IsRequired { get; }
|
||||
string Name { get; }
|
||||
|
||||
bool IsValid(string arg);
|
||||
T Read(string arg);
|
||||
string GetDescription();
|
||||
}
|
||||
}
|
41
NitroxServer/ConsoleCommands/Abstract/Type/TypeBoolean.cs
Normal file
41
NitroxServer/ConsoleCommands/Abstract/Type/TypeBoolean.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NitroxModel.Helper;
|
||||
|
||||
namespace NitroxServer.ConsoleCommands.Abstract.Type
|
||||
{
|
||||
public class TypeBoolean : Parameter<bool>, IParameter<object>
|
||||
{
|
||||
private static readonly string[] noValues = new string[]
|
||||
{
|
||||
bool.FalseString,
|
||||
"no",
|
||||
"off"
|
||||
};
|
||||
|
||||
private static readonly string[] yesValues = new string[]
|
||||
{
|
||||
bool.TrueString,
|
||||
"yes",
|
||||
"on"
|
||||
};
|
||||
|
||||
public TypeBoolean(string name, bool isRequired, string description) : base(name, isRequired, description) { }
|
||||
|
||||
public override bool IsValid(string arg)
|
||||
{
|
||||
return yesValues.Contains(arg, StringComparer.OrdinalIgnoreCase) || noValues.Contains(arg, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public override bool Read(string arg)
|
||||
{
|
||||
Validate.IsTrue(IsValid(arg), "Invalid boolean value received");
|
||||
return yesValues.Contains(arg, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
object IParameter<object>.Read(string arg)
|
||||
{
|
||||
return Read(arg);
|
||||
}
|
||||
}
|
||||
}
|
29
NitroxServer/ConsoleCommands/Abstract/Type/TypeEnum.cs
Normal file
29
NitroxServer/ConsoleCommands/Abstract/Type/TypeEnum.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using NitroxModel.Helper;
|
||||
|
||||
namespace NitroxServer.ConsoleCommands.Abstract.Type
|
||||
{
|
||||
public class TypeEnum<T> : Parameter<object> where T : struct, Enum
|
||||
{
|
||||
public TypeEnum(string name, bool required, string description) : base(name, required, description)
|
||||
{
|
||||
Validate.IsTrue(typeof(T).IsEnum, $"Type {typeof(T).FullName} isn't an enum");
|
||||
}
|
||||
|
||||
public override bool IsValid(string arg)
|
||||
{
|
||||
return Enum.TryParse<T>(arg, true, out _);
|
||||
}
|
||||
|
||||
public override object Read(string arg)
|
||||
{
|
||||
Validate.IsTrue(Enum.TryParse(arg, true, out T value), $"Unknown value received (pick from: {string.Join(", ", Enum.GetNames(typeof(T)))})");
|
||||
return value;
|
||||
}
|
||||
|
||||
public override string GetDescription()
|
||||
{
|
||||
return $"{base.GetDescription()} (values: {string.Join(", ", Enum.GetNames(typeof(T)))})";
|
||||
}
|
||||
}
|
||||
}
|
25
NitroxServer/ConsoleCommands/Abstract/Type/TypeFloat.cs
Normal file
25
NitroxServer/ConsoleCommands/Abstract/Type/TypeFloat.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using NitroxModel.Helper;
|
||||
|
||||
namespace NitroxServer.ConsoleCommands.Abstract.Type
|
||||
{
|
||||
public class TypeFloat : Parameter<float>, IParameter<object>
|
||||
{
|
||||
public TypeFloat(string name, bool isRequired, string description) : base(name, isRequired, description) { }
|
||||
|
||||
public override bool IsValid(string arg)
|
||||
{
|
||||
return float.TryParse(arg, out _);
|
||||
}
|
||||
|
||||
public override float Read(string arg)
|
||||
{
|
||||
Validate.IsTrue(float.TryParse(arg, out float value), "Invalid decimal number received");
|
||||
return value;
|
||||
}
|
||||
|
||||
object IParameter<object>.Read(string arg)
|
||||
{
|
||||
return Read(arg);
|
||||
}
|
||||
}
|
||||
}
|
25
NitroxServer/ConsoleCommands/Abstract/Type/TypeInt.cs
Normal file
25
NitroxServer/ConsoleCommands/Abstract/Type/TypeInt.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using NitroxModel.Helper;
|
||||
|
||||
namespace NitroxServer.ConsoleCommands.Abstract.Type
|
||||
{
|
||||
public class TypeInt : Parameter<int>, IParameter<object>
|
||||
{
|
||||
public TypeInt(string name, bool isRequired, string description) : base(name, isRequired, description) { }
|
||||
|
||||
public override bool IsValid(string arg)
|
||||
{
|
||||
return int.TryParse(arg, out _);
|
||||
}
|
||||
|
||||
public override int Read(string arg)
|
||||
{
|
||||
Validate.IsTrue(int.TryParse(arg, out int value), "Invalid integer received");
|
||||
return value;
|
||||
}
|
||||
|
||||
object IParameter<object>.Read(string arg)
|
||||
{
|
||||
return Read(arg);
|
||||
}
|
||||
}
|
||||
}
|
24
NitroxServer/ConsoleCommands/Abstract/Type/TypeNitroxId.cs
Normal file
24
NitroxServer/ConsoleCommands/Abstract/Type/TypeNitroxId.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using NitroxModel.DataStructures;
|
||||
using NitroxModel.Helper;
|
||||
|
||||
namespace NitroxServer.ConsoleCommands.Abstract.Type;
|
||||
|
||||
public class TypeNitroxId(string name, bool isRequired, string description) : Parameter<NitroxId>(name, isRequired, description)
|
||||
{
|
||||
public override bool IsValid(string arg)
|
||||
{
|
||||
return IsValid(arg, out _);
|
||||
}
|
||||
|
||||
private static bool IsValid(string arg, out Guid result)
|
||||
{
|
||||
return Guid.TryParse(arg, out result);
|
||||
}
|
||||
|
||||
public override NitroxId Read(string arg)
|
||||
{
|
||||
Validate.IsTrue(IsValid(arg, out Guid result), "Received an invalid NitroxId");
|
||||
return new NitroxId(result);
|
||||
}
|
||||
}
|
27
NitroxServer/ConsoleCommands/Abstract/Type/TypePlayer.cs
Normal file
27
NitroxServer/ConsoleCommands/Abstract/Type/TypePlayer.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using NitroxModel.Core;
|
||||
using NitroxModel.Helper;
|
||||
using NitroxServer.GameLogic;
|
||||
|
||||
namespace NitroxServer.ConsoleCommands.Abstract.Type
|
||||
{
|
||||
public class TypePlayer : Parameter<Player>
|
||||
{
|
||||
private static readonly PlayerManager playerManager = NitroxServiceLocator.LocateService<PlayerManager>();
|
||||
|
||||
public TypePlayer(string name, bool required, string description) : base(name, required, description)
|
||||
{
|
||||
Validate.NotNull(playerManager, "PlayerManager can't be null to resolve the command");
|
||||
}
|
||||
|
||||
public override bool IsValid(string arg)
|
||||
{
|
||||
return playerManager.TryGetPlayerByName(arg, out _);
|
||||
}
|
||||
|
||||
public override Player Read(string arg)
|
||||
{
|
||||
Validate.IsTrue(playerManager.TryGetPlayerByName(arg, out Player player), "Player not found");
|
||||
return player;
|
||||
}
|
||||
}
|
||||
}
|
20
NitroxServer/ConsoleCommands/Abstract/Type/TypeString.cs
Normal file
20
NitroxServer/ConsoleCommands/Abstract/Type/TypeString.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using NitroxModel.Helper;
|
||||
|
||||
namespace NitroxServer.ConsoleCommands.Abstract.Type
|
||||
{
|
||||
public class TypeString : Parameter<string>
|
||||
{
|
||||
public TypeString(string name, bool isRequired, string description) : base(name, isRequired, description) { }
|
||||
|
||||
public override bool IsValid(string arg)
|
||||
{
|
||||
return !string.IsNullOrEmpty(arg);
|
||||
}
|
||||
|
||||
public override string Read(string arg)
|
||||
{
|
||||
Validate.IsTrue(IsValid(arg), "Received null/empty instead of a valid string");
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user