Describe chat commands in a machine readable way
Some checks failed
.NET Core / build (push) Has been cancelled

The only description of a chat command was its usage string, which is
meant to be read by a human. A user interface which wants to offer the
commands to a player needs the parts separately: the command, what it
does, and one entry per parameter.

Add ChatCommandInfo and ChatCommandParameterInfo, built from the
metadata which is already there - ChatCommandHelpAttribute for the
command and its required character status, ArgumentAttribute for the
short names and whether a parameter is required, ValidValuesAttribute
for the accepted values. Name and description come from the display
attribute, so they are returned in the language of the player.

The description which is passed to ChatCommandHelpAttribute was never
stored anywhere. Keep it as a fallback for commands which have no
display attribute, instead of discarding it.

The reset info command had its texts hard coded in English. It now
refers to resources like every other chat command does.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MSpK6jkyF8ZS5nYXyGwYxA
This commit is contained in:
Claude
2026-07-25 16:09:46 +00:00
committed by Acentech Dev
parent 38fd435ff6
commit 452f0bb139
8 changed files with 212 additions and 14 deletions

View File

@@ -113,27 +113,46 @@ public static class CommandExtensions
/// <param name="argumentsType">Type of the arguments.</param>
/// <returns>A list of parameters with name, type, and valid values.</returns>
public static IEnumerable<(string Name, string Type, string ValidValues)> GetParameters(Type argumentsType)
{
return GetParameterInfos(argumentsType)
.Select(parameter => (Name: parameter.Name, Type: parameter.TypeName, ValidValues: string.Join('|', parameter.ValidValues)));
}
/// <summary>
/// Gets the description of the parameters of an argument class.
/// </summary>
/// <param name="argumentsType">Type of the arguments.</param>
/// <returns>The described parameters, in the order in which they are expected when they are passed without their short names.</returns>
public static IEnumerable<ChatCommandParameterInfo> GetParameterInfos(Type argumentsType)
{
var properties = argumentsType.GetProperties().Where(p => p.CanWrite);
foreach (var property in properties)
{
string validValues = string.Empty;
IReadOnlyList<string> validValues = [];
if (property.GetCustomAttribute<ValidValuesAttribute>() is { } validValuesAttribute)
{
validValues = string.Join('|', validValuesAttribute.ValidValues);
validValues = validValuesAttribute.ValidValues.ToList();
}
else if (property.PropertyType == typeof(bool))
{
validValues = "0|1";
validValues = ["0", "1"];
}
else if (property.PropertyType == typeof(byte) || property.PropertyType == typeof(ushort) || property.PropertyType == typeof(uint))
{
// todo: ranges in ParameterAttribute
// validValues = "";
}
yield return (property.Name, property.PropertyType.Name, validValues);
// A parameter without an ArgumentAttribute can't be required - the parser
// only counts the required arguments of the attributed properties.
var argumentAttribute = property.GetCustomAttribute<ArgumentAttribute>(inherit: true);
yield return new ChatCommandParameterInfo(
property.Name,
argumentAttribute?.ShortName,
property.PropertyType.Name,
argumentAttribute?.IsRequired ?? false,
validValues);
}
}