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 componentInstants; private static Dictionary> methodInstants; private static Dictionary> makeContextInstants; private static Dictionary> getterMethodInstants; private static Dictionary componentOrders; private static Dictionary> methodOrders; private static Queue> messageComponents; public static void Init() { if (componentInstants != null) return; componentInstants = new Dictionary(); componentInstants.CreateInstanceDictionary(); var methodInfos = new Dictionary>(); methodInfos.CreateMethodInfoDictionary(false, typeof(Unit), typeof(SLEntity), typeof(SLEntity)); methodInstants = new Dictionary>(); foreach (var methodInstant in methodInfos) { methodInstants.Add(methodInstant.Key, new Dictionary()); foreach (var method in methodInstant.Value) { methodInstants[methodInstant.Key][method.Key] = (ComponentMethod)method.Value.CreateDelegate(typeof(ComponentMethod), componentInstants[methodInstant.Key]); } } var getterMethodInfos = new Dictionary>(); getterMethodInfos.CreateMethodInfoDictionary(true, typeof(SLEntity)); getterMethodInstants = new Dictionary>(); foreach (var staticMethodInstant in getterMethodInfos) { getterMethodInstants.Add(staticMethodInstant.Key, new Dictionary()); foreach (var method in staticMethodInstant.Value) { getterMethodInstants[staticMethodInstant.Key][method.Key] = (GetterMethod)method.Value.CreateDelegate(typeof(GetterMethod), componentInstants[staticMethodInstant.Key]); } } var makeContextMethodInfos = new Dictionary>(); makeContextMethodInfos.CreateMethodInfoDictionary(false, typeof(SLEntity), typeof(IUnit), typeof(SLEntity)); makeContextInstants = new Dictionary>(); foreach (var makeContextInstant in makeContextMethodInfos) { makeContextInstants.Add(makeContextInstant.Key, new Dictionary()); foreach (var method in makeContextInstant.Value) { makeContextInstants[makeContextInstant.Key][method.Key] = (MakeContextMethod)method.Value.CreateDelegate(typeof(MakeContextMethod), componentInstants[makeContextInstant.Key]); } } componentOrders = new Dictionary(); methodOrders = new Dictionary>(); foreach (var component in componentInstants) { var type = component.Value.GetType(); var name = component.Key; var componentOrder = type.GetCustomAttribute()?.order ?? 0; componentOrders.Add(name, componentOrder); if (methodOrders.ContainsKey(name) == false) methodOrders.Add(name, new Dictionary()); var methodOrder = methodOrders[name]; if (methodInfos.TryGetValue(component.Key, out var methodCahces)) { foreach (var methodInfo in methodCahces) { var attribute = methodInfo.Value.GetCustomAttribute(); var method = attribute?.methodOverride ?? methodInfo.Value.Name; var order = attribute?.order ?? componentOrder; methodOrder.Add(method, order); } } } messageComponents = new Queue>(); } public static void Begin(Unit unit) { if (unit.components is null) { unit.components = new SortedList>(); unit.componentMessages = new Dictionary>>(); unit.startComponents = new List(); } 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(); 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>(); var messages = unit.componentMessages[method]; if (messages.ContainsKey(order) == false) messages[order] = new List(); 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>(); var messages = unit.componentMessages[method]; if (messages.ContainsKey(order) == false) messages[order] = new List(); 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(); 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 components; if (messageComponents.Count != 0) { components = messageComponents.Dequeue(); } else { components = new List(); } 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; } } }