Implementation status¶
The rest of this documentation describes the engine as a whole. This page says which parts of it are built, so that nobody follows a guide for code that is not there yet.
Last verified: 2026-08-19, on master.
Packages¶
| Package | State | Notes |
|---|---|---|
good |
Working | The kernel is real and tested — ECS, memory pool, ring buffers, scheduler, scenes, hierarchy, input, assets, coroutines, timelines, GameView |
goo2d |
Working | Transforms, world transforms, camera, colliders, sprite rendering, mouse picking, audio assets |
good_cli |
Working | create, generate, assets compact, assets pack, build windows/linux/android/ios. Verified end to end |
goo2d_ffi_box2d |
Working | Box2D v3.1.1 vendored, shim written, bindings generated, builds on Windows/Linux/Android/macOS/iOS |
goo2d_physics_box2d |
Working | Bodies, colliders, the nine joints, effectors, raycast and overlap queries |
good_net |
Working | Messages, targets, channels, sessions, roster events and LoopbackNetTransport, with a conformance suite every backend is run against |
good_net_p2p |
Working on a LAN | A real UDP protocol — acks, retransmission, ordering, batching, fragmentation, keepalives — plus address-carrying join codes and LAN discovery. Does not cross the internet yet: that needs STUN and a rendezvous |
goo3d |
Transforms only | Transform3D, composed WorldTransform3D and Camera3D are real and tested. Nothing draws: there is no renderer, no mesh, no material and no light. Not on pub.dev, and good create --3d is still refused |
goo3d's siblings |
Not started | goo3d_physics_box3d and the rest. The kernel split exists so they land without touching the shared half |
Verified end to end¶
These were actually run:
good create→flutter analyzeclean →flutter rungood assets compacton a real image, with ffmpeg auto-downloadgood generateproducing a populatedTexturesenumgood assets packwriting an encrypted chunk and the mappinggood build windowsproducing a launchable application whose bundle contains the chunk and not the loose asset- The
NetTransportconformance suite against both backends — the in-process one and real UDP sockets - Two
Games in one process, each with its ownP2PNetTransport, playing a round over loopback UDP: a client request handled on the host, the host's answer handled on both - The reliable channel delivering 30 messages in order across a link throwing away 30% of everything
Not yet implemented¶
Deferred, and documented in place instead of left as silent gaps.
Engine¶
- Array-typed
DataDescriptorfields in the codegen path - Z-ordering and culling beyond declaration order and the
zIndexsort - Dependency-based system ordering —
compareTois the mechanism today goo2d's transform helpers are still on the mixin.Transform2D.lookAt,distanceToand the rest take the entity as an argument (transform.lookAt(entity, x, y)). The accessor form the guide teaches,entity<Transform2D>().lookAt(x, y), isgoo3donly so far.- Audio playback.
AudioClipdecodes and the asset pipeline ships audio, but there is no audio backend, no mixer and no voice management. The asset half is done; the playback half is not. - The 3D renderer.
goo3dcomposes transforms and carries a camera; the half that draws is a native backend behind our own C shim and is not written.Renderable3D,MeshAsset,MaterialAssetandLight3Ddo not exist yet, so the 3D rendering guide describes them ahead of the code.
Tooling¶
- Struct-layout codegen.
good generatewrites the asset bindings. Hoisting the runtimeDataDescriptorlayout algorithm to build time by scanningComponent/EntityStructwithpackage:analyzeris a separate and much larger piece of work, and emitting a stub would make it look done. good build macos— run the pipeline steps andflutter build macos.good run— useflutter run.
Networking¶
- Crossing the internet.
good_net_p2pjoin codes carry the host's address, which is enough on one machine or one LAN and not enough through a home router. STUN (learning a peer's public address) and a rendezvous (swapping those addresses so both sides punch at once) are the next landing. - A relay fallback. Hole punching fails when both peers are behind symmetric NAT. "Free, no server" and "always connects" are not the same claim, and a TURN-style relay is a separate, clearly-scoped addition.
- Request/reply messages. Not provided — see the networking guide.
- The ECS replication layer — a
Replicatedmixin, delta compression, prediction and reconciliation.good_netmoves declared records; replication is built on top of that, and the channel split is the primitive it needs. - Congestion control. A link sends what the game asks it to and reports
packetLossso the game can decide to send less. Approximating a congestion controller would be worse than naming it as missing.
Platforms¶
- Web. The kernel uses
dart:ffifor storage and spawns an isolate for the simulation; neither exists there.good_net_p2padditionally needsdart:iosockets.
The Steam backend was dropped
good_net_steam was removed instead of built. The transport contract is
open, so a Steam backend remains possible as a separate package; nothing in
good_net assumes one. good_ffi_steamworks, the empty bindings package it
would have been built on, went with it.
Publishing¶
All seven packages are on pub.dev at 0.1.0, under BSD 3-Clause. Pin
^0.1.0; the long-dead goo2d 0.0.2 still resolves for ^0.0.1.
Known rough edges¶
Things that work but will catch you out:
good createkeeps Flutter'smain.dart.flutter createruns first and writes its counter app; the scaffolder never overwrites an existing file, so the goodmain.dartis skipped and the log saysKept existing lib/main.dart. Delete it and re-run with--no-flutter-create.- Re-running
good createafter editing the pubspec duplicates keys. The idempotence check matches the literal line it wrote, so an edited dependency line slips past it and you get twogoo2d:entries and twoassets:blocks. test/widget_test.dartfromflutter createreferencesMyApp, which no longer exists oncemain.dartis the good one.- The scaffolded
main.dartleaks the game if the widget is disposed during startup. It assigns a nullable_gameafterawait Game.start(...)and calls_game?.stop()indispose, so a dispose that lands mid-start stops nothing and the isolate outlives the widget.stop()is also a no-op on a run that has not finished booting, so both halves need fixing — see Lifecycle in a widget for the shape the docs prescribe.
Contributing to this page¶
When something in the "not yet implemented" list lands, move it up and delete the entry — and when a page elsewhere in these docs starts being true, nothing needs to change there, because it was already written as though it were.