202 lines
6.8 KiB
C#
202 lines
6.8 KiB
C#
using System.Collections.Generic;
|
|
using Minesweeper.Core;
|
|
using Minesweeper.ECS.Components;
|
|
using Unity.Collections;
|
|
using Unity.Entities;
|
|
|
|
namespace Minesweeper.ECS
|
|
{
|
|
public sealed class BoardEcsSyncService : IBoardEcsSyncService
|
|
{
|
|
private readonly Dictionary<int, Entity> cellsByIndex = new Dictionary<int, Entity>();
|
|
private int syncedWidth;
|
|
private int syncedHeight;
|
|
|
|
public void ClearBoard()
|
|
{
|
|
if (!TryGetEntityManager(out var entityManager))
|
|
{
|
|
return;
|
|
}
|
|
|
|
ClearCells(entityManager);
|
|
cellsByIndex.Clear();
|
|
syncedWidth = 0;
|
|
syncedHeight = 0;
|
|
}
|
|
|
|
public void SyncBoard(IBoardService boardService)
|
|
{
|
|
if (!TryGetEntityManager(out var entityManager))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (syncedWidth != boardService.Width || syncedHeight != boardService.Height || cellsByIndex.Count != boardService.Width * boardService.Height)
|
|
{
|
|
ClearCells(entityManager);
|
|
cellsByIndex.Clear();
|
|
syncedWidth = boardService.Width;
|
|
syncedHeight = boardService.Height;
|
|
}
|
|
else
|
|
{
|
|
ClearChangedTags(entityManager);
|
|
}
|
|
|
|
var boardEntity = GetOrCreateSingleton<BoardConfigComponent>(entityManager);
|
|
entityManager.SetComponentData(boardEntity, new BoardConfigComponent
|
|
{
|
|
Width = boardService.Width,
|
|
Height = boardService.Height,
|
|
MinesCount = boardService.MinesCount,
|
|
OpenedSafeCellsCount = boardService.OpenedSafeCellsCount,
|
|
FlaggedCellsCount = boardService.FlaggedCellsCount,
|
|
IsGenerated = ToByte(boardService.IsGenerated)
|
|
});
|
|
|
|
var archetype = entityManager.CreateArchetype(typeof(CellComponent));
|
|
var cells = boardService.GetCells();
|
|
for (var i = 0; i < cells.Count; i++)
|
|
{
|
|
var cell = cells[i];
|
|
var index = ToIndex(cell.X, cell.Y, boardService.Width);
|
|
if (!cellsByIndex.TryGetValue(index, out var entity) || !entityManager.Exists(entity))
|
|
{
|
|
entity = entityManager.CreateEntity(archetype);
|
|
cellsByIndex[index] = entity;
|
|
}
|
|
|
|
SetCell(entityManager, entity, cell, boardService.Width, false);
|
|
}
|
|
}
|
|
|
|
public void SyncCells(IReadOnlyList<BoardCellData> cells, IBoardService boardService)
|
|
{
|
|
if (cells == null || cells.Count == 0 || !TryGetEntityManager(out var entityManager))
|
|
{
|
|
return;
|
|
}
|
|
|
|
var boardEntity = GetOrCreateSingleton<BoardConfigComponent>(entityManager);
|
|
entityManager.SetComponentData(boardEntity, new BoardConfigComponent
|
|
{
|
|
Width = boardService.Width,
|
|
Height = boardService.Height,
|
|
MinesCount = boardService.MinesCount,
|
|
OpenedSafeCellsCount = boardService.OpenedSafeCellsCount,
|
|
FlaggedCellsCount = boardService.FlaggedCellsCount,
|
|
IsGenerated = ToByte(boardService.IsGenerated)
|
|
});
|
|
|
|
ClearChangedTags(entityManager);
|
|
|
|
for (var i = 0; i < cells.Count; i++)
|
|
{
|
|
var cell = cells[i];
|
|
var index = ToIndex(cell.X, cell.Y, boardService.Width);
|
|
if (cellsByIndex.TryGetValue(index, out var entity) && entityManager.Exists(entity))
|
|
{
|
|
SetCell(entityManager, entity, cell, boardService.Width, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SyncGameState(GameState state, bool hasFirstClick)
|
|
{
|
|
if (!TryGetEntityManager(out var entityManager))
|
|
{
|
|
return;
|
|
}
|
|
|
|
var stateEntity = GetOrCreateSingleton<GameStateComponent>(entityManager);
|
|
entityManager.SetComponentData(stateEntity, new GameStateComponent
|
|
{
|
|
State = state,
|
|
HasFirstClick = ToByte(hasFirstClick)
|
|
});
|
|
}
|
|
|
|
private static bool TryGetEntityManager(out EntityManager entityManager)
|
|
{
|
|
var world = World.DefaultGameObjectInjectionWorld;
|
|
if (world == null || !world.IsCreated)
|
|
{
|
|
entityManager = default;
|
|
return false;
|
|
}
|
|
|
|
entityManager = world.EntityManager;
|
|
return true;
|
|
}
|
|
|
|
private static void ClearCells(EntityManager entityManager)
|
|
{
|
|
var query = entityManager.CreateEntityQuery(typeof(CellComponent));
|
|
entityManager.DestroyEntity(query);
|
|
query.Dispose();
|
|
}
|
|
|
|
private static void ClearChangedTags(EntityManager entityManager)
|
|
{
|
|
var query = entityManager.CreateEntityQuery(typeof(CellChangedTag));
|
|
entityManager.RemoveComponent<CellChangedTag>(query);
|
|
query.Dispose();
|
|
}
|
|
|
|
private static void SetCell(EntityManager entityManager, Entity entity, BoardCellData cell, int width, bool markChanged)
|
|
{
|
|
entityManager.SetComponentData(entity, new CellComponent
|
|
{
|
|
X = cell.X,
|
|
Y = cell.Y,
|
|
Index = ToIndex(cell.X, cell.Y, width),
|
|
IsMine = ToByte(cell.IsMine),
|
|
IsOpened = ToByte(cell.IsOpened),
|
|
IsFlagged = ToByte(cell.IsFlagged),
|
|
NeighborMines = cell.NeighborMines
|
|
});
|
|
|
|
if (markChanged && !entityManager.HasComponent<CellChangedTag>(entity))
|
|
{
|
|
entityManager.AddComponent<CellChangedTag>(entity);
|
|
}
|
|
}
|
|
|
|
private static int ToIndex(int x, int y, int width)
|
|
{
|
|
return y * width + x;
|
|
}
|
|
|
|
private static Entity GetOrCreateSingleton<T>(EntityManager entityManager) where T : unmanaged, IComponentData
|
|
{
|
|
var query = entityManager.CreateEntityQuery(typeof(T));
|
|
Entity entity;
|
|
|
|
if (query.IsEmptyIgnoreFilter)
|
|
{
|
|
entity = entityManager.CreateEntity(typeof(T));
|
|
}
|
|
else
|
|
{
|
|
var entities = query.ToEntityArray(Allocator.Temp);
|
|
entity = entities[0];
|
|
for (var i = 1; i < entities.Length; i++)
|
|
{
|
|
entityManager.DestroyEntity(entities[i]);
|
|
}
|
|
|
|
entities.Dispose();
|
|
}
|
|
|
|
query.Dispose();
|
|
return entity;
|
|
}
|
|
|
|
private static byte ToByte(bool value)
|
|
{
|
|
return value ? (byte)1 : (byte)0;
|
|
}
|
|
}
|
|
}
|