Skip to content

Architecture

src/
Griff What a running game needs: scenes, serialization, runtime, physics, render lists, audio
(mixer, sound and music synths, the sound library every host reads Assets with),
networking (UDP with reliable and unreliable messages, and multiplayer on top), the
project and its files, tables of game data (Data), and the game loop every host runs
(Hosting). No UI dependencies, and nothing of the editor's or of any genre's.
Modules/ The genres, a folder each: Adventures, Battles, BlockWorlds, Cards, Contraptions, Dungeons,
FallingBlocks, Fighting, FirstPerson, Match3, PhysicsGames, Puzzles, Rhythm, Strategy,
Tactics, VisualNovels. Each folder is two assemblies: its running parts,
Griff.Modules.<Genre> (on Griff alone), and in Editor/ its editor parts,
Griff.Modules.<Genre>.Editor (its templates and their art, its Entity menu presets and
actions, on the module and Griff.Editing). Griff.Modules.csproj references them all, for
the programs that have every module (the editor, the tests, the tools)
Griff.Editing Making games: edits and the edit history, copying values, prefab copies and overrides, the
names a field may hold (KnownNames), the Entity menu's presets and actions, the component
catalog and the docs it shows, the font picker's choices, and New Project's templates with
their art and sounds (Griff.Templates)
Griff.Agent The agent surface: the MCP server and its HTTP transport, the scene and asset tools,
describing components, playtesting a running game
Griff.Scripting A project's scripts compiled with Roslyn's compiler and loaded into games (hot reload)
Griff.Exporting Exporting games, on the compiler: the desktop and web exporters, the web server a web
export plays from here, third-party notices and export fonts
Griff.Graphics How a frame is drawn, apart from any graphics API: IRenderer, the DrawList every renderer
draws (quads in clip space and the passes that draw them) and the shaders
Griff.Graphics.OpenGL The desktop's renderer: the draw list with OpenGL 3.3
Griff.Fonts Fonts read and glyphs rasterized in plain .NET (StbTrueTypeSharp), packed into atlas
pages and laid out as a quad a glyph; a font name resolved in one place (FontCatalog:
BuiltInFonts with their licenses, a project's font files, installed fonts)
Griff.Platform.Windows WGL context, WPF game surface, frame pacing, input, the sound device a host opens and MP3s
Griff.Editor WPF editor: modules, commands, panels, design system (Themes/), the script editor's
language services (Roslyn Workspaces) and the debugger's breakpoint probes
Griff.Player Runs exported games: in a window, or with --server as a console program with no WPF
Griff.Web Runs games in a browser: .NET for WebAssembly, the draw list drawn with WebGL 2 (a proof so far)
tests/
Griff.Tests Unit and integration tests, including playing every template
Griff.Editor.Tests The editor's real services, the agent surface, and exported games run for real
site/ griff.games: an Astro Starlight site, deployed to GitHub Pages (see site/README.md)

The engine lives in Griff so that the editor, the player and the tests all use one model. Its layers: Griff.Scenes holds the entities and components and how they’re saved, Griff.Runtime runs them and Griff.Rendering turns them into draw lists, reading the game rather than the other way round. A component holds its own state (what’s saved, and what it keeps track of as it goes) and the steps it advances when its system asks; it holds nothing of the game. When an action or a step of its needs the game (a panel opening, a volley fired, a flag set, a prefab spawned) it names the service it needs by type, an IGameService such as IPanels, IVolleys, IFlags or ISpawning: an action takes it as its first parameter and a step is handed it by its system, and the one system that owns that part of the game provides it (the panel system the panels, the level system volleys, dialogue and flags, the lifetime system spawning), found through the game’s IGameServices. So a new no-code action adds a small service to the system it belongs to rather than widening one interface everything implements. What a component’s state tells the game at once, whoever changed it, goes the same way through the services of the game playing its scene (Scene.Services): a Health tells IHealthNews, and the script system tells the entity’s scripts (OnDamage, OnDeath). What a running game asks of its view (the main camera, DefaultCameraSize, the view’s projection, ScreenEntity for draws that belong to the screen, what’s pinned to it, the order things are drawn in and so what’s on top, the lights reaching a point) it asks Griff.Runtime.Views, which the renderer reads too; the lights and their shadows (LightDraw, Shadows) and the pictures a game makes for itself (GamePictures) are the runtime’s as well. The renderer keeps nothing of one game that another sees: what a build is for (a frame to fill again, a picture for a saved game, which leaves out the open panels the game lists, and from how many entities its scene is built in chunks, Game.ChunkedFrom, on how many threads, Game.WorkerCount) goes along the build, and what lasts from frame to frame is the scene’s (each scene numbers its own frames drawn between steps, and the snapshots of its lists of components), so Play with Players’ windows, a game each on one thread, draw what each game would alone. What both sides need lives below them, in Griff (Color, Box, HudRect, Ease, Rand, DoNotSerialize), Griff.Input (Key, the mouse and gamepad buttons, an InputBinding and the built-in actions, InputActions.BuiltIn) and Griff.Data (tables: Table, its columns and cells, Dice, their files, CSV, formulas (Formula, read into a small tree and worked out by TableEvaluation, with powers and logarithms of its own in FormulaMath so every machine gets the same bits), the Curves level columns are generated from, a script’s class filled from a row, and the TableSet a game reads, its formulas worked out; a table never changes, so a running game, the editor and an undo history each hold one safely, and every edit makes a new one; and what a balance simulation is, a Simulation and its .sim file, SimulationFile). Playing one runs games, so it lives above the runtime, in Griff.Simulations: SimulationRunner plays each run as a headless Game of the arena scene (or a SimulationScript) with a seed of its own from the simulation’s, the step, the rows’ names and the run’s number, in blocks of runs shared across the Workers and added up in block order, so any number of cores gives the same SimulationResults, and a game plays its own with Game.Simulate (GameSimulations, which scripts reach as they reach the game). The project and its files (Griff.Projects: Project, its manifest, where its assets come from, and what a new one starts with, IProjectStart) sit below the runtime, which loads games from them; New Project’s templates (Griff.Templates: ProjectTemplate, ProjectTemplates, their art and sounds) sit above the scenes they build, and each module’s template uses them. The scene’s own checks of the names its fields hold (EntityReferences, ActionNames, OutputNames, NameChecks) and the problems a game reports (ProblemLog) are Griff.Scenes’, which the runtime reports as a game starts and the editor’s Inspector flags; what the Inspector offers to pick (NameChoices) and renaming with the fields that name it (RenameEntityEdit.WithReferences) are Griff.Editing’s. No two of the core’s namespaces use each other, however far round (NamespaceTests, a namespace counting with those inside it and each module apart), so each can become an assembly of its own; an entity knows whether it’s active (IsActive, counting its parents) and which machine runs it in a network game, and a field’s rows that say what a cell names get the project’s names as an IProjectNames (the editor’s KnownNames), not the editor’s own type.

A game moves in fixed steps (Time.StepRate, 60 a second): each frame the screen shows, Game.Advance takes as many as the real time since the last frame holds (none on a fast screen’s frame, two on a slow one’s; at most a tenth of a second’s worth, so a stall slows the game rather than piling up steps), keeps the rest for the next frame, and says how far the frame is between the last two steps (Time.Blend). A frame within 12% of a whole number of steps counts as exactly that many, so a steady screen takes the same steps every frame instead of now and then none and then two, and the player times frames by the compositor’s vertical blank (FramePacer, from DwmGetCompositionTimingInfo), the screen’s steady beat, rather than when the UI thread got to them. Input counts in the next step taken, once: each step ends the input’s frame, and a frame with no step keeps what was pressed for the next. While the renderer builds a running game’s frame, each transform’s WorldMatrix is where it’s drawn: one that moved in the last step is placed between where it was as the step began (each transform remembers that the first time it moves in a step) and where it is, inside wherever its parent is drawn, so children, the camera and everything pinned to it glide together. What jumped in the step is drawn where it is: added to the scene, moved to another parent, Teleported (respawns and doors do), a camera snapping to its target, a move further than a quarter of the view, a turn of more than 90 degrees or a flip; so is everything on the step a scene came in on, and a game held still (Game.ShowAsIs, which the editor’s pause and rewind use). What’s drawn from a component’s own state rather than a transform goes between steps the same way, by the same rules (StepBlend, from Drawing.Blend or FrameDrawing.Blend): a Smoothed<T> value remembers what it was the first time it changes in a step, as a transform does, and is drawn between that and what it is only if it changed in the last step and its entity (or one it’s inside) didn’t jump. Particles and rain keep where each was before it last moved (and how it was turned and how far through its flutter or flicker), a trail’s copies how old they were, a sprite’s, text’s or tilemap’s tint, a sprite’s flash and a sprite’s and text’s size are Smoothed, as are the screen’s fade and flash, the camera’s zoom, shake, background and turn, the screen effects’ settings, a light’s reach, color, brightness and flicker, the ambient light of a room fading in, a bar’s value and trail, health (for the meters that show it), a beat pulse settling back (a beat landing is drawn as it is), a crumbling platform’s shake, and a first-person viewer’s look up and down and walk’s bob, its weapon’s kick, flash and coming up, and billboards bobbing; debug shapes a step draws are drawn between those of the step before when they’re the same shapes in the same order. A match-3 board’s slides, falls and clears each keep how far along they were as the step began, and the gems that came home or cleared away in the step are drawn the rest of the way; a falling-block board’s piece is drawn between where it was and is when it slid or fell (as it is when it comes in or turns), full rows shrink between steps, and the step they go in they shrink the rest of the way as the rows above drop into their place. Strategy and tactics meters, building and training progress, order markers and notices, and a block world’s digging are drawn between steps too, and every system HUD is pinned to the view as it’s drawn. What changes all at once (a board’s tiles, a sprite’s frame, a turned piece) is drawn as it is. Ropes and wires follow the transforms they join. The step rate is the project’s (Project.StepRate, in project.griff), which every host sets as it starts a game (game.UseProjectSettings(project), with the controls). FixedStepTests plays the same input at 30, 60 and 144 frames a second and with frames all over the place, slow motion and hit stop in it, and gets the same game to the last bit; and a host and a player on screens of different rates stay in step. Each step goes through the game’s frame: a list of steps in eight phases (begin, logic, scripts, simulate, physics, resolve, present, end), taken in order (Game.Tick takes one step by hand, of any length); Game.Frame lists them, and FrameTests pins the order, so changing it is always on purpose. The steps belong to systems: Griff’s keep what a kind of game tracks while a scene runs (levels’ doors and conversations, switches, dragging, panels, and each module’s: battles, block worlds, cards, contraptions, dungeons, falling and match boards, fighting games’ hitboxes, first person, physics games’ slingshots, breakables and pinball tables, puzzles, strategy, tactics) and forget it when the scene changes, and scripts add their own. The scene keeps its components by type as it changes (Scene.All<T>(), Scene.Active<T>(), in hierarchy order, and safe to go through while spawning and destroying; enabling, disabling and destroying things makes no garbage once a game has settled, as the engine’s own lists are filled again two steps after they’re replaced, while a list handed to a script stays as it was however long it’s kept: EntityChangeTests), each entity keeps whether it’s active, and each transform keeps its world matrix until it or something it’s inside moves, so a step finds what it works on without walking the scene; a frame of sprites and scripts allocates nothing, and nor does drawing it into the last frame’s lists (BigSceneTests). A script is called only for the steps its type has (Update, FixedUpdate, LateUpdate), from lists kept until the scene’s scripts change, and only the sprites flashing or blinking are moved on each step. A frame is built in one pass over the entities: each is drawn as it’s found, in hierarchy order, and then the runs of draws are put back to front by what their entities sort by, each run kept together. Entities in order but for a few stretches (a crowd on one layer with a HUD over it) are merged; others are sorted by key (StableSort, a radix sort that skips whatever every entity shares) rather than by comparing them; and the draws move as little as they can (none in a scene already in order; otherwise the biggest block that stays together moves in place and the rest by way of a spare array). An entity knows what kinds of component it has and whether a sprite is all it draws, so the renderer skips the rest without asking each component, and the OpenGL renderer draws each quad as one instance of 80 bytes, in batches of up to 16,384 that each go into fresh buffer storage, so the GPU never waits for the last batch to be read. FastPathTests checks each shortcut gives the same game and picture as going through everything. A big scene’s work is spread across the cores (Workers: the game’s thread and up to 15 others take chunks of the work in turn, so a slower core takes fewer, and what comes out is the same however many there are). The threads are the process’s, but how many a game’s work takes is the game’s (Game.WorkerCount, Workers.Default to start), as whether it plays over the network is (INetwork.OverNetwork, off unless the player or the editor’s Play turns it on): what runs a game sets them for it alone, so two games in one process (Play with Players’ windows, a simulation’s runs, tests side by side) never share a setting, and nothing is set for a whole process. A frame of a scene of 2,048 entities or more is built in chunks of 1,024, in hierarchy order: each chunk, on whichever thread takes it, draws its plain sprites (an entity at the top of the hierarchy whose sprite is all it draws, with no effects or parallax on it: Griff’s own drawing, which reads its entity and writes nothing but its draws, working out where it’s drawn without keeping it) into a list of its own and puts them back to front among themselves, which a crowd on one layer already is, so it isn’t sorted at all. The game’s thread then draws the rest of the entities in hierarchy order (so scripts’ drawables are called as they always were), merges the chunks’ runs back to front a stretch at a time (as many stretches as chunks for a crowd on one layer with a HUD over it; when they’re mixed through each other, as things sorted by Y are, every entry is sorted by key instead), and the chunks copy their draws straight to their places in the last frame’s list, overwriting it rather than clearing it first. A crowd of scripts ([Crowd]) runs a chunk of 1,024 a thread, each script with numbers of its own from Rand (a stream from the crowd’s seed and its place in the crowd), and those inside other entities after, on the game’s thread. CoresTests builds every template’s frames (each scene in the editor at two aspects, and the start scene played for 240 uneven frames with random presses, drawn between steps) and mixed crowds of thousands (layers, NaN and -0 Z, sliced, blinking and flashing sprites, children, effects, parallax, things pinned to the screen, sorting by Y, a few things over and under a crowd, many layers) on one thread and in chunks on one thread and on many, and checks they’re the same frames, draw for draw and bit for bit; plays two crowds of hopping scripts with random kicks on 1, 5 and 16 threads to the same last bit; checks a crowd runs at its place among the other scripts, that each of its scripts gets numbers of its own, that what a crowd script can’t do throws (and changes nothing), that a fault stops the step with the first one in the crowd, and that neither a crowd’s step nor a big frame built in chunks allocates. The editor’s rewind snapshots (Timeline) are the scene file in binary (SnapshotCodec: each property read and written through typed delegates, with no text and no boxing, one record an entity, shared with the last snapshot when it hasn’t changed), and TimelineTests plays every template, rewinds it, and checks each snapshot loads as its scene file would and plays on the same. Physics files shapes in a grid each step (ShapeGrid), so a body or a trigger only tests what’s near it, in the order it would have met them testing everything. A running game advances once for every frame the screen shows, timed by Windows’ compositor (FramePacer) rather than WPF’s render loop, which on some machines only gets every other one. The editor itself is built from modules: each feature registers its services, commands, panels and menus, and the shell assembles them. Services reach each other through dependency injection (there are no static locators), and a window’s or panel’s state lives in its view model, which agents reach. A genre’s editor parts that need WPF (the Fighting Move List and its field editors, the Rhythm Chart panel, the Visual Novels Dialogue Editor, and their parts of the Debug demo player and snapshots) are a folder of the editor each, Genres/<Genre> (namespace Griff.Editor.Genres.<Genre>), the only place in the editor that names that genre’s module; the rest of the editor reaches them through extension points, so it names no genre: their editor modules are found rather than listed (GenreEditors), their templates are merged where they’re drawn (GenreTemplates, each genre’s Templates.xaml), the Clip Editor shows what a genre adds over the frame and beside it (ClipOverlay, the Fighting genre’s hitboxes and hurtboxes), Play keeps on the drawings to debug with that a module’s systems make (IDebugDrawing, which Game → Show Hitboxes switches), a field’s editor opens the window that edits it more fully (IEditsFields), a stat picks from what its attribute names ([Stat(typeof(Card), typeof(Health))]), and the demo player and snapshot runner take each genre’s commands and pictures (DemoGenre, SnapshotGenre). What needs no WPF stays in the module’s own editor parts (Add Breakable is the Physics Games module’s EntityAction). What kind of field a property is (a sound, a name, an editor of its own…) is worked out once, from its type, attributes and name (PropertyDescriptor.Kind), and the Inspector, component_describe and the tests all read it. The Scene view, for one, is the SceneView module: SceneViewModel (what it does with the selection, and painting), SceneViewport (where it looks) and TilePalette are services, and SceneViewControl only puts together drawing, where the handles are (SceneHandleGeometry) and the mouse and keys (SceneViewInput). The scene itself is drawn by the game’s renderer: SceneRenderer.BuildView builds the frame of the part of the world in view from the same drawable components, culled and sorted as a game’s (with none of what a camera adds: no dark, lights, screen effects or viewports, and parallax layers where they’re placed), the grid goes under it in the same frame, and SceneViewPicture draws it off the screen with the OpenGL renderer the Game view uses and shows it as a bitmap, drawn again only when the scene, the view or the theme changes. WPF draws only what goes over it (SceneViewDrawing: camera frames, markers, lights’ reach, colliders, links, the selection, handles and the tile palette, each only where it’s in view), so a new drawable has one implementation and looks the same in the Scene view as in the game. Every document the editor changes has one undo history, an EditHistory, behind IDocumentEditor: the open scene or prefab (which the Scene view, the Hierarchy, the Inspector and the Clip Editor change), a table (the Tables panel), a simulation, a rhythm chart and the Sprite Painter’s sheet; ActiveDocument follows which was changed or had the keyboard last (a panel or window whose view model is one, found by itself), and Edit → Undo and Redo, their shortcuts and agents’ Edit_Undo go there, so no view routes Ctrl+Z itself. Tables and simulations are files of one kind kept loaded by one FileStore each (TableStore, SimulationStore): it watches their folder, knows what it last wrote so its own saves aren’t read back as changes, reads a file changed outside again as a step of its history, and saves each change straight away. What a right-click menu does is a command placed on it (EditorCommand.ContextPlacement), given what it was opened on (Target, which agents name): the Inspector’s view builds a component’s menu and a changed field’s from those commands (Component.Reset to Component.ApplyToPrefab), and its view model (InspectorPanel, with ComponentSection, PropertyField and the rest a file each) does the work, reading placed copies’ prefabs through the cache PrefabFiles keeps. See src/Griff.Editor/Themes/README.md for the design system.

Every host plays a game the same way. It makes the game with LoadedGame.NewGame (a game’s content: its components, its start scene, how it loads its other scenes and prefabs, and the project it came from), handing it what only the host has in GameHostOptions: the input its window or page feeds, the sound it plays through, where saves go, whether it plays over the network, and where problems are said. The player (windowed and as a dedicated server), the web player, the editor’s Play and Play with Players and a balance simulation’s fights all make their games this way, so a game starts the same everywhere. The core’s GameLoop (Griff.Hosting, the one namespace above both the runtime and rendering) then plays it a frame at a time: the fixed steps the frame’s time holds, its picture built and drawn on the host’s IGameView, sound pumped for a host that plays it a frame at a time (IFrameSound, the browser’s Web Audio), a saved game’s picture, and an error that ends a frame caught and the game paused on it (or held where it was thrown by the editor’s debugger). The hosts keep no copy of it: WPF’s GameRunner only hands it a GameSurface, the keyboard, mouse, touch and controllers and holds the mouse for a first-person game; the browser hands it the canvas, the page’s input and Web Audio; and a dedicated server (--server) runs it with nothing to draw on, as a console program that never loads WPF (GameLoop.Run). Sound is read from Assets by one library on every host (AssetSoundLibrary: WAV, and OGG with NVorbis; MP3 with Windows’ codec on the desktop and decoded by the browser as a web game starts) and played through the host’s own SoundOutput (AudioOutput.Open), the editor’s a service of its own and the player’s its own, never a static of the process.

What a game needs of the machine it runs on comes through interfaces its host hands it, so another platform is a few classes. Its files come from an asset source (IAssetSource, Griff.Projects), which the renderers, the fonts and the sound library read them through: an exported game’s are a folder that doesn’t change (AssetFolder), each file read when it’s first wanted and never looked at again; a browser’s are the files fetched as the page opened (PreloadedAssets); and the editor’s are watched (WatchedAssetFolder, the open project’s IProjectSession.Assets), which says a file written, made, renamed or deleted once it has settled, so a sprite saved from a paint program or a sound remade in the Sound Maker is drawn and heard again in the game playing. Nothing that reads a game’s files checks their times. Its input is one InputState seen two ways: gameplay reads IInput (Game.Input), and the host drives IHostInput (Game.HostInput): the keyboard, mouse, touch and controllers as they happen, its frames, the project’s controls and the player’s rebinding kept in the saves, and the rumble, pointer and held mouse the game asks for back. A platform feeds that and nothing else, so a new one keeps the actions, rebinding and local players every other has, and nothing casts a game’s input. Its network play goes over a transport (INetTransport, INetwork.Transport, handed in GameHostOptions.Transport): UDP (UdpTransport) unless the host hands another (a MemoryNetwork in tests), so a browser’s is one more class. The OpenGL renderer runs wherever OpenGL does: the Windows contexts it draws in (a window’s, WglContext, and the off-screen OffscreenGL the Scene view, benchmarks and pixel comparisons draw in) are Griff.Platform.Windows’. And the editor’s own hooks in a game’s loop are extensions it adds (IGameLoopExtension, GameLoop.Add): its debugger (DebuggerLoopHooks: a game held where a script threw or stopped, the time it stood not owed) and an agent’s playtest pointer (PlaytestPointer), so an exported game’s loop has none and its player carries none of their code.

Making a game and playing one are apart: Griff keeps what a running game needs, and what only making one needs is its own assembly beside it, Griff.Editing (edits and undo, prefab copies and overrides, the names a field may hold, the Entity menu’s presets and actions, the component catalog, and New Project’s templates with their art and sounds) with the agent surface on it, Griff.Agent (the MCP server, the scene and asset tools, playtesting). So does each genre: its running parts are an assembly of their own, Griff.Modules.Match3, and its editor parts another beside them in its folder, Griff.Modules.Match3.Editor. A program plays with the module assemblies it has: those beside it, found by name (ModuleAssemblies: the editor, the tests and the tools reference every one through src/Modules/Griff.Modules.csproj), and those an exported game carries, which its host loads before it reads the game; the module classes, the components every game knows and the dialogue tags modules do are worked out from these rather than by scanning Griff, and the editor finds templates, presets and entity actions in Griff.Editing and the modules’ editor parts (EditorModules). A game uses the modules its project does (ProjectModules: those its scenes and prefabs have a component of, and those its scripts were built against) and builds the systems of those alone (Game.Modules, LoadedGame.Modules); its scripts get a using for each; and its export carries their running assemblies in its game folder’s Modules and nothing of any other module’s or of the editor’s. The players bundle Griff and no module, so an export of a game with no genre in it carries no module at all.

Scripts are compiled by Griff.Scripting, which holds Roslyn’s compiler and nothing more: ScriptCompiler and ScriptHost, which loads a build into the editor’s games and swaps it for the next. That’s all a game played from its sources and the exporter need. Exporting (Griff.Exporting: GameExporter, WebExporter, the WebServer a web export plays from, ThirdPartyNotices and ExportFonts) sits on the compiler, so a tool that only exports (tools/web.cs, tools/bunnymark.cs, tools/release-notices.cs) pulls in no Roslyn Workspaces. The desktop exporter asks the platform what a program is called (DesktopPlatform.ExecutableName: .exe on Windows) rather than writing an extension itself. The editor keeps what only it uses: the script editor’s language services (ScriptLanguage, Roslyn Workspaces and Features, with the reference assemblies’ documentation for its tooltips) and the debugger’s instrumenter (ScriptInstrumenter, ProbeCompiler, ProbeTable), which builds scripts with a probe on each line through ScriptHost.Compiler and is attached to the loaded build as its IScriptAttachment; it registers the scripts’ /// comments as they load (ReloadResult.Documentation). AssemblySplitTests keeps it so: neither the compiler, the exporter nor either player references Roslyn Workspaces or Features, the players carry no compiler, and the exporter writes no .exe of its own.

Each step goes through the frame (Game.Frame): Griff’s own steps in phases, in the order BuildFrame lists them (FrameTests pins it), with each module’s put in beside the steps it names. Every step is the work of a system, and Game is only the scheduler (Game.Frame.cs: the frame, adding and removing systems, stepping) and what every system shares (Game.cs: the scene, time, physics, sound, save data and the helpers they all use; Game.Players.cs: the players, their input and the network session). Griff’s own systems in Griff.Runtime each own their state and their steps the way a module’s or a script’s system does. Gameplay with no code: LevelSystem (movers, patrols, chasers, shooters, checkpoints and respawns, doors, and dialogue with the flags it sets), MovementSystem (platformer and top-down players, bouncers, crumbling platforms, and what crates and players are to physics), GridSystem (grid movers, their chases and patrols and the crates they push), TurnSystem, CombatSystem (attacks, touch damage, health), TriggerSystem (actions, triggers and scopes), SwitchSystem (switches and the keys doors take), ItemSystem (pickups, item uses, giving and paying) and DragSystem. The rest of what a game does: ClockSystem (time moving on, hit stops and slow motion), InputSystem, NetworkSystem (the session at either end of the step, players’ and the host’s commands, other players’ tools), ScriptSystem (scripts’ Start, Update, FixedUpdate and LateUpdate, crowds of one kind across the cores), TimerSystem (timers, coroutines and tweens), MessageSystem, LifetimeSystem (lifetimes, spawners, destroying), EffectSystem (animations, particles, camera shake, sprite effects, trails, followers, flashes), TilemapSystem, HudSystem (each system’s HUD, bars, stars, counters, text outputs, typewriters, screen anchors), PhysicsSystem (the physics world of the scene being played, which it steps and starts afresh for each scene), SceneSystem (scene changes and fades, carried values, remembered scenes, arrivals), MenuSystem, FocusSystem, PanelSystem, PauseSystem, ReadingSystem (skipping, reading on, logs), BeatSystem, CalibrationSystem, AtmosphereSystem (rooms, lights, sound sources and players), WeatherSystem, LobbySystem, ViewSystem, SaveSystem, SettingsSystem, TableSystem and MinigameSystem. What scripts call on Game (StartDialogue, Give, LoadScene, SaveGame, OpenPanel, After, Send…) stays there, each member a one-line forward to its system, in a file of Game for each area (Game.Panels.cs, Game.Saves.cs…). A genre’s settings are its module’s: the Rhythm module keeps the player’s note speed and the Visual Novels module how they like dialogue skipped and read on and the lines they’ve read (IPlayerSettings, IReadingPreferences), under the same keys in the save data as before, and Game.NoteSpeed, Game.SkipUnread, Game.AutoSpeed and Game.HasRead are those modules’ extension members.

A running game has one owner of its state: itself. What it keeps besides its scene is in its parts’ fields: the game, its clock, physics and every system (each an IKeepsState), and each component’s own fields that the scene file doesn’t hold (a script’s private fields, a body’s time in the air, a board’s random numbers); a field that isn’t state (a link to another part, a cache, a list kept only to use again) says so with [NotState]. A rewind snapshot (Timeline) keeps the scene’s records and, with them, that state (GameState, written by StateWriter as the snapshot goes through the entities, read back by StateReader): numbers as bytes, the entities and components it points to by id (put back as the restored scene’s own), and the objects it holds (lists, dictionaries, records, closures, coroutines) copied field by field. Rewinding puts the same Game back (Timeline.Restore(index, game), as the editor does), so timers, tweens, coroutines, systems scripts added, dialogue seen, a swing under way and every module’s state go back with it, and it plays on to the same last bit as before. Random numbers are the game’s own (GameRandom, the sequence a seeded Random gives, with its state in fields): Rand draws on the numbers of the game whose step is running on the thread, two games on one thread don’t share them, and a crowd’s scripts draw from a seed the crowd takes from them. Decisions are made in a fixed order, never a hash set’s: what touched what and what’s in each trigger are EntityPairSets, in the order physics found them, and what the drag system holds is kept in the order it was picked up.

Physics knows bodies and colliders, not what they are to the game. The solver (Physics, in Physics.cs and its parts) works out bodies, colliders, hinges, ropes and the tilemaps they meet, and names no other component: what a crate or a player is reaches it as the body’s own settings, which the system that owns them sets just before each step (the frame’s Body Roles step: MovementSystem makes a Pushable crate’s body give way to what pushes it, Body.GivesWay, unless a grid mover pushes it a cell at a time, and a player’s body hold ropes, Body.HoldsRopes, so a rope hangs in front of it to grab), and what it moved reports back for the systems and scripts to react to: what touched what (Game.Touches), the hard knocks (Physics.Impacts) and what shoved what (Physics.Pushes). The world belongs to the scene the game is playing: PhysicsSystem owns it (Game.Physics is it), steps it and starts it afresh when the scene changes, and its questions (Raycast, OverlapPoint, OverlapBox, OverlapCircle, IsOnSlope, OnlyOneWayBelow) are about that scene rather than one handed in each time. A tilemap whose cells aren’t square boxes doesn’t make physics branch on its shape: it hands physics its walls (Tilemap.Walls, a TileWalls made from its Shape: IsometricWalls, HexWalls), which say how a moving body is kept off them, what rays and overlaps meet, the cells a turning body touches and whether bodies meet them with their own shapes or with footprints (an isometric map’s walls are on the ground, met by the diamond under a body, and ropes hang over them). Physics asks every kind the same questions, so a new grid shape’s walls are one more class.

Drawing and networking are open to every component, so the renderer and the network schema know no genre by name. Each component that draws is an IDrawable (its draw order, and its step among its entity’s draws), changed by its entity’s IDrawEffects, and a running game’s systems draw what the game itself knows of (GameSystem.Draw): a strategy game’s fog and HUD, a tactics game’s reachable cells, a block world’s hotbar, the building tools, and (in DrawView, before the rest) a first-person view in place of the top-down one. Each component says what it sends over the network with attributes ([NetLocal], [PlayerMovement], [Sent], [NotSent]) or INetState, and keeps its entity from players who aren’t to see it with INetSecret (a card in a face-down deck or another side’s hand). SceneRenderer keeps only what a view is made of: cameras, parallax, light and dark, and what’s pinned to the screen.

Drawing is behind one interface, IRenderer (Griff.Graphics): a renderer draws a RenderFrame into whatever is bound (a window, a framebuffer, a canvas) and can read the pixels back. What a frame becomes is worked out once for every renderer, in DrawList (built by QuadRenderer, the renderers’ base): its quads (80 bytes each, their corners already in clip space, the part of the texture, tint and flash) in draw order, and the passes that draw them (DrawOp): runs of one texture’s sprites, text a quad a glyph from the glyph atlas’s pages, holes, the dark (a light map the size of the screen started at the ambient light, the lights without shadows added up in it together, each light with shadows after its shadows mark a stencil, then multiplied over everything but the HUD), and screen effects (the frame drawn into a picture, then onto the screen through them), with the colors and settings they take worked out and clamped there too. QuadRenderer also numbers the images a frame draws and loads each once (again when its file changes), and hands over the rows of glyph pages text added glyphs to before the frame is drawn. A renderer for a graphics API only runs the passes: OpenGLRenderer (Griff.Graphics.OpenGL) on the desktop, and WebGLRenderer in the browser, which hands the page the draw list in one call a frame for griff.js to run the same way with WebGL 2. The shaders are written once (Shaders, GLSL with a header for each: OpenGL 3.3, or GLSL ES 3.00 for WebGL), and both run them at full precision, so they draw the same pixels: tools\web.ps1 -Compare draws every template’s start scene, and scenes of lights, shadows, screen effects and a viewport, both ways and compares the pictures. The passes name no API’s state, so Direct3D 11 is one more class that runs them (with the shaders in HLSL), and textures drawn into are flipped by each renderer as its API needs. A camera drawn into part of the screen is a view: a View pass clips drawing to its part (clip space stays the whole screen, so its frame is projected there), then its background and its frame’s passes, lit by itself if it’s dark (RenderFrame.Views, drawn by DrawList.AddView). DrawListTests pins the passes (and that griff.js has no shader of its own and numbers the passes as DrawOp does), and a frame of text that changes and shadows makes no garbage.

A genre is a module: everything of it in one folder under src/Modules with a namespace to match (Griff.Modules.Match3): its running parts in the folder (its components, its system, which runs it, keeps what it tracks and draws what no one entity does, and its network commands), an assembly built on Griff alone, and its editor parts in its Editor folder (its template with its pictures, and the Entity menu’s presets for it (EntityPreset, listed in a submenu named after the module by its [Category]) and things it does to the selection (EntityAction, such as Generate Dungeon), which the editor makes into commands and agents’ tools), an assembly built on the module and Griff.Editing. Every genre is a module: Adventures, Battles, Block Worlds, Cards, Contraptions, Dungeons, Falling Blocks, Fighting (hitboxes and hurtboxes on animation frames), First Person, Match-3, Physics Games (slingshots, breakable things and pinball), Puzzles (goals and undo for grid puzzles), Strategy and Tactics. A module registers once, in its GameModule class’s AddTo: its systems, and each of their steps next to the step of the frame it follows or goes before (StepAfter(Steps.Dragging, "Match Boards", ...), a step named by a typed id, not its label, so a step that is not there cannot be named), so the frame keeps its order; the commands players’ machines send its host (OnNetCommand), and those the host sends one player’s machine (OnHostCommand). Everything else of it is found. The core names no module, and no module names another, so each stands alone: Griff’s own components reach a module’s through interfaces they call (a Counter counts any component’s [Output] by name, grid movement asks an IGridDriver for its steps, turns tell ITurnReady components a turn has begun, Game.Send tells IHearsMessages components, building tools leave the keys a component claims with [ClaimsKeys] to it, IPlaytestReport adds to what an agent playing the game is told, a module’s component lists itself on a core component’s Works With, [WorksWith(..., Back = ...)], and the Scene view edits the points of any IPointOutline, a polygon collider’s and an adventure’s walk areas and hotspots alike), and the game asks its systems through small interfaces they opt into, found once when a system is added (IMouseOnHud, IClaimsBack, IPlayersListener, IPlayerSettings, IReadingPreferences, IViewDrawer, ISeesThroughEyes, IHudLayout, INoticeSource, IPlayerTools, IGridStepListener, ISceneEndListener), while GameSystem keeps the frame’s own contract (Update, SceneChanged, Draw) and a module’s system is scheduled by its steps alone. Turns, grid movement, switches, inventories and trails stay in the core: Griff’s own components build on them (grid movers step a turn at a time, attacks use a turn’s actions, a door takes a key, a dash leaves a trail) and so do several genres, so each is a foundation with its own extension points rather than a genre. A script component or system can use every one of them, and a module uses only what a script could: everything it asks of Game and the other core types is public and documented. What a module offers scripts as part of Game is in extension members (Game.Train, Game.Selected, from StrategyGame), and scripts get the namespace of each module their project uses without a using (another is a using Griff.Modules.Strategy; away); components keep the names they’re saved under ([ComponentName("Griff.MatchBoard")]), so scenes made before the move load as they were.

The core keeps the foundations several genres build on, and what the engine is made of: scenes, sprites, text, the camera, physics, input, audio and the editor’s data. On top of those it keeps the ideas more than one genre uses: turns, grid movement and the things that push or step on it (a Pushable crate is pushed by a platformer’s body, a top-down one’s and a puzzle’s grid mover), switches, inventories (items are held by anything), trails, and the beat (BeatClock, which anything beat-driven reads, in any genre). A module keeps what only its genre has: the puzzle’s Goal and MoveHistory, the adventure’s InventoryBar (a view of an inventory) and the rhythm game’s NoteLanes (a view of the beat). When a piece is used by one genre, it is that genre’s, and the core reaches it only by an extension point that serves any component; when a second genre needs it, it moves to the core with a file of its own (Switch.cs, Inventory.cs, Trail.cs in Scenes; a module’s components are in its own folder, such as Modules/Puzzles/PuzzleComponents.cs). The test is in ExtensionPointTests: no core system names a module’s component, and each extension point has more than one user.

Components and templates are found rather than listed. Every component in Griff and in the modules a program has is registered without being named anywhere (ComponentRegistry.CreateDefault, which scripts’ components join the same way on each compile), and every ProjectTemplate (a class with its description, its place in New Project’s list and what it builds: scenes, prefabs, scripts, pictures and music) is offered by New Project and played by the template tests (ProjectTemplates.All). Adding either is adding a class. Scene data outlasts changes to them: a component can say what it was called before ([FormerName]), version its data with upgrade steps ([ComponentVersion], [UpgradeFrom]), and keeps values it has no property for. ComponentSwap is what hot reload uses to rebuild script components in place when their types change, and what the editor keeps of a script’s types it keeps only while they live (the samples KnownNames reads fields’ defaults from are in a ConditionalWeakTable), so a reloaded script’s assembly unloads.

Tests keep the architecture in shape, so a change that breaks it fails rather than slipping in:

  • Layers: nothing in Griff.Scenes (entities and components) uses Griff.Runtime or Griff.Rendering, nothing in Griff.Runtime or a module uses Griff.Rendering, and a module’s components use neither nor its systems; every type a type uses is followed through the layers, so a detour through another namespace (a Scenes type using an Editing type that uses the Runtime) counts (LayerTests). Each IGameService is provided by exactly one system (never the Game or a component), no component of the data’s or a module’s keeps a service in a field, and a script is reached through no internal interface of Griff.Scenes (LayerTests). Code outside a module never compares a type’s name with a string: it reaches a type through an interface or attribute (the places things arrive at, an adventure’s hotspots, are found by IArrivalSpot), where the rules see it (TypeNameTests).
  • Names: what scenes name by name stays safe: [FormerName] also renames actions and outputs, so a trigger, button, counter or wire that names the old one keeps working; every action, output and wire name in every template’s scenes resolves, and one that doesn’t is flagged by NameChecks in the Inspector, scene_tree, the loader and Output as a game starts; a dialogue tag word is written once, in the component’s [DialogueTags], and the module only says what to do with it (OnDialogueTags<T>), a word two components (or Griff’s own if, set, clear, send) claim fails; no entity name is a convention the core relies on (a sprite’s TeamColor component and a view’s IViewFocus, such as a camera’s Follow, rather than entities called Team Color and Player); components’ saved names (and the names they had before) are unique, and so is every type name a script sees through its implicit usings (NameSafetyTests).
  • Finding by name: a name finds an entity one way, EntityNames: it is trimmed and matched exactly; what is inside the entity that holds it comes first (a lever’s own “Hint”), then the scene, where one in the holder’s part of it (the minigame being played, or outside them) comes before another and, in a network game, this machine’s player’s own; a component that wants the nearest says so (a weld, a hinge, a rope, a chasing enemy), which skips the local-player rule. Scene.FindNamed, Game.Find and FindAll, triggers and message scopes, counters’ and bars’ tracking, a switch’s targets, renaming and the Inspector’s links all ask it, so the same text means the same entity in the running game, in renaming and for agents. A field that needs one entity (a camera’s Follow, a wire’s From) and holds a name several answer to is flagged in the Inspector, listed by scene_tree and said in Output, unless it names several or means every one ([EntityName(Many = true)], Every = true). No code outside EntityNames compares an entity’s name with one it was given (EntityNamesTests, which also checks every way agrees and renaming leaves alone a field that means another).
  • Component model: what a component exposes is worked out once per type, in its ComponentModel, and every part of Griff asks that: what’s saved (the serializer, the Inspector), its actions and outputs (triggers, wires, counters), focus, its area, what a network game sends, what a table’s row and a card’s effect may set (what’s saved and takes a table’s value, so a card never raises a number the component keeps to itself), and what agents read and set (what’s saved, and what it says while the game runs); no code outside the model looks up a component type’s properties, methods or fields by reflection (ComponentReflectionTests), and ComponentModelTests checks every component’s consumers agree with its model. Every cache keyed by a type holds it weakly (a ConditionalWeakTable; no static dictionary or set in any of Griff’s or the editor’s assemblies holds a type or its members, WeakTypeCacheTests), and a scene drops its lists of the kinds a reload replaced, so scripts compiled again leave the old ones to unload: a script’s component every part of Griff has asked about, in a scene a game has played, goes when it’s compiled again.
  • Mistakes: a mistake in a game’s data (a prefab that won’t load, a name that finds nothing, a bad row, a chart that doesn’t read) warns, once, and the game goes on; a mistake in code throws. Data warns in one place, the game’s ProblemLog (Game.Problems; the scene loader, the game loader and a simulation’s runs report to one too), as a Problem that names the entity and component it was found on: Output says it, the Inspector flags the component (a run’s problems, as long as it plays), scene_tree lists them for agents, and a script asks Game.Warn(this, ...). A system or script spawns what data names with Game.TrySpawn (or TryLoadPrefab), which warns and gives null where Game.Spawn throws, so a missing prefab no longer throws in one module, warns in another and goes unsaid in a third; nothing in a module or a running game’s system reaches for the prefab loader or has a catch that does nothing (ProblemTests). The pinball Tilt’s warnings are the table’s tilt counter, a rule of the game, not a diagnostic.
  • Small formats: the texts a field is typed in are each read one way, shared by the game, the field’s rows editor and its check: names (“Bullet, Enemy”), points (“4 0; 4 3”) and kind counts (“0:20, 3:15”) by Formats, items and amounts (“150 Gold, Wood 50”, “Rope + Hook”) by Items, item uses by ItemUsesAttribute.Read and tile numbers and animations by Tilemap. A text is read once for as long as the very string is held and the result is shared, so physics and the systems that read a list each step make no garbage; running state isn’t kept as text (a Match-3 board counts the gems cleared in numbers and writes the text only for the scene’s file, so files keep the same text and no old scene needs an upgrade). A part that doesn’t read is left out by the reader and flagged by the field’s editor, and a game says so in Output, naming the entity and component, as it starts (NameChecks runs every field editor’s check, not only names). FormatsTests keep it: no code splits a text into names or points itself.
  • Module API: a module uses only what a script’s system could: every Game member, component and service it touches is public and documented, none internal to the core, which the compiler keeps (a module is an assembly Griff opens no internals to), and the frame’s steps are distinct typed ids (ModuleApiTests).
  • Extension points: every extension point serves any component (public, said so in its documentation, and used by at least two components, a genre’s or a test’s: IHearsMessages, ILightSource, INetSecret, IDialogueStarter, IDrawsTilemap, [ClaimsKeys]), a counter knows no genre (it counts an output, so a puzzle’s goals and moves, a board’s score and a dungeon’s floor are outputs), and the renderer, the view lookups (Views), the frame systems draw into and the network schema name no component but those a view or an entity’s networking is made of, and no core system (nor Game) names a module’s component (ExtensionPointTests).
  • Physics: the solver names no component but bodies, colliders, hinges, ropes, the tilemaps they meet and Transform (the Physics category, found by its attribute), and no grid shape (neither TileShape nor a kind of TileWalls); every grid shape but the square has walls of its own (PhysicsBoundaryTests).
  • Networking: networking is a service, INetwork (Game.Network), provided by the network system, and a game has a network session (NetSession) only while it’s networked: the system makes it when the game hosts or joins and lets it go when that ends, keeping why in Status. Everything the service says has an answer offline, so a system or module asks it without checking first, and no module names the session. The session reaches the game only through the network system, which asks the system that owns each part (the scene, spawning, messages, the scene the host loads); it names neither the Game nor any other system (NetworkBoundaryTests). What runs for each other player on the host is handed that player and their controls (IPlayerTools.RunForPlayer(player, input), INetwork.RemotePlayers), and Game.Input is always this machine’s own, so a system keeps each player’s things apart by whom it was handed rather than by asking whom the host is acting for.
  • Systems: every step of the frame belongs to the system that owns its state and runs that system’s own code, none of them a method of Game; every one of Griff’s systems keeps its state in private fields; and Game is the files that schedule and share (named in the test), the rest of it scripts’ API that only forwards: no state, logic or private member (GameSystemsTests).
  • Game state: every field of the game, its clock, physics, every one of Griff’s systems and every component is in its scene file or a rewind snapshot, or marked [NotState] (GameStateTests, which also checks two games on one thread draw their own random numbers and a rewind takes them back); every template rewound partway plays on with the same input to the same last bit as the first time through (TimelineTests); and every template played with the same presses ends where it did, its components’ values to the last bit (TemplatePlaythroughTests, whose fingerprint reads what the components hold rather than how a file writes it, so a change to a file’s format leaves it alone).
  • Genre modules: who may name whom is the compiler’s to keep, by the assemblies each references: Griff references no Griff assembly (so no module, nor Griff.Editing or Griff.Agent) and opens its internals to the tests alone; each module’s running parts reference Griff alone, and its editor parts that module and Griff.Editing; the hosts and libraries a game runs in (Griff.Web, Griff.Player, Griff.Platform.Windows, Griff.Scripting, Griff.Exporting, Griff.Graphics, Griff.Graphics.OpenGL, Griff.Fonts) and the editor’s libraries (Griff.Editing, Griff.Agent) build on no module (the web player’s warm-up gets a module’s part through GameModule.AddToWarmUp, and its probe reads any component’s field), and the players build on nothing of the editor’s (ModuleTests). Each module keeps to its own folder and namespace with its two projects and one module class, and a module’s components hold their own state like the core’s, using neither Griff.Runtime, Griff.Rendering nor its systems (ModuleTests, LayerTests, which bind the core’s and the modules’ sources together). An exported template carries only its own modules and no editor assembly (ProjectModulesTests, PlayerSmokeTests). In the editor, only a genre’s folder names its module, no generic type of the editor declares a member of a module’s type, and a genre’s parts are found rather than listed (GenreEditorTests); the Inspector and component_describe give every field the kind its descriptor says (FieldKindTests).
  • Agents: every command is an MCP tool with a description, every panel and service is reachable (AgentSurfaceTests), and no view (any class of the editor’s that is a WPF element, found by its base type rather than its file’s name) runs an edit, starts a process or undoes from a key handler, where agents couldn’t reach it (CodeBehindTests).
  • One undo: every document the editor changes (the scene or prefab, a table, a simulation, a rhythm chart, a sprite sheet, the clips in a scene) keeps an EditHistory behind IDocumentEditor, and ActiveDocument sends Edit → Undo and Redo, Ctrl+Z and agents’ Edit_Undo to the one changed or worked in last; no panel or window has an undo command of its own (OneUndoTests, and each panel’s tests undo through it).
  • Inspector: its view model and the types it’s made of are a file each, its view models build no menus (the view builds a component’s menu and a changed field’s from the commands placed on them) and read placed copies’ prefabs through PrefabFiles (InspectorStructureTests).
  • Editor modules: the shell registers none of a feature’s types (each module registers its own), and no editor service sets a static of itself for others to reach it by (SceneViewStructureTests).
  • Presets and services: everything the Entity menu creates is an EntityPreset (Griff.Editing.Presets for the basics, the UI, the characters and the audio, each module’s editor parts for its genre), so templates, tests and agents without the editor use them too, and the editor makes each one a command; no create command is written by hand. The editor’s services take what they use in their constructors: none asks an IServiceProvider at run time (a Lazy<T> for what can’t be made first, a Func<T> for a window made new each time), none casts the application’s main window (they take EditorWindow), and commands are reached by their class (CommandRegistry.Get<HideEntityCommand>()), not by an id string. The workspace is four faces, IProjectSession, ISceneDocuments, IPlaySession and IScriptSession, and a service takes the ones it uses; the script editor’s font size is in the settings (HonestServicesTests, and the Debug-only snapshot runner and demo player, which drive the editor by command id as agents do, are left out of its rules).
  • Scene view: the Scene view’s picture of every template’s scenes is the renderer’s frame of them, pixel for pixel (SceneViewPictureTests), that frame is the game’s own, draw for draw (EditorViewTests), no Scene view code draws a component itself (SceneViewPictureTests), and panning and zooming over 65,536 tiles and 4,000 sprites stays quick (SceneViewSpeedTests).
  • Players: a template is exported with both players and run for real; it has to draw a frame (PlayerSmokeTests). Games side by side on one thread, as Play with Players runs them, draw the frames each would alone (SideBySideTests).
  • Hosts: every host makes its games with LoadedGame.NewGame and none keeps a copy of the loop (no host takes a game’s steps or builds its picture itself: HostTests); the loop takes its steps, draws, pumps the sound, takes a save’s picture and pauses on an error the same with a view or none (GameLoopTests); a dedicated server runs without loading WPF (PlayerSmokeTests); and no sound output is a static of the process (PlayHostTests); and what a game needs of its machine comes through interfaces: nothing that reads a game’s files checks their times, nothing casts its input to InputState, its sockets come from its transport, the OpenGL renderer makes no Windows calls, and the debugger’s and agents’ hooks are loop extensions only the editor has (HostInterfaceTests, with AssetSourceTests and GameLoopTests playing them; the runtime’s prefab library and the scenes it reads with count among what reads a game’s files).
  • Prefabs and saves: a prefab is read once into a fixed template (EntityTemplate) and every spawn copies it, setting what loading its file would, so spawning reads no file and parses nothing, in exported games too; the template is made again only when its file changes (as the game’s IAssetSource says: never in an export) or scripts are reloaded (ComponentRegistry.Revision). One PrefabLibrary is safe to spawn from on every thread at once, so a balance simulation’s games on all the cores share the editor’s, which plays and simulates the open project with one library and learns of a prefab changed outside it from its Changed (PrefabTemplateTests, and every template still plays to the same fingerprints). Each placed copy remembers the version of its prefab it was made from (Entity.PrefabVersion, kept once per version in the scene or prefab file and on the clipboard), so bringing it up to date, whenever and however its prefab changed, merges from that version. SaveData is written off the game’s thread, changes made meanwhile together in the next write; a failed write is said in the game’s ProblemLog, and Game.Stop waits for the last (SaveDataTests).
  • Renderers: a frame is worked out once for every renderer (DrawList), the browser’s page has no shader of its own and numbers the passes as DrawOp does (DrawListTests), and the OpenGL renderer draws sprites, the dark and screen effects with no garbage (OpenGLRendererTests); tools\web.ps1 -Compare checks the browser draws the same pixels.

Neither the layer test nor the code-behind test has exceptions left.