Skip to content

Latest commit

 

History

History
99 lines (78 loc) · 5.28 KB

File metadata and controls

99 lines (78 loc) · 5.28 KB

Architecture

Runtime and connections

The Worker forwards TCP streams to a named Durable Object using the platform's stream-piping API. The object lazily creates an Emscripten module and passes a workers-rs Socket to Pumpkin's injected connection entry point.

Each object owns separate wasm memory, Rust statics, filesystem descriptors, and one hosted Tokio runtime, supplied by workers-rs. Pumpkin's single-threaded feature selects cooperative inline chunk generation and the async ticker. The same scheduler loop serves native builds, which dispatch generation onto Rayon. Save batches await queue capacity; shutdown drains results with an elapsed-time timeout before the final flush. Bounded CPU tasks use regular Tokio tasks because the pinned fork's public spawn_blocking still requires OS threads. JSPI supplies stack suspension, not threads.

Structure templates store palette indices and share their block arrays between the placement and query APIs. Block-entity NBT is shared until a placement needs an owned copy. Generation chunks retain uniform or indexed sections; only the active noise pass uses a temporary dense block buffer. This keeps the generation neighborhood compact without changing its dependency rules. Emscripten grows memory in 2 MiB increments, configured by build.rs, while retaining the 8 MiB stack. See memory measurements.

Emscripten factory mode (MODULARIZE=1) isolates module instances. Wrangler provides the compiled wasm through instantiateWasm; the generated host glue uses Workers' Node compatibility APIs. The SDK's standalone-loader initialization and recovery hook is disabled for this embedding.

Filesystem and checkpoints

The host mounts LocalDOFilesystem(ctx.storage) from durable-object-fs at /data. Emscripten's NODERAWFS routes Rust filesystem calls through worker-fs-mount's synchronous Node API, including descriptors, positional I/O, rename, and sync. World metadata, chunks, entities, players, and configuration use this mount. Logging goes to the host console.

Each wasm instance retains its own mount context across requests and Tokio callbacks using AsyncLocalStorage.snapshot(). Descriptors opened at startup therefore remain valid until that runtime shuts down, without exposing one world's mount to another object.

The Emscripten compatibility library in worker/fs-library.js supplies the mounted Node filesystem to the factory: the generated loader's runtime createRequire calls cannot use Wrangler's build-time aliases. It also initializes file flags from public fs.constants and implements synchronous fd_sync. Paths resolve against the wasm instance's working directory (initially /data), without changing the isolate-wide Node process directory.

The upstream entries table stores metadata, and file_pages stores contents in 64 KiB rows. Descriptor writes update only affected pages. Truncation supports sparse files, and file writes and native rename use SQLite transactions. Whole-file reads still require a buffer large enough for the result.

After the final connection leaves, Pumpkin saves and shuts down, and the object awaits storage.sync(). New connections wait for this checkpoint, then create a fresh runtime from the stored files. Checkpoint failures remain visible in status. This preserves issued writes; terminating a server with active clients can still lose changes in Pumpkin's in-memory caches.

Development interfaces

The supplied configuration listens on loopback TCP port 25565 and HTTP port 8787. The world name comes from WORLD_NAME in wrangler.jsonc.

  • GET /health: Worker readiness.
  • GET /: phase, connection count, runtime statistics, and last checkpoint time.

Runtime statistics include Wasm capacity and allocator in-use/free/arena bytes. Allocator counters include allocation metadata and unused container capacity; Wasm capacity additionally includes static data, stack, and growth headroom.

The public status path reads no filesystem internals and remains available when startup fails. Checkpointing is internal to the connection lifecycle. The worker/pumpkin.ts facade binds exports to the module context once, so the Durable Object deals with connect, stop, and status methods.

Local databases live in .data/workers/server/. Integration tests use separate storage under .data/probes/ and restart Wrangler to verify restoration. The echo and large-file test fixture has its own Worker configuration and wasm binary; it is excluded from the application bundle.

Current workerd versions can report Network connection lost when TCP clients disconnect. Rust panics, Rust error logs, and runtime crashes fail the integration test.

Build constraints

  • Use the pinned Rust nightly and matching wasm-bindgen CLI from setup.
  • Keep static relocation, unwind semantics, the 8 MiB stack, and memory growth.
  • Pass Emscripten link settings through rustc so they do not affect C compilation.
  • Keep the compatibility initializer linked even when Rust does not call fd_sync; build.rs configures this and tracks the library as a build input.
  • Prefix Rust exports to avoid collisions with libc and Emscripten symbols.
  • The synchronous SQLite mount supplies fd_sync; checkpoint completion separately awaits Durable Object storage synchronization.

Dependency pins and patch maintenance