Skip to content

Commit ea2a8f1

Browse files
committed
wrote design docs
1 parent e32fb86 commit ea2a8f1

47 files changed

Lines changed: 11440 additions & 2717 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

MicropolisEngine/makefile

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,16 +69,17 @@
6969
EMCC = em++
7070

7171
EMCXXFLAGS = \
72-
-std=c++11 \
72+
-std=c++17 \
7373
-fPIC
7474

7575
EMLDFLAGS = \
7676
-s WASM=1 \
7777
-s MODULARIZE=1 \
7878
-s EXPORT_ES6=1 \
79-
-s 'ENVIRONMENT=web,worker' \
79+
-s 'ENVIRONMENT=web,worker,node' \
80+
-s ALLOW_MEMORY_GROWTH=1 \
8081
-lembind \
81-
--embind-emit-tsd micropolisengine.d.ts \
82+
--emit-tsd=micropolisengine.d.ts \
8283
--shell-file src/micropolisengine_template.html \
8384
--js-library src/micropolisengine_lib.js \
8485
--preload-file ../resources/cities@cities/
@@ -167,7 +168,7 @@ install: all
167168
cp build/micropolisengine.wasm ../micropolis/src/lib
168169
cp build/micropolisengine.data ../micropolis/src/lib
169170
cp build/micropolisengine.js ../micropolis/src/lib
170-
cp build/micropolisengine.d.ts ../micropolis/src/types
171+
if [ -f build/micropolisengine.d.ts ]; then cp build/micropolisengine.d.ts ../micropolis/src/types; else echo "micropolisengine.d.ts was not generated; keeping existing TypeScript bindings"; fi
171172

172173
clean:
173174
rm -rf \

MicropolisEngine/src/js_callback.h

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,153 +2,158 @@
22
#ifndef JS_CALLBACK_H
33
#define JS_CALLBACK_H
44

5+
#include <cstdint>
56
#include <emscripten/val.h>
67
#include "callback.h"
78

9+
inline emscripten::val micropolisPointerValue(Micropolis *micropolis) {
10+
return emscripten::val(reinterpret_cast<uintptr_t>(micropolis));
11+
}
12+
813
class JSCallback : public Callback {
914
public:
1015
explicit JSCallback(emscripten::val jsCallback)
1116
: Callback(), jsCallback(jsCallback) {}
1217

1318
// Implement all pure virtual functions from Callback
1419
void autoGoto(Micropolis *micropolis, emscripten::val callbackVal, int x, int y, std::string message) override {
15-
jsCallback.call<void>("autoGoto", emscripten::val(micropolis), callbackVal, x, y, message);
20+
jsCallback.call<void>("autoGoto", micropolisPointerValue(micropolis), callbackVal, x, y, message);
1621
}
1722

1823
void didGenerateMap(Micropolis *micropolis, emscripten::val callbackVal, int seed) override {
19-
jsCallback.call<void>("didGenerateMap", emscripten::val(micropolis), callbackVal, seed);
24+
jsCallback.call<void>("didGenerateMap", micropolisPointerValue(micropolis), callbackVal, seed);
2025
}
2126

2227
void didLoadCity(Micropolis *micropolis, emscripten::val callbackVal, std::string filename) override {
23-
jsCallback.call<void>("didLoadCity", emscripten::val(micropolis), callbackVal, filename);
28+
jsCallback.call<void>("didLoadCity", micropolisPointerValue(micropolis), callbackVal, filename);
2429
}
2530

2631
void didLoadScenario(Micropolis *micropolis, emscripten::val callbackVals, std::string name, std::string fname) override {
27-
jsCallback.call<void>("didLoadScenario", emscripten::val(micropolis), callbackVals, name, fname);
32+
jsCallback.call<void>("didLoadScenario", micropolisPointerValue(micropolis), callbackVals, name, fname);
2833
}
2934

3035
void didLoseGame(Micropolis *micropolis, emscripten::val callbackVal) override {
31-
jsCallback.call<void>("didLoseGame", emscripten::val(micropolis), callbackVal);
36+
jsCallback.call<void>("didLoseGame", micropolisPointerValue(micropolis), callbackVal);
3237
}
3338

3439
void didSaveCity(Micropolis *micropolis, emscripten::val callbackVal, std::string filename) override {
35-
jsCallback.call<void>("didSaveCity", emscripten::val(micropolis), callbackVal, filename);
40+
jsCallback.call<void>("didSaveCity", micropolisPointerValue(micropolis), callbackVal, filename);
3641
}
3742

3843
void didTool(Micropolis *micropolis, emscripten::val callbackVal, std::string name, int x, int y) override {
39-
jsCallback.call<void>("didTool", emscripten::val(micropolis), callbackVal, name, x, y);
44+
jsCallback.call<void>("didTool", micropolisPointerValue(micropolis), callbackVal, name, x, y);
4045
}
4146

4247
void didWinGame(Micropolis *micropolis, emscripten::val callbackVal) override {
43-
jsCallback.call<void>("didWinGame", emscripten::val(micropolis), callbackVal);
48+
jsCallback.call<void>("didWinGame", micropolisPointerValue(micropolis), callbackVal);
4449
}
4550

4651
void didntLoadCity(Micropolis *micropolis, emscripten::val callbackVal, std::string filename) override {
47-
jsCallback.call<void>("didntLoadCity", emscripten::val(micropolis), callbackVal, filename);
52+
jsCallback.call<void>("didntLoadCity", micropolisPointerValue(micropolis), callbackVal, filename);
4853
}
4954

5055
void didntSaveCity(Micropolis *micropolis, emscripten::val callbackVal, std::string filename) override {
51-
jsCallback.call<void>("didntSaveCity", emscripten::val(micropolis), callbackVal, filename);
56+
jsCallback.call<void>("didntSaveCity", micropolisPointerValue(micropolis), callbackVal, filename);
5257
}
5358

5459
void makeSound(Micropolis *micropolis, emscripten::val callbackVal, std::string channel, std::string sound, int x, int y) override {
55-
jsCallback.call<void>("makeSound", emscripten::val(micropolis), callbackVal, channel, sound, x, y);
60+
jsCallback.call<void>("makeSound", micropolisPointerValue(micropolis), callbackVal, channel, sound, x, y);
5661
}
5762

5863
void newGame(Micropolis *micropolis, emscripten::val callbackVal) override {
59-
jsCallback.call<void>("newGame", emscripten::val(micropolis), callbackVal);
64+
jsCallback.call<void>("newGame", micropolisPointerValue(micropolis), callbackVal);
6065
}
6166

6267
void saveCityAs(Micropolis *micropolis, emscripten::val callbackVal, std::string filename) override {
63-
jsCallback.call<void>("saveCityAs", emscripten::val(micropolis), callbackVal, filename);
68+
jsCallback.call<void>("saveCityAs", micropolisPointerValue(micropolis), callbackVal, filename);
6469
}
6570

6671
void sendMessage(Micropolis *micropolis, emscripten::val callbackVal, int messageIndex, int x, int y, bool picture, bool important) override {
67-
jsCallback.call<void>("sendMessage", emscripten::val(micropolis), callbackVal, messageIndex, x, y, picture, important);
72+
jsCallback.call<void>("sendMessage", micropolisPointerValue(micropolis), callbackVal, messageIndex, x, y, picture, important);
6873
}
6974

7075
void showBudgetAndWait(Micropolis *micropolis, emscripten::val callbackVal) override {
71-
jsCallback.call<void>("showBudgetAndWait", emscripten::val(micropolis), callbackVal);
76+
jsCallback.call<void>("showBudgetAndWait", micropolisPointerValue(micropolis), callbackVal);
7277
}
7378

7479
void showZoneStatus(Micropolis *micropolis, emscripten::val callbackVal, int tileCategoryIndex, int populationDensityIndex, int landValueIndex, int crimeRateIndex, int pollutionIndex, int growthRateIndex, int x, int y) override {
75-
jsCallback.call<void>("showZoneStatus", emscripten::val(micropolis), callbackVal, tileCategoryIndex, populationDensityIndex, landValueIndex, crimeRateIndex, pollutionIndex, growthRateIndex, x, y);
80+
jsCallback.call<void>("showZoneStatus", micropolisPointerValue(micropolis), callbackVal, tileCategoryIndex, populationDensityIndex, landValueIndex, crimeRateIndex, pollutionIndex, growthRateIndex, x, y);
7681
}
7782

7883
void simulateRobots(Micropolis *micropolis, emscripten::val callbackVal) override {
79-
jsCallback.call<void>("simulateRobots", emscripten::val(micropolis), callbackVal);
84+
jsCallback.call<void>("simulateRobots", micropolisPointerValue(micropolis), callbackVal);
8085
}
8186

8287
void simulateChurch(Micropolis *micropolis, emscripten::val callbackVal, int posX, int posY, int churchNumber) override {
83-
jsCallback.call<void>("simulateChurch", emscripten::val(micropolis), callbackVal, posX, posY, churchNumber);
88+
jsCallback.call<void>("simulateChurch", micropolisPointerValue(micropolis), callbackVal, posX, posY, churchNumber);
8489
}
8590

8691
void startEarthquake(Micropolis *micropolis, emscripten::val callbackVal, int strength) override {
87-
jsCallback.call<void>("startEarthquake", emscripten::val(micropolis), callbackVal, strength);
92+
jsCallback.call<void>("startEarthquake", micropolisPointerValue(micropolis), callbackVal, strength);
8893
}
8994

9095
void startGame(Micropolis *micropolis, emscripten::val callbackVal) override {
91-
jsCallback.call<void>("startGame", emscripten::val(micropolis), callbackVal);
96+
jsCallback.call<void>("startGame", micropolisPointerValue(micropolis), callbackVal);
9297
}
9398

9499
void startScenario(Micropolis *micropolis, emscripten::val callbackVal, int scenario) override {
95-
jsCallback.call<void>("startScenario", emscripten::val(micropolis), callbackVal, scenario);
100+
jsCallback.call<void>("startScenario", micropolisPointerValue(micropolis), callbackVal, scenario);
96101
}
97102

98103
void updateBudget(Micropolis *micropolis, emscripten::val callbackVal) override {
99-
jsCallback.call<void>("updateBudget", emscripten::val(micropolis), callbackVal);
104+
jsCallback.call<void>("updateBudget", micropolisPointerValue(micropolis), callbackVal);
100105
}
101106

102107
void updateCityName(Micropolis *micropolis, emscripten::val callbackVal, std::string cityName) override {
103-
jsCallback.call<void>("updateCityName", emscripten::val(micropolis), callbackVal, cityName);
108+
jsCallback.call<void>("updateCityName", micropolisPointerValue(micropolis), callbackVal, cityName);
104109
}
105110

106111
void updateDate(Micropolis *micropolis, emscripten::val callbackVal, int cityYear, int cityMonth) override {
107-
jsCallback.call<void>("updateDate", emscripten::val(micropolis), callbackVal, cityYear, cityMonth);
112+
jsCallback.call<void>("updateDate", micropolisPointerValue(micropolis), callbackVal, cityYear, cityMonth);
108113
}
109114

110115
void updateDemand(Micropolis *micropolis, emscripten::val callbackVal, float r, float c, float i) override {
111-
jsCallback.call<void>("updateDemand", emscripten::val(micropolis), callbackVal, r, c, i);
116+
jsCallback.call<void>("updateDemand", micropolisPointerValue(micropolis), callbackVal, r, c, i);
112117
}
113118

114119
void updateEvaluation(Micropolis *micropolis, emscripten::val callbackVal) override {
115-
jsCallback.call<void>("updateEvaluation", emscripten::val(micropolis), callbackVal);
120+
jsCallback.call<void>("updateEvaluation", micropolisPointerValue(micropolis), callbackVal);
116121
}
117122

118123
void updateFunds(Micropolis *micropolis, emscripten::val callbackVal, int totalFunds) override {
119-
jsCallback.call<void>("updateFunds", emscripten::val(micropolis), callbackVal, totalFunds);
124+
jsCallback.call<void>("updateFunds", micropolisPointerValue(micropolis), callbackVal, totalFunds);
120125
}
121126

122127
void updateGameLevel(Micropolis *micropolis, emscripten::val callbackVal, int gameLevel) override {
123-
jsCallback.call<void>("updateGameLevel", emscripten::val(micropolis), callbackVal, gameLevel);
128+
jsCallback.call<void>("updateGameLevel", micropolisPointerValue(micropolis), callbackVal, gameLevel);
124129
}
125130

126131
void updateHistory(Micropolis *micropolis, emscripten::val callbackVal) override {
127-
jsCallback.call<void>("updateHistory", emscripten::val(micropolis), callbackVal);
132+
jsCallback.call<void>("updateHistory", micropolisPointerValue(micropolis), callbackVal);
128133
}
129134

130135
void updateMap(Micropolis *micropolis, emscripten::val callbackVal) override {
131-
jsCallback.call<void>("updateMap", emscripten::val(micropolis), callbackVal);
136+
jsCallback.call<void>("updateMap", micropolisPointerValue(micropolis), callbackVal);
132137
}
133138

134139
void updateOptions(Micropolis *micropolis, emscripten::val callbackVal) override {
135-
jsCallback.call<void>("updateOptions", emscripten::val(micropolis), callbackVal);
140+
jsCallback.call<void>("updateOptions", micropolisPointerValue(micropolis), callbackVal);
136141
}
137142

138143
void updatePasses(Micropolis *micropolis, emscripten::val callbackVal, int passes) override {
139-
jsCallback.call<void>("updatePasses", emscripten::val(micropolis), callbackVal, passes);
144+
jsCallback.call<void>("updatePasses", micropolisPointerValue(micropolis), callbackVal, passes);
140145
}
141146

142147
void updatePaused(Micropolis *micropolis, emscripten::val callbackVal, bool simPaused) override {
143-
jsCallback.call<void>("updatePaused", emscripten::val(micropolis), callbackVal, simPaused);
148+
jsCallback.call<void>("updatePaused", micropolisPointerValue(micropolis), callbackVal, simPaused);
144149
}
145150

146151
void updateSpeed(Micropolis *micropolis, emscripten::val callbackVal, int speed) override {
147-
jsCallback.call<void>("updateSpeed", emscripten::val(micropolis), callbackVal, speed);
152+
jsCallback.call<void>("updateSpeed", micropolisPointerValue(micropolis), callbackVal, speed);
148153
}
149154

150155
void updateTaxRate(Micropolis *micropolis, emscripten::val callbackVal, int cityTax) override {
151-
jsCallback.call<void>("updateTaxRate", emscripten::val(micropolis), callbackVal, cityTax);
156+
jsCallback.call<void>("updateTaxRate", micropolisPointerValue(micropolis), callbackVal, cityTax);
152157
}
153158

154159
private:

README.md

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,13 +233,79 @@ His key trick: recursive weight-sharing in fractal convolutional blocks, where e
233233

234234
## Building
235235

236+
### Prerequisites
237+
238+
You need:
239+
240+
- Node.js 20+ and npm.
241+
- Git.
242+
- Python 3.
243+
- GNU Make.
244+
- Emscripten SDK (`emcc`, `em++`, `emar`) for rebuilding the C++ engine to WebAssembly.
245+
246+
On macOS, install the basic native tools first:
247+
248+
```bash
249+
xcode-select --install
250+
```
251+
252+
### Install Emscripten SDK
253+
254+
The engine build uses Emscripten/Embind. If `emcc --version` does not work, install `emsdk`:
255+
256+
```bash
257+
# Pick a parent directory for developer tools.
258+
mkdir -p ~/Developer
259+
cd ~/Developer
260+
261+
git clone https://github.com/emscripten-core/emsdk.git
262+
cd emsdk
263+
./emsdk install latest
264+
./emsdk activate latest
265+
266+
# Activate for the current shell.
267+
source ./emsdk_env.sh
268+
emcc --version
269+
```
270+
271+
To make Emscripten available in future shells, add this to your shell startup file:
272+
273+
```bash
274+
source ~/Developer/emsdk/emsdk_env.sh
275+
```
276+
277+
If you prefer not to auto-activate it in every shell, run that `source` command only before rebuilding the engine.
278+
279+
On macOS with Homebrew, this also works:
280+
281+
```bash
282+
brew install emscripten
283+
emcc --version
284+
```
285+
236286
### WASM Engine
237287

238288
```bash
239289
cd MicropolisEngine
240-
make # Requires Emscripten SDK
290+
make clean install
291+
```
292+
293+
`make install` builds the C++ engine and copies these generated artifacts into `micropolis/src/lib/`:
294+
295+
```text
296+
micropolisengine.js
297+
micropolisengine.wasm
298+
micropolisengine.data
241299
```
242300

301+
The checked-in makefile builds the engine for browser, worker, and Node environments:
302+
303+
```text
304+
-s 'ENVIRONMENT=web,worker,node'
305+
```
306+
307+
Node support is required for `npm run sim:headless`.
308+
243309
### SvelteKit App
244310

245311
```bash
@@ -249,13 +315,51 @@ npm run dev # Development server
249315
npm run build # Production build
250316
```
251317

318+
The Vite config copies `micropolisengine.wasm` and `micropolisengine.data` from `src/lib/` into the app build output so the browser can load them.
319+
252320
### CLI Tool
253321

254322
```bash
255323
cd micropolis
256324
npm run micropolis -- --help
257325
```
258326

327+
The `micropolis.js` CLI analyzes and edits `.cty`/`.mop` files without running the WASM simulator.
328+
329+
### Headless Simulator CLI
330+
331+
After rebuilding the engine with Emscripten:
332+
333+
```bash
334+
cd micropolis
335+
npm run sim:headless -- info
336+
npm run sim:headless -- smoke --ticks 10
337+
```
338+
339+
This loads the Emscripten/Embind WASM module in Node, instantiates `Micropolis`, loads a bundled city from the engine data package, runs ticks, and prints JSON state. It is the foundation for future GitHub Actions replay, command timeline validation, and agent-driven simulations.
340+
341+
### Quick Full Setup
342+
343+
From a fresh clone:
344+
345+
```bash
346+
# 1. Activate Emscripten.
347+
source ~/Developer/emsdk/emsdk_env.sh
348+
349+
# 2. Build the C++/WASM engine.
350+
cd MicropolisEngine
351+
make clean install
352+
353+
# 3. Install app dependencies.
354+
cd ../micropolis
355+
npm install
356+
357+
# 4. Verify static CLI, headless simulator, and web app.
358+
npm run micropolis -- city info ../resources/cities/haight.cty
359+
npm run sim:headless -- smoke --ticks 1
360+
npm run dev
361+
```
362+
259363
## Links
260364

261365
| Resource | URL |

designs/README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# MicropolisCore Design Notes
2+
3+
This directory captures design rationale that should outlive chat context.
4+
5+
Read these alongside:
6+
7+
- `notes/PIE-TAB-WINDOWS.md`
8+
- `notes/MultiPlayerIdeas.txt`
9+
- `skills/micropolis/`
10+
- `skills/micropolis-command-bus/`
11+
12+
## Documents
13+
14+
- `collaborative-microworld-lineage.md` — Engelbart, Kay, Papert, Piaget, SimCityNet, and constructionist multiplayer.
15+
- `multiplayer-browser-lessons.md` — Lessons from Sail/Muddy and the Hacker News discussion for MicropolisHub.
16+
- `command-path-collaboration-modes.md` — End-to-end command path, live/async collaboration modes, object placement, manifests, and source-of-truth rules.
17+
- `naming-conventions.md` — Big-endian Micropolis naming across files, branches, commands, events, callbacks, and serialized records.
18+
- `callback-interface-roadmap.md` — Simulator callback naming, event envelopes, reactive state bridge, and future introspection callbacks.
19+
- `wasm-bridge-and-testing-trajectory.md` — Vitest setup, **`wasmHeap`** / **`MicropolisReactive.svelte`**, integration tests against WASM, Embind teardown lessons, backlog, and alignment with command path / MCP goals.
20+
- `command-timeline-git-branches.md` — Persistent command leaves, coalesced commits, Git branches as universes, and TiVo replay.
21+
- `github-as-mmorpg-multiverse.md` — GitHub branches, issues, PRs, diffs, and forks as Micropolis multiplayer world mechanics.
22+
- `filesystem-object-model.md` — Modeling Micropolis objects as inspectable filesystem trees with Self-style prototypes and CARD advertisements.
23+
- `moollm-micropolis-integration.md` — How MOOLLM skills, characters, MCP, command bus, and GitHub compose around MicropolisCore.

0 commit comments

Comments
 (0)