2025-07-10 15:08:20 +08:00
|
|
|
from .Config import *
|
|
|
|
from .Reflection import *
|
2025-07-11 01:36:10 +08:00
|
|
|
from abc import ABC, abstractmethod
|
2025-07-10 15:08:20 +08:00
|
|
|
|
|
|
|
class ISignal(ABC):
|
|
|
|
pass
|
|
|
|
|
2025-09-25 11:52:26 +08:00
|
|
|
|
2025-07-10 15:08:20 +08:00
|
|
|
class IModel(ABC):
|
2025-09-25 11:52:26 +08:00
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class IDataModel(ABC, IModel):
|
2025-07-11 01:36:10 +08:00
|
|
|
@abstractmethod
|
2025-07-10 15:08:20 +08:00
|
|
|
def Save(self) -> str:
|
|
|
|
pass
|
2025-09-25 11:52:26 +08:00
|
|
|
|
2025-07-11 01:36:10 +08:00
|
|
|
@abstractmethod
|
2025-07-10 15:08:20 +08:00
|
|
|
def Load(self, data:str) -> None:
|
|
|
|
pass
|
|
|
|
|
2025-09-25 11:52:26 +08:00
|
|
|
|
2025-07-10 15:08:20 +08:00
|
|
|
class IConvertable[T](ABC):
|
2025-07-11 01:36:10 +08:00
|
|
|
@abstractmethod
|
|
|
|
def ConvertTo(self) -> T:
|
2025-07-10 15:08:20 +08:00
|
|
|
pass
|
|
|
|
|
2025-09-25 11:52:26 +08:00
|
|
|
|
2025-07-11 01:36:10 +08:00
|
|
|
class IConvertModel[T](IConvertable[T], IModel):
|
2025-07-10 15:08:20 +08:00
|
|
|
pass
|
|
|
|
|
2025-09-25 11:52:26 +08:00
|
|
|
|
2025-07-10 15:08:20 +08:00
|
|
|
class SingletonModel[T](IModel):
|
2025-07-12 22:16:23 +08:00
|
|
|
_InjectInstances:Dict[type,Any] = {}
|
2025-07-10 15:08:20 +08:00
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def GetInstance(t:Typen[T]) -> T:
|
2025-07-11 01:36:10 +08:00
|
|
|
return SingletonModel._InjectInstances[t]
|
2025-09-25 11:52:26 +08:00
|
|
|
|
2025-07-10 15:08:20 +08:00
|
|
|
@staticmethod
|
|
|
|
def SetInstance(t:Typen[T], obj:T) -> None:
|
2025-07-11 01:36:10 +08:00
|
|
|
SingletonModel._InjectInstances[t] = obj
|
2025-07-10 15:08:20 +08:00
|
|
|
|
|
|
|
def __init__(self, t:Typen[T]) -> None:
|
|
|
|
self.typen: type = t
|
|
|
|
|
|
|
|
|
2025-07-11 01:36:10 +08:00
|
|
|
class DependenceModel(IConvertModel[bool]):
|
2025-07-10 15:08:20 +08:00
|
|
|
def __init__(self, queries:Sequence[IConvertModel[bool]]) -> None:
|
2025-07-11 01:36:10 +08:00
|
|
|
self.queries:list[IConvertModel[bool]] = list(queries)
|
2025-07-10 15:08:20 +08:00
|
|
|
|
2025-07-11 01:36:10 +08:00
|
|
|
@override
|
2025-07-10 15:08:20 +08:00
|
|
|
def ConvertTo(self) -> bool:
|
|
|
|
for query in self.queries:
|
|
|
|
if query.ConvertTo() == False:
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
def __iter__(self):
|
2025-07-11 01:36:10 +08:00
|
|
|
return iter(self.queries)
|
2025-07-10 15:08:20 +08:00
|
|
|
|
|
|
|
|
2025-07-11 01:36:10 +08:00
|
|
|
SignalListener = Callable[[ISignal], None]
|
|
|
|
|
2025-09-25 11:52:26 +08:00
|
|
|
|
2025-07-11 01:36:10 +08:00
|
|
|
class Architecture:
|
2025-07-10 15:08:20 +08:00
|
|
|
@staticmethod
|
|
|
|
def FormatType(t:type) -> str:
|
|
|
|
return f"{t.__module__}::{t.__name__}"
|
|
|
|
|
|
|
|
@staticmethod
|
2025-07-11 01:36:10 +08:00
|
|
|
def LoadFromFormat(data:str, exception:Exception|None=None) -> type|None:
|
|
|
|
try:
|
|
|
|
module,name = data.split("::")
|
|
|
|
return StringWithModel2Type(name, module=module)
|
|
|
|
except Exception as ex:
|
|
|
|
if exception is not None:
|
|
|
|
exception = ex
|
|
|
|
return None
|
2025-07-10 15:08:20 +08:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def InternalReset(cls) -> None:
|
|
|
|
# Register System
|
2025-09-25 11:52:26 +08:00
|
|
|
cls._RegisteredObjects.clear()
|
|
|
|
cls._RegisteringRuntime.clear()
|
|
|
|
# Signal Listener
|
2025-07-11 01:36:10 +08:00
|
|
|
cls._SignalListener.clear()
|
2025-09-25 11:52:26 +08:00
|
|
|
# Timeline/Chain
|
2025-07-11 01:36:10 +08:00
|
|
|
cls._TimelineQueues.clear()
|
|
|
|
cls._TimelineContentID = 0
|
2025-07-10 15:08:20 +08:00
|
|
|
|
|
|
|
#region Objects Registered
|
|
|
|
|
|
|
|
class TypeQuery(IConvertModel[bool]):
|
|
|
|
def __init__(self, queryType:type) -> None:
|
2025-07-11 01:36:10 +08:00
|
|
|
self._queryType = queryType
|
2025-07-10 15:08:20 +08:00
|
|
|
|
|
|
|
@override
|
|
|
|
def ConvertTo(self) -> bool:
|
2025-09-25 11:52:26 +08:00
|
|
|
return self._queryType in Architecture._RegisteredObjects
|
2025-07-10 15:08:20 +08:00
|
|
|
|
|
|
|
class Registering(IConvertModel[bool]):
|
2025-09-25 11:52:26 +08:00
|
|
|
def __init__(self, registerSlot:type, target:Any, dependences:DependenceModel, action:Action) -> None:
|
|
|
|
self.registerSlot = registerSlot
|
|
|
|
self.target = target
|
|
|
|
self.dependences = dependences
|
|
|
|
self.action = action
|
2025-07-10 15:08:20 +08:00
|
|
|
|
|
|
|
@override
|
2025-09-25 11:52:26 +08:00
|
|
|
def ConvertTo(self) -> bool:
|
|
|
|
return self.dependences.ConvertTo()
|
2025-07-10 15:08:20 +08:00
|
|
|
|
2025-09-25 11:52:26 +08:00
|
|
|
_RegisteringRuntime: Dict[type, Registering] = {}
|
|
|
|
_RegisteredObjects: Dict[type, Any] = {}
|
2025-07-10 15:08:20 +08:00
|
|
|
|
|
|
|
@classmethod
|
2025-09-25 11:52:26 +08:00
|
|
|
def _InternalRegisteringComplete(cls) -> None:
|
|
|
|
CompletedSet: Set[Architecture.Registering] = set()
|
|
|
|
for dependence in cls._RegisteringRuntime.keys():
|
|
|
|
if cls._RegisteringRuntime[dependence].dependences.ConvertTo():
|
|
|
|
CompletedSet.add(cls._RegisteringRuntime[dependence])
|
|
|
|
for complete in CompletedSet:
|
|
|
|
del cls._RegisteringRuntime[complete.registerSlot]
|
|
|
|
complete.action()
|
|
|
|
cls._RegisteredObjects[complete.registerSlot] = complete.target
|
|
|
|
if len(CompletedSet) > 0:
|
|
|
|
cls._InternalRegisteringComplete()
|
2025-07-11 01:36:10 +08:00
|
|
|
|
|
|
|
@classmethod
|
2025-09-25 11:52:26 +08:00
|
|
|
def Register(cls, slot:type, target:Any, action:Action, *dependences:type) -> DependenceModel:
|
|
|
|
if slot in cls._RegisteringRuntime:
|
2025-07-11 01:36:10 +08:00
|
|
|
raise InvalidOperationError("Illegal duplicate registrations")
|
2025-09-25 11:52:26 +08:00
|
|
|
cls._RegisteringRuntime[slot] = Architecture.Registering(slot, target, DependenceModel(Architecture.TypeQuery(dependence) for dependence in dependences), action)
|
|
|
|
cls._InternalRegisteringComplete()
|
|
|
|
return cls._RegisteringRuntime[slot].dependences
|
2025-07-11 01:36:10 +08:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def Contains(cls, type_:type) -> bool:
|
2025-09-25 11:52:26 +08:00
|
|
|
return type_ in cls._RegisteredObjects
|
2025-07-11 01:36:10 +08:00
|
|
|
|
|
|
|
@classmethod
|
2025-07-12 22:16:23 +08:00
|
|
|
def Get(cls, type_:type) -> Any:
|
2025-09-25 11:52:26 +08:00
|
|
|
return cls._RegisteredObjects[type_]
|
2025-07-11 01:36:10 +08:00
|
|
|
|
|
|
|
@classmethod
|
2025-09-25 11:52:26 +08:00
|
|
|
def Unregister(cls, slot:type) -> bool:
|
|
|
|
if slot in cls._RegisteredObjects:
|
|
|
|
del cls._RegisteredObjects[slot]
|
|
|
|
return True
|
|
|
|
if slot in cls._RegisteringRuntime:
|
|
|
|
del cls._RegisteringRuntime[slot]
|
|
|
|
return True
|
|
|
|
return False
|
2025-07-11 01:36:10 +08:00
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region Signal & Update
|
|
|
|
|
2025-09-25 11:52:26 +08:00
|
|
|
_SignalListener: Dict[type, List[SignalListener]] = {}
|
2025-07-11 01:36:10 +08:00
|
|
|
|
|
|
|
@classmethod
|
2025-09-25 11:52:26 +08:00
|
|
|
def AddListener(cls, slot:type, listener:SignalListener) -> None:
|
2025-07-11 01:36:10 +08:00
|
|
|
if slot not in cls._SignalListener:
|
2025-09-25 11:52:26 +08:00
|
|
|
cls._SignalListener[slot] = []
|
2025-07-11 01:36:10 +08:00
|
|
|
|
2025-09-25 11:52:26 +08:00
|
|
|
cls._SignalListener[slot].append(listener)
|
2025-07-11 01:36:10 +08:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def SendMessage(cls, slot:type, signal:ISignal):
|
|
|
|
if slot in cls._SignalListener:
|
2025-09-25 11:52:26 +08:00
|
|
|
for listener in cls._SignalListener[slot]:
|
|
|
|
listener(signal)
|
2025-07-11 01:36:10 +08:00
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region Timeline/Chain & Update
|
|
|
|
|
|
|
|
class TimelineQueueEntry:
|
|
|
|
def __init__(self):
|
|
|
|
self.predicate: Callable[[], bool] = lambda: False
|
|
|
|
self.actions: List[Action] = []
|
|
|
|
|
|
|
|
class Timeline:
|
|
|
|
def __init__(self):
|
|
|
|
self.predicate_mapper: Dict[Callable[[], bool], int] = {}
|
|
|
|
self.queue: List[Architecture.TimelineQueueEntry] = []
|
|
|
|
self.context: int = 0
|
|
|
|
|
|
|
|
_TimelineQueues: Dict[int, Timeline] = {}
|
|
|
|
_TimelineContentID: int = 0
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def CreateTimeline(cls) -> int:
|
|
|
|
cls._TimelineQueues[cls._TimelineContentID] = cls.Timeline()
|
|
|
|
cls._TimelineContentID += 1
|
|
|
|
return cls._TimelineContentID - 1
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def AddStep(cls, timeline_id:int, predicate:Callable[[], bool], *actions:Action):
|
|
|
|
timeline = cls._TimelineQueues[timeline_id]
|
|
|
|
if predicate in timeline.predicate_mapper:
|
|
|
|
time = timeline.predicate_mapper[predicate]
|
|
|
|
timeline.queue[time].actions.extend(actions)
|
|
|
|
else:
|
|
|
|
time = len(timeline.queue)
|
|
|
|
timeline.predicate_mapper[predicate] = time
|
|
|
|
entry = cls.TimelineQueueEntry()
|
|
|
|
entry.predicate = predicate
|
|
|
|
entry.actions = list(actions)
|
|
|
|
timeline.queue.append(entry)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def UpdateTimeline(cls):
|
2025-07-14 22:10:55 +08:00
|
|
|
stats = True
|
|
|
|
while stats:
|
|
|
|
stats = False
|
|
|
|
for timeline in cls._TimelineQueues.values():
|
|
|
|
if timeline.context < len(timeline.queue):
|
|
|
|
if timeline.queue[timeline.context].predicate():
|
|
|
|
stats = True
|
|
|
|
for action in timeline.queue[timeline.context].actions:
|
|
|
|
action()
|
|
|
|
timeline.context += 1
|
2025-07-11 01:36:10 +08:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def ResetTimelineContext(cls, timeline_id:int):
|
|
|
|
cls._TimelineQueues[timeline_id].context = 0
|
2025-07-10 15:08:20 +08:00
|
|
|
|
|
|
|
#endregion
|
|
|
|
|