Look, no screen!#
August took me back into my work thoughts and so took Gremlin to places with no screen at all - a Raspberry Pi Zero keeping an eye on my internet connection, and the microcontroller inside a CNC machine. And back in game land, Tanx finally made Gremlin answer a question every multiplayer game has to face sooner or later: who gets to decide what actually happened?
Headless Gremlin#
A major pain for any thoughts I may have of ‘home automation’ is the constant (monthly it seems) hanging of my household router - never really got to the bottom of it but the ol’ ’turn it off and on again’ always resolves it. But the point of remote control is you’re remote! If I am there to physically restart the router, I might as well turn the heating on myself while I am at it!
So time to press Gremlin into the more prosaic task of relentlessly keeping my crappy router alive and connected.
There’s now a new build option for a headless Gremlin: just Core and Network, with no graphics or audio. Its first user is this little internet watchdog for the home network. It runs on a Pi Zero and regularly pokes a few plain-HTTP connectivity-check endpoints. If the internet looks dead for several checks in a row, it power-cycles the router through a GPIO-driven relay. There’s a small status page on the LAN, with a big button for a manual cycle.
To make that possible, Gremlin gained:
- HTTP client and server classes, now formally part of Gremlin and backed by ASIO. As with the game networking, ASIO stays out of the public headers.
- A
GpioLineabstraction, with a Linux implementation and a ’null’ one for everywhere else, so the app still builds and runs on my desktop. - A standard-library file reader to replace the SDL2-backed one, because a headless build has no SDL. (If I ever build for Android again I’ll need to revisit that. Future me’s problem.)
This is the abstraction layers paying off in a direction I never expected. Because the platform stuff sits behind interfaces, ‘Gremlin without a window’ turned out to be a CMake option and a few new implementations - not a fork. Nice.
Goblin: talking to microcontrollers#
The other big chunk of the month was CNCController, a replacement controller stack for my CNC machine based on grblHAL. Its architecture splits the job in two: a ‘host’ on a Linux machine plans the moves, and a ‘device’ - a dedicated microcontroller - executes them in real time. That means the link between the two has to be something I can really trust.
The answer is Goblin, a new, separate library that Gremlin now pulls in as a submodule. The design rules are strict:
- Freestanding C99 - no dynamic allocation, no blocking, no clock access.
- So exactly the same source compiles on the PC and on an AVR microcontroller, and there’s only one implementation of the wire format to get right.
- Its first protocol, Goblin Link, provides framing (COBS), CRC-16 checks, a versioned handshake, credit-based flow control and heartbeat supervision, over any ordered byte stream - a UART, TCP or a pty.
- Gremlin apps use it through a small C++ wrapper.
By the end of the month the first proper stepping code was in, and a real stepper motor moved on the X axis. There’s something about software making a physical thing move that never gets old!
Who decides?#
Back in game land, Tanx’s multiplayer hit a classic problem. Player A destroys a tank, and player B’s next routine position update - sent before B knew - puts it straight back. The update isn’t corrupt, and it isn’t out of order. It’s just describing a world that no longer exists.
So NetAuthority.h adds three small pieces, deliberately independent of any transport, ECS or game:
NetStamp/NetStampGate- continuous state (positions, orientations) belongs to its owner and the last writer wins, but every update is stamped with the tick and the object’s incarnation. When the host makes a decision about an object it bumps the incarnation, which voids every stale update still in flight.NetEventSequencer- discrete events (a hit, a death, a score) belong to the host, which numbers them. Clients never decide; they claim, and then apply the host’s ruling through the same code path the host uses.NetSessionClock- the shared, roughly-agreed timeline both of the above refer to.
Or, to put it in one line: continuous state is replicated, discrete events are adjudicated. Anything that has to be agreed belongs in the second pile. It ships with its own test suite, and Tanx was using it to resolve state through the host by the 31st - the same day it got a minimap.
Bits & bobs#
- Child
Transforms now compound world rotation and scale symmetrically, and Buccaneer’s particle firing now uses the new world rotations. Colourcan be read and written as a hex string, which is now how colours get stored.- UI parenting is consistent: no parent means ’the scene root’, with a debug warning if neither can be found.
- You can grab the render system’s copy of
UIThemeto tweak it independently. Tanx moved over to the latest UI patterns as part of a bigger tidy-up.
What’s next?#
The authority model assumes both peers are running the same game with the same data - and at the moment nothing actually checks that. Need to get that nonsense sorted out before Tanx gets an online playtest…

