/ 00
article

From engine to platform.

The October 2023 prototyping contract that finally moved MECS from an in-process model of authoritative-server multiplayer to a real UDP wire, the 2024 backend stand-up that grew up around the engine, and the five-layer architecture that turned the simulation and the services around it into one codebase.

The first post in this series ended at the close of 2023: MECS had survived roughly ten Unity prototypes, the architectural decisions that came out of two decades of multiplayer-game work had been re-expressed in modern C#, the late-2022 unmanaged rewrite had landed, and the framework was a battle-tested engine looking for a contract that would force the parts of the architecture that had been designed against a network layer to actually meet one. None of the prototypes had shipped. None had carried real UDP datagrams over a real wire yet — though, as I will get to below, the multiplayer architecture itself had been validated for years through a model that ran inside a single process.

That changed in October 2023. Wood Fired Games signed its first contract as an operational studio: a three-month prototyping engagement in which I had to prove that I — and by extension Wood Fired Games — could actually deliver. The studio had been a dormant LLC since 2018, waking back up in early 2021 when I went independent and started running the engine work seriously. By late 2023 the studio was operational again, and the engine was about to cross from an in-process model of an authoritative-server sim to the real thing operating over the open Internet.

This post is what happened next. The in-process two-thread model that had been validating MECS’s multiplayer architecture for years before any networking code was written. The November 2023 work that replaced the model’s in-process queues with real UDP datagrams. The 2024 backend stand-up that grew up around the engine. The five-layer architecture that crystallized in April of that year. The moment MECS stopped being shared game code and became a platform.

Before the contract: an in-process model of the wire

A point I want to make clearly before the contract enters the story. The fact that MECS had not actually moved bytes across a network by late 2023 is not the same as the fact that MECS had not been designed for one. Those are different statements, and conflating them gets the engine arc wrong.

Iteration speed is the dominant variable in game development. The number of times a designer can change a mechanic and see what it feels like is the single biggest determinant of whether a game is fun. Splitting your binary into a separate client and a separate server is one of the most expensive things you can do to that variable — every change touches two executables, every debugging session has to attach to two processes, and the surface area for stale-build mismatches roughly doubles. So the rule I had learned over twenty years of multiplayer work was clear: do not split the binary until the last possible moment, and only when not splitting poses more risk to shipping than splitting does.

But you can buy most of the architectural integrity of a split binary without paying the iteration cost, and that is what I had been doing inside MECS since the framework was first capable of running a simulation. The construction is simple in description. Run the client-side MECS service in Unity’s normal frame update, exactly as it will run in the eventually-shipped client. Run a second MECS service — the would-be server — on a separate thread inside the same process, with the thread itself controlling the simulation update interval rather than Unity. Connect the two services with a pair of ConcurrentQueue<T> instances and treat each queue exactly as if it were a UDP socket. The client-side service writes messages to the outbound queue using the same Packet<T> framing it would later use to write to a real socket; the server-side thread reads from that queue, processes those messages, and writes replies to the second queue, which the client-side service drains on its next frame. Forbid any other communication between the two threads. No shared state, no static singletons, no direct method calls — anything that needs to cross has to go through the queues.

The result is a fairly accurate model of a split-binary client-server architecture without the iteration burden of actually splitting. Single-process debugging. One build to compile. Unity’s hot-reload still working. And the exact message types, the exact serialization paths, the exact authoritative-server semantics that the real network layer would inherit, because the queues are the message bus the real transport would later replace one-for-one. Anything that worked in the in-process model would work over UDP. Anything that did not work in the in-process model — race conditions, ordering assumptions that crossed the queue boundary, mutable references held across the simulation/view firewall — would not have worked over UDP either, and would have shown up later at a much higher cost.

To stay honest about what real networking would feel like, I added artificial latency on both queues with a parameterized noise floor — messages sat in their queue for a configurable delay plus jitter before the other side could drain them. That turned the queues into a believable approximation of an actual socket. Delivery had a distribution. Frame-rate-sensitive code on the Unity side could not assume server responses were instant. Any timing or ordering assumption that would have broken over a real network broke over the queues first and got fixed years before any UDP code was ever written.

That model is also where MECS’s hard 512-byte component size limit comes from. The limit looks at first like an arbitrary constraint and is sometimes read as one in the framework’s docs, but it is not. It is the size below which a component is guaranteed to fit inside a single UDP datagram on every commodity network path the engine targets — and therefore the size below which a component can never be fragmented in transit. Fragment reassembly is one of the harder things to get right on top of UDP, especially when paired with reliable in-order delivery; the easiest way to make fragment reassembly bug-free is to make it impossible for the case to arise. The component size cap was set in 2022 with that property in mind, two years before any real UDP code was written. The whole framework had been designed against a wire it would eventually meet.

October 2023: the prototyping contract

I am not going to name the project, the publisher, or the target platform. The shape of the engagement is what matters for the engine arc. It was a three-month prototyping contract — not a shipping-game contract — through which I had to prove that I (and by extension Wood Fired Games as an operational studio) could actually deliver. The specific bar I was being asked to clear was a cloud-hosted authoritative server with players able to join ad-hoc from anywhere in the United States into a single server instance, end-to-end, in front of the customer. That bar was cleared.

A prototyping contract sounds smaller than a publishing contract and in some ways it is. The thing it is not smaller in is technical risk to the engine, because a three-month window to demonstrate a working multiplayer prototype is exactly the window in which an engine that has not yet faced real network traffic gets to find out whether the architectural choices that worked in-process work over a wire. From my perspective the contract was the box-check moment for MECS’s multiplayer architecture. I had been running the in-process two-thread model for years. I now had three months to make it real on a socket, in front of a customer, all while building a game that was actually fun to play.

The first practical change was that MECS started being consumed as a compiled MECS.dll rather than as embedded Unity source. The earlier prototypes had all carried MECS in-source under WoodFired.MECS.asmdef — every game project had its own copy of the engine, edited in-place when the engine needed changes. That works for cross-project hardening, where the engine is allowed to change underneath you. It does not work for a project that is going to be evaluated against external deliverables. The contracted game needed a stable engine binary it could depend on. So MECS got built out into a compiled dependency for the first time, with versioned releases, and the game consumed the binary instead of the source.

That seems like a small organizational change. It was not. It forced the engine’s surface area to become a real public API. Internal types had to be marked internal. Public types had to be designed against the use cases of consumers who could no longer modify the engine to suit their game. Breaking changes had to be considered rather than just made. The engine had to start behaving like a library, with the discipline that implies.

The prototype itself was where MECS first proved authoritative-server, drop-in/drop-out multiplayer over real sockets rather than across in-process queues. The simulation ran on a dedicated server process hosted in the cloud. The clients ran the same MECS framework but in a viewer role — receiving component-delta broadcasts from the server, applying them to a local replica of the simulation state, rendering against the replica. The sim/view separation principle from the Rise of Nations tri-class (covered in the previous post) was finally doing the work it had been designed for. The server was the simulation; the clients were the views; the framework that powered both was the same framework on both sides; the queues that had been carrying the messages had been replaced by a UDP socket.

November 2023: the UDP transport gets built

By the time the prototyping contract was signed, the multiplayer architecture had been validated for years in-process, but the actual transport — real UDP framing, real fragment reassembly, real reliable-resend, real heartbeating — had not been written. November 2023 is when it was.

The window was as short as it was because there was nothing left to design. UDP plus an application-layer reliability layer for low-latency multiplayer was a conclusion I had reached in the early 2000s — reinforced rather than challenged by the WebSocket-and-TCP educational platform I had just come off of — and the wire-protocol shape was familiar; I had written variants since college and had authored the wire protocols for both Rise of Nations and Rise of Legends at Big Huge Games. The server was always going to be pure .NET rather than Unity, which ruled out every Unity-side networking library before any of them could be evaluated. The question in November was not what to build but how quickly the existing message and component model could be re-expressed over a real socket.

The first transport commit lands on November 11, 2023:

“…getting closer to making it so players can share their world state.”

That commit introduces NetworkSocket.cs — a raw UDP socket wrapped in a state-broadcast model. There is no TCP layer underneath. There is no networking library being pulled in. There is a Socket opened in Dgram mode, a send loop, a receive loop, and the framing code that turns a list of component deltas into a sequence of UDP packets the other side can reassemble. The same managed-versus-unmanaged discipline that runs through MECS runs through the transport. The wire format is the in-memory format. The component bytes that go onto the wire are the component bytes that live in the unmanaged pool — and they fit in a single datagram because that was decided two years earlier.

Five days later, on November 16:

“I finally got the dedicated server working!”

That commit renames NetworkSocket to NetworkInterface and adds the things that turn a raw UDP socket into a viable game transport: compression of large state broadcasts, fragment reassembly for any messages that did exceed a single MTU (the BitArray2048 family that landed during the unmanaged rewrite a year earlier finally earns its keep as the backing for fragment-tracking bitmasks), heartbeating to detect dead connections, and a reliable resend queue with exponential backoff for the messages that need to arrive in order. The first dedicated server run was a few hours after that commit.

Two weeks of focused work, two load-bearing commits, one functioning multiplayer game-server transport. The honest framing of that timeline is that it was expedient rather than heroic. The architecture had been waiting for the transport. The transport just had to be written.

NetworkInterface is still the transport MECS uses today. The class has been rewritten in pieces — the reliable resend queue got hardened in 2024, the fragment reassembly got more careful about ordering in 2025, the heartbeating got smarter about distinguishing dead connections from briefly silent ones — but the basic shape has not changed since November 2023.

The prototype concluded successfully in late 2023. The next phase — a longer-form agreement covering the full game — required everyone involved to sign, which meant the first quarter of 2024 disappeared into legal. I had time on my hands that I had not had since the contract started. Two pieces of pre-platform work fall in that window.

The first was that a new requirement landed partway through Q1. The client would not be an installed binary; it would run inside the cloud service itself, with the rendered output streamed to the player’s device. The game we were building would be the first multiplayer title to ship on that service, and at the time the only multiplayer title in development on it. That requirement is what forced the platform stand-up I describe in the next section. It is worth flagging here because it changes the shape of the April work from “build the services this game happens to need” to “build the services any multiplayer game on this platform will need,” and the second framing is what I actually started executing against.

The second was that I used the available cycles to confront a question I had been carrying since 2020. Was C# really the right language for MECS? I had defended the choice every time it had come up — the case for it is laid out in the previous post — but I had never actually built MECS in another language and lived inside it, so the case was theoretical. In late February 2024 I started a separate repository, project-cfirelight, and rebuilt the engine from scratch in pure C. No managed runtime. No garbage collector. No reflection. No Roslyn. The exercise was a deliberate dogfood: I wanted to know what my best effort at a C MECS would actually feel like to live inside, day to day, and whether after a few weeks in that environment I would still prefer C#.

The branch ran from February 26 to March 29, 2024 — roughly five weeks. By the end of it cfirelight had a working entity manager, component pools implemented as hand-rolled arena allocators where the caller passes in a block of memory and the framework computes layouts inside it, a query system that walked component-type predicates to match entities, message headers, and a system-execution layer with per-system controls and depends_on arrays. The mapping from the C# concepts to the C concepts was nearly one-to-one. EntityID became eid. ComponentHandle — the ComponentType plus ComponentID pair that is the delta currency in the C# engine — became cref { ctype; cid }. UniqueID became a 16-byte uuid with a spec-conformant generator. The Roslyn incremental generator became a small C codegen/main.c tool serving the same role. The change-driven system skip — the one optimization I claim as my own architectural contribution — ported directly: mecs_system_update builds a types_changed set from the frame’s deltas and uses if (!execute_system) continue; exactly the way the 2021 BitArray version does in the C# engine.

The honest verdict was closer than I had expected. C felt better than I had been carrying in my head. The architectural shape of MECS survived the language change cleanly enough that the C version felt like the same engine, which I had not been certain it would. But after five weeks of working in it, I still wanted to be back in the C# codebase. The reasons were small things that added up — boilerplate I had been letting Roslyn generate now had to be written or codegen’d by hand; the type-safety I had been taking for granted was a discipline I now had to enforce manually; the iteration loop with a separate codegen pass and a fresh compile felt slower than the Unity-driven inner loop on the C# side. None of it was painful enough to argue against working in C if I had to. But by the end of March I had answered the question I had set out to answer. C# was the right choice not because it had been the easy choice, but because I had now lived inside the alternative and still preferred to be back. The decision was earned rather than assumed.

cfirelight did not continue past March 2024. The platform work was about to start, the .NET engine had the gravity of the surrounding Unity and platform code, and the question the dogfood was meant to answer had been answered. The branch stays in the repository as the C reference implementation of the MECS architecture — not because I intend to ship anything on it, but because building it was the experience that let the rest of the year proceed without me second-guessing the language under it.

March 2024: the platform appears

The new client-in-cloud requirement that landed during Q1 had a consequence I had not fully appreciated until I read the platform’s developer documentation carefully. The cloud service that would host the streamed client offered no server-side infrastructure. None. No matchmaker, no account system, no instance manager, no shared state primitive — nothing. The game I was building derived a significant part of its identity from its multiplayer features. Cutting them was not a difficult choice; it was an impossible one. Which meant I was not only building the engine, the game-server simulation, and the streamed client. I was also building the entire platform layer underneath all three.

That realization is what made me pick up the phone and call Richard Jose.

Richard — or Jose, as everyone at Big Huge Games called him — is an old friend from the BHG days. After BHG he became CTO at Backflip Studios and then at Scopely, where he has spent the last decade holding the technology stack for some of the largest free-to-play games on mobile. Monopoly Go is the one most people would recognize. The shape of platform work I was about to do was the shape of work Jose had been doing at scale for years. I reached out for advice. We had one phone call. It crystallized the foundation of what I needed to build.

The single most concrete piece of advice from that call was Redis. Jose was emphatic about Redis as the right shared-state primitive for the platform layer I was describing — fast, well-understood, mature client libraries in every language, and at the right level of abstraction for session state, leaderboards, instance directories, and the cross-service publish-and-subscribe patterns that hold a multiplayer backend together. Almost everything I built across the next month sits on top of that recommendation. The Authentication service, the GameDirectory, the asset and type databases, the analytics short-path — all of it leans on Redis in roughly the shape Jose described. He was a huge help. We did not need a second call.

The piece I brought to the conversation, which I want to credit my time inside MECS for, was the insight that the game-server instances themselves could be modeled as platform services. I already had most of the building blocks. MECS had a Service abstraction that ran a deterministic frame loop; it had a message-routing system that already crossed a wire; it had a serialization model that produced the same bytes for the network and for disk. What I did not yet have was a way to consistently package, serve, and update game-server binaries and the asset databases they consumed, at scale, against a fleet of cloud-hosted simulations. That is the gap the GameInstance service and the Assets service were built to close. The platform was, from day one, designed to host arbitrary game-server processes the way a SaaS backend hosts arbitrary worker pods.

I will admit one more thing about that month. Underneath the immediate need — get the platform stood up for one specific game on one specific publishing deadline — was a quieter ambition I could not quite suppress. I was building services I knew I wanted to be the foundation under every Wood Fired Games title, now and in the future. Multiplayer infrastructure the next game would not have to reinvent. The publisher’s deadline was the forcing function. The architecture I was reaching for was longer than that.

wood-fired-platform is created on March 30, 2024. The first commit reads, in its entirety:

“initial project setup”

Three days later, on April 2, the Authentication service lands:

“Set up a basic login system with a client and server component.”

It is a Redis-backed AccountDatabase with anonymous login, account creation, and token issuance. It does not need to be more sophisticated than that for what the contracted game needs at this stage; the architecture is the point, not the feature set. The Authentication service becomes the central identity service over the next two years (Steam, Google, and Apple OAuth integrations land in late 2025), but the day-one version is the right level of complexity for the day-one game.

Two days after that, on April 4, the Analytics service lands:

“I got an analytics service rolling.”

That commit introduces the first PostgreSQL use anywhere in the platform. The choice of database matters: Redis is fine for short-lived session state, but durable analytics needs a real relational store, and Postgres is the obvious default. The Analytics service has stayed mostly the same since this commit — AnalyticsDatabase writes AnalyticsEvent rows to PostgreSQL on a queue, services across the platform publish events asynchronously, dashboards and reporting consume from the same store.

By mid-April the platform has GameDirectory (April 13 — a Redis-backed catalog of available game instances), an Assets service (also April 13 — versioned asset manifests delivered before session launch), a GameInstance service (April 18 — “I stood up a service that runs a game server” — the project that spins up authoritative game-server processes and tracks their lifecycle), and a PlatformServices shared library that holds the cross-cutting types every service depends on. These are five distinct microservices, plus a shared library, plus a Redis instance, plus a Postgres instance, all stood up across about three weeks by one person who had committed to a single principle: that the same codebase that defines the simulation should define the services around the simulation.

This is the moment MECS stops being shared game code and starts being a platform.

April 2024: the five-layer architecture

The engine repository as a distinct artifact is created on April 9, 2024:

“I’m starting a larger MECS library that I intend to link in future Unity projects. This is also the beginning of a full asset pipeline.”

That commit introduces wood-fired-engine as a standalone repo and lays down the architectural split that organizes the codebase today. The framework is divided into five layers, each with a clear boundary and a defined direction of dependency:

  • Core — the unmanaged primitives. UniqueID, Timestamp, ComponentType, the BitArray* family, FastList<T>, the unsafe Bitwise helpers. Zero dependencies on anything else. The bottom of the stack.
  • MECS — the simulation framework itself. Service, EntityLibrary, ComponentFactory, the entity-component-system primitives, the message-routing system, the delta-broadcast model. Depends on Core. The engine.
  • Generate — the code-generation pipeline. Reads database-defined type entries and emits the C# scaffolding that wires components, systems, queries, and interpreters into the runtime. Depends on MECS and Core.
  • AssetManagement — the database that defines what types exist and what game content is loaded. AssetDatabase, TypeDatabase, the MessagePack formatters that serialize unmanaged types to and from disk. Depends on MECS and Core. Independent of the platform layer above it.
  • Platform — the server-side services that run on top of the simulation. Authentication, Analytics, GameDirectory, GameInstance, the persistence layer. Depends on everything below it but on nothing above it (because there is nothing above it).

The constraint that holds the architecture together is that dependencies flow strictly downward. Core never imports anything from MECS or Platform. MECS never imports anything from AssetManagement or Platform. The Platform layer can use anything underneath it, but the engine layers do not know the platform layer exists. That allows a game running on the Unity client side to consume Core, MECS, Generate, and AssetManagement without dragging in any of the server-side service code. It also allows the server to run the same engine layers plus the platform services, with full type-system compatibility between the two sides.

This split has stayed essentially unchanged from April 9, 2024 to the present. The boundaries it draws are the same boundaries the NuGet package extraction would later carve along (more on that in the next post). The five-layer architecture is not a refactoring; it is the original shape of the engine as a library rather than as a game’s source code.

Two weeks after the engine repo is created, on April 23, MECSEditor first appears:

“Adding a new project that gives me a gui that runs in ssh that I can use to edit the asset database.”

That commit introduces what is now the central tool for editing the asset and type databases that MECS uses. The original implementation was a TUI — a text-mode interface that could run inside an SSH session, which was important because a lot of the asset-database editing was going to happen on remote servers where a graphical editor was not an option. The tool has grown since then (it is now a full CLI, a dotnet global tool, and an MCP server that lets agents call its commands directly), but the kernel of the design is the same: define types and content in a database, let the code generator emit the scaffolding, edit the database through tooling rather than through manually-maintained C# files.

The final publishing agreement was signed around May 2024. By the time the ink was dry I had the engine repo, the platform repo, the five-layer split, an authoritative-server transport on real sockets, five microservices and a shared library running against Redis and Postgres, and a TUI for editing the type and asset databases. I also had a new expanded vision for what the platform was going to become. The contract was for one game. The architecture I had just stood up was for every game I was going to build after it.

What the platform layer answers

This is the part of the story where the threads from the previous post tie together. The frustration that came out of the education-MMO project — the SaaS-shaped platform team and the games-shaped game team unable to combine cleanly into a shared multiplayer experience — has been hanging over the engine work since late 2020. The five-layer architecture is the answer to that frustration, made concrete.

The simulation and the platform services are one codebase. They share a type system. They share a serialization format. They share an identity model. A change to a component definition propagates from the database through the code generator to both the game-server simulation and the platform-side service that persists the component to durable storage. No cross-team translation layer is required because there is no cross-team boundary. The team building the persistence service and the team building the combat system would be looking at the same mecs-types.registry file. They are not different stacks. They are different services in the same stack.

That property is what made the platform layer worth building from scratch rather than adopted from a shelf. PlayFab, Nakama, GameSparks and the rest would have given me 80% of what the game on the immediate publishing deadline needed, in a week. I built it myself for three reasons that compounded. First, the cloud platform offered no server infrastructure, so the choice was not between “build” and “adopt” — it was between “build” and “do not ship multiplayer,” and multiplayer was non-negotiable for the game. Second, the architectural commitment that the server be pure .NET (not Unity) ruled out the off-the-shelf options that expect a Unity runtime on both ends. And third — the one I would have built for even if the first two had not been there — I wanted the deep type-system compatibility between the simulation and the platform services that no off-the-shelf backend can offer. That last property is what closes the gap that had been the most expensive thing about the education-MMO project. The first two are what made the question moot in practice.

I would build the platform layer again. I would build it sooner.

Late 2024: the project ends, the platform does not

The publisher had an internal re-org in the second half of 2024. The details are not mine to share. The outcome that mattered for this story is that the project the platform had been built around was not going to continue. The proof point I had been longing for — the one that would have put the platform under real player load on a real publishing schedule — was going to have to wait.

The structural detail that mattered for what came next is that I had retained all rights to the technology stack. The engine, the platform, the asset pipeline, the transport, every service I had been building since March — all of it stayed with Wood Fired Games. The contract had been structured deliberately to produce that outcome if it came to it. It came to it, and the foresight paid off. I was not walking away from the platform. The platform was walking away from one specific game and toward whatever I pointed it at next.

The decision I made through the end of 2024 was to use the year ahead to harden the platform until it was capable of being put in front of any publisher I might bring to the table — rather than continuing to optimize it against the operational constraints of the first one. The first publisher’s requirements had shaped a lot of the original 2024 work. The 2025 work was going to be about turning the platform into something publisher-agnostic, fully owned by Wood Fired Games, and ready for whatever partner came next.

There was a second decision I made in roughly the same window, and it turned out to matter much more than I expected at the time. JetBrains AI Pro — the AI Assistant feature inside Rider — had been a daily tool for me since February 2024. I had been using it as an expert reference: error explanations, build-system advice, second opinions on code I had already written. Through most of the year that was where the practice stopped, because the publisher deadline owned my attention and the AI was a way to move faster inside Rider rather than a thing I was investing in for its own sake. Once the deadline went away in the second half of 2024, I had the room to actually expand the practice. The November expansion is documented in detail in the next post, but the relevant beat for this one is the decision I made at the end of the year: that the AI tooling I had been using as a productivity assistant was worth taking seriously as a strategic investment.

I was not yet letting AI write code for me. I was the one typing every line. The agentic-coding category did not exist yet; Claude Code did not exist yet. What existed was a scoped chat assistant inside the IDE, plus — by the end of November — a local-AI workstation, a filesystem-aware editor (Cursor), and two cloud chat subscriptions (ChatGPT Plus and Claude.ai Pro). The activation energy for picking up new infrastructure tooling — the long list of things I had known about but never committed to learning, Docker and proper CI prominent among them — had dropped enough that I could begin to see a different studio underneath my hands than the one I had been operating all year. The more carefully I looked at the value that practice was delivering, the more I suspected I could lean on it much harder than I had been letting myself.

Where this leaves us

That is where this post ends, on the last days of 2024. The first publishing contract was over. The platform — engine, asset pipeline, transport, services, and the years of architectural conviction underneath all of them — was owned outright by Wood Fired Games. I had no immediate revenue replacing the publisher and a year of hardening work ahead of me. I also had ten months of daily AI-assisted-work practice already on my hands, an expanded toolchain I had stood up in the last quarter, and a hypothesis about what all of it might enable.

The hypothesis was that AI, used correctly, might be exactly the thing I needed to bring the larger vision the studio had been quietly aiming at since the 2020 thesis into a form an independent studio could actually ship. I had spent two decades watching engine work, multiplayer work, and platform work get done by teams of dozens. I had just spent a year doing all three by myself. And at the end of that year I could see, in something close to peripheral vision, that the bottleneck was about to change.

What that hypothesis has turned into across 2025 and 2026 — the evolution of how I work with AI, the impact it has had on me, and the impact it has had on every line of code I have shipped since — is the subject of the next post in this series.