refactor: simplify component iteration and singleton handling

Update the ECS engine to support automatic mutation tracking for
singletons during iteration, removing the need for manual
`MarkModified` calls.

- Update `ComponentDiscoveryGenerator` to detect singleton components.
- Refactor `PlaceMarkCommand` and `WinCheckSystem` to leverage
  automatic mutation marking.
- Replace `ForEach` usage with `Select` and `ItemN` access in tests
  and systems to align with the updated iterator API.
This commit is contained in:
2026-07-21 09:15:27 +08:00
parent b98e8d66af
commit 96d732d6ab
6 changed files with 76 additions and 56 deletions
+10 -10
View File
@@ -15,15 +15,17 @@ public class WinCheckSystem : ISystem
if (world.ReadSingleton<GameState>().Status != GameStatus.Playing)
return;
// Get singleton ref inside iteration so all mutations are auto-marked
// by EndIteration — no MarkModified calls needed.
using var iter = world.Select<Cell, Mark>();
ref var state = ref world.GetSingleton<GameState>();
// Build a 3×3 grid of marks using the iterator API.
// Build a 3×3 grid of marks.
var grid = new Player[3, 3];
using var iter = world.Select<Cell, Mark>();
while (iter.MoveNext())
{
grid[iter.Current1.Row, iter.Current1.Col] = iter.Current2.Player;
grid[iter.Item1.Row, iter.Item1.Col] = iter.Item2.Player;
}
// Check rows.
@@ -31,7 +33,7 @@ public class WinCheckSystem : ISystem
{
if (TryGetWinner(grid[r, 0], grid[r, 1], grid[r, 2], out var winner))
{
SetWinner(world, ref state, winner);
SetWinner(ref state, winner);
return;
}
}
@@ -41,7 +43,7 @@ public class WinCheckSystem : ISystem
{
if (TryGetWinner(grid[0, c], grid[1, c], grid[2, c], out var winner))
{
SetWinner(world, ref state, winner);
SetWinner(ref state, winner);
return;
}
}
@@ -49,12 +51,12 @@ public class WinCheckSystem : ISystem
// Check diagonals.
if (TryGetWinner(grid[0, 0], grid[1, 1], grid[2, 2], out var diag1))
{
SetWinner(world, ref state, diag1);
SetWinner(ref state, diag1);
return;
}
if (TryGetWinner(grid[0, 2], grid[1, 1], grid[2, 0], out var diag2))
{
SetWinner(world, ref state, diag2);
SetWinner(ref state, diag2);
return;
}
@@ -62,7 +64,6 @@ public class WinCheckSystem : ISystem
if (state.MoveCount >= 9)
{
state.Status = GameStatus.Draw;
world.MarkModified<GameState>(World.SingletonEntity);
}
}
@@ -77,9 +78,8 @@ public class WinCheckSystem : ISystem
return false;
}
private static void SetWinner(World world, ref GameState state, Player winner)
private static void SetWinner(ref GameState state, Player winner)
{
state.Status = winner == Player.X ? GameStatus.XWon : GameStatus.OWon;
world.MarkModified<GameState>(World.SingletonEntity);
}
}