Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
4d49 committed Oct 9, 2022
0 parents commit 5979e38
Show file tree
Hide file tree
Showing 10 changed files with 584 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Normalize EOL for all files that Git considers text files.
* text=auto eol=lf

/.gitattributes export-ignore
/.gitignore export-ignore
/README.md export-ignore
/LICENSE.md export-ignore
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Godot auto generated files
*.import
21 changes: 21 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# MIT License

Copyright (c) 2020-2022 Mansur Isaev and contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
68 changes: 68 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Godot-Console

Simple in-game console for Godot 4.0.

![](https://user-images.githubusercontent.com/8208165/144989905-6d3eb45d-26e7-4acd-9a53-c31d7e49c400.png)

# Features

- Installed as plugin.
- The Console is Singleton.
- History of entered commands.
- Autocomplete commands.
- Static typing.

# Installation:

1. Clone this repository to `addons` folder.
2. Enabled `Godot Console` in Plugins.
3. Add `ConsoleContainer` node to the scene.
4. Profit.

# Usage:

## Create console command:

```gdscript
# player.gd
func teleport(x: float, y: float) -> void:
self.position = Vector2(x, y)
func _ready() -> void:
Console.create_command("tp", self.teleport, "Teleport the player.")
```

## Static typing:

With static typing, Console will try to cast arguments to a supported type.
```gdscript
# Arguments is float.
func teleport(x: float, y: float) -> void:
self.position = Vector2(x, y)
```

## Dynamic typing:

With dynamic typing, Console will NOT cast arguments to type, and arguments will be a String.
```gdscript
# Arguments is Strings.
func teleport(x, y):
# Convert arguments to float.
self.position = Vector2(x.to_float(), y.to_float())
```

## Optional return string for print result to the console.

```gdscript
func add_money(value: int) -> String:
self.money += value
# Prints: Player money:42
return "Player money:%d" % money
```

# License

Copyright (c) 2020-2022 Mansur Isaev and contributors

Unless otherwise specified, files in this repository are licensed under the
MIT license. See [LICENSE.md](LICENSE.md) for more information.
3 changes: 3 additions & 0 deletions icons/console_container.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions plugin.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[plugin]

name="Godot Console"
description="in-Game Console."
author="Mansur Isaev"
version="1.0"
script="plugin.gd"
23 changes: 23 additions & 0 deletions plugin.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright (c) 2020-2022 Mansur Isaev and contributors - MIT License
# See `LICENSE.md` included in the source distribution for details.

@tool
extends EditorPlugin


const AUTOLOAD_NAME = "Console"
const AUTOLOAD_PATH = "res://addons/godot-console/scripts/console.gd"

const CONSOLE_CONTAINER = "ConsoleContainer"
const CONSOLE_CONTAINER_SCRIPT = "res://addons/godot-console/scripts/console_container.gd"
const CONSOLE_CONTAINER_ICON = "res://addons/godot-console/icons/console_container.svg"


func _enter_tree() -> void:
add_autoload_singleton(AUTOLOAD_NAME, AUTOLOAD_PATH)
add_custom_type(CONSOLE_CONTAINER, "VBoxContainer", load(CONSOLE_CONTAINER_SCRIPT), load(CONSOLE_CONTAINER_ICON))


func _exit_tree() -> void:
remove_custom_type(CONSOLE_CONTAINER)
remove_autoload_singleton(AUTOLOAD_NAME)
166 changes: 166 additions & 0 deletions scripts/console.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# Copyright (c) 2020-2022 Mansur Isaev and contributors - MIT License
# See `LICENSE.md` included in the source distribution for details.

## ConsoleNode class.
##
## By default used as a Singleton. To create a new console command, use [method create_command].
class_name ConsoleNode
extends Node

## Emitted when the console prints a string.
signal printed_line(string: String)
## Emitted when the console history is cleared.
signal cleared()


var _command_map : Dictionary
var _command_list : PackedStringArray

var _history : PackedStringArray
var _history_index : int


func _init() -> void:
# Built-in methods:
create_command("target_fps", Engine.set_target_fps, "The desired frames per second. A value of 0 means no limit.")
create_command("physics_ticks", Engine.set_physics_ticks_per_second, "Set physic tick per second.")
# Custom methods:
create_command("clear", clear, "Clear the console history.")
create_command("help", _command_help, "Show all console command.")
create_command("version", _command_version, "Show engine version.")
create_command("test", _command_test, "Test console output.")
create_command("quit", _command_quit, "Quit the application.")

## Return [param true] if the console has a command.
func has_command(command: String) -> bool:
return _command_map.has(command)

## Return [param true] if command name is valid.
func is_valid_name(command: String) -> bool:
return command.is_valid_identifier()

## Add a command to the console.
## Can be used to directly add a custom command.
func add_command(command: ConsoleCommand) -> void:
assert(is_instance_valid(command) and command.is_valid(), "Invalid command.")
assert(not has_command(command.get_name()), "Has command.")

if is_instance_valid(command) and command.is_valid() and not has_command(command.get_name()):
_command_map[command.get_name()] = command
_command_list.clear() # Clear for lazy initialization.

## Remove a command from the console.
func remove_command(command: String) -> bool:
if _command_map.erase(command):
_command_list.clear()
return true

return false

## Return command.
func get_command(command: String) -> ConsoleCommand:
return _command_map[command] as ConsoleCommand

## Create and add a new console command.
func create_command(command: String, callable: Callable, description := "") -> void:
assert(not has_command(command), "Has command.")
assert(is_valid_name(command), "Invalid command name.")
assert(callable.is_valid(), "Invalid callable.")

if not has_command(command) and is_valid_name(command) and callable.is_valid():
self.add_command(ConsoleCommand.new(command, callable, description))

## Print string to the console.
func print_line(string: String) -> void:
printed_line.emit(string + "\n")

## Execute command. First word must be a command name, other is arguments.
func execute(string: String) -> void:
var args : PackedStringArray = string.split(" ", false)
if args.is_empty():
return

_history.push_back(string)
_history_index = _history.size()

print_line("[color=GRAY]> " + string + "[/color]")

if not has_command(args[0]):
return print_line("[color=RED]Command \"" + string + "\" not found.[/color]")

var command := get_command(args[0])

assert(is_instance_valid(command), "Invalid ConsoleCommand.")
if not is_instance_valid(command):
return

args.remove_at(0) # Remove name from arguments.

var result : String = command.execute(args)
if result:
print_line(result)

## Return the previously entered command.
func get_prev_command() -> String:
_history_index = wrapi(_history_index - 1, 0, _history.size())
return "" if _history.is_empty() else _history[_history_index]

## Return the next entered command.
func get_next_command() -> String:
_history_index = wrapi(_history_index + 1, 0, _history.size())
return "" if _history.is_empty() else _history[_history_index]

## Return a list of all commands.
func get_command_list() -> PackedStringArray:
if _command_list.is_empty(): # Lazy initialization.
_command_list = _command_map.keys()
_command_list.sort()

return _command_list

## Return autocomplete command.
func autocomplete_command(string: String) -> String:
if string.is_empty():
return string

for command in get_command_list():
if command.begins_with(string):
return command

return string

## Return a list of autocomplete commands.
func autocomplete_list(string: String) -> PackedStringArray:
var list := PackedStringArray()
if string.is_empty():
return list

for command in get_command_list():
if command.begins_with(string):
list.push_back(command)

return list

## Clear the console history.
func clear() -> void:
_history.clear()
_history_index = 0

cleared.emit()


func _command_help() -> void:
for i in get_command_list():
print_line(i + "- " + get_command(i).get_description())


func _command_version() -> String:
return "Godot Engine {major}.{minor}.{patch}".format(Engine.get_version_info())


func _command_test() -> String:
return "The quick brown fox jumps over the lazy dog."


func _command_quit() -> void:
get_tree().quit()
Loading

0 comments on commit 5979e38

Please sign in to comment.