A lightweight, simple, and type-safe command parsing and execution library for .NET with valve-like command syntax. Created for building in-game consoles but works for building command-line interfaces, debug consoles, or any application that needs to parse and execute text-based commands with typed arguments.
A command could look like this:
setPosition "player1" 100 200 50.5
-- or --
set_position player1 100 200 50.5
Targets .Net Standard 2.1. Therefore compatiable with the Unity game engine, as well with .NET Core 3.0+, .NET 5+, .NET Framework 4.7.2+
(Not AOT Compatible with trimming enabled)
- Fluent API - Intuitive method chaining for building commands
- Type Safety - Strongly-typed arguments (string, bool, float/double/decimal)
- Flexible Arguments - Support for up to 10 arguments with optional parameters
- Command Aliases - Multiple names for the same command
- Case Sensitivity - Configurable case-sensitive or case-insensitive matching
- Help System - Built-in help text support for commands
(AOT Compatible)
varcommand=CommandParser.Parse("setPosition ""player1""10020050.5");
// returns this:publicclass CommandParseResult
{public string Command {get;}// setPosition
public IReadOnlyList<object?> Arguments {get;}// contains string player1, then 3 float values}usingGameShellLite;// Create a command runnervarrunner=newCommandRunner();// Register a simple commandrunner.RegisterCommand("hello").WithExecution(()=>Console.WriteLine("Hello, World!"));// Execute the commandrunner.Execute("hello");// Command with a single string argumentrunner.RegisterCommand("greet").WithArg<string>()// Valid types are (string, bool, float/double/decimal).WithExecution(name =>Console.WriteLine($"Hello, {name}!"));runner.Execute("greet \"Alice\"");// Output: Hello, Alice!// Command with multiple typed argumentsrunner.RegisterCommand("setPosition").WithArg<float>()// x coordinate.WithArg<float>()// y coordinate.WithArg<float>()// z coordinate.WithExecution((x,y,z)=>{Console.WriteLine($"Position set to ({x}, {y}, {z})");});runner.Execute("setPosition 10.5 20.0 -5.5");// Output: Position set to (10.5, 20, -5.5)// Command with optional argumentsrunner.RegisterCommand("connect").WithArg<string>()// host (required).WithArg<float>(optional:true)// port (optional)// this would work too, for nullable float//.WithArg<float?>(true).WithExecution((host,port)=>{varportNum=port==0?8080:(int)port;Console.WriteLine($"Connecting to {host}:{portNum}");});runner.Execute("connect \"localhost\"");// Output: Connecting to localhost:8080runner.Execute("connect \"localhost\" 3000");// Output: Connecting to localhost:3000// Register a command with aliasesrunner.RegisterCommand("help").WithAlias("h").WithAlias("helpme").WithArg<string>().WithExecution(command =>Console.WriteLine(runner.GetHelpPrintFor(command)));runner.Execute("help");// Worksrunner.Execute("h");// Also worksrunner.Execute("helpme");// Also works// Add help documentation to commandsrunner.RegisterCommand("spawn").WithHelp("Spawns an entity at the specified coordinates").WithArg<string>()// entity type.WithArg<float>()// x position.WithArg<float>()// y position.WithExecution((type,x,y)=>{Console.WriteLine($"Spawning {type} at ({x}, {y})");});// Get help text for a commandstring?helpText=runner.GetHelpPrintFor("spawn");Console.WriteLine(helpText);runner.RegisterCommand("setDebug").WithArg<bool>().WithExecution(enabled =>{Console.WriteLine($"Debug mode: {(enabled?"ON":"OFF")}");});runner.Execute("setDebug true");// Output: Debug mode: ON// Access the raw parse result for dynamic handlingrunner.RegisterCommand("dynamic").WithExecution(result =>{Console.WriteLine($"Command: {result.Command}");Console.WriteLine($"Argument count: {result.Arguments.Count}");foreach(vararginresult.Arguments){Console.WriteLine($" - {arg} ({arg?.GetType().Name})");}});runner.Execute("dynamic \"test\" 42 true");// Output:// Command: dynamic// Argument count: 3// - test (Single)// - 42 (Single)// - True (Boolean)// Create a case-insensitive command runnervarrunner=newCommandRunner(caseInsensitiveCommandNames:true);runner.RegisterCommand("Test").WithExecution(()=>Console.WriteLine("Test executed"));runner.Execute("test");// Worksrunner.Execute("TEST");// Also worksrunner.Execute("TeSt");// Also worksStrings must be enclosed in double quotes:
greet "John Doe"
Supported escape sequences in strings:
\\- Backslash\n- Newline
runner.Execute("print \"Line 1\\nLine 2\"");runner.Execute("setPath \"C:\\\\Users\\\\Player\"");Numbers can be integers or decimals, with optional negative sign:
setHealth 100
setSpeed 12.5
adjust -10.5
Use true or false (case-insensitive):
setDebug true
enableCheats false
Unquoted alphanumeric strings:
enable debugMode
set config production
Use null keyword (if enabled in parser options, enabled by default):
setName null
Control whether null values are allowed:
varoptions=newCommandParserOptions{AllowNull=false// Disallow null arguments with null literal};varrunner=newCommandRunner(parserOptions:options);Choose the numeric type for number arguments (default is float):
varoptions=newCommandParserOptions{NumberPrecision=NumberPrecision.Double// Float, Double, or Decimal};varrunner=newCommandRunner(parserOptions:options);runner.RegisterCommand("calculate").WithArg<double>()// Must match the NumberPrecision setting.WithExecution(value =>Console.WriteLine($"Value: {value}"));CommandRunnerException- General command runner errorsCommandRunnerNotFoundException- Command not foundCommandParserException- Parsing errors
try{runner.Execute(userInput);}catch(CommandRunnerNotFoundExceptionex){Console.WriteLine($"Command not found: {ex.CommandName}");}catch(CommandRunnerExceptionex){Console.WriteLine($"Command error: {ex.Message}");}catch(CommandParserExceptionex){Console.WriteLine($"Parse error: {ex.Message}");}