rust rewrite design doc #11

Closed
opened 2026-07-09 18:19:40 +00:00 by coilysiren · 0 comments
Owner

Handoff: coilysiren/factory-game-v2 Revival Context

User / project intent

Kai has revived coilysiren/factory-game-v2. The current working thesis is that this repo is a strong candidate for a programming-first factory/logistics simulation, not merely a Unity game prototype.

The conversation leading into this was about games that are closer to “software engineering as gameplay”: Desynced, Screeps, Stationeers, The Farmer Was Replaced, JOY OF PROGRAMMING, etc. Kai’s stated discomfort with Factorio-like circuit networks is that they remain too visual/manual; she is interested in something closer to actual programming, autonomous agents, logistics protocols, simulation state, schedulers, control loops, and testable systems.

She then showed factory-game-v2, and the repo looked more aligned with that desire than expected.

Current strategic read

The repo’s structure appears to be “begging for Rust,” but the precise recommendation is not “rewrite the whole game in a Rust game engine immediately.”

The better formulation:

Build a Rust logistics simulation kernel first, then attach a dumb viewer later.

The likely best next project shape is:

factory-game-v3/
  crates/
    factory_sim/        pure Rust, deterministic, no renderer
    factory_content/    recipes, IDs, balancing data
    factory_cli/        headless runner / metrics output
    factory_viewer/     optional Bevy / macroquad / raylib viewer later

The key idea is to treat the game as:

simulation core
→ observability / metrics / tests
→ optional visual frontend

not:

Unity scene
→ MonoBehaviour gameplay
→ maybe some tests

Repo facts observed

The repo is coilysiren/factory-game-v2, default branch main. It was public and previously showed as archived when inspected; Kai has since said she revived it, so verify current archive status before making GitHub write assumptions.

It is a Unity project using Unity 6000.0.36f1.

The repo contains a separate tests.csproj targeting .NET 8 and using xUnit / OpenTelemetry packages. It also explicitly references Unity DLL paths, including local-machine-specific paths such as /Applications/Unity/... and X:\unity-editor\....

The .gitignore ignores generated .csproj files but explicitly un-ignores tests.csproj, which indicates an intentional external/unit-test project alongside Unity.

Existing architecture

The repo already separates many concepts into Core and Unity namespaces. This is the most important architectural signal.

WorldObjectCore holds simulation/game state: movement, battery, resources, resource inserters, dispatchers, dispatch receivers, deployments, production, power, power lines, mining, GUID, type, target info, Unity backref, mobility flags, alerts, and grid position.

WorldObject : MonoBehaviour wraps WorldObjectCore, projects grid position into Unity transform position, handles visual naming, and creates/initializes the core object.

Interpretation: this is not a normal Unity “everything is a MonoBehaviour” prototype. It is already halfway toward a headless simulation with Unity as a frontend adapter.

Game loop / simulation model

GameControllerCore owns the world-object grid and mutation queues: queuedForDeletion, queuedForSpawn, and queuedForMovement.

Unity GameController.Update() runs a tick loop when ready, ticks all world objects, then applies queued delete/move/spawn operations, regenerates pathfinding if needed, stops telemetry, increments tick count, and updates lastTick.

Interpretation: this is already using deferred mutation, which maps very naturally to Rust command buffers or ECS-style system scheduling.

Domain model

GameContent.Item is already pure-ish data: name, weight, volume, craft time, craft input/output behavior, stack size, ingredients, spawnability, and create-from-nothing flag.

Factory content includes resources such as iron ore, copper ore, coal, and stone; products such as iron bars, copper bars, building materials, motors, circuits, and frames; and spawnables such as storage warehouse, coal plant, factory, and mining drill.

Interpretation: content should probably move to data files eventually: RON, TOML, YAML, JSON, or another serde-friendly format if rewritten in Rust.

Resource system

ResourcesComponentCore is substantial and should be treated as one of the first core systems to port or preserve.

It models:

  • weight capacity
  • volume capacity
  • reserved capacity
  • resource dictionaries
  • total resources
  • used/remaining capacity
  • resource creation
  • forced creation
  • consumption
  • give/take/retrieve semantics
  • specific exception types for capacity and quantity failures

Relevant lines: resource fields and reserved-capacity comments. Resource creation capacity behavior. Give-resource transfer behavior. Retrieve-resource behavior.

Interpretation: this wants typed Result<T, ResourceError> semantics in Rust rather than exception-driven partial operations.

Potential Rust error model:

pub enum ResourceError {
    NoContainer,
    UnknownItem(ItemId),
    NotEnoughQuantity,
    NotEnoughWeightCapacity,
    NotEnoughVolumeCapacity,
    ReservedCapacityViolation,
}

Dispatch / logistics system

The dispatch system is probably the conceptual heart of the game.

DispatchComponentCore defines a small logistics grammar:

Collect / Deliver
Retrieve / Deploy
Me

These verbs and keywords are explicit in the code.

The dispatcher:

  1. Checks whether it is already assigned.
  2. Applies buffer/availability rules.
  3. Checks adjacent tile availability.
  4. Consumes battery.
  5. Finds target locations.
  6. Filters already-served target locations.
  7. Finds an available matching receiver.
  8. Queues a dispatch assignment.

Important target/receiver matching behavior appears here.

Interpretation: the interesting game is not belts and inserters. It is a distributed job-assignment protocol among autonomous world objects. Preserve this design axis.

Production system

ProductionComponentCore has product, quantity, requests/intermediates, current craft progress, power usage, reserved-capacity setup, inserter resource assignment, and tick-based crafting. It consumes inputs, uses battery, advances craft progress, and force-creates product output when a craft completes.

Interpretation: production is already deterministic/tick-based and should port cleanly.

Movement / pathfinding

MovementComponentCore keeps a path and path index, checks target position from dispatch receiver state, computes distance, recalculates path when needed, and queues movement through the controller.

Interpretation: movement should be extracted behind a pathfinding interface. Do not bind the simulation to Unity map/grid types.

Known sharp issues

1. Core still leaks Unity

IGameController exposes Unity-adjacent or host concerns such as SpriteMapComponent Map, logger, ActivitySource, WorldObjectTickActivity, and mutation queue methods.

Recommendation: split this interface into smaller simulation services:

ISimClock
IWorldMutationSink
IPathfinder
IEventSink / ILogger
IRandom

In Rust, this might become explicit resources passed into systems, or plain services owned by GameState.

2. Tests are promising but brittle

tests.csproj is clearly intentional, but it depends on absolute Unity install paths.

Recommendation: remove Unity dependencies from the core sim. If Unity references are unavoidable for now, use documented local configuration rather than hardcoded paths.

3. Credential-shaped OTEL/Honeycomb value is committed

GameControllerCore contains static OpenTelemetry/Honeycomb configuration including an auth-header-shaped value. Logging setup sends OTLP logs with that header.

Recommendation: rotate/delete any exposed token, even if archived/revived status changed. Move telemetry config to environment variables or local config.

4. Hand-rolled component bag wants ECS or typed tables

WorldObjectCore currently has many optional component fields.

Recommendation: if staying in C#, consider Arch/Flecs.NET/DefaultECS or a plain data-table system. If moving to Rust, consider Bevy ECS later, but start with a pure Rust sim crate first.

Do not immediately port the entire game.

Port or rebuild the smallest closed loop:

items
resources
world grid
one miner
one factory
one truck
dispatch assignment
movement
production
1000 deterministic ticks

Desired CLI target:

cargo test
cargo run -p factory_cli -- --seed 1 --ticks 10000

Desired output shape:

{
  "ticks": 10000,
  "iron_ore_mined": 1200,
  "iron_bars_produced": 350,
  "dispatches_assigned": 84,
  "dispatches_failed": 3,
  "deadlocks": 0
}

This converts the project from “Unity game prototype” into “simulation lab with a possible game frontend.”

Use Rust first, Bevy later.

Suggested crates / layers:

factory_sim:
  - pure Rust
  - deterministic tick loop
  - no renderer
  - no Bevy dependency initially

IDs:
  - slotmap, generational-arena, or stable integer IDs

content:
  - serde
  - ron/toml/json/yaml

tests:
  - cargo test
  - proptest later

observability:
  - tracing

cli:
  - clap

viewer:
  - Bevy, macroquad, raylib, or a terminal/web viewer later

Important product/design interpretation

The most promising direction is not “make Factorio again.” It is:

A programmable/autonomous logistics simulation where the player eventually designs or debugs protocols, schedulers, dispatch rules, production constraints, and agent behavior.

The existing dispatch system already points in this direction. Treat that as the seed crystal.

Suggested immediate agent task

First agent task should be an architecture extraction pass, not feature work:

  1. Inventory all Core classes and their Unity dependencies.
  2. Identify the minimal pure sim subset.
  3. Draft a Rust crate structure.
  4. Port GameContent.Item, ResourcesComponentCore, and a tiny GameState.
  5. Add deterministic tests for resource transfer and one production tick.
  6. Add CLI runner after tests pass.
  7. Only then consider rendering.

Avoid starting with Unity editor cleanup or Bevy visualization. That will recreate engine gravity before the sim kernel exists.

# Handoff: `coilysiren/factory-game-v2` Revival Context ## User / project intent Kai has revived `coilysiren/factory-game-v2`. The current working thesis is that this repo is a strong candidate for a **programming-first factory/logistics simulation**, not merely a Unity game prototype. The conversation leading into this was about games that are closer to “software engineering as gameplay”: Desynced, Screeps, Stationeers, The Farmer Was Replaced, JOY OF PROGRAMMING, etc. Kai’s stated discomfort with Factorio-like circuit networks is that they remain too visual/manual; she is interested in something closer to actual programming, autonomous agents, logistics protocols, simulation state, schedulers, control loops, and testable systems. She then showed `factory-game-v2`, and the repo looked more aligned with that desire than expected. ## Current strategic read The repo’s structure appears to be “begging for Rust,” but the precise recommendation is **not** “rewrite the whole game in a Rust game engine immediately.” The better formulation: > Build a **Rust logistics simulation kernel** first, then attach a dumb viewer later. The likely best next project shape is: ```text factory-game-v3/ crates/ factory_sim/ pure Rust, deterministic, no renderer factory_content/ recipes, IDs, balancing data factory_cli/ headless runner / metrics output factory_viewer/ optional Bevy / macroquad / raylib viewer later ``` The key idea is to treat the game as: ```text simulation core → observability / metrics / tests → optional visual frontend ``` not: ```text Unity scene → MonoBehaviour gameplay → maybe some tests ``` ## Repo facts observed The repo is `coilysiren/factory-game-v2`, default branch `main`. It was public and previously showed as archived when inspected; Kai has since said she revived it, so verify current archive status before making GitHub write assumptions. It is a Unity project using **Unity 6000.0.36f1**. The repo contains a separate `tests.csproj` targeting **.NET 8** and using xUnit / OpenTelemetry packages. It also explicitly references Unity DLL paths, including local-machine-specific paths such as `/Applications/Unity/...` and `X:\unity-editor\...`. The `.gitignore` ignores generated `.csproj` files but explicitly un-ignores `tests.csproj`, which indicates an intentional external/unit-test project alongside Unity. ## Existing architecture The repo already separates many concepts into `Core` and `Unity` namespaces. This is the most important architectural signal. `WorldObjectCore` holds simulation/game state: movement, battery, resources, resource inserters, dispatchers, dispatch receivers, deployments, production, power, power lines, mining, GUID, type, target info, Unity backref, mobility flags, alerts, and grid position. `WorldObject : MonoBehaviour` wraps `WorldObjectCore`, projects grid position into Unity transform position, handles visual naming, and creates/initializes the core object. Interpretation: this is not a normal Unity “everything is a MonoBehaviour” prototype. It is already halfway toward a headless simulation with Unity as a frontend adapter. ## Game loop / simulation model `GameControllerCore` owns the world-object grid and mutation queues: `queuedForDeletion`, `queuedForSpawn`, and `queuedForMovement`. Unity `GameController.Update()` runs a tick loop when ready, ticks all world objects, then applies queued delete/move/spawn operations, regenerates pathfinding if needed, stops telemetry, increments tick count, and updates `lastTick`. Interpretation: this is already using deferred mutation, which maps very naturally to Rust command buffers or ECS-style system scheduling. ## Domain model `GameContent.Item` is already pure-ish data: name, weight, volume, craft time, craft input/output behavior, stack size, ingredients, spawnability, and create-from-nothing flag. Factory content includes resources such as iron ore, copper ore, coal, and stone; products such as iron bars, copper bars, building materials, motors, circuits, and frames; and spawnables such as storage warehouse, coal plant, factory, and mining drill. Interpretation: content should probably move to data files eventually: RON, TOML, YAML, JSON, or another serde-friendly format if rewritten in Rust. ## Resource system `ResourcesComponentCore` is substantial and should be treated as one of the first core systems to port or preserve. It models: * weight capacity * volume capacity * reserved capacity * resource dictionaries * total resources * used/remaining capacity * resource creation * forced creation * consumption * give/take/retrieve semantics * specific exception types for capacity and quantity failures Relevant lines: resource fields and reserved-capacity comments. Resource creation capacity behavior. Give-resource transfer behavior. Retrieve-resource behavior. Interpretation: this wants typed `Result<T, ResourceError>` semantics in Rust rather than exception-driven partial operations. Potential Rust error model: ```rust pub enum ResourceError { NoContainer, UnknownItem(ItemId), NotEnoughQuantity, NotEnoughWeightCapacity, NotEnoughVolumeCapacity, ReservedCapacityViolation, } ``` ## Dispatch / logistics system The dispatch system is probably the conceptual heart of the game. `DispatchComponentCore` defines a small logistics grammar: ```text Collect / Deliver Retrieve / Deploy Me ``` These verbs and keywords are explicit in the code. The dispatcher: 1. Checks whether it is already assigned. 2. Applies buffer/availability rules. 3. Checks adjacent tile availability. 4. Consumes battery. 5. Finds target locations. 6. Filters already-served target locations. 7. Finds an available matching receiver. 8. Queues a dispatch assignment. Important target/receiver matching behavior appears here. Interpretation: the interesting game is not belts and inserters. It is a distributed job-assignment protocol among autonomous world objects. Preserve this design axis. ## Production system `ProductionComponentCore` has product, quantity, requests/intermediates, current craft progress, power usage, reserved-capacity setup, inserter resource assignment, and tick-based crafting. It consumes inputs, uses battery, advances craft progress, and force-creates product output when a craft completes. Interpretation: production is already deterministic/tick-based and should port cleanly. ## Movement / pathfinding `MovementComponentCore` keeps a path and path index, checks target position from dispatch receiver state, computes distance, recalculates path when needed, and queues movement through the controller. Interpretation: movement should be extracted behind a pathfinding interface. Do not bind the simulation to Unity map/grid types. ## Known sharp issues ### 1. Core still leaks Unity `IGameController` exposes Unity-adjacent or host concerns such as `SpriteMapComponent Map`, logger, `ActivitySource`, `WorldObjectTickActivity`, and mutation queue methods. Recommendation: split this interface into smaller simulation services: ```text ISimClock IWorldMutationSink IPathfinder IEventSink / ILogger IRandom ``` In Rust, this might become explicit resources passed into systems, or plain services owned by `GameState`. ### 2. Tests are promising but brittle `tests.csproj` is clearly intentional, but it depends on absolute Unity install paths. Recommendation: remove Unity dependencies from the core sim. If Unity references are unavoidable for now, use documented local configuration rather than hardcoded paths. ### 3. Credential-shaped OTEL/Honeycomb value is committed `GameControllerCore` contains static OpenTelemetry/Honeycomb configuration including an auth-header-shaped value. Logging setup sends OTLP logs with that header. Recommendation: rotate/delete any exposed token, even if archived/revived status changed. Move telemetry config to environment variables or local config. ### 4. Hand-rolled component bag wants ECS or typed tables `WorldObjectCore` currently has many optional component fields. Recommendation: if staying in C#, consider Arch/Flecs.NET/DefaultECS or a plain data-table system. If moving to Rust, consider Bevy ECS later, but start with a pure Rust sim crate first. ## Recommended next step Do not immediately port the entire game. Port or rebuild the smallest closed loop: ```text items resources world grid one miner one factory one truck dispatch assignment movement production 1000 deterministic ticks ``` Desired CLI target: ```bash cargo test cargo run -p factory_cli -- --seed 1 --ticks 10000 ``` Desired output shape: ```json { "ticks": 10000, "iron_ore_mined": 1200, "iron_bars_produced": 350, "dispatches_assigned": 84, "dispatches_failed": 3, "deadlocks": 0 } ``` This converts the project from “Unity game prototype” into “simulation lab with a possible game frontend.” ## Recommended Rust stack Use Rust first, Bevy later. Suggested crates / layers: ```text factory_sim: - pure Rust - deterministic tick loop - no renderer - no Bevy dependency initially IDs: - slotmap, generational-arena, or stable integer IDs content: - serde - ron/toml/json/yaml tests: - cargo test - proptest later observability: - tracing cli: - clap viewer: - Bevy, macroquad, raylib, or a terminal/web viewer later ``` ## Important product/design interpretation The most promising direction is not “make Factorio again.” It is: > A programmable/autonomous logistics simulation where the player eventually designs or debugs protocols, schedulers, dispatch rules, production constraints, and agent behavior. The existing dispatch system already points in this direction. Treat that as the seed crystal. ## Suggested immediate agent task First agent task should be an architecture extraction pass, not feature work: 1. Inventory all `Core` classes and their Unity dependencies. 2. Identify the minimal pure sim subset. 3. Draft a Rust crate structure. 4. Port `GameContent.Item`, `ResourcesComponentCore`, and a tiny `GameState`. 5. Add deterministic tests for resource transfer and one production tick. 6. Add CLI runner after tests pass. 7. Only then consider rendering. Avoid starting with Unity editor cleanup or Bevy visualization. That will recreate engine gravity before the sim kernel exists.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-gaming/factory-game-v3#11
No description provided.