Age of Empires II Trial
Verified state (2026-08-26)
binaries/shareware/aoe2/aoe2_ex/EMPIRES2.EXE now reaches live Random Map
gameplay through the registered aoe2 app on the same asset manifest used by
the web host. The verified frame contains villagers, a town center under
construction, explored terrain and fog, the resource bar, command panel, and
minimap. test/test-aoe2-gameplay.js reproduces first run and checks the frame.
Web clean-exit cause
The old app entry mounted 25 files. It included the core DRS archives and
language.dll, but omitted EBUEula.dll. The trial loads that DLL dynamically,
so it is not listed in the EXE import table. A local CLI run could find the
sibling file from the checkout; the web VFS could not, and EMPIRES2.EXE
returned through ExitProcess(0) before creating the main game window. The
clean status made this look like a successful smoke run.
The same manifest also omitted the files enumerated only after player creation:
FindFirstFile("campaign\\*.cpn")
FindFirstFile("scenario\\*.scn")
Both searches failed even though the trial payload ships campaign/cam8.cpn
(the embedded William Wallace scenarios) and the coastal-map scenarios.
Player-name field
The new-player EDIT accepted characters but looked blank during typing. API and surface traces showed each prefix reaching the native control and being painted into the top-level GDI surface. The exclusive compositor subsequently placed an opaque DirectDraw frame layer over it. AoE I uses the same subclassed-native- EDIT arrangement and exhibited the same rendering bug.
The renderer now retains that otherwise-suppressed GDI surface and composites
only visible native-child rectangles above DirectDraw. This keeps the game
frame authoritative outside the child while making the live name and caret
visible. Retention is intentionally independent of whether the child already
exists: in browser worker mode the surface attachment can arrive before the
later CreateWindow(EDIT) host notification. The focused regression creates
the child after both surfaces, and the AoE II acceptance captures Codex
before clicking OK and rejects the old uniformly black field.
EULA text
The trial passes RichEdit EM_STREAMIN an EDITSTREAM whose dwCookie is
opaque state and whose pfnCallback supplies EULA.RTF. The control shim had
treated dwCookie itself as a C string and ignored the callback, so the dialog
created a correctly sized but empty text box.
The edit control now invokes the documented guest callback synchronously,
streams bounded chunks, reports callback errors through dwError, and projects
visible RTF text into the native control while dropping formatting destinations
such as font/color tables and pictures. The real web EULA streams 6,449 bytes
and displays 4,793 visible characters. test/test-richedit-stream-callback.js
covers both direct-cookie compatibility and a real x86 stdcall callback.
Camera-pan terrain rectangles
Holding a camera key into unexplored fog exposed repeated 24x32 terrain islands in both AoE I and AoE II. They were not compositor overlays or bad texture coordinates. DirectDraw traces identified the rectangle as the game's software- cursor background surface:
Blt render <- saved background (restore old cursor rectangle)
Blt saved background <- render (save new cursor rectangle)
Blt primary <- render (present frame)
AoE CPU-redraws the render surface through Lock/Unlock before the first
restore. Replaying the prior frame's saved background therefore stamps obsolete
terrain onto the new world; subsequent camera motion carries each stamp across
the fog. DirectDraw now records bounded small-surface save provenance and a
per-surface CPU-write epoch. Only an exact inverse restore whose source surface
was saved before a later Unlock becomes a successful no-op. Same-epoch cursor
erase and non-inverse surface copies still execute normally.
The focused DirectDraw regression covers all three cases. The AoE II acceptance also holds Left through gameplay and checks the central fog band: the broken frame contained 1,800 colored pixels in repeated islands; the fixed CLI and web captures contain none.
Manifest and acceptance route
lib/apps.js now mounts 95 required files (68.8 MiB): the EULA DLL/document,
core data and fonts, the complete trial campaign media/audio, both loose trial
scenarios, and MIDI music. requiredFiles: true turns a missing payload file
into a launch error instead of silently exposing empty gameplay choices.
The acceptance route is:
EULA Accept -> Single Player -> create player -> Random Map -> Start Game
-> Trial Coastal Map -> OK -> live map
With 50,000 interpreter steps per batch and 100 ms of guest time per batch, the
first terrain-and-HUD gameplay frame is stable by batch 1600. The test requires
a 640x480 capture with a substantial green terrain region, detailed world
pixels, and the dark lower command/minimap HUD; it also rejects clean early
ExitProcess as a failure. It then pans from batch 1602 through 1640 and rejects
stale cursor-background rectangles in the fog.