Lumbridge Simulate Engine — the city, and the licence it can actually ship under
LSE is the third of the three, beside lumbridge-compute and lumbridge-bench: a 3D engine for walkable places. This first commit is the outside of the world — San Francisco — plus the seams the inside will attach to. The engine renders a City and a list of Markers and knows nothing else. It does not know markers are usually companies and it will never learn that "rejected" is red; that mapping lives in an adapter. Which is what lets one renderer serve a private map, a public one, and a self-hoster with no Lumbridge account, none of them a fork of the others. Three things were designed around the licence rather than discovered after it, because each one is a promise Apache 2.0 makes that is easy to break by accident. No trademarks in the repo — logos are fetched at runtime, and public/logos/ is gitignored. No OpenStreetMap-derived coordinates, which is why every coastline in cities/sf.ts was traced by hand: Nominatim output is ODbL, share-alike, and would attach to the whole pack. And no FlightRadar24 client — their terms forbid scraping and redistribution, so flights are an interface with a simulator and open community ADS-B behind it. The privacy constraint and the licence constraint turned out to want the same thing. Geocoded company positions and pipeline status both stay behind Workie's API; the open repo holds the city and the renderer. The tempting shortcut — commit an sf-companies.json — breaks both at once. Ported out of Workie, where a 3D city engine had no business living. Workie's /live is deleted rather than deprecated. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+274
@@ -0,0 +1,274 @@
|
||||
# Lumbridge Simulate Engine — architecture
|
||||
|
||||
**LSE** is the third of the three: `lumbridge-compute`, `lumbridge-bench`,
|
||||
`lumbridge-simulate`. A 3D engine for walkable places — a city from above, an
|
||||
office from inside, one renderer and one asset library.
|
||||
|
||||
Ships at `lumbridgecorp.com/live`. Apache 2.0, self-hostable, Gitea.
|
||||
|
||||
---
|
||||
|
||||
## 1. Why this is its own repo, and why it is public
|
||||
|
||||
LSE began as `/live` inside Workie, the private career portal. That is now
|
||||
**deleted**, not deprecated — a 3D city engine has no business living in a job
|
||||
tracker, and Workie is a private repo besides.
|
||||
|
||||
**You cannot cleanly Apache-2.0 a subdirectory of a private repo.** The licence
|
||||
attaches to a distribution, and a consumer has to be able to clone the thing the
|
||||
licence covers. A folder inside `lumbridgecorp-website` or inside
|
||||
`lumbridge-runtime` would mean every release is a manual extraction, and every
|
||||
extraction is a chance to ship something private by accident. The point of this
|
||||
project — a reusable asset library other people can build on and self-host — needs
|
||||
a repo that *is* the open-source artefact.
|
||||
|
||||
So: **`lumbridge-public/lumbridge-simulate` on the fleet's own Gitea.** Not
|
||||
GitHub. Self-hosting the forge is the same claim the engine makes: the whole
|
||||
thing runs on hardware you own, and nothing about it requires somebody else's
|
||||
platform to stay alive.
|
||||
|
||||
### World 1 is this
|
||||
|
||||
This is the part that makes LSE load-bearing rather than a demo. The existing
|
||||
Lumbridge world is a Phaser 2D office, and its tileset is what stops it being
|
||||
open-sourceable. World 1 becomes **the San Francisco city view plus the office
|
||||
interiors**, rendered here, with original assets.
|
||||
|
||||
That reframes the whole repo: LSE is not a map that happens to live next to the
|
||||
platform, it is the platform's renderer. The city is the outside of the world
|
||||
and the offices are the inside of it, and a tenant moves between them.
|
||||
|
||||
---
|
||||
|
||||
## 2. Layout
|
||||
|
||||
```
|
||||
lumbridge-simulate/
|
||||
├── src/engine/ # renderer. Knows about terrain, blocks, streets, flights.
|
||||
│ ├── types.ts # City, District, Hill, Marker, FlightSource
|
||||
│ ├── world.ts # projection + cached heightfield, built per city
|
||||
│ ├── terrain.ts # relief, coastline, parks
|
||||
│ ├── blocks.ts # the built city, on a per-district street lattice
|
||||
│ ├── structures.ts # bridges, streets, freeways
|
||||
│ ├── markers.ts # pins. Takes Marker[], knows nothing about companies.
|
||||
│ ├── flights.ts # FlightSource interface + a simulated implementation
|
||||
│ └── scene.ts # lights, sky, camera flights, render loop
|
||||
├── src/interiors/ # the inside of the world — floorplans, desks, presence
|
||||
├── src/assets/ # the library. Original meshes and materials, Apache 2.0.
|
||||
├── src/cities/ # data packs. Pure geography, no code.
|
||||
│ ├── sf.ts # ~1000 lines of coastline, hills, districts, landmarks
|
||||
│ └── la.ts # LA / OC / Riverside
|
||||
├── src/adapters/ # where outside data plugs in
|
||||
│ └── workie.ts # Workie API -> Marker[]
|
||||
└── src/main.ts # the standalone demo app
|
||||
```
|
||||
|
||||
The split that matters: **`engine` never imports `cities`, and neither imports
|
||||
`adapters`.** A city is data handed to the engine. A marker is a `{id, lat, lng,
|
||||
label, colorKey}` handed to the engine. The engine has no idea Workie exists,
|
||||
which is what makes it publishable, self-hostable by someone with no Lumbridge
|
||||
account at all, and free of anyone's private data.
|
||||
|
||||
`interiors` and `assets` sit beside `engine` rather than inside it: an office
|
||||
and a city share the projection, camera, lighting and render loop, and differ
|
||||
only in what they put in the scene. That is why `scene.ts` owns the loop and
|
||||
knows nothing about terrain specifically.
|
||||
|
||||
---
|
||||
|
||||
## 3. Three rules that keep Apache 2.0 honest
|
||||
|
||||
Apache 2.0 is a promise that everything in the repo is ours to give away. Three
|
||||
things would quietly break that promise, and all three are easy to walk into.
|
||||
|
||||
### 3.1 No trademarks in the repo
|
||||
|
||||
Company logos are **trademarks**, not code. Salesforce's logo is not ours to
|
||||
relicense no matter how the file got here, and an Apache-2.0 repo containing
|
||||
`logos/salesforce.svg` is making a claim it cannot back.
|
||||
|
||||
The NYC atlas ships 47 of these in `public/logos/`. We do not.
|
||||
|
||||
- Logos are fetched **at runtime**, client-side, and cached in the browser.
|
||||
- The repo carries a fetch script and zero logo files. `public/logos/` is
|
||||
`.gitignore`d, and CI fails if anything lands there.
|
||||
- `NOTICE` carries the standard "trademarks are the property of their
|
||||
respective owners; their use here is nominative" line.
|
||||
|
||||
### 3.2 No OSM-derived coordinates in the repo
|
||||
|
||||
This is the subtle one, and it is the reason the geography in `cities/sf.ts` is
|
||||
hand-traced rather than imported.
|
||||
|
||||
OpenStreetMap data — including anything that comes out of Nominatim geocoding —
|
||||
is **ODbL**. ODbL is share-alike: a "derivative database" has to be released
|
||||
under ODbL too. Committing a table of company lat/lngs geocoded from Nominatim
|
||||
into an Apache-2.0 repo mixes an incompatible share-alike obligation into a
|
||||
permissive one. That is exactly the kind of thing that makes a repo unusable for
|
||||
the people we want using it.
|
||||
|
||||
So:
|
||||
|
||||
- **Geography** (coastlines, hills, districts) is traced by hand from scratch.
|
||||
Original expression, ours, Apache 2.0. This is already true for SF.
|
||||
- **Elevation**, when we want real terrain, comes from **USGS/SRTM**, which is
|
||||
US-government public domain. Not OSM.
|
||||
- **Geocoded company coordinates never enter this repo.** They live in Workie's
|
||||
private database and arrive over the API at runtime.
|
||||
|
||||
Note where that lands: the licence constraint and the privacy constraint want
|
||||
exactly the same thing. Company positions and pipeline status both stay behind
|
||||
the API; the open-source repo holds the city and the renderer. That is a nice
|
||||
result and it should be defended, because the tempting shortcut — "just commit a
|
||||
`sf-companies.json`" — breaks both at once.
|
||||
|
||||
### 3.3 The engine takes no position on what a marker means
|
||||
|
||||
`markers.ts` renders `Marker[]`. A marker has a `colorKey`, not a `status`. The
|
||||
mapping from "rejected" to red lives in the *adapter*, in the consuming app.
|
||||
|
||||
That is what lets the same engine serve the private Workie build (coloured by
|
||||
pipeline state), the public Lumbridge demo (coloured by sector), and whatever
|
||||
someone else builds from this repo, without any of them being a fork.
|
||||
|
||||
---
|
||||
|
||||
## 4. Flights: FlightRadar24 is the wrong source for this
|
||||
|
||||
Real aircraft over the Bay is a great idea. FR24 specifically is a problem, and
|
||||
it is worth being clear about why before any code is written against it.
|
||||
|
||||
FlightRadar24's terms prohibit scraping and prohibit redistributing their data;
|
||||
their commercial API is licensed per-seat and explicitly does not grant
|
||||
redistribution. An Apache-2.0 repo shipping an FR24 client is publishing
|
||||
instructions for violating a ToS, and any FR24 data that reached the browser
|
||||
could not be relicensed onward. Neither is fatal to a private deployment, but
|
||||
both are fatal to the open-source story that is the point of this repo.
|
||||
|
||||
The engine therefore takes a **`FlightSource` interface**, and the repo ships
|
||||
only sources it can actually give away:
|
||||
|
||||
| source | licence | notes |
|
||||
| --- | --- | --- |
|
||||
| **`SimulatedFlights`** | ours, Apache 2.0 | great-circle tracks on real SFO/OAK/SJC approach and departure paths. **This is what ships first**, and it is genuinely enough — the map wants convincing motion, not a spotter's log. |
|
||||
| **`adsb.lol` / `airplanes.live`** | community, open terms | free, no key, real aircraft. The default "real data" adapter. |
|
||||
| **OpenSky Network** | free, non-commercial | fine for a demo, awkward for a commercial product page. |
|
||||
| **your own receiver** | *no licence at all* | an RTL-SDR + `dump1090` on any fleet box in the Bay produces first-party ADS-B. Nothing to comply with, and it is very on-brand. |
|
||||
| FlightRadar24 | commercial, no redistribution | if it is ever wanted, it is a **private adapter in the Lumbridge deployment**, not in this repo. |
|
||||
|
||||
Recommendation: build `SimulatedFlights` now, `adsb.lol` next, and treat a
|
||||
receiver as the eventual real answer.
|
||||
|
||||
---
|
||||
|
||||
## 5. LA is not "SF but more data"
|
||||
|
||||
SF is 0.20° × 0.36°. At the 45 m cells the SF heightfield uses, that is 336k
|
||||
lattice points and a 1.0 s build.
|
||||
|
||||
LA + Orange County + Riverside is roughly 0.5° × 1.9° — about **14× the area**.
|
||||
The same approach would be 4.6M lattice points and a mesh nobody can load. This
|
||||
is the one place where the engine as written does not simply extend, and it is
|
||||
better to know that before writing `cities/la.ts` than after.
|
||||
|
||||
Two changes make it work, and both are cheap now:
|
||||
|
||||
1. **Cell size is per-city, not a constant.** LA's basin gets coarse cells;
|
||||
nothing is lost, because LA's relief is mountains at the edges rather than
|
||||
SF's hills every four blocks.
|
||||
2. **Cities declare `focusRegions`** — a handful of boxes (DTLA, Santa Monica,
|
||||
Culver, Irvine, Pasadena) rendered at fine resolution, with the basin between
|
||||
them coarse. This is the LOD story, and it is a per-city data declaration
|
||||
rather than engine machinery.
|
||||
|
||||
SF gets one focus region covering the whole city and behaves exactly as it does
|
||||
now. LA gets six. NYC, later, gets Manhattan plus the inner boroughs.
|
||||
|
||||
---
|
||||
|
||||
## 6. How Workie feeds it
|
||||
|
||||
Workie stays the system of record for companies. LSE never gets a database.
|
||||
|
||||
```
|
||||
Workie GET /api/live/markers (private, tailnet) -> status colours
|
||||
Workie GET /api/public/markers (public, allowlist) -> sector colours
|
||||
|
|
||||
v
|
||||
adapters/workie.ts -> Marker[]
|
||||
|
|
||||
v
|
||||
engine/markers.ts
|
||||
```
|
||||
|
||||
The public endpoint goes through the **same `export-site.ts` field allowlist**
|
||||
that already guards radar.karti.ai and work.karti.ai — the one that is
|
||||
fail-closed and aborts a deploy rather than shipping an unknown field. No second
|
||||
implementation of that gate. That was the argument for keeping `/live` inside
|
||||
Workie originally, and it still holds — but only for the *data path*. The
|
||||
renderer left; the gate stayed where it was.
|
||||
|
||||
Self-hosters get neither endpoint and do not need one: `setMarkers()` takes an
|
||||
array, and where it comes from is the deployment's business.
|
||||
|
||||
`lumbridgecorp.com/live` is a static page, so it calls a small
|
||||
`lumbridge-simulate` service behind the existing Caddy `handle /api/*` — the
|
||||
same pattern `lumbridge-intake.service` already uses on cloud-2 — for flights
|
||||
and for proxying the public marker feed.
|
||||
|
||||
---
|
||||
|
||||
## 7. Interiors — and why the asset library is the real product
|
||||
|
||||
The existing Lumbridge world is a Phaser 4 2D office, and Karti's read is right:
|
||||
its tileset is the thing that stops it being open-sourceable. Redrawing that in
|
||||
3D with original assets is not a graphics exercise, it is a **licensing
|
||||
unlock** — it is what lets the walkable-office half of Lumbridge ship under
|
||||
Apache 2.0 at all.
|
||||
|
||||
That makes `src/assets` the highest-leverage part of this repo over time: an
|
||||
original, consistent, Apache-2.0 library of desks, chairs, partitions, screens,
|
||||
doors, lighting rigs, floor and wall materials. Built once, used by every
|
||||
Lumbridge world and by anyone else who wants one.
|
||||
|
||||
Interiors share the engine's projection, camera and render loop, and swap the
|
||||
city layer for a floorplan layer. Same `Marker` type — a desk with a person at
|
||||
it is a marker with a different `colorKey`. The city view and the office view
|
||||
are the outside and the inside of one world, and moving between them is a camera
|
||||
transition, not a different application.
|
||||
|
||||
Reference point: the per-office spatial products in this space (Simile and
|
||||
friends) are the shape to aim at. The difference LSE is going for is that this
|
||||
one is Apache 2.0 and self-hostable — you can run your own world on your own
|
||||
hardware, which is the same claim `lumbridge-compute` makes about compute.
|
||||
|
||||
This is the next phase after the city lands, and it is where `src/assets` starts
|
||||
earning its keep.
|
||||
|
||||
---
|
||||
|
||||
## 8. Order of work
|
||||
|
||||
1. **Port** SF out of Workie into `engine` + `cities/sf`, parameterised by city;
|
||||
delete Workie's `/live`. *(this commit)*
|
||||
2. **Markers + simulated flights.** A demo worth showing, with no data pipeline
|
||||
and no licence questions. *(this commit)*
|
||||
3. **Mount at `lumbridgecorp.com/live`**, with a small `lumbridge-simulate`
|
||||
service behind Caddy for flights.
|
||||
4. **Interiors + the asset library** — the office view, original assets, and
|
||||
World 1 moving off Phaser. This is the one that unlocks the platform.
|
||||
5. **Workie adapter**, once its geocoding pipeline lands. Workie gets an API,
|
||||
not a renderer.
|
||||
6. **`cities/la`** — LA / OC / Riverside. Needs `focusRegions` from §5 first.
|
||||
7. **NYC.**
|
||||
|
||||
## 9. Open questions
|
||||
|
||||
1. **Package name.** `@lumbridge/simulate` implies an npm publish; consuming
|
||||
straight from Gitea is simpler until someone outside asks for a registry.
|
||||
2. **Licence for the asset library specifically.** Apache 2.0 covers code
|
||||
cleanly; art is sometimes better served by CC0 or CC-BY so it can be reused
|
||||
outside software. Worth deciding before the first mesh lands, because
|
||||
relicensing art after contributors exist is painful.
|
||||
3. **How much of World 1 moves at once.** The Phaser world is live and has real
|
||||
tenants; the city view can ship at `/live` well before any office does.
|
||||
Reference in New Issue
Block a user