Skip to content

Commit d2c8479

Browse files
committed
feat: add data types to command arguments
This change will allow commands to define a data type per argument, this will make the usage more user friendly if commands are getting registered. You can now choose the data types and the console will validate the input throwing an error if something went wrong. Add new method to add commands with strong typed arguments. Change example to use new method for strong typed arguments. Fix #43 BREAKING_CHANGE: This will add some breaking changes, if you create a command instance you cannot use the packedstring array anymore. Instead create a new "CommandArgument" providing the type as first parameter, the name as the second and if needed a description of the parameter as last.
1 parent 8ed67e5 commit d2c8479

File tree

11 files changed

+207
-48
lines changed

11 files changed

+207
-48
lines changed

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,30 @@ func _reload() -> String:
3434
get_tree().reload_current_scene()
3535
return "reloaded scene"
3636
```
37+
> This will add an command where the arguments are provided as Strings, your method accepting those arguments will get strings as well
38+
> you will need to convert to the correct data type all by yourself.
39+
40+
### Register a strong argument command
41+
42+
```gdscript
43+
Console.register_custom_strong_command("spawn_entity",
44+
_spawn_entity,
45+
[CommandArgument.new(CommandArgument.Type.INT, "X Position", "The x position on screen for the entity"),
46+
CommandArgument.new(CommandArgument.Type.INT, "y Position", "The y position on screen for the entity"),
47+
CommandArgument.new(CommandArgument.Type.FLOAT, "time to life", "The time the entity should life in seconds")
48+
],
49+
"Spawn an example entity at a global position",
50+
"",
51+
["spawn_entity 200 300 4"]
52+
)
53+
54+
func _spawn_entity(pos_x: int, pos_y: int, ttl: float) -> String:
55+
pass
56+
57+
```
58+
> This will add a new argument but the argument types are defined, this will parse the input data automatically making it easier to use in your method. If a wrong type was provided to any
59+
> of the arguments an error will be thrown preventing the method execution.
60+
3761

3862
### Unregister a command
3963

addons/gameconsole/builtin/command_argument_count_not_matching.gd

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
extends CommandTemplate
22

33
func create_command() -> Command:
4-
var command = Command.new("argument not matching", _argument_count_not_matching, ["command_name"], "The provided arguments do not match")
4+
var command = Command.new("argument not matching",
5+
_argument_count_not_matching,
6+
[CommandArgument.new(CommandArgument.Type.STRING, "Command Name", "")],
7+
"The provided arguments do not match"
8+
)
59
command.is_hidden = true
610
return command
711

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
extends CommandTemplate
2+
3+
func create_command() -> Command:
4+
var command = Command.new("argument_not_valid",
5+
_argument_is_not_valid,
6+
[CommandArgument.new(CommandArgument.Type.STRING, "Command Name", ""),
7+
CommandArgument.new(CommandArgument.Type.STRING, "Argument Name", ""),
8+
CommandArgument.new(CommandArgument.Type.STRING, "Expected Type", ""),
9+
CommandArgument.new(CommandArgument.Type.STRING, "Provided Data", "")
10+
],
11+
"The type of the given argument is not correct"
12+
)
13+
command.is_hidden = true
14+
return command
15+
16+
func _argument_is_not_valid(command_name: String, argument_name: String, expected_type: String, provided_data: String) -> String:
17+
argument_name = argument_name.replace("_", " ")
18+
return "[color=red]Command \"%s\" does require (%s) type for argument \"%s\" but provided value was \"%s\"[/color]" % [command_name, expected_type, argument_name, provided_data]

addons/gameconsole/builtin/command_man.gd

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
extends CommandTemplate
22

33
func create_command() -> Command:
4-
var command = Command.new("man", manual, ["Name of the command"], "Command to get a manual for an command", "", ["man list_commands"])
4+
var command = Command.new("man",
5+
_manual,
6+
[CommandArgument.new(CommandArgument.Type.STRING, "Name of the command", "")],
7+
"Command to get a manual for an command",
8+
"",
9+
["man list_commands"])
510
return command
611

7-
func manual(command_name: String) -> String:
12+
func _manual(command_name: String) -> String:
813
var command = Console.get_specific_command(command_name)
914
if command == null or command.is_hidden:
1015
return "[color=red]No command with name %s was found[/color]" % command_name

addons/gameconsole/builtin/command_not_found.gd

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
extends CommandTemplate
22

33
func create_command() -> Command:
4-
var command = Command.new("not found", _not_found, ["Command name"], "Command was not found")
4+
var command = Command.new("not found",
5+
_not_found,
6+
[CommandArgument.new(CommandArgument.Type.STRING, "Command Name", "")],
7+
"Command was not found"
8+
)
59
command.is_hidden = true
610
return command
711

addons/gameconsole/console/command/command.gd

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ class_name Command extends Node
22

33
var command: String
44
var function: Callable
5-
var arguments : PackedStringArray
5+
var arguments : Array[CommandArgument]
66

77
var short_description: String
88
var description: String
@@ -16,7 +16,7 @@ var self_example_links := {}
1616

1717
func _init(command_name: String,
1818
functionality: Callable,
19-
in_arguments : PackedStringArray = [],
19+
in_arguments : Array[CommandArgument] = [],
2020
command_description: String = "",
2121
long_description: String = "",
2222
command_examples: PackedStringArray = []
@@ -41,8 +41,32 @@ func _init(command_name: String,
4141
func get_command_name() -> String :
4242
return command
4343

44-
func execute(arguments: Array) -> String:
45-
var data = function.callv(arguments)
44+
func execute(in_arguments: Array) -> String:
45+
if in_arguments.size() != arguments.size():
46+
Console.search_and_execute_command("argument_not_matching %s" % command)
47+
return ""
48+
49+
var is_valid: bool = true
50+
for i in in_arguments.size():
51+
var current_argument_type = arguments[i]
52+
if not current_argument_type.is_valid_for(in_arguments[i]):
53+
is_valid = false
54+
var data = "%s %s %s %s %s" % ["argument_not_valid",
55+
command,
56+
current_argument_type.get_display_name().to_snake_case(),
57+
current_argument_type.Type.keys()[current_argument_type.get_type()],
58+
in_arguments[i]
59+
]
60+
Console.search_and_execute_command(data)
61+
return ""
62+
63+
64+
var converted_data: Array[Variant] = []
65+
for i in in_arguments.size():
66+
var data = arguments[i].convert_data(in_arguments[i])
67+
converted_data.append(data)
68+
69+
var data = function.callv(converted_data)
4670
if data == null:
4771
data = ""
4872
return data
@@ -57,7 +81,7 @@ func get_command_short_description():
5781
func get_arguments() -> String:
5882
var return_arguments = ""
5983
for argument in arguments:
60-
return_arguments += "[%s]" % argument
84+
return_arguments += "[%s]" % argument.get_display_name()
6185
return return_arguments
6286

6387
func as_stripped() -> StrippedCommand:
@@ -77,7 +101,12 @@ func get_man_page() -> String:
77101
return_text += "\n[i][b]Arguments[/b][/i]\n\n"
78102
return_text += "[ul]"
79103
for argument in arguments:
80-
return_text += "%s\n" % argument
104+
return_text += "%s" % argument.get_display_name()
105+
var argument_description = argument.get_description()
106+
if not argument_description.is_empty():
107+
return_text += " -> %s" % argument_description
108+
return_text += "\n"
109+
81110
return_text += "[/ul]"
82111
if examples.size() > 0:
83112
return_text += "\n\n[i][b]Examples[/b][/i]\n\n"
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
class_name CommandArgument extends Resource
2+
3+
enum Type
4+
{
5+
UNKNOWN,
6+
STRING,
7+
INT,
8+
FLOAT,
9+
BOOL,
10+
}
11+
12+
var _argument_type: Type
13+
var _argument_name: String
14+
var _argument_description: String = ""
15+
16+
func _init(type: Type, name: String, description: String):
17+
_argument_type = type
18+
_argument_name = name
19+
_argument_description = description
20+
21+
func get_type() -> Type:
22+
return _argument_type
23+
24+
func get_display_name() -> String:
25+
var prefix: String = "(%s)"
26+
match _argument_type:
27+
Type.UNKNOWN:
28+
prefix = ""
29+
Type.STRING:
30+
prefix = prefix % "String"
31+
Type.INT:
32+
prefix = prefix % "Int"
33+
Type.FLOAT:
34+
prefix = prefix % "Float"
35+
Type.BOOL:
36+
prefix = prefix % "Bool 0/1"
37+
return "%s %s" % [prefix, _argument_name]
38+
39+
func get_description() -> String:
40+
return _argument_description
41+
42+
func is_valid_for(data: String) -> bool:
43+
match _argument_type:
44+
Type.UNKNOWN:
45+
return true
46+
Type.STRING:
47+
return true
48+
Type.INT:
49+
return data.is_valid_int()
50+
Type.FLOAT:
51+
return data.is_valid_float()
52+
Type.BOOL:
53+
if not data.is_valid_int():
54+
return false
55+
var converted_data = int(data)
56+
return converted_data == 0 or converted_data == 1
57+
_:
58+
return false
59+
60+
func convert_data(data: String) -> Variant:
61+
if not is_valid_for(data):
62+
return null
63+
match _argument_type:
64+
Type.UNKNOWN:
65+
return data
66+
Type.STRING:
67+
return data
68+
Type.INT:
69+
return int(data)
70+
Type.FLOAT:
71+
return float(data)
72+
Type.BOOL:
73+
return int(data) == 1
74+
_:
75+
return null
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
class_name StrippedCommand extends Resource
22

33
var command: String
4-
var arguments: PackedStringArray
4+
var arguments: Array[CommandArgument]

addons/gameconsole/console/console.gd

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -150,15 +150,29 @@ func _register_builtin_command(command: Command):
150150
command.built_in = true
151151
_console_commands[command.get_command_name()] = command
152152

153-
func register_custom_command(command: String,
154-
function: Callable,
155-
in_arguments : PackedStringArray = [],
156-
short_description: String = "",
157-
description: String = "",
158-
example: PackedStringArray = []):
153+
## Register a custom command with strong typed parameters
154+
func register_custom_strong_command(command: String,
155+
function: Callable,
156+
in_arguments: Array[CommandArgument],
157+
short_description: String = "",
158+
description: String = "",
159+
example: PackedStringArray = []):
159160
var real_command = Command.new(command, function, in_arguments, short_description, description, example)
160161
register_command(real_command)
161162

163+
## Register a custom command without using the new parameter types
164+
func register_custom_command(command: String,
165+
function: Callable,
166+
in_arguments : PackedStringArray = [],
167+
short_description: String = "",
168+
description: String = "",
169+
example: PackedStringArray = []):
170+
var converted_arguments: Array[CommandArgument] = []
171+
for argument in in_arguments:
172+
converted_arguments.append(CommandArgument.new(CommandArgument.Type.UNKNOWN, argument, ""))
173+
register_custom_strong_command(command, function, converted_arguments, short_description, description, example)
174+
175+
## Register a new command you already created the object instance for
162176
func register_command(command: Command):
163177
var name = command.get_command_name()
164178
command.built_in = false
@@ -180,8 +194,6 @@ func search_and_execute_command(command_text: String):
180194
if command_to_run == null:
181195
search_and_execute_command("not_found %s" % executer.command)
182196
return
183-
if executer.arguments.size() != command_to_run.arguments.size():
184-
search_and_execute_command("argument_not_matching %s" % executer.command)
185197
var result = command_to_run.execute(executer.arguments)
186198
if result != "":
187199
console_output.emit(result + "\n")

addons/gameconsole/console/console/autocomplete_label.gd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func _display_autocomplete(data: StrippedCommand):
4343
var color: Color = Console.console_settings.autocomplete_argument_color_odd
4444
if argument_counter % 2 == 0:
4545
color = Console.console_settings.autocomplete_argument_color_even
46-
arguments += "[color=%s]%s[/color] " % [color.to_html(), argument]
46+
arguments += "[color=%s]%s[/color] " % [color.to_html(), argument.get_display_name()]
4747
argument_counter += 1
4848
arguments = arguments.trim_suffix(" ")
4949

0 commit comments

Comments
 (0)