M7 Automation: deterministic Harvester to Conveyor to Fabricator chains
Server-only production chains (never predicted): components + server systems + pure byte-only math (ProductionMath/ConveyorMath/MachineSlotMath), authoring + 3 machine prefabs wired into the Gameplay subscene, StructureCatalog rows, BuildPlace Direction/RuntimePlacedTag, Tuning, and 35 EditMode tests (catch-up gating, conveyor shuffle-invariance, SaveData v2 round-trip). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
using ProjectM.Simulation;
|
||||
using Unity.Entities;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ProjectM.Authoring
|
||||
{
|
||||
/// <summary>
|
||||
/// Authoring for a Conveyor belt ghost prefab. Bakes <see cref="PlacedStructure"/>{Type=Conveyor} +
|
||||
/// <see cref="Conveyor"/> (default facing; BuildPlaceSystem overrides Direction per placement from the RPC) +
|
||||
/// a DISABLED <see cref="ConveyorItem"/> (an empty belt). BuildPlaceSystem stamps the Cell; the transport
|
||||
/// system initializes the period gate on first encounter.
|
||||
/// </summary>
|
||||
public class ConveyorAuthoring : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Default belt facing (0=+X, 1=-X, 2=+Z, 3=-Z); the build RPC overrides this per placement.")]
|
||||
public byte Direction = 0;
|
||||
[Min(1)] public int PeriodTicks = 20;
|
||||
|
||||
private class ConveyorBaker : Baker<ConveyorAuthoring>
|
||||
{
|
||||
public override void Bake(ConveyorAuthoring authoring)
|
||||
{
|
||||
var entity = GetEntity(authoring, TransformUsageFlags.Dynamic);
|
||||
AddComponent(entity, new PlacedStructure
|
||||
{
|
||||
Type = StructureType.Conveyor,
|
||||
Cell = default,
|
||||
NextTick = 0u,
|
||||
LastProcessedTick = 0u,
|
||||
});
|
||||
AddComponent(entity, new Conveyor
|
||||
{
|
||||
Direction = authoring.Direction,
|
||||
PeriodTicks = authoring.PeriodTicks,
|
||||
});
|
||||
AddComponent(entity, new ConveyorItem { ResourceId = 0, Count = 0 });
|
||||
SetComponentEnabled<ConveyorItem>(entity, false); // baked empty (disabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 23131e6166dce204582bbedc8511658e
|
||||
@@ -0,0 +1,47 @@
|
||||
using ProjectM.Simulation;
|
||||
using Unity.Entities;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ProjectM.Authoring
|
||||
{
|
||||
/// <summary>
|
||||
/// Authoring for a Fabricator machine ghost prefab. Bakes <see cref="PlacedStructure"/>{Type=Fabricator} +
|
||||
/// <see cref="Fabricator"/> recipe + an empty <see cref="MachineInput"/> buffer (a conveyor fills it; the
|
||||
/// fabricator deposits its output directly into the GLOBAL ledger, so it needs no output buffer). Default
|
||||
/// recipe: 2 Ore -> 1 Aether (both existing resources — the "auto-gather existing resources" terminal).
|
||||
/// </summary>
|
||||
public class FabricatorAuthoring : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Input resource id consumed per run (1=Aether, 2=Ore, 3=Biomass).")]
|
||||
public byte InResourceId = 2; // Ore
|
||||
[Min(1)] public int InAmount = 2;
|
||||
[Tooltip("Output resource id deposited to the global ledger.")]
|
||||
public byte OutResourceId = 1; // Aether
|
||||
[Min(1)] public int OutAmount = 1;
|
||||
[Min(1)] public int PeriodTicks = 90;
|
||||
|
||||
private class FabricatorBaker : Baker<FabricatorAuthoring>
|
||||
{
|
||||
public override void Bake(FabricatorAuthoring authoring)
|
||||
{
|
||||
var entity = GetEntity(authoring, TransformUsageFlags.Dynamic);
|
||||
AddComponent(entity, new PlacedStructure
|
||||
{
|
||||
Type = StructureType.Fabricator,
|
||||
Cell = default,
|
||||
NextTick = 0u,
|
||||
LastProcessedTick = 0u,
|
||||
});
|
||||
AddComponent(entity, new Fabricator
|
||||
{
|
||||
InResourceId = authoring.InResourceId,
|
||||
InAmount = authoring.InAmount,
|
||||
OutResourceId = authoring.OutResourceId,
|
||||
OutAmount = authoring.OutAmount,
|
||||
PeriodTicks = authoring.PeriodTicks,
|
||||
});
|
||||
AddBuffer<MachineInput>(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 014833c467fb1d6499f437b7bf76db80
|
||||
@@ -0,0 +1,42 @@
|
||||
using ProjectM.Simulation;
|
||||
using Unity.Entities;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ProjectM.Authoring
|
||||
{
|
||||
/// <summary>
|
||||
/// Authoring for a Harvester machine ghost prefab (duplicate a structure ghost so the ownerless interpolated
|
||||
/// GhostAuthoringComponent comes free). Bakes <see cref="PlacedStructure"/>{Type=Harvester} + <see cref="Harvester"/>
|
||||
/// stats + an empty <see cref="MachineOutput"/> buffer. BuildPlaceSystem stamps the Cell at placement; the
|
||||
/// production system initializes the tick baseline on first encounter (NextTick/LastProcessedTick baked 0).
|
||||
/// </summary>
|
||||
public class HarvesterAuthoring : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Resource id this generator produces (1=Aether, 2=Ore, 3=Biomass).")]
|
||||
public byte OutputResourceId = 2; // Ore
|
||||
[Min(1)] public int Yield = 1;
|
||||
[Min(1)] public int PeriodTicks = 60;
|
||||
|
||||
private class HarvesterBaker : Baker<HarvesterAuthoring>
|
||||
{
|
||||
public override void Bake(HarvesterAuthoring authoring)
|
||||
{
|
||||
var entity = GetEntity(authoring, TransformUsageFlags.Dynamic);
|
||||
AddComponent(entity, new PlacedStructure
|
||||
{
|
||||
Type = StructureType.Harvester,
|
||||
Cell = default,
|
||||
NextTick = 0u,
|
||||
LastProcessedTick = 0u,
|
||||
});
|
||||
AddComponent(entity, new Harvester
|
||||
{
|
||||
ResourceId = authoring.OutputResourceId,
|
||||
Yield = authoring.Yield,
|
||||
PeriodTicks = authoring.PeriodTicks,
|
||||
});
|
||||
AddBuffer<MachineOutput>(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d69d7296747332b4fbe7901ec5210149
|
||||
Reference in New Issue
Block a user