better command system for scarpet
gnembon opened this issue ยท 15 comments
And types handling
something like that maybe:
in foo.sc
__command() -> m(
l('', 'foo_handler'),
l('sub', 'foo_sub_handler'),
l('sub quux', 'quux_handler')
);
quux_handler(pos(target), block(smthing)) -> set(target, smthing);
would enable command like /foo sub quux ~ ~ ~ stone
Any suggestions? Improvements? different implementations?
Maybe for variable arguments there could add a third element to that list like 'variable' and pass an array to the function with all arguments instead?
Something like
__command() -> m(
l('variablesub', 'variable_handler','variable'),
);
variable_handler(arguments) -> (print(arguments) );
That would allow commands like printing a paginated list with the same command, like /foo list
for first page and /foo list [n]
for other pages without requiring different subcommands for that. It would also allow to input strings without quotes.
Or maybe allowing to define multiple functions with the same name but a different number of arguments and handle it depending on that.
little spec
__command() -> {
'sphere at <pos> radius <int>' -> 'draw_sphere', // arguments pos and int are inferred types, function called with two args, as in the order in the command
'sphere at <pos>' -> _(pos) -> draw_sphere(pos, 1), // example handling of optional args, similar to brigadier, except not defined as a tree, cause this would be madness. Commands can be defined with brigadier one by one, so that's not a problem
// this is clear that the command can be a string (function name), function value, or a customized map
'another <pos> <angles> from <dim> with <custom:1>' -> { // there will be standard arguments types, but could be specified also below
'command' -> _(pos, angles, dim, cust) -> print('this is a custom command'), // functions can be specified anonymous as well
'arguments' ->
'custom:1' -> {
// not sure what goes here / probably depending on the type
'options' -> ['foo', 'bar', 'baz'], // or suggestions (for suggestion provider)
'type' -> 'word' // can use exact types as per mojang command system and fabric names for mc type arguments
},
'dim' -> { // this could have been just the dimension, but can be customized as well.
'type' -> 'int',
'gt' -> 0, // probably could use similar terms here as elastic search uses in their query spec
// this shows that different types will have different options, and some of them shared, like 'options' or 'suggestions'
}
}
}
};
draw_sphere(pos, radius) -> // works
This is looking good, how about adding a permission argument so only players with that permission or higher can use that command?
I know i could use if(query(player(), 'permission_level') > 3,...);
but then the command would still show to lower permission players.
I guess it could just be added to the map, like 'permission' -> 4
or smth
Problem with this is in the code, where I need a CarpetContext and Token for it to work, so I've got two temporary solutions which I'm working on, so I can improve on them later:
The first is to make it so that certain keywords, like 'block', or 'pos' will make certain input types.
The second is to have a function command() which will register a command, and that would be a lot more configurable, cos rn I have no clue how to evaluate the map which would be returned from the __Command() func.
Edit: Regarding the last one, I just discovered a new method which may work, but I have yet to test it out, and see if it works.
I'm working on it on this branch if anyone's interested: https://github.com/Ghoulboy78/fabric-carpet/tree/update-commands
I also have the overhaul of distance.sc, and draw.sc, which I hope to use as examples of how to use the new system.
Also thank @gnembon for the callUDF
function in CarpetScriptHost.java, which allows me to actually get the return mapValue of __command() without all the other weird stuff. I'm now expereimenting to see exactly what's possible, and how to do it.
ok I have given up with the given suggestions, which would anyways not be backwards compatible.
Instead, I propose a new system which would be to make all arguments strings, and then figure out a way to define the type of arg.
Maybe through a different function:
test.sc
__command()->print('Bla bla bla, same old, same old');
say(what,rand_num)->print(what*num); // command: /test say <(string) what> <(string) rand_num>
___multiply()->{//three underscores, to differentiate and avoid confusion. Or smth else if u want, but that way I can do funcName.startwith("___")
'num1'->'number',
'num2'->'number'
};
// command: /test multiply <(string) rand_str> <(int) num1> <(int) num2>
multiply(rand_str, num1, num2)->print(rand_str+num1*num2);
my system would not be able to handle things like /fill <from> <to> block
as well as /fill <from> <to> block <replace> block
since that would mean variable number of arguments
but I don't think its very limiting
as long as you can deep dive into /app (sub moresub subscribe) (arg arg1)
that woudl be powerful enough
where $1 is call descriptor and $2 are args
draw.sc:
__command()->m(
l('1','draw','sphere','func',l('pos','pos'),l('int','radius'),l('block','block')),
l('1','draw','cone','func',l('pos','pos'),l('int','radius'),l('int','height'),l('block','block')),
l('1','draw','prism','tree'),
l('2','prism','circular','func',l('pos','pos'),l('int','radius'),l('int','height'),l('block','block')),
l('2','prism','square','func',l('pos','pos'),l('int','radius'),l('int','height'),l('block','block')),
)
How would this be?
I did the prism
tree just to show how it could be done.
And the command would turn out like: /draw ( sphere / cone / prism ( circular / square ) )
The structure would be
l(depth_level,'parent','this_name','func/tree/literal?',args))
args: l('type','name')
where args is obviously there only if it is a func, and also, you can have multiple args in order, with types being similar to those found in java (or not):
int
str
pos(tab-completion?)
block(Like BlockStateArgument?)
others?
What do you think of that, @gnembon ?
here is my current view on it.
RFC for commands
for invoke, one can assume autospec to happen, based on params names. If a type is not supported, it can be shallow parsed, defaulting in parsing to StringArgumentType.word -> then read as a scarpet value with the tokenizer
e.g.
fun(pos, second_pos, target_entity) -> ...
be like
/script in <app> invoke fun <x, y, z> <x, y, z> <entity>
problem is that for this to happen, app needs to register these when loading, not dynamically. We can keep the previous ability to invoke any public function without custom args support on the fly
The type of the parameter could be implied from the prefix, up to the optional '_'
Then the only difference for _command
based apps, is that the prefix in the long command is shortened to
/<app> fun ....
but the rest stays the same.
But lets say we want to customize them - then the _config
could return keys 'commands'
(since _command() ->
is already assuming to be expect to execute some code, and we want to avoid that to just get the spec). I was thinking for a flat list, rather than a parse tree, defining one call at a time - this looks clearer to me and the effect is the same. Config could also return custom arg spec for customized params
__config() -> {
'commands' -> {
'give from <giving_player> to <givee_player> <int> <candy>' -> 'give_candy'
},
'arguments' -> {
'prob'-> {
'type'-> 'float',
'ge'-> 0, # greater or equal
'lt'-> 1, # less than
},
'candy'-> {
'type'-> 'word', # as per brigadier StringArguemntType.word()
'suggests'-> ['kitkat', 'gummibears']
}
}
};
give_candy(giving_player, givee_player, int, candy) -> ...
Another example, a la vanilla fill:
fill.sc
__config() -> {
'commands' -> {
'<from_pos> <to_pos> <block>' -> _(from, to, block) -> volume(from, to, set(_, block)),
'<from_pos> <to_pos> <block> outline' -> _(from, to, block) -> ...,
...
}
}
Maybe also a way to specify command permissions: you can pass a numeric permission level and then you have a function to change that level from the code, so it hides the command like it does with other Carpet commands instead of having the command always visible, then checking, then saying Sorry, you no good
. (for example to use with Scarpet Rules or to allow ops to change perms of an app on the fly)