#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Graph Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.GraphDesigner.Runtime.Variables
{
using System;
using System.Collections.Generic;
using Opsive.GraphDesigner.Runtime.ECS.Core;
using Unity.Entities;
using UnityEngine;
///
/// Managed registry created once per entity during graph initialization. Collects SharedVariable registrations from all ECS tasks, deduplicates by name+scope,
/// bakes initial values into DynamicBuffer, and supports syncing runtime values between the ECS buffer and managed SharedVariables.
///
public class ECSVariableRegistry
{
private readonly ECSVariableRegistryCore m_Core = new();
private readonly List>> m_SyncToManagedActions = new();
private readonly List>> m_SyncToECSActions = new();
///
/// Gets the number of variables registered so far.
///
public int Count => m_Core.Count;
///
/// Returns authoring metadata for a registered SharedVariable.
///
/// The SharedVariableElement buffer index.
/// The registered variable name.
/// The registered variable value type.
/// True if the index maps to a tracked SharedVariable.
public bool TryGetVariableMetadata(int index, out string name, out Type elementType)
{
return m_Core.TryGetVariableMetadata(index, out name, out elementType);
}
///
/// Registers a SharedVariable with the registry and returns its buffer index.
///
/// The SharedVariable that should be registered.
/// The buffer index of the registered SharedVariable, or -1 if the SharedVariable is null.
public int Register(SharedVariable sharedVariable) where T : unmanaged
{
if (sharedVariable == null) {
return -1;
}
var size = SharedVariableBufferCore.SizeOf();
Debug.Assert(size <= SharedVariableBufferCore.MaxValueSize, $"SharedVariable<{typeof(T).Name}> value size ({size} bytes) exceeds the {SharedVariableBufferCore.MaxValueSize}-byte SharedVariableElement limit. Use a smaller unmanaged type.");
if (m_Core.TryGetExistingIndex(sharedVariable, out var existingIndex)) {
return existingIndex;
}
var index = m_Core.Register(sharedVariable);
if (index < 0) {
return index;
}
// Capture sync delegates so managed and ECS tasks stay in sync while the tree is running.
var capturedVar = sharedVariable;
var capturedIndex = index;
m_SyncToManagedActions.Add((buffer) => {
capturedVar.Value = buffer.Get(capturedIndex);
});
m_SyncToECSActions.Add((buffer) => {
buffer.Set(capturedIndex, capturedVar.Value);
});
return index;
}
///
/// Creates the DynamicBuffer on the entity and writes all registered initial values. Call once after all tasks have registered their variables.
///
/// The world that owns the entity.
/// The entity that should receive the shared variable buffer.
public void Bake(World world, Entity entity)
{
if (m_Core.Count == 0) {
return;
}
DynamicBuffer buffer;
if (world.EntityManager.HasBuffer(entity)) {
buffer = world.EntityManager.GetBuffer(entity);
buffer.Clear();
} else {
buffer = world.EntityManager.AddBuffer(entity);
}
for (int i = 0; i < m_Core.Count; ++i) {
buffer.Add(new SharedVariableElement { Value = m_Core.GetInitialValue(i) });
}
}
///
/// Writes the current managed SharedVariable values into the ECS buffer.
///
/// The world that owns the entity.
/// The entity whose shared variable buffer should be synced.
public void SyncToECS(World world, Entity entity)
{
if (m_SyncToECSActions.Count == 0 || world == null || entity == Entity.Null) {
return;
}
if (!world.EntityManager.HasBuffer(entity)) {
return;
}
var buffer = world.EntityManager.GetBuffer(entity);
for (int i = 0; i < m_SyncToECSActions.Count; ++i) {
if (!m_Core.IsManagedValueDirty(i)) {
continue;
}
m_SyncToECSActions[i](buffer);
m_Core.MarkSyncedToECS(i, buffer[i].Value);
}
}
///
/// Reads current ECS buffer values back into the managed SharedVariable instances.
///
/// The world that owns the entity.
/// The entity whose shared variable buffer should be synced.
public void SyncToManaged(World world, Entity entity)
{
if (m_SyncToManagedActions.Count == 0 || world == null || entity == Entity.Null) {
return;
}
if (!world.EntityManager.HasBuffer(entity)) {
return;
}
var buffer = world.EntityManager.GetBuffer(entity);
m_Core.SuppressManagedValueTracking(() => {
for (int i = 0; i < m_SyncToManagedActions.Count; ++i) {
var bufferValue = buffer[i].Value;
if (!m_Core.ShouldSyncToManaged(i, bufferValue)) {
continue;
}
m_SyncToManagedActions[i](buffer);
m_Core.MarkSyncedToManaged(i, bufferValue);
}
});
}
///
/// Removes any managed SharedVariable change listeners registered by the registry.
///
public void Dispose()
{
m_Core.Dispose();
}
}
}
#endif