Skip to content
JohnEsleyerPublic

About

Fast-iteration 2D/3D game engine powered by wgpu and GoScript. Data-driven architecture with hot-reloadable scripts, RON prefabs, and a native plugin system.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

55 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Groot

Groot is a fast-iteration 2D/3D game engine powered by wgpu (modern GPU rendering) with GoScript — an embeddable Go-syntax scripting VM written in pure Rust. Game logic lives in .gos files under assets/scripts/ and hot-reloads on save; visuals and prefab hierarchies live in RON asset files under assets/prefabs/ and assets/scenes/.

Architecture

Groot is data-driven: scripts own behavior and data; the host engine owns representation and rendering. Visuals are declared as RON prefab data.

assets/scenes/*.scene.ron  ──►  hecs ECS (3D Meshes, Lights, Sprites, UI)
assets/prefabs/*.prefab.ron ──►  RonAssetWatcher (Visual Hot Reloading)
assets/scripts/*.gos        ──►  GoScript VM (Logic Hot Reloading)  ──► Entity Data
                                         ▲                                  │
                                         └── groot.* host bindings ──────────┘
  • assets/config.ron — project config & render settings.
  • assets/prefabs/*.prefab.ron — 2D/3D prefabs (sprites, text, 3D PBR meshes, lights, colliders, parent-child hierarchies).
  • assets/scenes/*.scene.ron — scene layout, environment settings, cameras, entity initializers.
  • src/main.rs — initializes winit window and wgpu render context, runs main event loop.
  • src/platform/ — platform abstraction layer (desktop event loop, Android entry).
  • src/render/ — pure wgpu rendering engine (3D meshes, 2D sprites, text, gizmos).
  • src/ecs/ — hecs-based ECS components and queries.
  • src/assets/ — asset loading, encryption, archive packing (see Asset Security).
  • src/script/ — GoScript VM host, input tracking, script execution.

Asset Security

Groot ships a built-in asset protection pipeline that prevents plaintext inspection of scripts, prefabs, and scenes in release builds.

Encryption (src/assets/crypto.rs)

All assets are encrypted with a ChaCha20-inspired XOR stream cipher keyed by a compile-time secret and an asset-path nonce. A CRC32 integrity header detects tampering. The default key is baked in at compile time; production builds can override it at compile time by setting the GROOT_ENCRYPTION_KEY environment variable before building:

GROOT_ENCRYPTION_KEY="MyStrongKey32BytesExactly______!" cargo build --release
Function Description
encrypt_asset(path, data) Encrypt bytes with a path-derived nonce
decrypt_asset(path, data) Decrypt bytes; falls back to raw if not encrypted (debug)
minify_goscript(source) Strip // and /* */ comments + blank lines from .gos

Binary Archive — .gpak (src/assets/pak.rs)

Assets can be packed into a single encrypted .gpak binary archive, similar to Unreal .pak or Unity asset bundles.

Archive format:

[GPAK]  4 bytes  magic
[u16]   2 bytes  version
[u32]   4 bytes  entry count
  per entry:
  [u16]   path length
  [N]     UTF-8 relative path
  [u32]   data length
  [M]     encrypted payload

Pack via the CLI (see CLI):

groot pack assets/ assets.gpak

Open a .gpak from Rust:

let archive = GpakArchive::open("assets.gpak")?;
let bytes = archive.entries.get("scripts/player.gos");

In-Memory & Ephemeral Script Loading (src/assets/embed.rs)

Build mode Strategy
cargo run (debug, desktop) Raw .gos read from disk — hot-reload preserved
cargo build --release Decrypt + minify in memory via HotReloadEngine::from_str — no temp file
Android Same as release — fully in-memory

When a physical path is required (debug hot-reload watcher), prepare_script_path writes a minified, hashed .goc file to /tmp/.groot_cache/ with 0600 permissions and registers a panic hook that deletes the cache on process exit.

CLI

# Install the CLI (binary is now `groot`)
cargo install --path .        # installs `groot` to ~/.cargo/bin

# Scaffold a new project folder
groot new my-game             # or: cargo run --bin groot -- new my-game

# Run the current Groot game
groot run                     # or: cargo run --bin groot -- run

# Build a release bundle
groot build                   # or: cargo run --bin groot -- build

# Pack and encrypt the assets directory into a .gpak archive
groot pack assets/ assets.gpak

# Scaffold prefabs, scripts, and scenes
groot generate prefab enemy
groot generate script player
groot generate scene arena    # alias: groot g <type> <name>

# Diagnose toolchain (Rust, cargo-apk, Android SDK/NDK, Vulkan)
groot doctor

# Shell completions
groot completions bash        # bash|zsh|fish
# or: groot completions zsh > ~/.zsh/completions/_groot

Multi-Platform Targets

Desktop and Android builds share a single --target flag on run and build (defaults to desktop):

# Desktop (current host)
groot run
groot build --target desktop

# Android APK (requires rustup target aarch64-linux-android + cargo-apk)
groot run --target android    # cargo apk run (deploys to a connected device via adb)
groot build --target android  # cargo apk build --release

On Android, groot run --target android auto-detects connected devices with adb; if several are present it lists them and lets you pick one, or you can pass --device <serial> (or --device <index>) to select directly.

Android Asset Handling

Assets (assets/) are compiled into the APK using rust-embed with the debug-embed feature, so games work on-device in both debug and release builds. On desktop debug builds, asset files are still read from disk first so .gos and .prefab.ron edits hot-reload while developing.

Android Requirements

  • rustup target add aarch64-linux-android
  • cargo install cargo-apk
  • Android SDK with platform android-34 (or set target_sdk_version / min_sdk_version under [package.metadata.android.sdk] in Cargo.toml)

Run the Demo

cargo run

The Flappy Groot demo starts up: a neon 2D side-scroller where you flap a bird through pipe gaps, avoiding solid ground and ceiling. Save any .gos or .prefab.ron file to see live hot reloading!

2D sprites are drawn back-to-front by their layer field (background → pipes → ground/ceiling → bird) and scaled to their world-space size from the prefab's size. Sprite textures (bird, pipes, ground, ceiling, grid) are generated procedurally at startup and cached as PNG bind groups.

Writing scripts

type Player struct { Speed float64 }
var self = Player{Speed: 5.0}

func OnUpdate(dt float64) {
    var pos = groot.GetSelfPosition()
    var moveX = groot.GetAxis("Horizontal")
    var moveZ = -groot.GetAxis("Vertical")
    groot.SetSelfPosition(pos[0] + moveX*self.Speed*dt, pos[1], pos[2] + moveZ*self.Speed*dt)

    groot.SetSelfCollider(1.0, 1.0, 1.0)
    groot.Log("Hello from Groot 3D GoScript!")
}

Dependencies

  • winit 0.29 - Cross-platform windowing and input
  • wgpu 0.19 - Modern GPU rendering (Vulkan/Metal/DX12)
  • glam 0.27 - Fast 3D/2D math library
  • hecs 0.10 - Minimalist archetype ECS
  • bytemuck 1.14 - Safe casting for GPU buffers
  • pollster 0.3 - Block on async operations
  • ron 0.8 - Rusty Object Notation for assets
  • serde 1 - Serialization framework
  • goscript (git: github.com/johnesleyer/goscript) - GoScript VM

Case Studies

Engineering deep-dives from the engine's development, including the Android bring-up:

About

Fast-iteration 2D/3D game engine powered by wgpu and GoScript. Data-driven architecture with hot-reloadable scripts, RON prefabs, and a native plugin system.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages