diff --git a/src/SampSharp.OpenMp.Entities.Commands/Attributes/CommandParameterAttribute.cs b/src/SampSharp.OpenMp.Entities.Commands/Attributes/CommandParameterAttribute.cs
new file mode 100644
index 00000000..2767ea4e
--- /dev/null
+++ b/src/SampSharp.OpenMp.Entities.Commands/Attributes/CommandParameterAttribute.cs
@@ -0,0 +1,36 @@
+namespace SampSharp.Entities.SAMP.Commands;
+
+///
+/// Specifies metadata for a command parameter, such as its display name and optional parser type.
+///
+[AttributeUsage(AttributeTargets.Parameter)]
+public class CommandParameterAttribute : Attribute
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CommandParameterAttribute()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with a name and optional parser type.
+ ///
+ /// The display name of the command parameter or to use the default name.
+ /// The type used to parse the parameter value, or to use the default parser.
+ public CommandParameterAttribute(string? name, Type? parserType = null)
+ {
+ Name = name;
+ ParserType = parserType;
+ }
+
+ ///
+ /// Gets or sets the display name of the command parameter.
+ ///
+ public string? Name { get; set; }
+
+ ///
+ /// Gets or sets the type used to parse the command parameter.
+ ///
+ public Type? ParserType { get; set; }
+}
\ No newline at end of file
diff --git a/src/SampSharp.OpenMp.Entities.Commands/Core/CommandScanner.cs b/src/SampSharp.OpenMp.Entities.Commands/Core/CommandScanner.cs
index 66949e1c..37b49c9d 100644
--- a/src/SampSharp.OpenMp.Entities.Commands/Core/CommandScanner.cs
+++ b/src/SampSharp.OpenMp.Entities.Commands/Core/CommandScanner.cs
@@ -348,10 +348,16 @@ private static bool TryCollectParameters(ParameterInfo[] parameters, int prefixP
{
var param = parameters[i];
- var paramName = param.Name ?? $"param{i}";
+ var paramAtribute = param.GetCustomAttribute();
+
+ var paramName = paramAtribute?.Name ?? param.Name ?? $"param{i}";
// Try to get a parser for this parameter
- var parser = parserFactory.CreateParser(parameters, i);
+ var parserInstance = paramAtribute?.ParserType is not null
+ ? Activator.CreateInstance(paramAtribute.ParserType)
+ : null;
+
+ var parser = parserInstance as ICommandParameterParser ?? parserFactory.CreateParser(parameters, i);
if (parser == null)
{
diff --git a/test/SampSharp.OpenMp.Entities.Commands.Tests/Core/CommandScannerTests.cs b/test/SampSharp.OpenMp.Entities.Commands.Tests/Core/CommandScannerTests.cs
index 67fecb80..41c335b2 100644
--- a/test/SampSharp.OpenMp.Entities.Commands.Tests/Core/CommandScannerTests.cs
+++ b/test/SampSharp.OpenMp.Entities.Commands.Tests/Core/CommandScannerTests.cs
@@ -159,6 +159,12 @@ private class CommandWithSuffixSystem : ISystem
public void HelpCommand(Player player) { }
}
+ private class CommandWithCustomNameSystem : ISystem
+ {
+ [PlayerCommand]
+ public void MessageCommand(Player player, [CommandParameter("custom")]string text) { }
+ }
+
private class InvalidReturnTypeSystem : ISystem
{
// int is not a valid return type for player commands
@@ -259,6 +265,21 @@ public void ScanPlayerCommands_StripsSuffixFromMethodName()
FindByName(registry, "help").ShouldNotBeNull();
}
+ [Fact]
+ public void ScanPlayerCommands_ProvidesCustomName()
+ {
+ var registry = CreateCommandRegistry();
+ var scanner = CreateScanner(typeof(CommandWithCustomNameSystem));
+
+ scanner.ScanPlayerCommands(registry, CreateParserFactory());
+
+ FindByName(registry, "message")
+ .ShouldNotBeNull()
+ .ParsedParameters[0]
+ .Name
+ .ShouldBe("custom");
+ }
+
[Fact]
public void ScanPlayerCommands_ExtractsParsedParameters()
{
diff --git a/test/TestMode.OpenMp.Entities/Systems/TestConsoleCommandsSystem.cs b/test/TestMode.OpenMp.Entities/Systems/TestConsoleCommandsSystem.cs
index 27856d08..a4552aee 100644
--- a/test/TestMode.OpenMp.Entities/Systems/TestConsoleCommandsSystem.cs
+++ b/test/TestMode.OpenMp.Entities/Systems/TestConsoleCommandsSystem.cs
@@ -43,4 +43,26 @@ public void AddCommand(int a, int b)
{
Console.WriteLine($"{a} + {b} = {a + b}");
}
-}
\ No newline at end of file
+
+ [ConsoleCommand(Name = "double_number")]
+ public void DoubleValueCommand([CommandParameter("value", typeof(DoubleIntParser))] int v)
+ {
+ Console.WriteLine(v);
+ }
+
+ private class DoubleIntParser : ICommandParameterParser
+ {
+ private readonly IntParser _inner = new();
+
+ public bool TryParse(IServiceProvider services, ref StringSpan inputText, out object? result)
+ {
+ if (_inner.TryParse(services, ref inputText, out result) && result is int num)
+ {
+ result = num * 2;
+ return true;
+ }
+
+ return false;
+ }
+ }
+}