Scripting

aether-scripting

WASM script runtime with resource caps and scheduling

The aether-scripting crate implements the WASM scripting runtime for Aether. It provides per-script resource limits, rate limiting, priority-based scheduling, and a sandboxed API surface that scripts use to interact with the engine.

Overview

Scripts in Aether run as sandboxed WebAssembly modules. The runtime enforces strict resource budgets to prevent any single script from monopolizing CPU, memory, or network resources. Key features include:

  • Per-script resource caps for memory, CPU time, entity spawns, and network calls.
  • Rate limiting to throttle storage writes and RPC calls per second.
  • Priority scheduling with aging to ensure fair execution across all scripts in a world.
  • API surface traits that provide controlled access to ECS entities, physics, audio, networking, and UI.
  • Visual script compilation from node graphs into executable WASM artifacts.

Key Types

WorldScriptScheduler

Orchestrates script execution across a world tick. Assigns CPU budget to scripts based on priority and aging.

use aether_scripting::{WorldScriptScheduler, WorldScriptLimits, WorldTick};

let limits = WorldScriptLimits {
    max_total_cpu_ms: 8.0,
    max_scripts: 256,
};
let mut scheduler = WorldScriptScheduler::new(limits);

ScriptResourceLimits

Defines the resource budget for a single script instance.

use aether_scripting::{ScriptResourceLimits, DEFAULT_PER_SCRIPT_MEMORY_BYTES};

let limits = ScriptResourceLimits {
    max_memory_bytes: DEFAULT_PER_SCRIPT_MEMORY_BYTES,
    max_cpu_ms_per_tick: 2.0,
    max_entity_spawns_per_second: 10,
    max_network_rpcs_per_second: 5,
};

ScriptRuntime

Manages the lifecycle of a single WASM script instance, including loading, execution, and teardown.

use aether_scripting::{ScriptRuntime, ScriptDescriptor, ScriptState};

let descriptor = ScriptDescriptor {
    id: script_id,
    priority: 5,
    resource_limits: limits,
};

RateLimiter

Enforces per-second rate limits on script actions such as storage writes and network RPCs.

use aether_scripting::RateLimiter;

let mut limiter = RateLimiter::new(10); // 10 actions per second
assert!(limiter.try_acquire());

API Surface Traits

Scripts interact with the engine through trait-based APIs. Each trait exposes a narrow, sandboxed slice of engine functionality.

use aether_scripting::{EntityApi, PhysicsApi, AudioApi, NetworkApi, StorageApi, UIApi};

// These traits are implemented by the host runtime and passed to WASM modules.
// Scripts call methods like:
//   entity_api.spawn_entity(position);
//   physics_api.apply_force(entity, force);
//   audio_api.play_sound(sound_id, position);

ScriptArtifact

Represents a compiled WASM binary with metadata about its source language and target platform.

use aether_scripting::{ScriptArtifact, ScriptLanguage, CompilationProfile};

let artifact = ScriptArtifact {
    language: ScriptLanguage::Rust,
    profile: CompilationProfile::Release,
    wasm_bytes: compiled_bytes,
};

Usage Examples

Scheduling Scripts in a World Tick

use aether_scripting::{
    WorldScriptScheduler, WorldScriptLimits, ScriptDescriptor,
    ScriptId, TickUsageResult,
};

let limits = WorldScriptLimits {
    max_total_cpu_ms: 8.0,
    max_scripts: 256,
};
let mut scheduler = WorldScriptScheduler::new(limits);

// Register scripts
scheduler.register(ScriptDescriptor {
    id: ScriptId(1),
    priority: 10,
    resource_limits: ScriptResourceLimits::default(),
});

// Each tick, the scheduler decides which scripts run and for how long
let result: TickUsageResult = scheduler.tick();

Visual Script Compilation

use aether_scripting::{
    VisualScriptCompiler, VisualScriptGraph, VisualScriptNode,
};

let graph = VisualScriptGraph::new();
// Nodes are added via the Creator Studio visual editor
let result = VisualScriptCompiler::compile(&graph);