186 lines
5.6 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
// Class to handle registering and accessing objects by GUID
public class GuidManager
{
// for each GUID we need to know the Game Object it references
// and an event to store all the callbacks that need to know when it is destroyed
private struct GuidInfo
{
public GameObject go;
public event Action<GameObject> OnAdd;
public event Action OnRemove;
public GuidInfo(GuidComponent comp)
{
go = comp.gameObject;
OnRemove = null;
OnAdd = null;
}
public void HandleAddCallback()
{
if (OnAdd != null)
{
OnAdd(go);
}
}
public void HandleRemoveCallback()
{
if (OnRemove != null)
{
OnRemove();
}
}
}
// Singleton interface
static GuidManager Instance;
// All the public API is static so you need not worry about creating an instance
public static bool Add(GuidComponent guidComponent )
{
if (Instance == null)
{
Instance = new GuidManager();
}
return Instance.InternalAdd(guidComponent);
}
public static void Remove(System.Guid guid)
{
if (Instance == null)
{
Instance = new GuidManager();
}
Instance.InternalRemove(guid);
}
public static GameObject ResolveGuid(System.Guid guid, Action<GameObject> onAddCallback, Action onRemoveCallback)
{
if (Instance == null)
{
Instance = new GuidManager();
}
return Instance.ResolveGuidInternal(guid, onAddCallback, onRemoveCallback);
}
public static GameObject ResolveGuid(System.Guid guid, Action onDestroyCallback)
{
if (Instance == null)
{
Instance = new GuidManager();
}
return Instance.ResolveGuidInternal(guid, null, onDestroyCallback);
}
public static GameObject ResolveGuid(System.Guid guid)
{
if (Instance == null)
{
Instance = new GuidManager();
}
return Instance.ResolveGuidInternal(guid, null, null);
}
// instance data
private Dictionary<System.Guid, GuidInfo> guidToObjectMap;
private GuidManager()
{
guidToObjectMap = new Dictionary<System.Guid, GuidInfo>();
}
private bool InternalAdd(GuidComponent guidComponent)
{
Guid guid = guidComponent.GetGuid();
GuidInfo info = new GuidInfo(guidComponent);
if (!guidToObjectMap.ContainsKey(guid))
{
guidToObjectMap.Add(guid, info);
return true;
}
GuidInfo existingInfo = guidToObjectMap[guid];
if ( existingInfo.go != null && existingInfo.go != guidComponent.gameObject )
{
// normally, a duplicate GUID is a big problem, means you won't necessarily be referencing what you expect
if (Application.isPlaying)
{
Debug.AssertFormat(false, guidComponent, "Guid Collision Detected between {0} and {1}.\nAssigning new Guid. Consider tracking runtime instances using a direct reference or other method.", (guidToObjectMap[guid].go != null ? guidToObjectMap[guid].go.name : "NULL"), (guidComponent != null ? guidComponent.name : "NULL"));
}
else
{
// however, at editor time, copying an object with a GUID will duplicate the GUID resulting in a collision and repair.
// we warn about this just for pedantry reasons, and so you can detect if you are unexpectedly copying these components
Debug.LogWarningFormat(guidComponent, "Guid Collision Detected while creating {0}.\nAssigning new Guid.", (guidComponent != null ? guidComponent.name : "NULL"));
}
return false;
}
// if we already tried to find this GUID, but haven't set the game object to anything specific, copy any OnAdd callbacks then call them
existingInfo.go = info.go;
existingInfo.HandleAddCallback();
guidToObjectMap[guid] = existingInfo;
return true;
}
private void InternalRemove(System.Guid guid)
{
GuidInfo info;
if (guidToObjectMap.TryGetValue(guid, out info))
{
// trigger all the destroy delegates that have registered
info.HandleRemoveCallback();
}
guidToObjectMap.Remove(guid);
}
// nice easy api to find a GUID, and if it works, register an on destroy callback
// this should be used to register functions to cleanup any data you cache on finding
// your target. Otherwise, you might keep components in memory by referencing them
private GameObject ResolveGuidInternal(System.Guid guid, Action<GameObject> onAddCallback, Action onRemoveCallback)
{
GuidInfo info;
if (guidToObjectMap.TryGetValue(guid, out info))
{
if (onAddCallback != null)
{
info.OnAdd += onAddCallback;
}
if (onRemoveCallback != null)
{
info.OnRemove += onRemoveCallback;
}
guidToObjectMap[guid] = info;
return info.go;
}
if (onAddCallback != null)
{
info.OnAdd += onAddCallback;
}
if (onRemoveCallback != null)
{
info.OnRemove += onRemoveCallback;
}
guidToObjectMap.Add(guid, info);
return null;
}
}