using ProjectM.Simulation; using Unity.Burst; using Unity.Collections; using Unity.Entities; using Unity.Mathematics; using Unity.Transforms; namespace ProjectM.Server { /// /// Server-only, one-shot dummy spawner. On its first update it reads the baked /// singleton and instantiates Count training-dummy /// ghosts in a row, spaced Spacing world-units apart along +X starting at Origin. /// It then destroys the spawner singleton entity so RequireForUpdate /// is no longer satisfied and the system stops running (idempotent: dummies are spawned exactly once). /// Runs in the default (NOT the prediction loop) since spawning is /// a non-predicted, server-authoritative event; the dummies replicate to clients as interpolated ghosts. /// All structural changes are batched through an . /// [BurstCompile] [WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)] public partial struct TrainingDummySpawnSystem : ISystem { [BurstCompile] public void OnCreate(ref SystemState state) { state.RequireForUpdate(); } [BurstCompile] public void OnUpdate(ref SystemState state) { // Grab both the singleton entity (to destroy when done) and its baked config. var spawnerEntity = SystemAPI.GetSingletonEntity(); var spawner = SystemAPI.GetComponent(spawnerEntity); var ecb = new EntityCommandBuffer(Allocator.Temp); for (int i = 0; i < spawner.Count; i++) { var dummy = ecb.Instantiate(spawner.Prefab); var position = spawner.Origin + new float3(i * spawner.Spacing, 0f, 0f); ecb.SetComponent(dummy, LocalTransform.FromPosition(position)); } // One-shot: remove the spawner so RequireForUpdate fails and the system idles. ecb.DestroyEntity(spawnerEntity); ecb.Playback(state.EntityManager); } } }