ProjectDDD/Packages/SLUnity/UnitSytem/UnitHandler.cs

354 lines
14 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Superlazy
{
public static class UnitHandler
{
private delegate void ComponentMethod(Unit unit, SLEntity component, SLEntity context);
private delegate void MakeContextMethod(SLEntity context, IUnit unit, SLEntity addContext);
private delegate SLEntity GetterMethod();
private static Dictionary<string, UnitComponent> componentInstants;
private static Dictionary<string, Dictionary<string, ComponentMethod>> methodInstants;
private static Dictionary<string, Dictionary<string, MakeContextMethod>> makeContextInstants;
private static Dictionary<string, Dictionary<string, GetterMethod>> getterMethodInstants;
private static Dictionary<string, int> componentOrders;
private static Dictionary<string, Dictionary<string, int>> methodOrders;
private static Queue<List<SLEntity>> messageComponents;
public static void Init()
{
if (componentInstants != null) return;
componentInstants = new Dictionary<string, UnitComponent>();
componentInstants.CreateInstanceDictionary();
var methodInfos = new Dictionary<string, Dictionary<string, MethodInfo>>();
methodInfos.CreateMethodInfoDictionary<UnitComponent>(false, typeof(Unit), typeof(SLEntity), typeof(SLEntity));
methodInstants = new Dictionary<string, Dictionary<string, ComponentMethod>>();
foreach (var methodInstant in methodInfos)
{
methodInstants.Add(methodInstant.Key, new Dictionary<string, ComponentMethod>());
foreach (var method in methodInstant.Value)
{
methodInstants[methodInstant.Key][method.Key] = (ComponentMethod)method.Value.CreateDelegate(typeof(ComponentMethod), componentInstants[methodInstant.Key]);
}
}
var getterMethodInfos = new Dictionary<string, Dictionary<string, MethodInfo>>();
getterMethodInfos.CreateMethodInfoDictionary<UnitComponent>(true, typeof(SLEntity));
getterMethodInstants = new Dictionary<string, Dictionary<string, GetterMethod>>();
foreach (var staticMethodInstant in getterMethodInfos)
{
getterMethodInstants.Add(staticMethodInstant.Key, new Dictionary<string, GetterMethod>());
foreach (var method in staticMethodInstant.Value)
{
getterMethodInstants[staticMethodInstant.Key][method.Key] = (GetterMethod)method.Value.CreateDelegate(typeof(GetterMethod), componentInstants[staticMethodInstant.Key]);
}
}
var makeContextMethodInfos = new Dictionary<string, Dictionary<string, MethodInfo>>();
makeContextMethodInfos.CreateMethodInfoDictionary<UnitComponent>(false, typeof(SLEntity), typeof(IUnit), typeof(SLEntity));
makeContextInstants = new Dictionary<string, Dictionary<string, MakeContextMethod>>();
foreach (var makeContextInstant in makeContextMethodInfos)
{
makeContextInstants.Add(makeContextInstant.Key, new Dictionary<string, MakeContextMethod>());
foreach (var method in makeContextInstant.Value)
{
makeContextInstants[makeContextInstant.Key][method.Key] = (MakeContextMethod)method.Value.CreateDelegate(typeof(MakeContextMethod), componentInstants[makeContextInstant.Key]);
}
}
componentOrders = new Dictionary<string, int>();
methodOrders = new Dictionary<string, Dictionary<string, int>>();
foreach (var component in componentInstants)
{
var type = component.Value.GetType();
var name = component.Key;
var componentOrder = type.GetCustomAttribute<ComponentOrderAttribute>()?.order ?? 0;
componentOrders.Add(name, componentOrder);
if (methodOrders.ContainsKey(name) == false) methodOrders.Add(name, new Dictionary<string, int>());
var methodOrder = methodOrders[name];
if (methodInfos.TryGetValue(component.Key, out var methodCahces))
{
foreach (var methodInfo in methodCahces)
{
var attribute = methodInfo.Value.GetCustomAttribute<ComponentOrderAttribute>();
var method = attribute?.methodOverride ?? methodInfo.Value.Name;
var order = attribute?.order ?? componentOrder;
methodOrder.Add(method, order);
}
}
}
messageComponents = new Queue<List<SLEntity>>();
}
public static void Begin(Unit unit)
{
if (unit.components is null)
{
unit.components = new SortedList<int, List<SLEntity>>();
unit.componentMessages = new Dictionary<string, SortedList<int, List<SLEntity>>>();
unit.startComponents = new List<SLEntity>();
}
foreach (var component in unit.Entity["Components"].Where(c => c["Run"] == false))
{
var name = component["Component"];
var componentOrder = componentOrders[component["Component"]];
if (unit.components.ContainsKey(componentOrder) == false) unit.components[componentOrder] = new List<SLEntity>();
unit.components[componentOrder].Add(component);
foreach (var methodOrder in methodOrders[name])
{
var method = methodOrder.Key;
var order = methodOrder.Value;
if (unit.componentMessages.ContainsKey(method) == false) unit.componentMessages[method] = new SortedList<int, List<SLEntity>>();
var messages = unit.componentMessages[method];
if (messages.ContainsKey(order) == false) messages[order] = new List<SLEntity>();
messages[order].Add(component);
}
}
foreach (var componentOrders in unit.components)
{
foreach (var component in componentOrders.Value)
{
component["Run"] = true;
componentInstants[component["Component"]].Begin(unit, component);
}
}
}
// 성능을 위해 run체크를 안하기 위함. Start와 유사
public static void AddComponent(Unit unit, string key, string name, SLEntity context)
{
if (unit.Entity["Components"].HasChild(key))
{
var component = unit.Entity["Components"][key];
if (component.HasChild("End") == false) SLLog.Error($"component cannot be added. {key}:{component} already exists");
componentInstants[component["Component"]].End(unit, component);
unit.components[componentOrders[name]].Remove(component);
foreach (var methodOrder in methodOrders[name])
{
unit.componentMessages[methodOrder.Key][methodOrder.Value].Remove(component);
}
unit.Entity["Components"][component.ID] = false;
}
unit.Entity["Components"][key] = context;
unit.Entity["Components"][key]["Component"] = name;
{
var component = unit.Entity["Components"][key];
foreach (var methodOrder in methodOrders[name])
{
var method = methodOrder.Key;
var order = methodOrder.Value;
if (unit.componentMessages.ContainsKey(method) == false) unit.componentMessages[method] = new SortedList<int, List<SLEntity>>();
var messages = unit.componentMessages[method];
if (messages.ContainsKey(order) == false) messages[order] = new List<SLEntity>();
messages[order].Add(component);
}
{
component["Run"] = true;
componentInstants[name].Begin(unit, component);
unit.startComponents.Add(component); // AddComponent 때에는 딜레이 Add가 되어야해서 별도로 관리
}
}
}
public static void Update(Unit unit)
{
// 컴포넌트 업데이트는 추가된 다음프레임부터 발생, 그전에는 AddComponent등에서 메시지는 세팅 가능하다
//TODO: Run = false 등의 코드로 재갱신이 들어간다면 추가 필요
foreach (var component in unit.startComponents)
{
var componentOrder = componentOrders[component["Component"]];
if (unit.components.ContainsKey(componentOrder) == false) unit.components[componentOrder] = new List<SLEntity>();
unit.components[componentOrder].Add(component);
}
unit.startComponents.Clear();
foreach (var components in unit.components)
{
foreach (var component in components.Value)
{
if (component.HasChild("Run") && component.HasChild("End") == false)
{
componentInstants[component["Component"]].Update(unit, component);
}
}
}
foreach (var components in unit.components)
{
components.Value.RemoveAll(component =>
{
if (component.HasChild("End"))
{
string name = component["Component"];
componentInstants[name].End(unit, component);
foreach (var methodOrder in methodOrders[name])
{
unit.componentMessages[methodOrder.Key][methodOrder.Value].Remove(component);
}
unit.Entity["Components"][component.ID] = false;
return true;
}
return false;
});
}
}
public static void End(Unit unit)
{
// 유닛내의 변수들을 굳이 지우지 않아도 Unit 객체자체를 재사용하지 않아 알아서 가비지가 될 예정
foreach (var components in unit.components)
{
foreach (var component in components.Value)
{
if (component.HasChild("Run") && component.HasChild("End") == false)
{
componentInstants[component["Component"]].End(unit, component);
}
}
}
}
public static void Message(string message, Unit unit, SLEntity context)
{
if (unit.componentMessages.ContainsKey(message))
{
List<SLEntity> components;
if (messageComponents.Count != 0)
{
components = messageComponents.Dequeue();
}
else
{
components = new List<SLEntity>();
}
foreach (var messages in unit.componentMessages[message])
{
components.AddRange(messages.Value);
}
foreach (var component in components)
{
if (component.HasChild("Run") == false) continue;
if (component.HasChild("End")) continue;
string name = component["Component"];
var method = methodInstants[name][message];
method(unit, component, context);
}
components.Clear();
messageComponents.Enqueue(components);
}
unit.Zone.Event(unit, message, context);
}
public static void MakeContext(string message, string name, SLEntity context, IUnit unit, SLEntity addContext)
{
if (makeContextInstants.TryGetValue(name, out var ms))
{
if (ms.TryGetValue(message, out var m))
{
m(context, unit, addContext);
}
}
}
public static SLEntity GetStaticMethodValue(string message, string name)
{
if (getterMethodInstants.TryGetValue(name, out var m))
{
if (m.TryGetValue(message, out var method))
{
return method();
}
}
return SLEntity.Empty;
}
// TODO: 제거?
public static SLEntity DispatchMessage(string message, string compnentName, Unit unit, SLEntity component, SLEntity context)
{
if (methodInstants[compnentName].ContainsKey(message))
{
if (context)
{
context = context.Override();
}
else
{
context = SLEntity.Empty;
}
if (component)
{
component.Override();
}
else
{
component = SLEntity.Empty;
}
methodInstants[compnentName][message]?.Invoke(unit, component, context);
}
return context;
}
public static SLEntity UpdateComponentMessage(string message, string compnentName, Unit unit, SLEntity component, SLEntity context)
{
if (methodInstants[compnentName].ContainsKey(message))
{
if (context)
{
context = context.Override();
}
else
{
context = SLEntity.Empty;
}
methodInstants[compnentName][message]?.Invoke(unit, component, context);
}
return context;
}
}
}