Skip to content

Latest commit

 

History

1,191 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Godot Sandbox Build

Website | Code Examples | Discord | Web Demo


Safe, low-latency and fast sandbox for the Godot game engine.

GodotCon 2024 Presentation


Godot Sandbox lets players run untrusted code safely. Write gameplay in SafeGDScript, a safe GDScript-dialect executed inside a memory-safe sandbox with limits and execution timeouts. Restricted programs cannot access the host beyond what you explicitly allow, making it safe to load mods, user-generated content and scripts from other players. All Godot platforms are supported.

Installation

  • Automatic (Recommended): Download the plugin from the official Godot Asset Store using the AssetLib tab in Godot by searching for Godot Sandbox.

  • Manual: Download the latest github release and move only the addons folder into your project addons folder.

SafeGDScript

SafeGDScript (.sgd) is the default language for sandboxed code. It is compatible with and can replace GDScript in your projects. It is a GDScript-dialect with the same syntax, and some additions (like structs). It is generally much faster than GDScript: Bintr/JIT 5.0x, Interpreter 1.6x.

VS Code users can install the SafeGDScript extension for syntax highlighting and basic editor support.

SafeGDScript supports reusable traits. A trait can contribute state, constants, enums, signals and concrete/static methods, while abstract methods state what the using class must provide:

uses Damageable

trait Damageable:
	var health: int = 100
	func take_damage(amount: int) -> void:
		health -= amount
	@abstract func on_death() -> void

func on_death() -> void:
	queue_free()

Traits are nominal for SafeGDScript classes (value is Damageable) and may be used as type hints. Foreign Godot objects can satisfy a trait structurally by providing every instance method declared by it. Disable that compatibility path with sandbox/safe_gdscript/trait_structural_fallback for strictly nominal matching.

struct Item:
	var name: String
	var value: int
	var dropped_at: Vector2?

	func try_stack(other: Item) -> bool:
		if self.name != other.name:
			return false
		self.value += other.value
		return true

	func drop(at: int | Vector2) -> void:
		if at is int:
			self.dropped_at = Vector2(at, 0)
		else:
			self.dropped_at = at

var inventory: Array[Item] = []

func add_item(item_name: String, item_value: int) -> bool:
	var item := Item(item_name, item_value)
	for stored in inventory:
		if stored.try_stack(item):
			return true
	inventory.append(item)
	return false

func _ready():
	add_item("coin", 1)
	add_item("gem", 5)
	add_item("coin", 1)
	inventory[0].drop(Vector2(64, 32))

SafeGDScript also supports await:

func cutscene(player_knocked : Signal) -> String:
	$Gate.text = "The gate is sealed."
	await get_tree().create_timer(0.8).timeout
	await player_knocked
	return "opened"

The host receives a Signal and awaits it like any other coroutine: var result = await $Director.cutscene(knocked). See examples/async for a complete example.

Testing

A function decorated with @test becomes a test case:

@test func test_stacking_merges_equal_items() -> void:
	assert(add_item("coin", 1) == false)
	assert(add_item("coin", 1) == true, "the second coin stacks")

Right-click a node/script in the editor for Run Tests or Run Test at Cursor, or run all tests headlessly:

godot --headless --path . -s addons/godot_sandbox/run_sgd_tests.gd

Modding and user-generated content

See examples/modding for a complete mod loader with a hostile-mod audit.

For modding with traits, see examples/modding_typed.

C++ and Rust

For maximum performance, you can also write sandboxed programs in C++ or Rust. They use the same sandbox and the same restrictions. See the code examples repository and the demo repository.

Performance

  • Sandboxed C++ is 2.5-10x faster than GDScript by default, 5-50x with binary translation
  • Enable full binary translation for maximum performance on all platforms (including locked down iOS, Web, Switch etc.)
  • JIT builds are available in the Releases section for Windows, macOS, Android and Linux

Using typed variables in SafeGDScript will help the compiler optimize:

--- logic CPU dispatch ---
case                               ns/emulated instr     vs ref    vs base
SafeGDScript (sandbox)                         115.4      5.36x      -4.1%
GDScript (engine)                              618.0      1.00x      -7.2%

In the modding example that implements a virtual CPU, we gained 5x over GDScript by using typed variables.

Usage

Godot Sandbox Plus

The commercial studio version of the addon is at https://plus.libriscv.no/. It has a custom backend for translating GDScript to highly efficient native machine code, supports obfuscation and includes modding support. Studios receive access to a Godot module in source form, which exports native machine code that is faster than idiomatic GDScript: https://plus.libriscv.no/demo/.

Contributing

Requirements:

If you want to contribute to this repo, here are steps on how to build locally:

./build.sh

You can also use scons similar to how godot-cpp addons are built.

Contributors

Thanks goes to these wonderful people (emoji key):

Alf-André Walla
Alf-André Walla

💻
K. S. Ernest (iFire) Lee
K. S. Ernest (iFire) Lee

💻 🔬 ⚠️
Dragos Daian
Dragos Daian

💻

This project follows the all-contributors specification. Contributions of any kind welcome!

Other Projects

The Jenova Framework has a built-in C++ compiler, and supports writing C++ in the Godot editor with hot-reloading support.

Releases

Packages

Used by

Contributors

Languages