From 7e2247449ddb298426bc14304ab734000a7f4def Mon Sep 17 00:00:00 2001 From: ElectroJr Date: Thu, 2 Jan 2025 16:08:34 +1300 Subject: [PATCH 01/13] Update toolshed documentation --- src/SUMMARY.md | 10 +- src/en/robust-toolbox/toolshed.md | 160 ++++++++++++++++-- src/en/robust-toolbox/toolshed/blocks.md | 48 ++++++ .../toolshed/commands/emplace.md | 35 ++++ src/en/robust-toolbox/toolshed/development.md | 25 +++ .../toolshed/explain_example_1.txt | 10 -- .../toolshed/explain_example_2.txt | 6 - .../toolshed/explain_example_3.txt | 25 --- .../toolshed/toolshed-examples.md | 8 +- src/en/robust-toolbox/toolshed/types.md | 72 +++++++- src/en/robust-toolbox/toolshed/variables.md | 52 ++++++ .../toolshed/writing-commands.md | 1 - 12 files changed, 391 insertions(+), 61 deletions(-) create mode 100644 src/en/robust-toolbox/toolshed/blocks.md create mode 100644 src/en/robust-toolbox/toolshed/commands/emplace.md create mode 100644 src/en/robust-toolbox/toolshed/development.md delete mode 100644 src/en/robust-toolbox/toolshed/explain_example_1.txt delete mode 100644 src/en/robust-toolbox/toolshed/explain_example_2.txt delete mode 100644 src/en/robust-toolbox/toolshed/explain_example_3.txt create mode 100644 src/en/robust-toolbox/toolshed/variables.md delete mode 100644 src/en/robust-toolbox/toolshed/writing-commands.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index f5d858645d..3a9dd15444 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -71,15 +71,19 @@ Robust Toolbox - [Physics](en/robust-toolbox/transform/physics.md) - [Grids](en/robust-toolbox/transform/grids.md) - [Toolshed](en/robust-toolbox/toolshed.md) + - [Variables](en/robust-toolbox/toolshed/variables.md) + - [Blocks](en/robust-toolbox/toolshed/blocks.md) - [Types](en/robust-toolbox/toolshed/types.md) - - [Toolshed and (S)CSI](en/robust-toolbox/toolshed/toolshed-and-scsi.md) - - [Environments](en/robust-toolbox/toolshed/environments.md) - - [Invocation contexts](en/robust-toolbox/toolshed/invocation-contexts.md) - [Commands](en/robust-toolbox/toolshed/commands.md) + - [Emplace](en/robust-toolbox/toolshed/commands/emplace.md) - [Entities](en/robust-toolbox/toolshed/commands/entity-control.md) - [General](en/robust-toolbox/toolshed/commands/general.md) - [Miscellaneous](en/robust-toolbox/toolshed/commands/misc.md) - [Toolshed Examples](en/robust-toolbox/toolshed/toolshed-examples.md) + - [Development](en/robust-toolbox/toolshed/development.md) + - [Toolshed and (S)CSI](en/robust-toolbox/toolshed/toolshed-and-scsi.md) + - [Environments](en/robust-toolbox/toolshed/environments.md) + - [Invocation contexts](en/robust-toolbox/toolshed/invocation-contexts.md) - [User Interface](en/robust-toolbox/user-interface.md) - [IoC](en/robust-toolbox/ioc.md) - [Rendering]() diff --git a/src/en/robust-toolbox/toolshed.md b/src/en/robust-toolbox/toolshed.md index e0f218c507..119fab966c 100644 --- a/src/en/robust-toolbox/toolshed.md +++ b/src/en/robust-toolbox/toolshed.md @@ -8,33 +8,171 @@ Toolshed is one of the three primary built-in debug tools (alongside `scsi` and Toolshed is not yet available on the client, so you need to use the `>` prefix command on the client in order to run its commands server-side. Ommiting this will often result in an error stating that you lack permission to run the command even if this is not the case. ``` -Toolshed is a **pipeline shell**, and the primary method of performing complex actions is composition of commands. You can simply write multiple commands one after the other and as long as they are compatible, they will have their inputs successively fed to one another. For example, take the following **command run**: +Toolshed is a **pipeline shell**, and the primary method of performing complex actions is composition of commands. You can simply write multiple commands one after the other and as long as they are compatible, they will have their inputs successively fed to one another. For those familiar with shells like bash, this is typically done with an explicit pipe symbol like '|'. However in toolshed the pipe operator is optional. +For example, take the following **command run**: ``` entities with Item count ``` +which could also be written with explicit pipe operators: +``` +entities | with Item | count +``` +This is three commands, `entities`, `with`, and `count`. They together form a **command run**, a set of successive commands. In this case, the combined effect is to return the total number of entities that have the `ItemComponent`. + +For a description of some commonly useful commands, see the [commands section](./toolshed/commands.md). For some examples of how to string toolshead commands together see [toolshead examples](./toolshed/toolshed-examples.md) -This is three commands, `entities`, `with`, and `count`. They together form a **command run**, a set of successive commands. You can use the `explain` command to provide information about a command run's flow. It's highly recommended you `explain` command runs you don't understand to get an idea of their flow. Note that unlike other pipeline based shells, toolshed's pipeing is *implicit*; you do not need to include a special pipe symbol for commands to be joined into a pipeline. Simply writing them one after another is enough. +## Explain + +You can use the `explain` command to provide information about a command run's flow. It's highly recommended you `explain` command runs you don't understand to get an idea of their flow. It will break any valid command run into its constituent commands, and for each command it will provide: +- A short description of the command +- The command's signature, including argument names & types +- The specific input and output types in the context of the given command run. ``` -{{#include toolshed/explain_example_1.txt}} +> explain entities with Item count + +entities - Returns all entities on the server. +Pipe input: [none] +Pipe output: IEnumerable +Signature: + entities + +with - Filters the input entities by whether or not they have the given component. +Pipe input: IEnumerable +Pipe output: IEnumerable +Signature: + → with + +count - Counts the amount of entries in it's input, returning an integer. +Pipe input: IEnumerable +Pipe output: Int32 +Signature: + → count ``` -a -As the `explain` output might suggest, Toolshed is a strongly typed language. All commands have a type signature, and this signature can vary dynamically based on the type of the piped in value and on any **type arguments** provided. -For example, the `comp` command has the signature `IEnumerable -> IEnumerable`, where T is any user specified component. +## Help + +The `help` command can be used to show the description and generic signatures of commands. For example: ``` -{{#include toolshed/explain_example_2.txt}} +> help count + +count - Counts the amount of entries in it's input, returning an integer. +Usage: + )> → count → Int32 ``` +Note that the type of the piped "input" argument here is `IEnumerable`, unlike in the `explain` example, which used the type specific to the command run that was being explained(`IEnumerable`). + +The syntax of the piped and command arguments is ``. If a command argument is optional, it will instead use square brackets (i.e., `[Name (Type)]`. Some commands also accept infinitely repeatable arguments, which are denoted with ellipses (i.e., `[Name (Type)]...`). + +If a command has more than one valid signature, the help command will list all of them. For example, the `with` command that was used in the previous section can take in either an entity or an entity prototype: -Toolshed also supports variables in which you can store values. You can use the `=>` command to do this. Variables can then be used anywhere a command accepts a `ValueRef`, which can be a block, constant, or variable. You can put `=>` in the middle of a command run as well to tee the value. ``` -{{#include toolshed/explain_example_3.txt}} +> help with + +with - Filters the input entities by whether or not they have the given component. +The behaviour of this command can be inverted using the "not" prefix. +Usage: + )> → [not] with → IEnumerable + )> → [not] with → IEnumerable + >)> → [not] with )> → IEnumerable> ``` +As the previous example explains, some commands can be given an optional "not" prefix to invert their behaviour. So if we wanted to get a count of all entities that do not have an item component, we could use `entities not with Item count`. + + +## Subcommands + +Some commands are just groups of "subcommands". These are collections of related commands whose names consist of two parts separated by a colon, the command name, and the subcommand name. E.g., there are commands for adding, removing, ensuring, and checking for components, and these are all grouped together as part of the "comp" command: +* `comp:get` +* `comp:has` +* `comp:add` +* `comp:rm` +* `comp:ensure` + +Currently, this is mainly an organisational convention. For users, subcommands behave just like regular commands. + +## Common commands + +This section briefly describes some simple commands that are commonly used to help construct more complex **command runs**. + +### Constants + +These commands are often used at the start of a **command run** to provide some initial value that gets piped into other commands: +* `i`, returns an integer. E.g., `i 2`. +* `f`, returns an float. E.g., `f 2.1`. +* `b`, returns an bool. E.g., `b true`. +* `s`, returns an string. E.g., `s "foo"`. +* `ent`, returns an EntityUid. E.g., `ent 123`. +* `fpi`, `dpi` returns Pi, in floating or double precision. + + +### Maths +Toolshed supports many kinds of math operations, including: +* Simple operations: `+`, `-`, `\*`, `/`, `%` +* Common functions: `sin`, `abs`,`min`, `pow`, `ceil`, etc. +* Vector operations (i.e., multiplying a list by a number): `+/`, `-/`, `\*/`, `//`, `%/` +* Bitwise operations: `&`, `^`, `bitor`, `~`, `&~` `^~`, `bitornot` + +### Ranges & Sequences + +There are a few commands that are useful for creating or manipulatiting lists/sequences: +* `count` returns the total number of items in a sequence +* `to` is used to create a range of numbers. E.g., `i 3 to 5` returns `[3,4,5]` +* `iota` is used to create a range of numbers up to some value. E.g., `i 3 iota` returns `[1, 2, 3]`. +* `rep` repeats the input value. E.g., `i 5 rep 3` returns `[5, 5, 5]`. +* `append` adds a number to the end of a sequence. +* `join` combines two sequences (or strings). Can also be used to prepend a single item. +* `first` selects the first element in a sequence +* `take` takes the first N element in a sequence +* `select` randomly selects N elements or a percentage of elements from a sequence + + +## Terminators + +Command argument parsing can be interrupted using either the explicit pipe symbol `|` or terminator symbol `;`. The former is primarily useful if a command has optional or repeatable arguments, or to make some commands easier to read & understand. The latter is mainly useful because it drops the piped output value, which can be used to chain otherwise incompatible commands together. For example: +``` +> f 2 * 1.5; i 1 + 1 +2 +``` +Without the `;` the above command would fail to parse, as the `i` command does not accept any piped in values. + +## Errors and invalid commands + +Before toolshed will attempt to execute a command run, it first has to successfully parse it. If it fails to parse the command, it should try to print out a useful error message that points to the specific part of the command that toolshed failed to parse. Note that the `explain` command only works on a valid command run, and cannot be used to figure out why a command run is not working. + ```admonish note -Toolshed often spits out lengthy stacktraces upon a command being used incorrectly. Typically, there is a more clear error above the stacktrace in your console. +Toolshed often spits out lengthy stacktraces upon a command being used incorrectly. But typically there is a clearer error message above the stacktrace in your console, though you may have to scroll up to see it. ``` -For examples of how to string toolshead commands together see [toolshead examples](./toolshed/toolshed-examples.md) +For example, in one of the earlier examples, there is a `with` command that expects either an EntityUid or Prototype piped input . If we instead give it an integer it will inform us that there is no `with` command that takes in that type: + +``` +> entities count with Items + +entities count with Items + ^^^^ +Could not find an implementation of the 'with' command given the input type 'Int32'. +Accepted types: 'IEnumerable','IEnumerable','IEnumerable>'. + + at Robust.Shared.Toolshed.ToolshedCommandImplementor.TryParse(ParserContext ctx, Func`2& invocable, Nullable`1& method) + at Robust.Shared.Toolshed.Syntax.ParsedCommand.TryParseCommand(ParserContext ctx, Func`2& invocable, Nullable`1& method, ToolshedCommandImplementor& implementor) + at Robust.Shared.Toolshed.Syntax.ParsedCommand.TryParse(ParserContext ctx, Type piped, ParsedCommand& result) + at Robust.Shared.Toolshed.Syntax.CommandRun.TryParse(ParserContext ctx, Type pipedType, Type targetOutput, CommandRun& expr) + at Robust.Shared.Toolshed.ToolshedManager.InvokeCommand(IInvocationContext ctx, String command, Object input, Object& result) + at Robust.Shared.Toolshed.ToolshedManager.InvokeCommand(IConsoleShell session, String command, Object input, Object& result, IInvocationContext& ctx) + at Robust.Server.Console.ServerConsoleHost.ExecuteInShell(IConsoleShell shell, String command) + at Robust.Server.Console.ServerConsoleHost.ExecuteCommand(ICommonSession session, String command) + at Robust.Server.Console.ServerConsoleHost.ProcessCommand(MsgConCmd message) + at Robust.Shared.Network.NetManager.<>c__DisplayClass109_0`1.b__0(NetMessage msg) + at Robust.Shared.Network.NetManager.DispatchNetMessage(NetIncomingMessage msg) + at Robust.Shared.Network.NetManager.ProcessPackets() + at Robust.Server.BaseServer.Input(FrameEventArgs args) + at Robust.Server.BaseServer.b__67_0(Object sender, FrameEventArgs args) + at Robust.Shared.Timing.GameLoop.Run() + at Robust.Server.BaseServer.MainLoop() +``` + + + diff --git a/src/en/robust-toolbox/toolshed/blocks.md b/src/en/robust-toolbox/toolshed/blocks.md new file mode 100644 index 0000000000..3e9d11cc6c --- /dev/null +++ b/src/en/robust-toolbox/toolshed/blocks.md @@ -0,0 +1,48 @@ +# Blocks + +Blocks are **command runs** that are wrapped in curly braces. Much like variables, command blocks can be used in place of a command's normal arguments. For example, this command: +``` +i 2 * { i 3 + 4 } +``` +is effectively equivalent to +``` +i 2 * 7 +``` + +This quite useful for overriding the normal order of operations, or for combining together commands where the output of one is information that needs to be specified as an argument instead of as a piped input. Note the `explain` command currently does not explain the contents of command blocks, it simply treats them as if they were regularly specified arguments. + +Some commands explicitly require a block to be provided **as** the argument, and cannot use normal arguments. The simplest example is the `map` command, which simply repeatedly evaluates a block for each input value: +``` +> help map + +map - Maps the input over the given block. +Usage: + )> → map )> → IEnumerable +``` +This command is particularly useful if another command only takes in a single `TIn` and has no `IEnumerable` support. For example, this command doubles then increments all values in a sequence using a command block that only takes in a single integer at a time: +``` +> i 1 to 4 map { * 2 + 1 } +3, +5, +7, +9 +``` +Note that this is just an example, as there actually are specific maths commands for multiplying or adding single numbers to a sequence (`i 1 to 4 */ 2 +/ 1`). + +As another example, the `sortby` command requires a block that takes in a type `T` and returns some other type that is comparable to itself (`TOrd : IComomparable`). You could use this to do something like sorting entities based on the number of components they have: +``` +entities with MobState sortby { allcomps count } +``` + +Blocks can be nested and combined together. For example, this command teleports all mobs to the mob that has the fewest components: +``` +entities with MobState tp:to { entities with MobState sortby { allcomps count } first } +``` +Though in practice you might want to split this command up to make it more readable: +``` +entities with MobState => $mobs +var $mobs sortby { allcomps count } first => $destination +var $mobs tp:to $destination +``` + +Some commands that require blocks may also define block-local variables. I.e., variables that are only defined within the block, and that are often read-only. For examples, see the [Emplace command](./commands/emplace.md). \ No newline at end of file diff --git a/src/en/robust-toolbox/toolshed/commands/emplace.md b/src/en/robust-toolbox/toolshed/commands/emplace.md new file mode 100644 index 0000000000..496e8a8087 --- /dev/null +++ b/src/en/robust-toolbox/toolshed/commands/emplace.md @@ -0,0 +1,35 @@ +# Emplace command + +The `emplace` command is a command that takes in some enumerable and repeatedly executes a command block while providing some special read-only variables that are local to that command block. The variables that are provided depends on the input type. In particular, if given entities it will create variables that give access to some of the properties of that entity including: +* The entity's map coordinates `$wx`, `$wy` +* The entity's prototype `$proto` +* The entity's name `$name` +* The entity's description `$desc` +* Whether the entity is paused `$paused` + +For example, this simple command will just return the y-coordinates of all entities: +``` +entities emplace { var $wy } +``` +In general, the `emplace` command is useful if you want to use the properties of entities as the arguments for some other toolshed command. + +Some of the variables defined by the `emplace` command are listed in the `help` command: +``` +> help emplace + +emplace - Runs the given block over it's inputs, with the input value placed into the variable $value within the block. +Additionally breaks out $wx, $wy, $proto, $desc, $name, and $paused for entities. +Can also have breakout values for other types, consult the documentation for that type for further info. +Usage: + → emplace → TOut + )> → emplace → IEnumerable +``` + +# Do command + +The `do` command is similar to the emplace command. It takes in some enumerable and command string, and will then try to repeatedly evaluate the command string (as if it had been typed into the console), possibly after having performed some string substitutions. + +The `do` command mainly exists for backwards compatibility with non-toolshed commands, which do not support toolshed variables. For example, as of the time of writing the `say` command is not a toolshed command. So if you wanted to make your own character say the positions of all mobs, you would need to use the `do` command: +``` +entities with MobState do "say $WX $WY" +``` \ No newline at end of file diff --git a/src/en/robust-toolbox/toolshed/development.md b/src/en/robust-toolbox/toolshed/development.md new file mode 100644 index 0000000000..4f1b82b845 --- /dev/null +++ b/src/en/robust-toolbox/toolshed/development.md @@ -0,0 +1,25 @@ + +# Development + +This section isn't finished yet, hence this PR is a draft. TODO: + +* subcommands (if any exist, all must be subcommands) +* type signature restrictions + * (name, pipedType) tuple must be unique. + * TakesPipedTypeAsGenericAttribute limitations +* example implementations + * optional & params args + * invertible + * invocation context & variables +* Loc strings + * subcommand loc keys: "comp:add" -> "comp-add" + * required descriptions 'command-description-' + * optional command help: 'command-help-{name}' + * If not specified, auto-generated + * Optional argument hints 'command-arg-hint-{name}-{argument}' + * If not specified, auto-generated +* type parsers + * custom type parsers + * completion hints +* type arguments +* variable parsing context diff --git a/src/en/robust-toolbox/toolshed/explain_example_1.txt b/src/en/robust-toolbox/toolshed/explain_example_1.txt deleted file mode 100644 index 1029ce2dfb..0000000000 --- a/src/en/robust-toolbox/toolshed/explain_example_1.txt +++ /dev/null @@ -1,10 +0,0 @@ -> explain entities with Item count -entities: Returns all entities on the server. -[none] -> IEnumerable - -with: Filters the input entities by whether or not they have the given component. -This command can be inverted with not. -IEnumerable -> IEnumerable - -count: Counts the amount of entries in it's input, returning an integer. -IEnumerable -> Int32 \ No newline at end of file diff --git a/src/en/robust-toolbox/toolshed/explain_example_2.txt b/src/en/robust-toolbox/toolshed/explain_example_2.txt deleted file mode 100644 index 882bfa35a5..0000000000 --- a/src/en/robust-toolbox/toolshed/explain_example_2.txt +++ /dev/null @@ -1,6 +0,0 @@ -> explain entities comp Item -entities: Returns all entities on the server. -[none] -> IEnumerable - -comp: Returns the given component from the input entities, discarding entities without that component. -IEnumerable -> IEnumerable \ No newline at end of file diff --git a/src/en/robust-toolbox/toolshed/explain_example_3.txt b/src/en/robust-toolbox/toolshed/explain_example_3.txt deleted file mode 100644 index b8a5e1dff3..0000000000 --- a/src/en/robust-toolbox/toolshed/explain_example_3.txt +++ /dev/null @@ -1,25 +0,0 @@ -> entities count => $myCount -1783 - -> explain entities count => $myCount -entities: Returns all entities on the server. -[none] -> IEnumerable - -count: Counts the amount of entries in it's input, returning an integer. -IEnumerable -> Int32 - -=>: Assigns the input to a variable. -Int32 -> Int32 - -> entities => $ents count -1783 - -> val IEnumerable $ents - (1, Sandbox), -Dev (1052, StandardNanotrasenStation), -Dev (3), - (4), -advanced capacitor (5, AdvancedCapacitorStockPart), -air alarm (6, AirAlarm), -Air canister (7, AirCanister), -... \ No newline at end of file diff --git a/src/en/robust-toolbox/toolshed/toolshed-examples.md b/src/en/robust-toolbox/toolshed/toolshed-examples.md index cc673546dd..67f922e21b 100644 --- a/src/en/robust-toolbox/toolshed/toolshed-examples.md +++ b/src/en/robust-toolbox/toolshed/toolshed-examples.md @@ -26,7 +26,7 @@ Will check if the entity has the "Mindshield" component and add it if needed, th ### Targeting yourself -`self` will always return the entity currently controlled by you. +The `self` command will always return the entity currently controlled by you. Alternatively, you can also use the `$self` variable. ``` > self rejuvenate / Will heal your character @@ -99,9 +99,9 @@ actor:controlled Takes in a list of entities and will return all entities that are currently controlled by a player. ``` -> entities actor:controlled tp:to self +> entities actor:controlled tp:to $self ``` -`self` can also be used as an argument - this will teleport all players to your location. +Above, the `$self` variable is being used as a command argument - this will teleport all players to your location. ``` select [N] @@ -115,7 +115,7 @@ Takes in a list of entities and will select a percentage of the input. Combining filters together we can do as follows: ``` -> entities actor:controlled select 5 tp:to self +> entities actor:controlled select 5 tp:to $self ``` Will teleport 5 randomly selected players to the entity you are currently controlling. diff --git a/src/en/robust-toolbox/toolshed/types.md b/src/en/robust-toolbox/toolshed/types.md index 33bfd10804..655e396cc3 100644 --- a/src/en/robust-toolbox/toolshed/types.md +++ b/src/en/robust-toolbox/toolshed/types.md @@ -1,5 +1,75 @@ + # Types -Toolshed's type system is, **for the most part** that of C#'s. Toolshed adds some additional type rules for the convenience of both users and developers. + +As the output of the `explain` and `help` commands might suggest, Toolshed is a strongly typed language. The type system is, **for the most part** that of C#'s, though toolshed adds some additional type rules for the convenience of both users and developers. I.e., the outputs of commands are actual C# types like `int`, `EntityUid`, or some `IEnumerable`, and not just strings. All commands have a type signature, and this signature can vary dynamically based on the type of the piped value. + +For example, the type piped into the addition command `+` determines the output type, and how the argument is parsed. +For example, `i 1 + 2` and `f 1 + 2.5` are valid commands, but `i 1 + 1.5` will fail to parse "1.5" as an int argument. + +``` +> explain i 1 + 1 + +i - Integer constant. +Pipe input: [none] +Pipe output: Int32 +Signature: + i + ++ - Performs numeric addition. +Pipe input: Int32 +Pipe output: Int32 +Signature: + → + + +> explain f 1 + 1.5 + +f - Float constant. +Pipe input: [none] +Pipe output: Single +Signature: + f + ++ - Performs numeric addition. +Pipe input: Single +Pipe output: Single +Signature: + → + + +> i 1 + 1.5 + +i 1 + 1.5 + ^^^ +The value 1.5 is not a valid Int32. +``` + +## Type Arguments + +Some command arguments also take in **type arguments**. These arguments usually determine the output type or how some of the other arguments are parsed. **Type arguments** arguments are always specified before any other arguments, but otherwise appear like normal arguments. In the `help` and `explain` command signatures they show up as part of the command's name using the c# generics syntax, i.e., ``, ``. + +For example, the `comp:get` command has the signature `IEnumerable -> IEnumerable`, where T is any user specified component. The actual component type specified via a **type arguments**: +``` +> help comp:get + +comp:get - Gets the given component from the given entity. +Usage: + )> → comp:get → IEnumerable + → comp:get → T + + +> explain entities comp:get Item + +entities - Returns all entities on the server. +Pipe input: [none] +Pipe output: IEnumerable +Signature: + entities + +comp:get - Gets the given component from the given entity. +Pipe input: IEnumerable +Pipe output: IEnumerable +Signature: + → comp:get +``` ## For users ### All values are a list of length 1 (`T -> IEnumerable`) diff --git a/src/en/robust-toolbox/toolshed/variables.md b/src/en/robust-toolbox/toolshed/variables.md new file mode 100644 index 0000000000..32858756a6 --- /dev/null +++ b/src/en/robust-toolbox/toolshed/variables.md @@ -0,0 +1,52 @@ +# Variables + +The outputs of toolshed commands can be stored in named variables using the assignment command `=>`. Variable names must begin with a `$`. For example, the following will define a simple int variable and repeatedly increment it a couple of times: + +``` +> i 1 => $x +1 + +> i 1 + $x => $x +2 + +> i 1 + $x => $x +3 +``` + +The currently defined variables can be listed using the `vars` command: +``` +> vars +more = , +x = 4, +self = 3015 +``` + +The `$self` and `$more` variables are special variables defined by the shell. `$self` points to the player's current entity, while `$more` is used if the output of a command is too large to print, in which case the remaining output can be requested by running the `more` command. + + +## `Var` Command + +If you want to pipe the value stored in a variable into another command, you can use the `var` command, which effectively just reads out the value of a variable. + +e.g., + +``` +> var $x +3 + +> var $x * 2 +6 +``` + +## `Val` Command + + +As toolshed is a strongly typed, parsing a valid command run requires knowing the types of the values that will be returned by a command. Seeing as the contents of a variable may change as a command is executed, this means that some commands may fail to parse. In those instances, you can use the `val` command instead, though that requires you to explicitly specify the type. + +``` +> val int $x +3 + +> val int $x * 2 +6 +``` \ No newline at end of file diff --git a/src/en/robust-toolbox/toolshed/writing-commands.md b/src/en/robust-toolbox/toolshed/writing-commands.md deleted file mode 100644 index 56618cf88f..0000000000 --- a/src/en/robust-toolbox/toolshed/writing-commands.md +++ /dev/null @@ -1 +0,0 @@ -# Writing commands From 2ede1bc537624f88e04e921c04002a85b241792b Mon Sep 17 00:00:00 2001 From: ElectroJr Date: Thu, 2 Jan 2025 16:41:40 +1300 Subject: [PATCH 02/13] a --- src/en/robust-toolbox/toolshed.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/en/robust-toolbox/toolshed.md b/src/en/robust-toolbox/toolshed.md index 119fab966c..7ca2a40416 100644 --- a/src/en/robust-toolbox/toolshed.md +++ b/src/en/robust-toolbox/toolshed.md @@ -20,8 +20,6 @@ entities | with Item | count ``` This is three commands, `entities`, `with`, and `count`. They together form a **command run**, a set of successive commands. In this case, the combined effect is to return the total number of entities that have the `ItemComponent`. -For a description of some commonly useful commands, see the [commands section](./toolshed/commands.md). For some examples of how to string toolshead commands together see [toolshead examples](./toolshed/toolshed-examples.md) - ## Explain You can use the `explain` command to provide information about a command run's flow. It's highly recommended you `explain` command runs you don't understand to get an idea of their flow. It will break any valid command run into its constituent commands, and for each command it will provide: @@ -62,9 +60,9 @@ count - Counts the amount of entries in it's input, returning an integer. Usage: )> → count → Int32 ``` -Note that the type of the piped "input" argument here is `IEnumerable`, unlike in the `explain` example, which used the type specific to the command run that was being explained(`IEnumerable`). +Note that the type of the piped "input" argument here is `IEnumerable`, unlike in the `explain` example, which used the type specific to the command run that was being explained (`IEnumerable`). -The syntax of the piped and command arguments is ``. If a command argument is optional, it will instead use square brackets (i.e., `[Name (Type)]`. Some commands also accept infinitely repeatable arguments, which are denoted with ellipses (i.e., `[Name (Type)]...`). +The syntax of the piped and command arguments is ``, where the argument name and are taken from the C# method associated with that command. If a command argument is optional, it will instead use square brackets (i.e., `[Name (Type)]`. Some commands also accept infinitely repeatable arguments, which are denoted with ellipses (i.e., `[Name (Type)]...`). If a command has more than one valid signature, the help command will list all of them. For example, the `with` command that was used in the previous section can take in either an entity or an entity prototype: @@ -79,7 +77,7 @@ Usage: >)> → [not] with )> → IEnumerable> ``` -As the previous example explains, some commands can be given an optional "not" prefix to invert their behaviour. So if we wanted to get a count of all entities that do not have an item component, we could use `entities not with Item count`. +As the above example explains, some commands can be given an optional "not" prefix to invert their behaviour. So if we wanted to get a count of all entities that do not have an item component, we could use `entities not with Item count`. ## Subcommands @@ -95,7 +93,9 @@ Currently, this is mainly an organisational convention. For users, subcommands b ## Common commands -This section briefly describes some simple commands that are commonly used to help construct more complex **command runs**. +This section briefly describes some simple commands that are commonly used to help construct more complex **command runs**. These may be used throughout the docs when providing examples for how to use other commands. + +For a description of some other commonly useful commands, see the [commands section](./toolshed/commands.md). For some examples of how to string toolshead commands together see [toolshead examples](./toolshed/toolshed-examples.md) ### Constants @@ -109,7 +109,7 @@ These commands are often used at the start of a **command run** to provide some ### Maths -Toolshed supports many kinds of math operations, including: +Toolshed supports many kinds of math operations, including, but not limited to: * Simple operations: `+`, `-`, `\*`, `/`, `%` * Common functions: `sin`, `abs`,`min`, `pow`, `ceil`, etc. * Vector operations (i.e., multiplying a list by a number): `+/`, `-/`, `\*/`, `//`, `%/` @@ -117,7 +117,7 @@ Toolshed supports many kinds of math operations, including: ### Ranges & Sequences -There are a few commands that are useful for creating or manipulatiting lists/sequences: +There are a few commands that are useful for creating or manipulating lists/sequences: * `count` returns the total number of items in a sequence * `to` is used to create a range of numbers. E.g., `i 3 to 5` returns `[3,4,5]` * `iota` is used to create a range of numbers up to some value. E.g., `i 3 iota` returns `[1, 2, 3]`. From fe3a3c865e5706e419af1fb98993d0788d366fae Mon Sep 17 00:00:00 2001 From: ElectroJr Date: Thu, 2 Jan 2025 16:57:51 +1300 Subject: [PATCH 03/13] Better do docs --- src/SUMMARY.md | 2 +- .../toolshed/commands/emplace.md | 25 ++++++++----------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 3a9dd15444..fc7e2debe7 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -75,7 +75,7 @@ Robust Toolbox - [Blocks](en/robust-toolbox/toolshed/blocks.md) - [Types](en/robust-toolbox/toolshed/types.md) - [Commands](en/robust-toolbox/toolshed/commands.md) - - [Emplace](en/robust-toolbox/toolshed/commands/emplace.md) + - [Emplace & Do](en/robust-toolbox/toolshed/commands/emplace.md) - [Entities](en/robust-toolbox/toolshed/commands/entity-control.md) - [General](en/robust-toolbox/toolshed/commands/general.md) - [Miscellaneous](en/robust-toolbox/toolshed/commands/misc.md) diff --git a/src/en/robust-toolbox/toolshed/commands/emplace.md b/src/en/robust-toolbox/toolshed/commands/emplace.md index 496e8a8087..c2862232f9 100644 --- a/src/en/robust-toolbox/toolshed/commands/emplace.md +++ b/src/en/robust-toolbox/toolshed/commands/emplace.md @@ -1,11 +1,12 @@ # Emplace command -The `emplace` command is a command that takes in some enumerable and repeatedly executes a command block while providing some special read-only variables that are local to that command block. The variables that are provided depends on the input type. In particular, if given entities it will create variables that give access to some of the properties of that entity including: +The `emplace` command is a command that takes in some enumerable and repeatedly executes a command block while providing some special read-only variables that are local to that command block. The variables that are provided depends on the input type, though the actual value itself is always available via the `$value` variable. In particular, if given entities it will define local variables that give access to some of the properties of that entity including: * The entity's map coordinates `$wx`, `$wy` * The entity's prototype `$proto` * The entity's name `$name` * The entity's description `$desc` * Whether the entity is paused `$paused` +Similarly, an `ICommonSession` will provide the session's attached entity `$ent`, username `$name`, and `NetUserId` via `$userid`. For example, this simple command will just return the y-coordinates of all entities: ``` @@ -13,23 +14,17 @@ entities emplace { var $wy } ``` In general, the `emplace` command is useful if you want to use the properties of entities as the arguments for some other toolshed command. -Some of the variables defined by the `emplace` command are listed in the `help` command: -``` -> help emplace - -emplace - Runs the given block over it's inputs, with the input value placed into the variable $value within the block. -Additionally breaks out $wx, $wy, $proto, $desc, $name, and $paused for entities. -Can also have breakout values for other types, consult the documentation for that type for further info. -Usage: - → emplace → TOut - )> → emplace → IEnumerable -``` - # Do command -The `do` command is similar to the emplace command. It takes in some enumerable and command string, and will then try to repeatedly evaluate the command string (as if it had been typed into the console), possibly after having performed some string substitutions. +The `do` command is similar to the emplace command. It takes in some enumerable and command string, and will then try to repeatedly evaluate the command string (as if it had been typed into the console), possibly after having performed some string replacements. The `do` command mainly exists for backwards compatibility with non-toolshed commands, which do not support toolshed variables. For example, as of the time of writing the `say` command is not a toolshed command. So if you wanted to make your own character say the positions of all mobs, you would need to use the `do` command: ``` entities with MobState do "say $WX $WY" -``` \ No newline at end of file +``` + +The list of string replacements differs from the variables made available in the emplace block. In particular, the current value is `$SELF` instead of `$value`. When given an entity, the following string replacements are made: +* `$ID` is replaced with the current EntityUid. +* `$PID` is replaced with the executing player's attached EntityUid. +* `$WX`, `$WY` is replaced with the entity's map coordinates. +* `$LX`, `$LY` is replaced with the entity's local coordinates. From 82119d3e6c123359f1aa753bd76c7e0d96c056eb Mon Sep 17 00:00:00 2001 From: ElectroJr Date: Fri, 3 Jan 2025 21:06:32 +1300 Subject: [PATCH 04/13] Better help & explain docs --- src/en/robust-toolbox/toolshed.md | 122 ++++++++++++------ src/en/robust-toolbox/toolshed/blocks.md | 3 +- .../toolshed/commands/emplace.md | 4 +- src/en/robust-toolbox/toolshed/development.md | 13 +- .../toolshed/toolshed-examples.md | 2 +- src/en/robust-toolbox/toolshed/variables.md | 6 +- 6 files changed, 102 insertions(+), 48 deletions(-) diff --git a/src/en/robust-toolbox/toolshed.md b/src/en/robust-toolbox/toolshed.md index 7ca2a40416..20fe8e08ac 100644 --- a/src/en/robust-toolbox/toolshed.md +++ b/src/en/robust-toolbox/toolshed.md @@ -20,37 +20,24 @@ entities | with Item | count ``` This is three commands, `entities`, `with`, and `count`. They together form a **command run**, a set of successive commands. In this case, the combined effect is to return the total number of entities that have the `ItemComponent`. -## Explain - -You can use the `explain` command to provide information about a command run's flow. It's highly recommended you `explain` command runs you don't understand to get an idea of their flow. It will break any valid command run into its constituent commands, and for each command it will provide: -- A short description of the command -- The command's signature, including argument names & types -- The specific input and output types in the context of the given command run. +```admonish warning +For convenience some of the examples used throughout the documentation may use types or commands that are specific to SS14 (e.g., ItemComponent), even though Toolshed is a part of RobustToolbox and not tied to SS14. ``` -> explain entities with Item count -entities - Returns all entities on the server. -Pipe input: [none] -Pipe output: IEnumerable -Signature: - entities -with - Filters the input entities by whether or not they have the given component. -Pipe input: IEnumerable -Pipe output: IEnumerable -Signature: - → with +## Subcommands -count - Counts the amount of entries in it's input, returning an integer. -Pipe input: IEnumerable -Pipe output: Int32 -Signature: - → count -``` +Some commands are grouped together into a "command" with "subcommands". These are just collections of related commands whose names consist of two parts separated by a colon, the command name, and the subcommand name. E.g., there are commands for adding, removing, ensuring, and checking for components, and these are all grouped together as part of the "comp" command: +* `comp:get` +* `comp:has` +* `comp:add` +* `comp:rm` +* `comp:ensure` -## Help +Currently, this is mainly an organisational convention. For users, subcommands behave just like regular commands. They have no special relationship to each other, and the "comp" command doesn't actually exist. +## Help The `help` command can be used to show the description and generic signatures of commands. For example: ``` @@ -60,11 +47,15 @@ count - Counts the amount of entries in it's input, returning an integer. Usage: )> → count → Int32 ``` -Note that the type of the piped "input" argument here is `IEnumerable`, unlike in the `explain` example, which used the type specific to the command run that was being explained (`IEnumerable`). -The syntax of the piped and command arguments is ``, where the argument name and are taken from the C# method associated with that command. If a command argument is optional, it will instead use square brackets (i.e., `[Name (Type)]`. Some commands also accept infinitely repeatable arguments, which are denoted with ellipses (i.e., `[Name (Type)]...`). +The usage block shows the syntax for all implementations of that command. In this case there is only one. The usage syntax consists of up to three parts per implementation: +* The name and type of the piped input argument. Here this is the `)> → ` part. This is omitted if there is no piped input. +* The command itself, with any prefixes & arguments . As the count command has no arguments, here this is just `count`. +* The type of the output that can be piped into other commands. Here this is the `→ Int32` part. This is ommited if the command doesn't return anything. + +The syntax of the piped and command arguments is ``, where the argument name and type are taken from the C# method associated with that command. If a command argument is optional, it will instead use square brackets (i.e., `[Name (Type)]`. Some commands also accept infinitely repeatable arguments, which are denoted with ellipses (i.e., `[Name (Type)]...`). -If a command has more than one valid signature, the help command will list all of them. For example, the `with` command that was used in the previous section can take in either an entity or an entity prototype: +For a a more complicated example lets examine the `with` command that was used in the previous section. It can take in either an entity or an entity prototype, so it has multiple implementations. It also requires one argument and supports prefixes: ``` > help with @@ -77,23 +68,68 @@ Usage: >)> → [not] with )> → IEnumerable> ``` -As the above example explains, some commands can be given an optional "not" prefix to invert their behaviour. So if we wanted to get a count of all entities that do not have an item component, we could use `entities not with Item count`. +The syntax of each command arguments is ``, where the argument name and type are taken from the C# method associated with that implementation. If a command argument is optional, it will instead use square brackets (i.e., `[Name (Type)]`. Some commands also accept infinitely repeatable arguments, which are denoted with ellipses (i.e., `[Name (Type)]...`). Note that in general each implementation can have different numbers and types of arguments, though in this case each implementation has exactly one argument. +As the above help output explains, the `with` command can be given an optional "not" prefix to invert its behaviour. So if we wanted to get a count of all entities that do not have an item component, we could use `entities not with Item count`. If a command supports prefixes, the help page should say so. -## Subcommands +### Argument names -Some commands are just groups of "subcommands". These are collections of related commands whose names consist of two parts separated by a colon, the command name, and the subcommand name. E.g., there are commands for adding, removing, ensuring, and checking for components, and these are all grouped together as part of the "comp" command: -* `comp:get` -* `comp:has` -* `comp:add` -* `comp:rm` -* `comp:ensure` +The name of an argument in the command's syntax can also help resolve some ambiguities about the order in which arguments should be specified. For example, the `tp:to` command teleports one entity to another. If you were unsure about whether the destination entity is the piped input or the first argument, you can check the name of the argument printed by the help command. + +``` +> help tp:to -Currently, this is mainly an organisational convention. For users, subcommands behave just like regular commands. +tp:to - Teleports the given entities to the target entity. +Usage: + → tp:to → EntityUid + )> → tp:to → IEnumerable +``` + +```admonish warning +Note that using the c# argument names to auto-generate help strings is relatively new, and some commands may have badly named arguments. +``` + + +## Explain + +You can use the `explain` command to provide information about a command run's flow. It's highly recommended you `explain` command runs you don't understand to get an idea of their flow. It will break any valid command run into its constituent commands, and for each command it will provide: +* A short description of the command +* The specific input and output types in the context of the given command run. +* The command's signature, including the **name** and type of any parsed arguments. + +```admonish warning +Note that the explain command only works on **valid** commands. It can't be used to troubleshoot invalid commands. If you are unsure about how to use a command, maybe use the `help` command instead. +``` + +The generated explanation can be useful for understanding how each command in a run transforms the data being piped around, and the command signature hopefully makes it clearer what each argument does. For example, explaining the command from the previous section yields: + +``` +> explain entities with Item count + +entities - Returns all entities on the server. +Pipe input: [none] +Pipe output: IEnumerable +Signature: + entities + +with - Filters the input entities by whether or not they have the given component. +Pipe input: IEnumerable +Pipe output: IEnumerable +Signature: + → with + +count - Counts the amount of entries in it's input, returning an integer. +Pipe input: IEnumerable +Pipe output: Int32 +Signature: + → count +``` + +Note that here the type of the piped input argument for the count command is `IEnumerable`, unlike in the `help` example, which used generic type (`IEnumerable`). The explain command will only ever show the type and syntax for the concrete command implementation that is relevant to the command run that is being explained. ## Common commands -This section briefly describes some simple commands that are commonly used to help construct more complex **command runs**. These may be used throughout the docs when providing examples for how to use other commands. +This section briefly describes some simple commands that are commonly used to help construct more complex command runs. These may be used throughout the docs when providing examples for how to use other commands. For a description of some other commonly useful commands, see the [commands section](./toolshed/commands.md). For some examples of how to string toolshead commands together see [toolshead examples](./toolshed/toolshed-examples.md) @@ -146,7 +182,7 @@ Before toolshed will attempt to execute a command run, it first has to successfu Toolshed often spits out lengthy stacktraces upon a command being used incorrectly. But typically there is a clearer error message above the stacktrace in your console, though you may have to scroll up to see it. ``` -For example, in one of the earlier examples, there is a `with` command that expects either an EntityUid or Prototype piped input . If we instead give it an integer it will inform us that there is no `with` command that takes in that type: +For example, in one of the earlier examples, there is a `with` command that expects either an EntityUid or Prototype input. If we instead give it an integer it will inform us that there is no `with` command that takes in that type: ``` > entities count with Items @@ -174,5 +210,15 @@ Accepted types: 'IEnumerable','IEnumerable','IEnumer at Robust.Server.BaseServer.MainLoop() ``` +## Searching for commands + +You can combine the `cmd:list` to get a list of all commands. You can then combine this with the `search` commands to search through this list for a command you are trying to find. E.g., +``` +cmd:list search "awn" + +spawn:at, +spawn:on, +spawn:attached +``` diff --git a/src/en/robust-toolbox/toolshed/blocks.md b/src/en/robust-toolbox/toolshed/blocks.md index 3e9d11cc6c..b8080211b5 100644 --- a/src/en/robust-toolbox/toolshed/blocks.md +++ b/src/en/robust-toolbox/toolshed/blocks.md @@ -22,6 +22,7 @@ Usage: This command is particularly useful if another command only takes in a single `TIn` and has no `IEnumerable` support. For example, this command doubles then increments all values in a sequence using a command block that only takes in a single integer at a time: ``` > i 1 to 4 map { * 2 + 1 } + 3, 5, 7, @@ -45,4 +46,4 @@ var $mobs sortby { allcomps count } first => $destination var $mobs tp:to $destination ``` -Some commands that require blocks may also define block-local variables. I.e., variables that are only defined within the block, and that are often read-only. For examples, see the [Emplace command](./commands/emplace.md). \ No newline at end of file +Some commands that require blocks may also define block-local variables. I.e., variables that are only defined within the block, and that are often read-only. For an example, see the [Emplace command](./commands/emplace.md). \ No newline at end of file diff --git a/src/en/robust-toolbox/toolshed/commands/emplace.md b/src/en/robust-toolbox/toolshed/commands/emplace.md index c2862232f9..19d246011f 100644 --- a/src/en/robust-toolbox/toolshed/commands/emplace.md +++ b/src/en/robust-toolbox/toolshed/commands/emplace.md @@ -16,14 +16,14 @@ In general, the `emplace` command is useful if you want to use the properties of # Do command -The `do` command is similar to the emplace command. It takes in some enumerable and command string, and will then try to repeatedly evaluate the command string (as if it had been typed into the console), possibly after having performed some string replacements. +The `do` command is similar to the emplace command. It takes in some enumerable and a command string, and will then try to repeatedly evaluate the command string (as if it had been typed into the console), possibly after having performed some string replacements. The `do` command mainly exists for backwards compatibility with non-toolshed commands, which do not support toolshed variables. For example, as of the time of writing the `say` command is not a toolshed command. So if you wanted to make your own character say the positions of all mobs, you would need to use the `do` command: ``` entities with MobState do "say $WX $WY" ``` -The list of string replacements differs from the variables made available in the emplace block. In particular, the current value is `$SELF` instead of `$value`. When given an entity, the following string replacements are made: +The possible string replacements differs from the variables made available in the emplace block. In particular, the current value is `$SELF` instead of `$value`. When given an entity, the following string replacements are made: * `$ID` is replaced with the current EntityUid. * `$PID` is replaced with the executing player's attached EntityUid. * `$WX`, `$WY` is replaced with the entity's map coordinates. diff --git a/src/en/robust-toolbox/toolshed/development.md b/src/en/robust-toolbox/toolshed/development.md index 4f1b82b845..8195a957dd 100644 --- a/src/en/robust-toolbox/toolshed/development.md +++ b/src/en/robust-toolbox/toolshed/development.md @@ -13,11 +13,14 @@ This section isn't finished yet, hence this PR is a draft. TODO: * invocation context & variables * Loc strings * subcommand loc keys: "comp:add" -> "comp-add" - * required descriptions 'command-description-' - * optional command help: 'command-help-{name}' - * If not specified, auto-generated - * Optional argument hints 'command-arg-hint-{name}-{argument}' - * If not specified, auto-generated + * required descriptions 'command-description-{command name}' + * optional command help: 'command-help-{command name}' + * If not specified, auto-generatedmand-arg-hin + * Optional argument hints 'comt-{command name}-{argument name}' + * If not specified, defaults to the argument's signature + * optional argument signature 'command-arg-sig-{command name}-{argument name}' + * if not specified, autogenerated as or [name (type)] or [name (type)]... + * used for hints, explain, etc. * type parsers * custom type parsers * completion hints diff --git a/src/en/robust-toolbox/toolshed/toolshed-examples.md b/src/en/robust-toolbox/toolshed/toolshed-examples.md index 67f922e21b..ddcc49f178 100644 --- a/src/en/robust-toolbox/toolshed/toolshed-examples.md +++ b/src/en/robust-toolbox/toolshed/toolshed-examples.md @@ -1,6 +1,6 @@ # Toolshed Examples -This guide covers some basic examples of Toolshed commands. +This guide covers some examples of Toolshed commands. Note that several of these commands & referenced C# types are specific to SS14, but they still provide a good overview of how to use toolshed for other forks/games. ## Interacting with entities diff --git a/src/en/robust-toolbox/toolshed/variables.md b/src/en/robust-toolbox/toolshed/variables.md index 32858756a6..1add734c9b 100644 --- a/src/en/robust-toolbox/toolshed/variables.md +++ b/src/en/robust-toolbox/toolshed/variables.md @@ -49,4 +49,8 @@ As toolshed is a strongly typed, parsing a valid command run requires knowing th > val int $x * 2 6 -``` \ No newline at end of file +``` + +## `$marked` Variable + +Variables don't necessarily need to be set via `=>` or other toolshed commands, and could be getting set by other systems. For example, in SS14 there is a "mark" admin verb that assigns an entity to the `$marked` toolshed variable, which also has a dedicated `marked` command for reading out its contents. Though in this case the verb & command are mainly just there for convenience. From 8318b1bf7ecd8b505162c8ac37751795988b2721 Mon Sep 17 00:00:00 2001 From: ElectroJr Date: Fri, 10 Jan 2025 21:25:52 +1300 Subject: [PATCH 05/13] Remove duplication --- src/en/robust-toolbox/toolshed.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/en/robust-toolbox/toolshed.md b/src/en/robust-toolbox/toolshed.md index 20fe8e08ac..16f512761c 100644 --- a/src/en/robust-toolbox/toolshed.md +++ b/src/en/robust-toolbox/toolshed.md @@ -50,7 +50,7 @@ Usage: The usage block shows the syntax for all implementations of that command. In this case there is only one. The usage syntax consists of up to three parts per implementation: * The name and type of the piped input argument. Here this is the `)> → ` part. This is omitted if there is no piped input. -* The command itself, with any prefixes & arguments . As the count command has no arguments, here this is just `count`. +* The command itself, with any prefixes & arguments. As the count command has no arguments, here this is just `count`. * The type of the output that can be piped into other commands. Here this is the `→ Int32` part. This is ommited if the command doesn't return anything. The syntax of the piped and command arguments is ``, where the argument name and type are taken from the C# method associated with that command. If a command argument is optional, it will instead use square brackets (i.e., `[Name (Type)]`. Some commands also accept infinitely repeatable arguments, which are denoted with ellipses (i.e., `[Name (Type)]...`). @@ -68,9 +68,7 @@ Usage: >)> → [not] with )> → IEnumerable> ``` -The syntax of each command arguments is ``, where the argument name and type are taken from the C# method associated with that implementation. If a command argument is optional, it will instead use square brackets (i.e., `[Name (Type)]`. Some commands also accept infinitely repeatable arguments, which are denoted with ellipses (i.e., `[Name (Type)]...`). Note that in general each implementation can have different numbers and types of arguments, though in this case each implementation has exactly one argument. - -As the above help output explains, the `with` command can be given an optional "not" prefix to invert its behaviour. So if we wanted to get a count of all entities that do not have an item component, we could use `entities not with Item count`. If a command supports prefixes, the help page should say so. +Note that in general each implementation can have different numbers and types of arguments, though in this case each implementation has exactly one argument. As the above help output explains, the `with` command can be given an optional "not" prefix to invert its behaviour. So if we wanted to get a count of all entities that do not have an item component, we could use `entities not with Item count`. If a command supports prefixes, the help page should say so. ### Argument names From a73b399c278b0b68b2e4b1130c8c08df180302e2 Mon Sep 17 00:00:00 2001 From: ElectroJr Date: Sat, 11 Jan 2025 15:03:09 +1300 Subject: [PATCH 06/13] Spelling and capitalization --- src/en/robust-toolbox/toolshed.md | 20 +++++++++---------- .../toolshed/commands/emplace.md | 4 ++-- .../toolshed/toolshed-examples.md | 6 +++--- src/en/robust-toolbox/toolshed/variables.md | 6 +++--- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/en/robust-toolbox/toolshed.md b/src/en/robust-toolbox/toolshed.md index 16f512761c..4a0a46ca46 100644 --- a/src/en/robust-toolbox/toolshed.md +++ b/src/en/robust-toolbox/toolshed.md @@ -8,7 +8,7 @@ Toolshed is one of the three primary built-in debug tools (alongside `scsi` and Toolshed is not yet available on the client, so you need to use the `>` prefix command on the client in order to run its commands server-side. Ommiting this will often result in an error stating that you lack permission to run the command even if this is not the case. ``` -Toolshed is a **pipeline shell**, and the primary method of performing complex actions is composition of commands. You can simply write multiple commands one after the other and as long as they are compatible, they will have their inputs successively fed to one another. For those familiar with shells like bash, this is typically done with an explicit pipe symbol like '|'. However in toolshed the pipe operator is optional. +Toolshed is a **pipeline shell**, and the primary method of performing complex actions is composition of commands. You can simply write multiple commands one after the other and as long as they are compatible, they will have their inputs successively fed to one another. For those familiar with shells like bash, this is typically done with an explicit pipe symbol like '|'. However in Toolshed the pipe operator is optional. For example, take the following **command run**: ``` @@ -51,7 +51,7 @@ Usage: The usage block shows the syntax for all implementations of that command. In this case there is only one. The usage syntax consists of up to three parts per implementation: * The name and type of the piped input argument. Here this is the `)> → ` part. This is omitted if there is no piped input. * The command itself, with any prefixes & arguments. As the count command has no arguments, here this is just `count`. -* The type of the output that can be piped into other commands. Here this is the `→ Int32` part. This is ommited if the command doesn't return anything. +* The type of the output that can be piped into other commands. Here this is the `→ Int32` part. This is omitted if the command doesn't return anything. The syntax of the piped and command arguments is ``, where the argument name and type are taken from the C# method associated with that command. If a command argument is optional, it will instead use square brackets (i.e., `[Name (Type)]`. Some commands also accept infinitely repeatable arguments, which are denoted with ellipses (i.e., `[Name (Type)]...`). @@ -84,7 +84,7 @@ Usage: ``` ```admonish warning -Note that using the c# argument names to auto-generate help strings is relatively new, and some commands may have badly named arguments. +Note that using the C# argument names to auto-generate help strings is relatively new, and some commands may have badly named arguments. ``` @@ -143,11 +143,11 @@ These commands are often used at the start of a **command run** to provide some ### Maths -Toolshed supports many kinds of math operations, including, but not limited to: +Toolshed supports many kinds of maths operations, including, but not limited to: * Simple operations: `+`, `-`, `\*`, `/`, `%` -* Common functions: `sin`, `abs`,`min`, `pow`, `ceil`, etc. +* Common functions: `sin`, `abs`, `min`, `pow`, `ceil` * Vector operations (i.e., multiplying a list by a number): `+/`, `-/`, `\*/`, `//`, `%/` -* Bitwise operations: `&`, `^`, `bitor`, `~`, `&~` `^~`, `bitornot` +* Bitwise operations: `&`, `^`, `bitor`, `~`, `&~` `^~`, `bitornot` (note that `|` is the explicit pipe symbol, hence it is not used for the bitwise or commands) ### Ranges & Sequences @@ -174,7 +174,7 @@ Without the `;` the above command would fail to parse, as the `i` command does n ## Errors and invalid commands -Before toolshed will attempt to execute a command run, it first has to successfully parse it. If it fails to parse the command, it should try to print out a useful error message that points to the specific part of the command that toolshed failed to parse. Note that the `explain` command only works on a valid command run, and cannot be used to figure out why a command run is not working. +Before Toolshed will attempt to execute a command run, it first has to successfully parse it. If it fails to parse the command, it should try to print out a useful error message that points to the specific part of the command that Toolshed failed to parse. Note that the `explain` command only works on a valid command run, and cannot be used to figure out why a command run is not working. ```admonish note Toolshed often spits out lengthy stacktraces upon a command being used incorrectly. But typically there is a clearer error message above the stacktrace in your console, though you may have to scroll up to see it. @@ -210,13 +210,13 @@ Accepted types: 'IEnumerable','IEnumerable','IEnumer ## Searching for commands -You can combine the `cmd:list` to get a list of all commands. You can then combine this with the `search` commands to search through this list for a command you are trying to find. E.g., +You can use the `cmd:list` command to get a list of all commands. You can then combine this with the `search` command to search through this list for a command you are trying to find. E.g., ``` -cmd:list search "awn" +> cmd:list search "awn" spawn:at, spawn:on, spawn:attached ``` - +If you are trying to find a command that can accept the output of some other command, you can use the `types:consumers` command. E.g., `ent 1 types:consumers` will list all types that can take in an `EntityUid`. Note that the `types:consumers` command is currently somewhat flawed, particularly when it comes to C# methods using generic constraints. E.g., It will often misreport that the various math commands accept any type, even though they only accept numbers. diff --git a/src/en/robust-toolbox/toolshed/commands/emplace.md b/src/en/robust-toolbox/toolshed/commands/emplace.md index 19d246011f..8d8d3c5c20 100644 --- a/src/en/robust-toolbox/toolshed/commands/emplace.md +++ b/src/en/robust-toolbox/toolshed/commands/emplace.md @@ -12,13 +12,13 @@ For example, this simple command will just return the y-coordinates of all entit ``` entities emplace { var $wy } ``` -In general, the `emplace` command is useful if you want to use the properties of entities as the arguments for some other toolshed command. +In general, the `emplace` command is useful if you want to use the properties of entities as the arguments for some other Toolshed command. # Do command The `do` command is similar to the emplace command. It takes in some enumerable and a command string, and will then try to repeatedly evaluate the command string (as if it had been typed into the console), possibly after having performed some string replacements. -The `do` command mainly exists for backwards compatibility with non-toolshed commands, which do not support toolshed variables. For example, as of the time of writing the `say` command is not a toolshed command. So if you wanted to make your own character say the positions of all mobs, you would need to use the `do` command: +The `do` command mainly exists for backwards compatibility with non-Toolshed commands, which do not support Toolshed variables. For example, as of the time of writing the `say` command is not a Toolshed command. So if you wanted to make your own character say the positions of all mobs, you would need to use the `do` command: ``` entities with MobState do "say $WX $WY" ``` diff --git a/src/en/robust-toolbox/toolshed/toolshed-examples.md b/src/en/robust-toolbox/toolshed/toolshed-examples.md index ddcc49f178..6b111d62a5 100644 --- a/src/en/robust-toolbox/toolshed/toolshed-examples.md +++ b/src/en/robust-toolbox/toolshed/toolshed-examples.md @@ -1,13 +1,13 @@ # Toolshed Examples -This guide covers some examples of Toolshed commands. Note that several of these commands & referenced C# types are specific to SS14, but they still provide a good overview of how to use toolshed for other forks/games. +This guide covers some examples of Toolshed commands. Note that several of these commands & referenced C# types are specific to SS14, but they still provide a good overview of how to use Toolshed for other forks/games. ## Interacting with entities -Most of the time that you are interacting with toolshead you are manipulating entities in some way. +Most of the time that you are interacting with Toolshed you are manipulating entities in some way. ### Basics -`ent` is a toolshed command that allows us to return an entity with the provided ENTID. By pipeing the output of the command, we can use it to various things on the entity. Some examples include: +`ent` is a Toolshed command that allows us to return an entity with the provided ENTID. By pipeing the output of the command, we can use it to various things on the entity. Some examples include: ``` > ent 12345 delete diff --git a/src/en/robust-toolbox/toolshed/variables.md b/src/en/robust-toolbox/toolshed/variables.md index 1add734c9b..5901ebfd21 100644 --- a/src/en/robust-toolbox/toolshed/variables.md +++ b/src/en/robust-toolbox/toolshed/variables.md @@ -1,6 +1,6 @@ # Variables -The outputs of toolshed commands can be stored in named variables using the assignment command `=>`. Variable names must begin with a `$`. For example, the following will define a simple int variable and repeatedly increment it a couple of times: +The outputs of Toolshed commands can be stored in named variables using the assignment command `=>`. Variable names must begin with a `$`. For example, the following will define a simple int variable and then increment it a couple of times: ``` > i 1 => $x @@ -41,7 +41,7 @@ e.g., ## `Val` Command -As toolshed is a strongly typed, parsing a valid command run requires knowing the types of the values that will be returned by a command. Seeing as the contents of a variable may change as a command is executed, this means that some commands may fail to parse. In those instances, you can use the `val` command instead, though that requires you to explicitly specify the type. +As Toolshed is a strongly typed, parsing a valid command run requires knowing the types of the values that will be returned by a command. Seeing as the contents of a variable may change as a command is executed, this means that some commands may fail to parse. In those instances, you can use the `val` command instead, though that requires you to explicitly specify the type. ``` > val int $x @@ -53,4 +53,4 @@ As toolshed is a strongly typed, parsing a valid command run requires knowing th ## `$marked` Variable -Variables don't necessarily need to be set via `=>` or other toolshed commands, and could be getting set by other systems. For example, in SS14 there is a "mark" admin verb that assigns an entity to the `$marked` toolshed variable, which also has a dedicated `marked` command for reading out its contents. Though in this case the verb & command are mainly just there for convenience. +Variables don't necessarily need to be set via `=>` or other Toolshed commands, and could be getting set by other systems. For example, in SS14 there is a "mark" admin verb that assigns an entity to the `$marked` Toolshed variable, which also has a dedicated `marked` command for reading out its contents. Though in this case the verb & command are mainly just there for convenience. From b3695ca845ecfe1964e7e31bb23ac4bfed066c87 Mon Sep 17 00:00:00 2001 From: ElectroJr Date: Sat, 11 Jan 2025 15:09:22 +1300 Subject: [PATCH 07/13] More details for params arguments --- src/en/robust-toolbox/toolshed.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/en/robust-toolbox/toolshed.md b/src/en/robust-toolbox/toolshed.md index 4a0a46ca46..b2467a1c1d 100644 --- a/src/en/robust-toolbox/toolshed.md +++ b/src/en/robust-toolbox/toolshed.md @@ -53,7 +53,7 @@ The usage block shows the syntax for all implementations of that command. In thi * The command itself, with any prefixes & arguments. As the count command has no arguments, here this is just `count`. * The type of the output that can be piped into other commands. Here this is the `→ Int32` part. This is omitted if the command doesn't return anything. -The syntax of the piped and command arguments is ``, where the argument name and type are taken from the C# method associated with that command. If a command argument is optional, it will instead use square brackets (i.e., `[Name (Type)]`. Some commands also accept infinitely repeatable arguments, which are denoted with ellipses (i.e., `[Name (Type)]...`). +The syntax of the piped and command arguments is ``, where the argument name and type are taken from the C# method associated with that command. If a command argument is optional, it will instead use square brackets (i.e., `[Name (Type)]`. Some commands also accept infinitely repeatable arguments, which are denoted with ellipses (i.e., `[Name (Type)]...`). These kinds of arguments will always be last, and would mean that the command needs to be terminated with an explicit pipe `|` if you want to pipe the output of the command into another command. For a a more complicated example lets examine the `with` command that was used in the previous section. It can take in either an entity or an entity prototype, so it has multiple implementations. It also requires one argument and supports prefixes: From 029f0870a0121e290430811a420d7e0f48a324d6 Mon Sep 17 00:00:00 2001 From: ElectroJr Date: Sun, 13 Apr 2025 22:40:09 +1200 Subject: [PATCH 08/13] Add development section --- src/en/robust-toolbox/toolshed/development.md | 344 ++++++++++++++++-- .../toolshed/toolshed-examples.md | 2 +- 2 files changed, 320 insertions(+), 26 deletions(-) diff --git a/src/en/robust-toolbox/toolshed/development.md b/src/en/robust-toolbox/toolshed/development.md index 8195a957dd..77e0db34da 100644 --- a/src/en/robust-toolbox/toolshed/development.md +++ b/src/en/robust-toolbox/toolshed/development.md @@ -1,28 +1,322 @@ # Development -This section isn't finished yet, hence this PR is a draft. TODO: - -* subcommands (if any exist, all must be subcommands) -* type signature restrictions - * (name, pipedType) tuple must be unique. - * TakesPipedTypeAsGenericAttribute limitations -* example implementations - * optional & params args - * invertible - * invocation context & variables -* Loc strings - * subcommand loc keys: "comp:add" -> "comp-add" - * required descriptions 'command-description-{command name}' - * optional command help: 'command-help-{command name}' - * If not specified, auto-generatedmand-arg-hin - * Optional argument hints 'comt-{command name}-{argument name}' - * If not specified, defaults to the argument's signature - * optional argument signature 'command-arg-sig-{command name}-{argument name}' - * if not specified, autogenerated as or [name (type)] or [name (type)]... - * used for hints, explain, etc. -* type parsers - * custom type parsers - * completion hints -* type arguments -* variable parsing context +This section is intended for developers who want create their own toolshed commands. It is likely not useful for people who just want to use toolshed commands, though it may help them to understand errors or the Toolshed's limitations. + +## Creating a new Command + +To create a new toolshed command, you have to create a new class that inherits from `ToolshedCommand`, is annotated with the `ToolshedCommandAttribute`. and has one or more (non-static) methods that are annotated with the `CommandImplementationAttribute`. A minimal working example that defines a `foo` command would be: +```cs +[ToolshedCommand] +public sealed class FooCommand : ToolshedCommand +{ + [CommandImplementation] + public void Bar() + { + } +} +``` +The name of the command in the previous example is taken automatically from the name of the class, the method's name doesn't matter. I.e., the `FooCommand` class gets mapped to `foo`. Alternatively, the command name can be specified using the class attribute, e.g., `[ToolshedCommand(Name = "foo")]`. + + +To define a method that returns some value that can then be piped into another command, you just have to give the method some return type. And to make the command take in some arguments you can generally just use most of the standard C#/RobustToolbox types and it should automatically parse arguments for you. For example, +``` +[ToolshedCommand] +public sealed class FooCommand : ToolshedCommand +{ + [CommandImplementation] + public string Bar(string text, float number, EntityUid entity, BodyType physicsBodyType) + { + return $"{text}, {number}, {entity}, {physicsBodyType}"; + } +} +``` + +Results in a command that will automatically parse the arguments in the standard way and generates an output string that can be piped into another command: +``` +> foo "A!" 42 10 Dynamic +A!, 42, 10, Dynamic + +> foo "A!" 42 10 Dynamic | join ", suffix" +A!, 42, 10, Dynamic, suffix +``` + +In order to create a command that can accept input values that get piped in from another command, you have to give the method an argument annotated with the `PipedArgumentAttribute`. For example, this creates a simple addition command: +```cs +[ToolshedCommand] +public sealed class AddCommand : ToolshedCommand +{ + [CommandImplementation] + public int Add([PipedArgument] int x, int y) + { + return x + y; + } +} +``` + +Any method argument that does not have an attribute (and is not an `IInvocationContext` type) is assumed to be a normal command argument will attempt to be parsed from the command string by Toolshed. Optionally, these can also be explicitly annotated with the `CommandArgumentAttribute`. + +Toolshed also supports methods with optional and `params []` arguments. E.g., +```cs +[ToolshedCommand] +public sealed class SumCommand : ToolshedCommand +{ + [CommandImplementation] + public int Add(params int[] values) + { + return values.Sum(); + } +} +``` + + +## Invertible Commands + +If the method has a `bool` argument that is annotated with the `CommandInvertedAttribute`, then the command is "invertible". I.e., the command can be prefixed with the "not" keyword, and the annotated argument will tell you whether or not the behaviour of the command should be inverted. For example, this is a simple command that looks for a specific number: +```cs +[ToolshedCommand] +internal sealed class ContainsIntCommand : ToolshedCommand +{ + [CommandImplementation] + public bool ContainsInt([PipedArgument] IEnumerable input, int value, [CommandInverted] bool inverted) + { + var result = input.Contains(value); + return inverted ? !result : result; + } +} +``` + +``` +> i 1 to 5 | containsint 2 +true + +> i 1 to 5 | not containsint 2 +false +``` + + +## Invocation Contexts + +If you want to create a command that writes output to the console or that can read & write Toolshed variables, you need to have a method that takes in an `IInvocationContext` argument. This argument can also optionally be annotated with the `CommandInvocationContextAttribute`. E.g., this is a simple command that will give out one-time greetings: +```cs +[ToolshedCommand] +public sealed class HelloCommand : ToolshedCommand +{ + [CommandImplementation] + public void Hello(IInvocationContext ctx) + { + if (ctx.ReadVar("greeted") is true) + return; + + ctx.WriteLine("Hello World!"); // Or WriteMarkup, or WriteError + ctx.WriteVar("greeted", true); + } +} +``` + +## Dependencies + +Toolshed commands support normal `EntitySystem` & manager dependency injections. So if your command needs to work with entity transforms, you can just give your class a dependency field, i.e., `[Dependency] private readonly SharedTransformSystem _sys = default!;`. The base `ToolshedCommand` class already provides the `ToolshedManager`, `ILocalizationManager`, and `IEntityManager` dependencies. It also defines some helpful `IEntityManager` proxy methods (e.g., `TryComp()`, `Spawn()`, etc). So in general, you can just write code like you normally would within an `EntitySystem`. + + +## Multiple Implementations & Subcommands + +So far, all of the examples have defined a command with a single implementation method. However, commands can have more than one implementation, however each implementation must take in a different piped type. For example, this would result in a valid command that can take in either an integer or float: +```cs +[ToolshedCommand] +public sealed class ToStringCommand : ToolshedCommand +{ + [CommandImplementation] + public string Impl([PipedArgument] int x) + { + return x.ToString(); + } + + [CommandImplementation] + public string Impl([PipedArgument] float x) + { + return x.ToString(); + } +} +``` + +However, one of the limitations of toolshed is that the combination of command name & piped input type must be unique. So you cannot define two implementations that take in the same piped type, but have some different number of arguments or functionality. I.e., this is **not** a valid way of defining a command that takes in either a map or entity coordinate: +```cs +public sealed class TpCommand : ToolshedCommand +{ + [Dependency] private readonly SharedTransformSystem _sys = default!; + + [CommandImplementation] + public void Teleport([PipedArgument] EntityUid uid, EntityCoordinates entCoords) + { + _sys.SetCoordinates(uid, entCoords); + } + + [CommandImplementation] + public void Teleport([PipedArgument] EntityUid uid, MapCoordinates mapCoords) + { + _sys.SetCoordinates(uid, _sys.ToCoordinates(mapCoords)); + } +} +``` + +This limitation is mainly due to the fact that toolshed would have no way of figuring out which command or which arguments it should be attempting to parse. It can only figure it out if the combination of command name and piped input type are unique. Instead, if you wanted to introduce these kinds of variations of a command, you need to use **subcommands**. In some sense commands with subcommands are just normal commands that associate each implementation with its own unique name. These can be specified via the `CommandImplementationAttribute`. Note that if a command contains **any** named implementations, then all of them must be given a name. + +As an example, fix our earlier command could be fixed by giving the implementations a name: +```cs +public sealed class TpCommand : ToolshedCommand +{ + [Dependency] private readonly SharedTransformSystem _sys = default!; + + [CommandImplementation("ent")] + public void Teleport([PipedArgument] EntityUid uid, EntityCoordinates entCoords) + { + _sys.SetCoordinates(uid, entCoords); + } + + [CommandImplementation("map")] + public void Teleport([PipedArgument] EntityUid uid, MapCoordinates mapCoords) + { + _sys.SetCoordinates(uid, _sys.ToCoordinates(mapCoords)); + } +} +``` +This would define then define the "tp:ent" and "tp:map" commands. + + +## Generics + +Toolshed commands have some support for C# generics, though there are a few limitations. The most common use case is when you want to define a method that takes in some arbitrary piped input type and should use the type of the input as the generic argument. In that case, you just give your generic method the `TakesPipedTypeAsGenericAttribute`. E.g., this is part of how the actual addition command is defined: +```cs +public sealed class AddCommand : ToolshedCommand +{ + [CommandImplementation, TakesPipedTypeAsGeneric] + public T Operation([PipedArgument] T x, T y) where T : IAdditionOperators + { + return x + y; + } +} +``` + +The attribute also supports extracting the generic type even if it doesn't directly corresond to the type of the piped argument. For example, this is what the append command does: +```cs +[ToolshedCommand] +public sealed class AppendCommand : ToolshedCommand +{ + [CommandImplementation, TakesPipedTypeAsGeneric] + public IEnumerable Append([PipedArgument] IEnumerable x, T y) + { + return x.Append(y); + } +} +``` + +However, in more complex situations this will likely fail. E.g., a signature like `Foo([PipedArgument] Dictionary> input)` will probably fail to, though this may change in the future. There is also no support for automatically determining multiple generic arguments from the piped input. If you want commands that use more complex generics, you will generally need to define a command that has explicit type arguments. + +### Type Arguments + +If you need to create a command that uses multiple generic arguments or can't automatically be inferred from the piped input, you need to use explicit type arguments. When writing out a command type arguments just look like regular arguments, but they always precede any other argument. To make your command require type arguments, you have to override the command's `TypeParameterParsers` property. This should return an array of types that inherit from `TypeParser`, and will be used to actually parse the type arguments from the command string. Note that as this is a class-wide property, this means that **all** implementations or subcommands must require the same number of type arguments. You can also combine explicit type arguments with the `TakesPipedTypeAsGenericAttribute`. Note that the automatically inferred type argument must always be the last type argument of that function. + +For example, these two commands make use of explicit type arguments to print a C# style method invocation syntax: +```cs +[ToolshedCommand] +public sealed class FooCommand : ToolshedCommand +{ + public override Type[] TypeParameterParsers { get; } = [typeof(TypeTypeParser), typeof(TypeTypeParser)]; + + [CommandImplementation] + public string Foo(int x) + { + return $"Foo<{typeof(T1).Name}, {typeof(T2).Name}>({x})"; + } +} + +[ToolshedCommand] +public sealed class BarCommand : ToolshedCommand +{ + public override Type[] TypeParameterParsers { get; } = [typeof(TypeTypeParser)]; + + [CommandImplementation, TakesPipedTypeAsGeneric] + public string Bar([PipedArgument] TAuto x) + { + return $"Bar<{typeof(TExplicit).Name}, {typeof(TAuto).Name}>({x})"; + } +} + +``` + +``` +> foo string int 123 +Foo(123) + +> i 123 | bar string +Bar(123) + +> f 1.23 | bar String +Bar(1.23) +``` + +## Automatic Type Conversions + +As mentioned elsewhere int the docs, Toolshed will perform some automatic type conversions. Most notably, any command that expects an `IEnumerable` will also accept being piped a `T`, as toolshed will automatically converted it into an `IEnumerable` with one element. + +Toolshed will also automatically cast any type that implements the `IAsType` interface. E.g., `Entity` implements `IAsType`. So Toolshed will allow you to pipe an `Entity` output into a method that expects an `EntityUid` input. + +## Custom Parsers & Completion Hints + +If you want to create a method that uses a custom parser, you can specify a custom parser via the an argument's `CommandArgumentAttribute`. This is particularly useful if you want more control over the console auto-completion options/hints. For example, the following defines a method that uses a custom parser to get an integer from a binary string. Though in this specific case, you could just as easily have made the command take in a string and done the conversion within the command's own method, though then the argument would need to be wrapped in quotes. + +```cs +[ToolshedCommand] +public sealed class FromBinaryCommand : ToolshedCommand +{ + [CommandImplementation] + public int FromBinary([CommandArgument(typeof(BinaryParser))] int value) => value; +} + +public sealed class BinaryParser : CustomTypeParser +{ + public override bool TryParse(ParserContext ctx, out int result) + { + var binaryText = ctx.GetWord(); + try + { + result = Convert.ToInt32(binaryText, 2); + return true; + } + catch + { + result = 0; + return false; + } + } + + public override CompletionResult? TryAutocomplete(ParserContext ctx, CommandArgument? arg) + => CompletionResult.FromHint(""); +} +``` + +``` +> frombinary 10101 +21 +``` + + +## Permissions + +```admonish warning +This section is specific to SS14, as RobustToolbox does not come with a command permission implementation. +``` + +All Toolshed commands need to specify some permissions in order to be executable, and there is an integration test that checks that this is the case (`AdminTest.AllCommandsHavePermissions`). The permissions for commands defined in the engine are specified in `/Resources/toolshedEngineCommandPerms.yml`, while Content commands can be given permissions by annotating the command class with the usual attributes (`AnyCommandAttribute`, `AdminCommandAttribute`). A limitation of this is that if a command has subcommands, they all have the same permissions. + + +## Auto-Completion, Hints, & localization + +Every Toolshed command should have a localized description. The key for the localized string is based on the (sub)command name. E.g., `foo` or `foo:bar` uses "command-description-foo" or "command-description-foo-bar". If the command name contains non-ascii characters, it will instead use the name of the class. E.g., the addition command (+) is defined in the `AddCommand` class, thus it uses "command-description-AddCommand". + +Toolshed will automatically generate help strings for commands in the form of the method's signature. The auto-generated help string can be overridden by defining a localized string. E.g., the foo command's help can be overridden by defining "command-help-foo". + +### Argument Hints + +Most of the Toolshed argument parsers will automatically generate console-completion hints while writing the arguments for a command. E.g., while writing the argument for a method like `Foo(int myNumber)` it will generate the hint `[myNumber (int)]`. If you want to override the auto-generated hint, you can do so by defining a localized string with the key "command-arg-hint-foo-myNumber". If you want more control over the hint or auto-completion suggestions, you can use a custom parser for that argument. \ No newline at end of file diff --git a/src/en/robust-toolbox/toolshed/toolshed-examples.md b/src/en/robust-toolbox/toolshed/toolshed-examples.md index 6b111d62a5..ac8d84bcc6 100644 --- a/src/en/robust-toolbox/toolshed/toolshed-examples.md +++ b/src/en/robust-toolbox/toolshed/toolshed-examples.md @@ -1,6 +1,6 @@ # Toolshed Examples -This guide covers some examples of Toolshed commands. Note that several of these commands & referenced C# types are specific to SS14, but they still provide a good overview of how to use Toolshed for other forks/games. +This guide covers some examples of Toolshed commands. Note that several of these commands & referenced C# types are specific to SS14, but they still provide a good overview of how to use Toolshed for other games. ## Interacting with entities From 076d9f96c833954a78e10434a53feb1355d0637d Mon Sep 17 00:00:00 2001 From: ElectroJr Date: Mon, 14 Apr 2025 13:25:55 +1200 Subject: [PATCH 09/13] Misc cleanup --- src/en/robust-toolbox/toolshed/development.md | 99 ++++++++++++------- 1 file changed, 65 insertions(+), 34 deletions(-) diff --git a/src/en/robust-toolbox/toolshed/development.md b/src/en/robust-toolbox/toolshed/development.md index 77e0db34da..4c9d452271 100644 --- a/src/en/robust-toolbox/toolshed/development.md +++ b/src/en/robust-toolbox/toolshed/development.md @@ -1,11 +1,16 @@ # Development -This section is intended for developers who want create their own toolshed commands. It is likely not useful for people who just want to use toolshed commands, though it may help them to understand errors or the Toolshed's limitations. +This section is intended for developers who want create their own Toolshed commands and largely consists of a lot of examples. -## Creating a new Command +## Creating a New Command -To create a new toolshed command, you have to create a new class that inherits from `ToolshedCommand`, is annotated with the `ToolshedCommandAttribute`. and has one or more (non-static) methods that are annotated with the `CommandImplementationAttribute`. A minimal working example that defines a `foo` command would be: +To create a new Toolshed command, you have to create a new class that: +* Inherits from `ToolshedCommand` +* Is annotated with the `ToolshedCommandAttribute` +* Has one or more (non-static) methods that are annotated with the `CommandImplementationAttribute`. + +A minimal working example that defines a `foo` command would be: ```cs [ToolshedCommand] public sealed class FooCommand : ToolshedCommand @@ -18,9 +23,9 @@ public sealed class FooCommand : ToolshedCommand ``` The name of the command in the previous example is taken automatically from the name of the class, the method's name doesn't matter. I.e., the `FooCommand` class gets mapped to `foo`. Alternatively, the command name can be specified using the class attribute, e.g., `[ToolshedCommand(Name = "foo")]`. - -To define a method that returns some value that can then be piped into another command, you just have to give the method some return type. And to make the command take in some arguments you can generally just use most of the standard C#/RobustToolbox types and it should automatically parse arguments for you. For example, -``` +## Arguments & Return Values +To define a command that returns some value that can then be piped into another command, you just have to give the method some return value. To give the command some arguments you just add arguments to the method. For example, +```cs [ToolshedCommand] public sealed class FooCommand : ToolshedCommand { @@ -31,8 +36,6 @@ public sealed class FooCommand : ToolshedCommand } } ``` - -Results in a command that will automatically parse the arguments in the standard way and generates an output string that can be piped into another command: ``` > foo "A!" 42 10 Dynamic A!, 42, 10, Dynamic @@ -41,38 +44,48 @@ A!, 42, 10, Dynamic A!, 42, 10, Dynamic, suffix ``` -In order to create a command that can accept input values that get piped in from another command, you have to give the method an argument annotated with the `PipedArgumentAttribute`. For example, this creates a simple addition command: +Any method argument that does not have an attribute (and is not an `IInvocationContext` type) is assumed to be a normal command argument will attempt to be parsed from the command string by Toolshed. Optionally, these can also be explicitly annotated with the `CommandArgumentAttribute`. + +Toolshed also supports methods with optional and `params []` arguments. E.g., ```cs [ToolshedCommand] -public sealed class AddCommand : ToolshedCommand +public sealed class SumCommand : ToolshedCommand { [CommandImplementation] - public int Add([PipedArgument] int x, int y) + public int Sum(params int[] values) { - return x + y; + return values.Sum(); } } ``` -Any method argument that does not have an attribute (and is not an `IInvocationContext` type) is assumed to be a normal command argument will attempt to be parsed from the command string by Toolshed. Optionally, these can also be explicitly annotated with the `CommandArgumentAttribute`. +## Argument Parsers +Toolshed can parse any type of argument that has a corresponding `TypeParser` implementation. For example, string arguments are parsed by the `StringTypeParser : TypeParser` class. The parser is responsible for generating console command auto-completion options & hints. If a type is not yet supported, you can always just create your own parser. -Toolshed also supports methods with optional and `params []` arguments. E.g., +If you want more control over how one of your arguments is parsed, or more control over auto-completion suggestions, you can also use the argument's attribute to specify that it should use a [custom parser](#custom-type-parsers). + +## Piped Input Arguments +In order to create a command that can accept input values that get piped in from another command, you have to give the method an argument annotated with the `PipedArgumentAttribute`. For example, this creates a simple addition command: ```cs [ToolshedCommand] -public sealed class SumCommand : ToolshedCommand +public sealed class AddCommand : ToolshedCommand { [CommandImplementation] - public int Add(params int[] values) + public int Add([PipedArgument] int x, int y) { - return values.Sum(); + return x + y; } } ``` +``` +> i 2 | add 3 +5 +``` ## Invertible Commands -If the method has a `bool` argument that is annotated with the `CommandInvertedAttribute`, then the command is "invertible". I.e., the command can be prefixed with the "not" keyword, and the annotated argument will tell you whether or not the behaviour of the command should be inverted. For example, this is a simple command that looks for a specific number: +In order to create a command whose behaviour can be inverted by prefixing it with the "not" keyword, you have to give the method has a `bool` argument that is annotated with the `CommandInvertedAttribute`. For example, this is a simple command that looks for a specific number in a sequence: ```cs [ToolshedCommand] internal sealed class ContainsIntCommand : ToolshedCommand @@ -94,7 +107,6 @@ true false ``` - ## Invocation Contexts If you want to create a command that writes output to the console or that can read & write Toolshed variables, you need to have a method that takes in an `IInvocationContext` argument. This argument can also optionally be annotated with the `CommandInvocationContextAttribute`. E.g., this is a simple command that will give out one-time greetings: @@ -113,15 +125,20 @@ public sealed class HelloCommand : ToolshedCommand } } ``` +Currently the most common invocation context is the `OldShellInvocationContext`, where each player will have their own context that persists across disconnects & reconnects, but not server restarts. The context is also not networked, so commands executed client-side & server-side will use different invocation contexts. ## Dependencies -Toolshed commands support normal `EntitySystem` & manager dependency injections. So if your command needs to work with entity transforms, you can just give your class a dependency field, i.e., `[Dependency] private readonly SharedTransformSystem _sys = default!;`. The base `ToolshedCommand` class already provides the `ToolshedManager`, `ILocalizationManager`, and `IEntityManager` dependencies. It also defines some helpful `IEntityManager` proxy methods (e.g., `TryComp()`, `Spawn()`, etc). So in general, you can just write code like you normally would within an `EntitySystem`. +Toolshed commands support normal `EntitySystem` & manager dependency injections. So if your command needs to work with entity transforms, you can just give your class a normal dependency field, i.e., +```cs +[Dependency] private readonly SharedTransformSystem _sys = default!; +``` +The base `ToolshedCommand` class already provides the `ToolshedManager`, `ILocalizationManager`, and `IEntityManager` dependencies. It also defines some helpful `IEntityManager` proxy methods (e.g., `TryComp`, `Spawn`, etc). So in general, you can just write code like you normally would within an `EntitySystem`. ## Multiple Implementations & Subcommands -So far, all of the examples have defined a command with a single implementation method. However, commands can have more than one implementation, however each implementation must take in a different piped type. For example, this would result in a valid command that can take in either an integer or float: +So far, all of the examples have defined a command with a single implementation method. Commands can have more than one implementation, however each implementation must take in a different piped type. For example, this would result in a valid command that can take in either an integer or float: ```cs [ToolshedCommand] public sealed class ToStringCommand : ToolshedCommand @@ -140,7 +157,7 @@ public sealed class ToStringCommand : ToolshedCommand } ``` -However, one of the limitations of toolshed is that the combination of command name & piped input type must be unique. So you cannot define two implementations that take in the same piped type, but have some different number of arguments or functionality. I.e., this is **not** a valid way of defining a command that takes in either a map or entity coordinate: +However, one of the limitations of Toolshed is that the combination of command name & piped input type must be unique. So you cannot define two implementations that take in the same piped type, but have different arguments. I.e., this is **not** a valid way of defining a command that takes in either a map or entity coordinate: ```cs public sealed class TpCommand : ToolshedCommand { @@ -160,9 +177,9 @@ public sealed class TpCommand : ToolshedCommand } ``` -This limitation is mainly due to the fact that toolshed would have no way of figuring out which command or which arguments it should be attempting to parse. It can only figure it out if the combination of command name and piped input type are unique. Instead, if you wanted to introduce these kinds of variations of a command, you need to use **subcommands**. In some sense commands with subcommands are just normal commands that associate each implementation with its own unique name. These can be specified via the `CommandImplementationAttribute`. Note that if a command contains **any** named implementations, then all of them must be given a name. +This limitation is mainly due to the fact that Toolshed would have no way of figuring out which command or arguments it should be attempting to parse. Instead, if you wanted to introduce these kinds of variations of a command, you need to use **subcommands**. -As an example, fix our earlier command could be fixed by giving the implementations a name: +In some sense commands with subcommands are just normal commands that associate each implementation with its own unique name. These can be specified via the `CommandImplementationAttribute`. Note that if a command contains **any** named implementations, then all of them must be given a name. As an example, our earlier command could be fixed by naming the implementations: ```cs public sealed class TpCommand : ToolshedCommand { @@ -198,7 +215,7 @@ public sealed class AddCommand : ToolshedCommand } ``` -The attribute also supports extracting the generic type even if it doesn't directly corresond to the type of the piped argument. For example, this is what the append command does: +The `TakesPipedTypeAsGeneric` attribute also supports extracting the generic type even if it doesn't directly correspond to the type of the piped argument. E.g., if the piped argument is an `IEnumerable`, it can still extract the generic type `T` from the piped value. For example, this is what the append command does: ```cs [ToolshedCommand] public sealed class AppendCommand : ToolshedCommand @@ -211,11 +228,15 @@ public sealed class AppendCommand : ToolshedCommand } ``` -However, in more complex situations this will likely fail. E.g., a signature like `Foo([PipedArgument] Dictionary> input)` will probably fail to, though this may change in the future. There is also no support for automatically determining multiple generic arguments from the piped input. If you want commands that use more complex generics, you will generally need to define a command that has explicit type arguments. +However, in more complex situations this will likely fail. E.g., a signature like `Foo([PipedArgument] Dictionary> input)` will probably fail to extract `T` from a given piped input value. + +There is also no support for automatically determining multiple generic arguments from the piped input. If you want commands that use more complex generics, you will generally need to define a command that has explicit type arguments. -### Type Arguments +## Type Arguments -If you need to create a command that uses multiple generic arguments or can't automatically be inferred from the piped input, you need to use explicit type arguments. When writing out a command type arguments just look like regular arguments, but they always precede any other argument. To make your command require type arguments, you have to override the command's `TypeParameterParsers` property. This should return an array of types that inherit from `TypeParser`, and will be used to actually parse the type arguments from the command string. Note that as this is a class-wide property, this means that **all** implementations or subcommands must require the same number of type arguments. You can also combine explicit type arguments with the `TakesPipedTypeAsGenericAttribute`. Note that the automatically inferred type argument must always be the last type argument of that function. +If you need to create a command that uses multiple generic arguments or has generics that can't automatically be inferred from the piped input, you need to use explicit type arguments. When writing out a shell command the type arguments look just like regular arguments, but they always precede any other argument and are used to determine the types for a generic implementation. + +To make your command require type arguments, you have to override the command's `TypeParameterParsers` property. This should return an array of types that inherit from `TypeParser`, and will be used to actually parse the type arguments from the command string. As this is a class-wide property this means that **all** implementations or subcommands must require the same number of type arguments. You can also combine explicit type arguments with the `TakesPipedTypeAsGenericAttribute`. Note that the automatically inferred type argument must always be the last type argument of that function. For example, these two commands make use of explicit type arguments to print a C# style method invocation syntax: ```cs @@ -258,14 +279,15 @@ Bar(1.23) ## Automatic Type Conversions -As mentioned elsewhere int the docs, Toolshed will perform some automatic type conversions. Most notably, any command that expects an `IEnumerable` will also accept being piped a `T`, as toolshed will automatically converted it into an `IEnumerable` with one element. +As mentioned elsewhere int the docs, Toolshed will perform some automatic type conversions. Most notably, any command that expects an `IEnumerable` will also accept being piped a `T`, as Toolshed will automatically converted it into an `IEnumerable` with one element. Toolshed will also automatically cast any type that implements the `IAsType` interface. E.g., `Entity` implements `IAsType`. So Toolshed will allow you to pipe an `Entity` output into a method that expects an `EntityUid` input. -## Custom Parsers & Completion Hints +## Custom Type Parsers -If you want to create a method that uses a custom parser, you can specify a custom parser via the an argument's `CommandArgumentAttribute`. This is particularly useful if you want more control over the console auto-completion options/hints. For example, the following defines a method that uses a custom parser to get an integer from a binary string. Though in this specific case, you could just as easily have made the command take in a string and done the conversion within the command's own method, though then the argument would need to be wrapped in quotes. +If you want to create a method that uses a custom parser, you can specify a custom parser via the an argument's `CommandArgumentAttribute`. This is useful if you want more control over the parsing or console auto-completion options/hints. +For example, the following defines a method that uses a custom parser to get an integer from a binary string. Though in this specific case, you could just as easily have made the command take in a string and done the conversion within the command's own method, though then the argument would need to be wrapped in quotes (all string arguments need to be wrapped in quotes). ```cs [ToolshedCommand] public sealed class FromBinaryCommand : ToolshedCommand @@ -308,15 +330,24 @@ public sealed class BinaryParser : CustomTypeParser This section is specific to SS14, as RobustToolbox does not come with a command permission implementation. ``` -All Toolshed commands need to specify some permissions in order to be executable, and there is an integration test that checks that this is the case (`AdminTest.AllCommandsHavePermissions`). The permissions for commands defined in the engine are specified in `/Resources/toolshedEngineCommandPerms.yml`, while Content commands can be given permissions by annotating the command class with the usual attributes (`AnyCommandAttribute`, `AdminCommandAttribute`). A limitation of this is that if a command has subcommands, they all have the same permissions. +All Toolshed commands need to specify some permissions in order to be executable, and there is an integration test that checks that this is the case (`AdminTest.AllCommandsHavePermissions`). The permissions for commands defined in the engine are specified in `/Resources/toolshedEngineCommandPerms.yml`, while Content commands can be given permissions by annotating the command class with the usual attributes (`AnyCommandAttribute`, `AdminCommandAttribute`). Permissions can't be specified per-subcommands, all subcommands must have the same permissions. ## Auto-Completion, Hints, & localization Every Toolshed command should have a localized description. The key for the localized string is based on the (sub)command name. E.g., `foo` or `foo:bar` uses "command-description-foo" or "command-description-foo-bar". If the command name contains non-ascii characters, it will instead use the name of the class. E.g., the addition command (+) is defined in the `AddCommand` class, thus it uses "command-description-AddCommand". -Toolshed will automatically generate help strings for commands in the form of the method's signature. The auto-generated help string can be overridden by defining a localized string. E.g., the foo command's help can be overridden by defining "command-help-foo". +Toolshed will automatically generate help strings for commands in the form of the method's signature. The auto-generated help string can be overridden by defining a localized string. E.g., the foo command's help can be overridden by defining a localized string with the key "command-help-foo". ### Argument Hints -Most of the Toolshed argument parsers will automatically generate console-completion hints while writing the arguments for a command. E.g., while writing the argument for a method like `Foo(int myNumber)` it will generate the hint `[myNumber (int)]`. If you want to override the auto-generated hint, you can do so by defining a localized string with the key "command-arg-hint-foo-myNumber". If you want more control over the hint or auto-completion suggestions, you can use a custom parser for that argument. \ No newline at end of file +Most of the Toolshed argument parsers will automatically generate console-completion hints while writing the arguments for a command. E.g., while writing the argument for a method like `Foo(int myNumber)` it will generate the hint `[myNumber (int)]`. If you want to override the auto-generated hint, you can do so by defining a localized string with the key "command-arg-hint-foo-myNumber". If you want more control over the hint or auto-completion suggestions, you can use a custom parser. + + +## Error Reporting + +TODO + +## Command Blocks + +TODO \ No newline at end of file From fcfef1c0c012be134baf41187bbb56d649a91c76 Mon Sep 17 00:00:00 2001 From: ElectroJr Date: Mon, 14 Apr 2025 13:42:28 +1200 Subject: [PATCH 10/13] More stuff --- src/en/robust-toolbox/toolshed/development.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/en/robust-toolbox/toolshed/development.md b/src/en/robust-toolbox/toolshed/development.md index 4c9d452271..487dca0f96 100644 --- a/src/en/robust-toolbox/toolshed/development.md +++ b/src/en/robust-toolbox/toolshed/development.md @@ -7,6 +7,7 @@ This section is intended for developers who want create their own Toolshed comma To create a new Toolshed command, you have to create a new class that: * Inherits from `ToolshedCommand` +* Has a class name that ends with "Command" * Is annotated with the `ToolshedCommandAttribute` * Has one or more (non-static) methods that are annotated with the `CommandImplementationAttribute`. @@ -21,7 +22,7 @@ public sealed class FooCommand : ToolshedCommand } } ``` -The name of the command in the previous example is taken automatically from the name of the class, the method's name doesn't matter. I.e., the `FooCommand` class gets mapped to `foo`. Alternatively, the command name can be specified using the class attribute, e.g., `[ToolshedCommand(Name = "foo")]`. +The name of the command in the previous example is taken automatically from the name of the class, the method's name doesn't matter. I.e., the `FooCommand` class gets mapped to `foo`. Alternatively, the command name can be specified using the class attribute, e.g., `[ToolshedCommand(Name = "foo")]`. If the name is explicitly specified, the name of the class doesn't **have** to end in "Command", but it is still a good convention to follow. ## Arguments & Return Values To define a command that returns some value that can then be piped into another command, you just have to give the method some return value. To give the command some arguments you just add arguments to the method. For example, @@ -88,10 +89,10 @@ public sealed class AddCommand : ToolshedCommand In order to create a command whose behaviour can be inverted by prefixing it with the "not" keyword, you have to give the method has a `bool` argument that is annotated with the `CommandInvertedAttribute`. For example, this is a simple command that looks for a specific number in a sequence: ```cs [ToolshedCommand] -internal sealed class ContainsIntCommand : ToolshedCommand +internal sealed class ContainsintCommand : ToolshedCommand { [CommandImplementation] - public bool ContainsInt([PipedArgument] IEnumerable input, int value, [CommandInverted] bool inverted) + public bool Containsint([PipedArgument] IEnumerable input, int value, [CommandInverted] bool inverted) { var result = input.Contains(value); return inverted ? !result : result; @@ -200,6 +201,9 @@ public sealed class TpCommand : ToolshedCommand ``` This would define then define the "tp:ent" and "tp:map" commands. +```admonish note "Naming Convention" +By convention, any new commands should use snake_case naming +``` ## Generics @@ -290,7 +294,7 @@ If you want to create a method that uses a custom parser, you can specify a cust For example, the following defines a method that uses a custom parser to get an integer from a binary string. Though in this specific case, you could just as easily have made the command take in a string and done the conversion within the command's own method, though then the argument would need to be wrapped in quotes (all string arguments need to be wrapped in quotes). ```cs [ToolshedCommand] -public sealed class FromBinaryCommand : ToolshedCommand +public sealed class BinaryCommand : ToolshedCommand { [CommandImplementation] public int FromBinary([CommandArgument(typeof(BinaryParser))] int value) => value; @@ -319,7 +323,7 @@ public sealed class BinaryParser : CustomTypeParser ``` ``` -> frombinary 10101 +> binary 10101 21 ``` From b1b36bf10a1c24882c37f5d8c700c32821e33251 Mon Sep 17 00:00:00 2001 From: ElectroJr Date: Mon, 14 Apr 2025 14:27:52 +1200 Subject: [PATCH 11/13] Add naming convention --- src/en/robust-toolbox/toolshed/development.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/en/robust-toolbox/toolshed/development.md b/src/en/robust-toolbox/toolshed/development.md index 487dca0f96..b255425996 100644 --- a/src/en/robust-toolbox/toolshed/development.md +++ b/src/en/robust-toolbox/toolshed/development.md @@ -24,6 +24,11 @@ public sealed class FooCommand : ToolshedCommand ``` The name of the command in the previous example is taken automatically from the name of the class, the method's name doesn't matter. I.e., the `FooCommand` class gets mapped to `foo`. Alternatively, the command name can be specified using the class attribute, e.g., `[ToolshedCommand(Name = "foo")]`. If the name is explicitly specified, the name of the class doesn't **have** to end in "Command", but it is still a good convention to follow. + +```admonish note "Naming Convention" +Auto-generated command names can be configure per-project to either use camel_case. So to support camel_case commands, you should should avoid using class names with abbreviations that have consecutive capitals. I.e., use something like `GetIdCommand` instead of `GetIDCommand`, as the latter would be converted to "get_i_d". +``` + ## Arguments & Return Values To define a command that returns some value that can then be piped into another command, you just have to give the method some return value. To give the command some arguments you just add arguments to the method. For example, ```cs From ab220a9902ea8120c730073127f7f374eb9880ce Mon Sep 17 00:00:00 2001 From: ElectroJr Date: Mon, 14 Apr 2025 15:37:19 +1200 Subject: [PATCH 12/13] a --- src/en/robust-toolbox/toolshed/development.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/en/robust-toolbox/toolshed/development.md b/src/en/robust-toolbox/toolshed/development.md index b255425996..b3a89ef160 100644 --- a/src/en/robust-toolbox/toolshed/development.md +++ b/src/en/robust-toolbox/toolshed/development.md @@ -26,7 +26,7 @@ The name of the command in the previous example is taken automatically from the ```admonish note "Naming Convention" -Auto-generated command names can be configure per-project to either use camel_case. So to support camel_case commands, you should should avoid using class names with abbreviations that have consecutive capitals. I.e., use something like `GetIdCommand` instead of `GetIDCommand`, as the latter would be converted to "get_i_d". +Auto-generated command names can be configure per-project to use snake_case. So to support conversion CamelCase class names, you should should avoid using class names with abbreviations that have consecutive capitals. I.e., use something like `GetNpcCommand` instead of `GetNPCCommand`, as the latter would be converted to "get_n_p_c". ``` ## Arguments & Return Values From 2fe7d132b0987424c2a6b01891c8b28fb0745458 Mon Sep 17 00:00:00 2001 From: ElectroJr Date: Tue, 15 Apr 2025 17:17:41 +1200 Subject: [PATCH 13/13] fix example --- src/en/robust-toolbox/toolshed/development.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/en/robust-toolbox/toolshed/development.md b/src/en/robust-toolbox/toolshed/development.md index b3a89ef160..0bde03f477 100644 --- a/src/en/robust-toolbox/toolshed/development.md +++ b/src/en/robust-toolbox/toolshed/development.md @@ -170,31 +170,31 @@ public sealed class TpCommand : ToolshedCommand [Dependency] private readonly SharedTransformSystem _sys = default!; [CommandImplementation] - public void Teleport([PipedArgument] EntityUid uid, EntityCoordinates entCoords) + public void Teleport([PipedArgument] EntityUid uid, EntityUid parent, Vector2 pos) { - _sys.SetCoordinates(uid, entCoords); + _sys.SetCoordinates(uid, new EntityCoordinates(parent, pos)); } [CommandImplementation] - public void Teleport([PipedArgument] EntityUid uid, MapCoordinates mapCoords) + public void Teleport([PipedArgument] EntityUid uid, MapId map, Vector2 pos) { - _sys.SetCoordinates(uid, _sys.ToCoordinates(mapCoords)); + _sys.SetCoordinates(uid, _sys.ToCoordinates(new MapCoordinates(pos, map))); } } ``` This limitation is mainly due to the fact that Toolshed would have no way of figuring out which command or arguments it should be attempting to parse. Instead, if you wanted to introduce these kinds of variations of a command, you need to use **subcommands**. -In some sense commands with subcommands are just normal commands that associate each implementation with its own unique name. These can be specified via the `CommandImplementationAttribute`. Note that if a command contains **any** named implementations, then all of them must be given a name. As an example, our earlier command could be fixed by naming the implementations: +In some sense subcommands are just named implementations/methods of a command, where the name is assigned via the `CommandImplementationAttribute`. Note that if a command contains **any** named implementations, then all of them must be given a name. As an example, our earlier command could be fixed by naming the implementations: ```cs public sealed class TpCommand : ToolshedCommand { [Dependency] private readonly SharedTransformSystem _sys = default!; [CommandImplementation("ent")] - public void Teleport([PipedArgument] EntityUid uid, EntityCoordinates entCoords) + public void Teleport([PipedArgument] EntityUid uid, EntityUid parent, Vector2 pos) { - _sys.SetCoordinates(uid, entCoords); + _sys.SetCoordinates(uid, new EntityCoordinates(parent, pos)); } [CommandImplementation("map")] @@ -204,10 +204,10 @@ public sealed class TpCommand : ToolshedCommand } } ``` -This would define then define the "tp:ent" and "tp:map" commands. +This would then define the `tp:ent` and `tp:map` "sub"-commands ```admonish note "Naming Convention" -By convention, any new commands should use snake_case naming +By convention, any new commands should use snake_case when naming commands or subcommands. ``` ## Generics