The CommandAPI's function execution should support function parameters (macros)
JorelAli opened this issue · comments
Description
In Minecraft 1.20.2, Minecraft added support for "macros" (more info here) which lets you run functions with parameters:
/function foo:bar {key_1:"Example String", key_2:10}
# function foo:bar
say This is a normal non-macro command
$say This is a macro line, using $(key_1)!
$teleport @s ~ ~$(key_2) ~
Expected code
NBT structure
Function arguments are represented as NBT compounds. Spigot does not have a data structure to represent an NBT compound, so we have options on how we want to represent this data structure.
Say we have the following NBT compound:
{
name1: 123,
name2: "sometext1",
name3: {
subname1: 456,
subname2:"sometext2"
}
}
CommandAPI NBT compound structure
We could create our own NBT compound data structure:
public class NBTCompound {
// Getters
public String getString(String key);
public int getInt(String key);
// ...
// Setters/Builders
public NBTCompound setString(String key, String value);
public NBTCompound setInt(String key, int value);
// ...
// Static constructors
public static NBTCompound fromNMS(Object nmsNBTCompound);
public static NBTCompound fromString(String nbtCompoundString);
}
This would look something like this:
NBTCompound functionArguments = new NBTCompound()
.setInt("name1", 123)
.setString("name2", "sometext1")
.setNBTCompound("name3", new NBTCompound()
.setInt("subname1", 456)
.setString("subname2", "sometext2")
);
Using Java's Maps
Or to save a lot of hassle, we could take inspiration from Rtag and use standard Java objects and primitive types. This would look something like this:
Map<String, Object> functionArguments = Map.of(
"name1", 123,
"name2", "sometext1",
"name3", Map.of(
"subname1", 456,
"subname2", "sometext2"
)
);
Usage
This can be used with the CommandAPI's existing FunctionWrapper.getFunction().execute()
method:
FunctionWrapper.getFunction("my_namespace:my_function").execute(player, functionArguments);
Extra details
On an implementation note, we can call functions by instantiating them directly using the instantiate
method which accepts the tag:
CommandFunction<CommandSourceStack> commandFunction = ...;
commandFunction.instantiate(tag, brigadierDispatcher, css);
We currently use this method here:
However, we don't call this in 1.20.2:
For 1.20.2 compatibility, we can call the execute(CommandFunction, CommandSourceStack, TraceCallbacks, CompoundTag)
method instead of having to instantiate the command explicitly