A map of the upstream haven engine: where each subsystem lives, what owns what, and the gotchas
that cost time. It is for someone changing the client’s own source. Writing an addon needs none
of it — that is the AddOn documentation.
The engine is around 100k lines of unannotated Java. This subtree exists so that finding a seam is a lookup instead of a search.
A map, not an authority. When a page here disagrees with
src/,src/wins — and the task that noticed fixes the page. Class and member names are the anchors;grep -ngives you the line.
Never read the tree. Open the one or two subsystems the work touches.
| Page | Covers |
|---|---|
| terminology | the words the engine’s own code uses — gob, grid, pagina, wdgmsg, Loading — each anchored to the class that defines it |
| boot and the frame loop | main → login → RemoteUI.init → GameUI; the per-frame tick/draw loop; the UI monitor, Loader/Connection threads and the Loading protocol; the profiling seams; building a UI headlessly |
| the widget system | the Widget/UI tree, the @RName registry, the create/place/destroy seams (unbind ≠ unlink: a Window fades, and its death notice arrives before its death; entry runs under the tree’s monitor), GOut |
| reading a widget | the read-only walk over a live tree: children() vs the recursive children(Class), the server id, text sources, the hit-test walk and xlate/parentpos, and who resizes the root |
| the tick and draw traversals | how a frame reaches a widget: the two recursion seams, why an override of draw may paint no child at all, where the screen GOut is built, and what a frame allocates |
| widget input | how an event reaches a widget: the grab checked before the tree, the three propagation walks, focus bookkeeping vs delivery (hasfocus is the wrong read), the per-frame point queries, popup rooting, and the two drop families |
| GameUI’s own windows | the Hidewnd wrappers, the menu bars, and the one private togglewnd/wndstate path |
| where GameUI’s windows stand | the wndc-* position store: every key, where each is read and written, the clamp, ContentsWindow’s three states, and what GameUI.resize re-places |
| chrome | what draws a frame: Window.deco and the Deco contract (the ctor’s sz is the CONTENT size), IBox, and the window-less panels |
| controls | SIWidget’s rasterise-once cache, Button, IButton, ACheckBox’s value spine, the four plain display controls, RadioGroup |
| panels and tabs | Tabs, which is not a widget, OptWnd’s panel model: the caption that follows the panel, the list that is the navigation, and where a re-fit stops — and PackCont, the container that stays packed |
| lists, text and scrolling | HSlider/Scrollbar, TextEntry and its ReadLine, Scrollport, and the model-backed family — SListWidget/SListBox, SDropBox, GridList, TableBox |
| search lists | SSearchBox: allitems() vs items(), typing, search/stopsearch and the change a search makes, and its four subclasses — kin, polity members, map markers, map icons |
| gob sprites | how a resource-drawn object becomes render nodes: ModSprite and its Mod pipeline, which GAttribs take part and the identity test that rebuilds it, and lib/vmat, the served attribute that dresses a model’s tagged parts in the server’s materials |
| state roots | where game state lives: Glob, OCache/Gob and the GAttrib lifetime, MCache, player, inventory and ItemInfo, party, time |
| networking | Session/Connection, uimsg in and wdgmsg out, and the full action channel |
| the map database | the recorded map: the one RW lock and its processor thread, gridinfo/segments, ZoomGrid, markers and the merge that re-bases them |
| the minimap | the live ⇄ recorded coordinate bridge (sessloc), resolve’s tryLock rule, and how a grid becomes a picture |
| the 3D world | the MapView scene, a client-only gob in it, ground overlays over the terrain, materials, billboards, glTF and render-to-texture |
| weather, tree sway, overlay plumes and plant sprites | Glob.wmap/Weather/Weather.Factory and the two places that compose and tick it, the weather numbers wmap does not keep and the light MapView.amblight builds, the lib/svaj adopted copy and how Gob.Placed.Placement recomposes a sway, Gob.Overlay’s lazy init()/slots lifecycle a plume rides, and the lib/plants/lib/gplant factories a crop’s sprouts come from |
| the pointer and the ground | screen ↔ world and the space both speak, placement snapping, the pick pass, and the click dispatch a synchronous hook gets in front of |
| ground detail | how a cut’s decoration is seeded — Tileset.SpriteFlavor.flavor’s per-tile reseed and draw, MCache.Grid.makeflavor/Flavobjs — what its mesh is made of — TerrainTile.Blend’s weights and en layers, faces/_faces, MapMesh.build’s two passes and dotrans — and the cut lifecycle: Deferred.get vs cur, invalidate vs rebuild, and the identity swap in MapRaster.Grid.tick |
| which ground is drawn | the terrain display lists: the one bolted to the player, one over a second cache, and what bounds each |
| terrain height | where a height comes from and who reads it: MCache.Grid.z and the record, MCache.getfz as the drawn read and getrealfz as the streamed one, getcz/getzp, the ZSurface a cut hands out and the water’s bottom, the placers, the camera, the placers that cache on chseq, and the ridge model (Ridges: broken edges, the five shapes, connect, TexCons) and how, flat, the cliff keeps its shape on the plane |
| the camera | reading the installed camera outside a render pass, the name→class registry and the two prefs, and the frustum’s two traps, with the default camera’s planes that follow its distance |
| render targets | what a map-view frame draws into, a second fragment output (FragData, its null = GL_NONE, per-output blending), the RenderedNormals/Outlines pattern of a buffer the scene writes and a ScreenQuad reads back, the draw-order table, and the output-numbering gotcha that made the program cache miss |
| the render backend | the scene counters :stats on reads, where a frame’s draw calls are submitted, the 2D blit path, the fixed-function pipe states and what each tells GL, and writing a shader state of your own |
| several sessions at once | one process holding several logged-in sessions with one on screen: the drawn-UI split, what a session that is not drawn stops doing, aligning two sessions’ coordinate frames, ordering one that is not drawn, and the merged scene |
| text and fonts | Text.Foundry and every named surface that bakes one, RichText, DPI scaling, custom TTF loading |
| UI scaling | the one factor the whole 2D interface is drawn at: UI.scalef and its converters, where the number comes from, art that is scaled at load, and the round-trip that is exact in only one direction |
| the chat’s colours | one colour per kind of line, each a literal where the line is built, and the two urgency arrays that are not the same array |
| the chat | ChatUI and its channels: where a line arrives, the three argument shapes one message name wears, what selects a tab, and what is never trimmed |
| the console | the : command line: what registers a command, the three tiers a name resolves in, what a command that throws does, and which thread its body runs on |
| resources | what a .res carries: reading a layer by class, predicate or id, the id == null gotcha, and the OD_RES delta that carries a gob’s resource and state bytes under the gob monitor |
| resource loading | how a name becomes a Resource: the two pools and their parent link, the source order, the asking and the parsing version rules, the soft cache, Loading and the two failures, and the load/init order layers are built in |
| the on-disk store | ResCache and its two implementations: who writes which key family (res/, map/, data/mm-icons-2/), HashDirCache’s slots, header, lock and temp-and-rename, the gotchas a hash table of files has, and the haven.store seam that puts the map and the resource cache in two SQLite files beside the jar |
| published code | the Java a .res carries: the code layer, where a resource is fetched from and in what order, @FromResource adoption, and the gotcha that every public member of haven is an ABI the server’s code links against by name and descriptor |
| services | keybindings, Resource and code adoption, buffs, the vitals bars, the speed selector, the belt, the action menu, minimap icons, crafting, equipment |
| the fight | Fightview, Fightsess and GiveButton: the relations and their numbers, the buffs drawn beside each side, the action row and its cooldowns, every message in and out, and why GameUI.fv outlives its view |
| audio | the three channels, playing a clip and stopping one, per-clip volume, ambience, the per-session mute, the mixer’s lazy drain, and the instrument keyboard: what it sends, how the server plays it back, and why a high key is a short one |
| the character sheet | CharWnd and its six tabs: attributes and FEP, study, skills and credos, the combat-school deck builder, the quest log, the wound tree |
| the Kin window | the buddy roster and its palette, the village and realm panels beside it, and the two uimsg arms that dereference find(id) unguarded |
| the radial menu | FlowerMenu: what the server sends, the grabs a ring takes, the three ways one ends, and the fork seams in it |
| the game clock | Glob.globtime and the loop that converges it on the server’s, the 500-second re-base, and the astronomy blob |
| Steam | the steamworks4j bridge and what init needs from the process (the app id, per working directory), the steam ticket login and what it does not remember, the Workshop launcher’s two launch modes and the upload tool |
| preferences and the Options window | the two disjoint stores a setting lands in, the 6144-byte budget one preference value has, and what each OptWnd panel writes |
tick → draw → swap under
synchronized(ui), and Loader threads apply that session’s server messages under that same
monitor — but there is a UI per session and so a monitor per session, and the one guarding a
widget is w.ui’s (multi-session.md). Never call into a scripting layer from
a Connection worker — queue and drain on the tick.Loading is control flow, not an error. Any resource, gob or grid read can throw it. Swallow
it to nil or partial, or defer to a loader task; never let it escape into user code.added()/attached(). Widget.add links directly and does not route through addchild..res resolve asynchronously. Indir.get() throws Loading until cached, and some .res
files carry published Java code this fork does not ship. Never assume a resource’s name — servers
ship -alt variants; read it off the running client.Gob.rc is login-relative. The shareable anchor is a grid
id plus a within-grid offset, and grid and segment ids are 64-bit — expose them as decimal strings.The standard is DOCUMENTATION.md §12, and it is six rules:
| Rule | |
|---|---|
| Never a line number | cite the class and the member — MapView.click, Session.sendmsg |
| Map, not narrative | tables of where a thing lives; prose only for lifecycle, threading, ownership |
Upstream haven only |
never io.brodgar — that is already src/ and the AddOn docs |
| Gotchas live on their subsystem’s page | not in a tier of their own, and only when they cost time |
| A page is born from need | no completeness goal; a missing subsystem is one nobody has needed yet |
| Ceiling 150 lines | corrected in place, never appended to |
[!WARNING] This documentation has been entirely generated using artificial intelligence. While efforts have been made to provide accurate and useful information, it may contain inaccuracies, omissions, or outdated information.
If you notice anything that is incorrect, unclear, or missing, please feel free to open an issue or submit a pull request with a correction.
Community contributions and corrections are always welcome!