first commit

This commit is contained in:
2025-07-06 00:23:46 +02:00
commit 38f50c8819
1788 changed files with 112878 additions and 0 deletions

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

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

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

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

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

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

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

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

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

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