Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cspell/project-words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ eamodio
Erdokovy
esbenp
Fira
fflate
Flaticon
fphysics
fract
Expand Down
38 changes: 38 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Human contributors: see [CONTRIBUTING.md](./CONTRIBUTING.md) for a shorter, huma
- [Testing](#testing)
- [Documentation Site Demos](#documentation-site-demos)
- [Documentation Site Blog](#documentation-site-blog)
- [Browser Playground](#browser-playground)
- [Common Patterns](#common-patterns)
- [Security Considerations](#security-considerations)

Expand Down Expand Up @@ -59,6 +60,7 @@ Forge is a browser-based, code-only game engine built with TypeScript. It provid

/demo # Demo application
/documentation-site # Docusaurus documentation
/playground # Browser-only IDE for writing/exporting a Forge game
/scripts # Build and utility scripts
/assets # Static assets (images, etc.)
```
Expand Down Expand Up @@ -605,6 +607,42 @@ Conventions:
`documentation-site/`) to catch broken links/MDX errors, the same way a
demo change is per the "Documentation Site Demos" section above.

## Browser Playground

`/playground` is a standalone, browser-only IDE (Vite + Monaco +
`@typescript/vfs` + esbuild-wasm, no React, no backend) for writing a small
single-file Forge game and downloading it as a self-contained `index.html`
+ `game.js` zip. It's a separate npm project (its own `package.json`,
`node_modules`, `tsconfig.json`), the same pattern as `documentation-site`,
so it isn't covered by the root `npm run check-types`/`lint`/`test`
commands - verify it independently with `npm run typecheck` from
`playground/`.

Like the documentation site's demos, it depends on
`@forge-game-engine/forge` via a `file:..` link resolved through this
repo's `package.json` `exports`, which point at `/dist`. Two registries in
`playground/src/forge-registry.ts` snapshot `/dist` (both its compiled JS
and its `.d.ts` files) into the playground's own app bundle at dev/build
time via Vite's `import.meta.glob`, and derive the set of valid
`@forge-game-engine/forge/<subpath>` import specifiers directly from the
root `package.json`'s `exports` map rather than hardcoding them. This means:

- `npm run build` must be run at the repo root first (and again after any
`/src` change) before `playground/`'s dev server or build will reflect
it, the same gotcha as the documentation site's demos.
- The esbuild-wasm plugin (`playground/src/build/forge-esbuild-plugin.ts`)
resolves a game's `@forge-game-engine/forge/<subpath>` imports - plus
Forge's own internal relative imports and its one runtime dependency
(`seedrandom`) - against that embedded snapshot, so Forge is inlined into
the downloaded `game.js` rather than referenced externally.
- Monaco's extra libs (`playground/src/editor/forge-types.ts`) and the
`@typescript/vfs` pre-build type-check environment
(`playground/src/editor/type-check.ts`) both register the same `.d.ts`
snapshot, plus a tiny re-export shim per subpath (e.g. `fsm` ->
`finite-state-machine`) so `@forge-game-engine/forge/<subpath>` resolves
under classic Node module resolution even where the public subpath name
doesn't match Forge's internal dist folder name.

## Common Patterns

### Readonly Fields
Expand Down
2 changes: 2 additions & 0 deletions playground/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
dist
58 changes: 58 additions & 0 deletions playground/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Forge Playground

A bare-bones, browser-only IDE for writing a small Forge game and downloading
it as a standalone, self-contained web page - no backend involved.

- **Monaco** for the editor, with `@forge-game-engine/forge`'s real type
declarations registered as extra libs (via `src/editor/forge-types.ts`),
so autocomplete, hover, and inline diagnostics work against Forge's actual
public API.
- **`@typescript/vfs`** powers an independent, in-browser TypeScript
language service (`src/editor/type-check.ts`) that type-checks the game
source before every build - esbuild only strips types, it never checks
them.
- **esbuild-wasm** bundles the game's TypeScript, with a custom plugin
(`src/build/forge-esbuild-plugin.ts`) that resolves
`@forge-game-engine/forge/<subpath>` imports against Forge's own compiled
`dist` output (embedded into this app's bundle at build time via
`import.meta.glob`), so Forge itself is inlined into the output rather
than referenced externally.
- **fflate** zips the generated `index.html` and bundled `game.js` and
triggers a browser download.

## Running

From this directory:

```bash
npm install
npm run dev
```

Requires `@forge-game-engine/forge`'s own `dist` to already exist - run
`npm run build` in the repo root first (and again after changing `/src`,
since this app's type/JS registries are snapshotted from `dist` at
dev-server start, same as the documentation site's demos).

## How a build works

1. **Type-check**: the current editor contents are checked against a
`@typescript/vfs` environment seeded with Forge's real `.d.ts` files.
Errors are shown and the build stops there.
2. **Bundle**: on success, esbuild-wasm bundles the game source (stdin
entry point) through the Forge resolver plugin into a single minified
ES module.
3. **Zip & download**: the bundle is paired with a generated `index.html`
(`<div id="game">` + `<script type="module" src="./game.js">`, matching
what `createGame('game')` expects) and zipped client-side for download.

## Limitations

This is intentionally minimal, not a general-purpose bundler:

- Single file only - the editor holds one game file, no multi-file
projects or a file tree.
- Only resolves Forge's own dependencies needed by its compiled `dist`
output (currently just `seedrandom`); an arbitrary third-party import in
your game code won't resolve.
- No visual scene editor - this is a code editor with a build button, nothing more.
13 changes: 13 additions & 0 deletions playground/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Forge Playground</title>
<link rel="stylesheet" href="/src/style.css" />
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
Loading
Loading