Mr. Phil Games’ Blog

Posts for Tag: 4X Game

Stellar Throne Devlog #10 — From Bug Fixes to Breakthroughs

Yesterday marked a massive surge of progress on Stellar Throne, my 4X strategy game. For those new to the project, Stellar Throne is built with Godot (or “go-DOH”) for the UI client and Zig for the high-performance simulation engine. After validating that the dual-engine architecture was correctly preserving visual data, yesterday’s focus was all about systematically fixing critical bugs and implementing major missing systems.

Fixing Fleet Movement: The Multi-Hop Breakthrough

The day began with a curious issue — fleets would stop at their first waypoint instead of continuing to their final destination. If I ordered a fleet to travel across five star systems, it would jump to the first and then just… stop.

There were two culprits:

  1. The Zig simulation backend was missing multi-hop continuation logic.

  2. The has_planned_route flag wasn’t being properly set during deserialization.

I fixed both by adding continuation logic to the Zig fleet arrival handler and correcting route flag inference from the route array. Fleets now correctly travel across all waypoints to reach distant colonies.


Event Manager Revival: “Salvage the Past” Returns

Next, I discovered that the “Salvage the Past” event — which provides narrative context and a small resource bonus at game start — wasn’t firing.
The cause? The Event Manager was still trying to load from a deprecated JSON file instead of the new TOML configuration system. After migrating all configs months ago, this subsystem had simply never been updated.

I replaced the hardcoded loader with a TOML-based one through the Config Manager, added automatic format conversion (snake_case → camelCase), and fixed an autoload order issue that caused Event Manager to initialize too early. The fix was simple but pivotal: moving Config Manager earlier in the autoload sequence. Events now trigger properly again.


Research System Overhaul: Persistence, Costs, and Effects

The research system was next — and it was a tangle. Research progress wasn’t persisting correctly after saving and loading, and techs were completing at incorrect cost values. Digging in revealed:

  • A serialization key mismatch (active_tech vs. active_tech_id)

  • Corrupted deserialization from nested research dictionaries

  • Over-accumulating progress past 100%

  • Flat serialization structures incompatible with the Godot side

I rebuilt the serialization/deserialization layer and restructured ResearchStateSimEmpire, and TurnSimulator to restore consistent persistence.

But research completion costs were still wrong — some techs completed at arbitrary thresholds. The problem was broken tier-matching logic: Zig was inferring tech costs from tiers instead of fetching them from the TOML configuration. After replacing it with proper ID-based lookups, research now respects real costs.

Finally, I discovered that tech effects weren’t being applied when research completed. The Zig backend had a “simplified” completeTech() function that unlocked techs but didn’t apply effects or send notifications. I implemented a temporary workaround in the TurnSimulationService, post-processing newly completed techs and calling the full Godot-side applyTechnologyEffects() method.

This fix is documented for a proper long-term solution in TODO_TECH_EFFECTS.md, which estimates a 15–20 hour implementation.


Planning for Parity: Building a Structured Roadmap

After the bug marathon, I shifted to long-term planning. The Zig backend currently includes 10 simplified systems — construction, combat, AI, events, and more. To bring full parity with Godot, I created a detailed roadmap across five new documents:

  1. Zig Implementation Plan — 13-week roadmap with system-by-system breakdowns, totaling 360–450 estimated hours.

  2. Zig Parity Roadmap — Executive summary showing 8 full systems, 10 simplified, and a move from 65% tolerance to near 0% parity.

  3. Parity Test Examples — Deterministic test specifications for construction, combat, AI, and events using exact RNG seeding.

  4. Zig Parity Progress — Session tracking, milestone checklists, and velocity metrics.

  5. Session Workflow Guide — Practical start/during/end checklists for ongoing development.


Phase Five Complete: Construction System Implemented

With the roadmap ready, I dove into Phase Five — Construction.

This system enables both turn-based building construction and production-based shipbuilding in Zig. I created:

  • SimConstruction.zig (with ConstructionOrder and ConstructionQueue)

  • Extended SimGalaxy for build and ship queues

  • Added processing to TurnSimulator

Each construction order tracks 13 fields — progress, turns, costs, and more — while queues handle FIFO operations. The logic now supports both building completion and ship production (logged for now, pending full ship creation parity).

The system clocked in at 8 hours — 33% ahead of the 12–16 hour estimate.
Current test results:

  • 339 Zig unit tests: ✅ Passing

  • 739/740 GUT integration tests: ✅ Passing

Zig parity stands at 1/7 phases complete (14%).


Construction Bugs & Fixes

The new construction system exposed three deeper issues:

  1. Disappearing Orders — Planets lacked system_id after deserialization. Fixed by setting it during StarSystem.from_dict().

  2. “Unknown Building” Labels — Type strings weren’t deserializing properly. Implemented a temporary queue-preservation workaround.

  3. Wrong Building Creation — Empty item types caused incorrect building spawns. Fixed via state preservation until full parity logic is in place.


Smarter Documentation: Resume Management Automation

Finally, I overhauled documentation. The massive RESUME.md file had grown to over 2,000 lines, bloating context windows. I:

  • Archived sessions 1–59 to a separate file

  • Reduced RESUME.md to 400 lines (80% reduction)

  • Created two new Claude agents:

    • Resume Writer Agent — auto-archives when context exceeds 150K tokens or on “done for today”

    • Resume Reader Agent — summarizes state at session start

Now, context is lean, searchable, and structured for long-term development.


Technical Summary

Yesterday’s session generated 13 commits and over 1,000 lines of code changes:

  • Fleet multi-hop fix — 34 lines

  • Event system TOML migration — 63 lines

  • Autoload dependency fix — 1 line

  • Research persistence & cost fixes — 128 lines

  • Tech effect workaround — 53 lines

  • Planning documents — 2,000+ lines (5 new files)

  • Construction system — 454 lines

  • Construction bug fixes — 108 lines

  • Resume refactor — 1,500 archived lines + 2 Claude agents


Current Status

✅ Fleets now travel through multi-hop routes.
✅ Events trigger properly at game start.
✅ Research completes at correct costs and applies tech effects.
✅ Construction system is fully implemented in Zig.
✅ One of seven parity phases complete (14%).
✅ Documentation system streamlined and automated.

This marks a shift from “fixing bugs in the Zig backend” to “systematically implementing full system parity.” The new roadmap provides the structure to reach 100% Zig simulation parity over the next 13 weeks.

Next up: Phase Nine — Combat Resolution, estimated at 24–32 hours.


You can follow ongoing updates and technical breakdowns at mrphilgames.com.
Thanks for reading — and for joining me on this journey to build Stellar Throne from the ground up.

— MrPhil

🚀 Stellar Throne Devlog #7: The Zig Simulation Goes Live

Hey everyone, Mister Phil here with another devlog update!

Today marks a major milestone for Stellar Throne, my sci-fi 4X strategy game. For those just joining: Stellar Throne runs on a dual-engine architecture, using Godot for the UI client and Zig for the high-performance simulation backend.

Yesterday’s work was all about one huge goal — finally enabling the Zig simulation backend in production and fixing the critical serialization bugs that had been blocking deployment for days.


🧩 The Context: A Fully Functional Engine That Couldn’t Run

At the start of the day, I had a complete Zig simulation engine — all tests passing, a 52× performance improvement validated, and everything ready to go.

Except… I couldn’t actually use it.

The bridge between Godot and Zig — serialization — was broken. The game state couldn’t properly flow between the two, leaving the new engine stranded behind a wall of data mismatches.


⚙️ Fixing the Turn Processing Hang

The first issue was a classic async race condition. The game was using await to wait for a “turn completed” signal, but the listener was being connected after the signal was emitted.

Result: the code waited forever for an event that had already happened.

Fix: I stored the signal reference before calling the turn coordination function. That ensured the listener was ready before any signals could fire. I also added yield calls in the turn sequencer to give the engine a frame to process signals between phases.

✅ Result: Turn processing is now stable and reliable.


🔄 The Real Challenge: Serialization Across Engines

The real battle was with serialization mismatches.

Godot properties used snake_case (star_system_idfleet_name), while Zig structs used camelCase (starSystemIdfleetName). Every deserialization failed because of these naming conflicts.

I added manual field mapping in Zig — tedious, error-prone, and partial. Empires loaded correctly, but fleets, planets, and star systems still broke.

The breakthrough.
I implemented bidirectional camelCase serialization across all four major data structures:

  • SimEmpire

  • SimFleet

  • SimPlanet

  • SimStarSystem

Each gained custom toJSON() and fromJSON() methods that output camelCase and accept both camelCase and snake_case.

Struct Lines Added
SimEmpire +210
SimFleet +274
SimPlanet +44
SimStarSystem +131

All test fixtures were also updated to camelCase, covering five scenarios:

  • Edge Case Deficit

  • Edge Case Empty Empire

  • Fleet Upkeep

  • Single Turn Minimal

  • Ten Turn Economy


🧠 Type System Fixes & Data Integrity

Once serialization worked, I enabled the Zig backend in production. Immediately, Godot’s type system rebelled.

Files like Building.gdEmpire.gd, and Fleet.gd had overly strict type annotations that didn’t match the Zig output. I went through each file and refined type declarations to handle the new data correctly.

Then came the fleet disappearance bug — fleets vanished after turn processing. Turns out:

  1. The location field was structured incorrectly.

  2. fleet_name sometimes wasn’t serialized at all.

Both fixed.

And finally, empire colors were lost during serialization. The culprit?
Godot’s Color type can appear as RGB, RGBA, hex, or even name strings. Zig wasn’t handling that variety.

Solution: standardize on RGBA objects — {r, g, b, a} — for full round-trip fidelity between engines.


📈 The Numbers

Category Changes
Zig serialization +659 lines
GDScript type fixes 6 files, +41 lines
Fleet & color bug fixes +89 lines across 5 files
Test framework +150 lines new assertions
Test fixtures +59 lines camelCase JSON

8 commits in total.
The Zig backend is now fully active in production.
Game state round-trips cleanly. Colors, fleets, and names all persist. Integration tests are green across the board.


⚡ The Results

The game now runs 52× faster with the Zig simulation engine enabled by default.
Large galaxies that once took seconds now resolve in milliseconds. The performance leap is live — and dramatic.


🧭 Next Steps

  • Monitor gameplay for any hidden serialization edge cases

  • Expand integration test coverage

  • Begin profiling the Zig engine for further optimization


That’s the devlog update!
Thanks for following along as Stellar Throne crosses another huge milestone.

👉 Visit mrphilgames.com for more updates, devlogs, and behind-the-scenes deep dives.