using System; using System.Collections.Generic; namespace Cryville.Crtr.Event { public class EventBus : StateBase, IDisposable { EventBus prototype = null; public ContainerState RootState { get; private set; } Dictionary states = new Dictionary(); List activeContainers = new List(); HashSet workingStates = new HashSet(); HashSet invalidatedStates = new HashSet(); public EventBus(ContainerState root, List b) : base(b) { RootState = root; Expand(); AttachBus(); RootState.Working = true; } public EventBus Clone(byte ct, float offsetTime = 0) { var r = (EventBus)MemberwiseClone(); r.prototype = this; r.states = new Dictionary(); r.activeContainers = new List(); r.workingStates = new HashSet(); r.invalidatedStates = new HashSet(); r.tempEvents = new List(); r.Time += offsetTime; r.RootState = RootState.Clone(ct); r.RootState.StartUpdate(); r.Expand(); r.AttachBus(); foreach (var s in r.states) r.invalidatedStates.Add(s.Value); r.ValidateStates(); return r; } public void CopyTo(byte ct, EventBus dest) { base.CopyTo(dest); dest.workingStates.Clear(); dest.invalidatedStates.Clear(); RootState.CopyTo(ct, dest.RootState); if (ct >= 2) { dest.activeContainers.Clear(); foreach (var c in activeContainers) { if (states[c].Working) { states[c].CopyTo(ct, dest.states[c]); dest.activeContainers.Add(c); } } } dest.ValidateStates(); } public void Dispose() { RootState.Dispose(); } public void NotifyWorkingChanged(ContainerState state) { if (!invalidatedStates.Contains(state)) invalidatedStates.Add(state); } void Expand(ContainerState s = null) { if (s == null) { states.Clear(); s = RootState; } AddState(s); foreach (var c in s.Children.Values) Expand(c); } public void AddState(ContainerState s) { states[s.Container] = s; s.Bus = this; } void EnsureActivity(EventContainer c) { if (activeContainers.Contains(c)) return; var state = states[c]; if (state.Parent != null) EnsureActivity(state.Parent.Container); if (RootState.CloneType >= 2) prototype.states[c].CopyTo(RootState.CloneType, state); state.Active = true; activeContainers.Add(c); if (state.StampedContainer.Coevents == null) return; foreach (var cev in state.StampedContainer.Coevents) { if (cev.Container == null) continue; EnsureActivity(cev.Container); states[cev.Container].Handle(cev); } } void AttachBus() { foreach (var s in states.Values) s.Bus = this; } public void AttachSystems(PdtSkin skin, Judge judge) { foreach (var s in states.Values) s.AttachSystems(skin, judge); } List tempEvents = new List(); public void PushTempEvent(StampedEvent ev) { var index = tempEvents.BinarySearch(ev); if (index < 0) index = ~index; tempEvents.Insert(index, ev); } readonly StampedEvent _dummyEvent = new StampedEvent(); public void StripTempEvents() { _dummyEvent.Time = Time; var index = tempEvents.BinarySearch(_dummyEvent); if (index < 0) index = ~index; tempEvents.RemoveRange(0, index); } public override void ForwardOnceToTime(double toTime) { double time1 = EventId < Events.Count ? Events[EventId].Time : double.PositiveInfinity; double time2 = tempEvents.Count > 0 ? tempEvents[0].Time : double.PositiveInfinity; double time0 = Math.Min(time1, time2); if (time0 <= toTime && time0 != double.PositiveInfinity) { Time = time0; foreach (var s in workingStates) s.Handle(null); ValidateStates(); if (time1 == time0) { var batch = Events[EventId]; for (var i = 0; i < batch.Count; i++) { var ev = batch[i]; if (ev.Container != null) { if (ev.Unstamped is EventContainer) EnsureActivity((EventContainer)ev.Unstamped); else EnsureActivity(ev.Container); states[ev.Container].Handle(ev); } else if (ev.Coevents != null) EnsureActivity((EventContainer)ev.Unstamped); } EventId++; } if (time2 == time0) { while (tempEvents.Count > 0) { var ev = tempEvents[0]; if (ev.Time != time0) break; if (ev.Container != null) { EnsureActivity(ev.Container); states[ev.Container].Handle(ev); } tempEvents.RemoveAt(0); } } ValidateStates(); } else { Time = toTime; foreach (var s in workingStates) s.Handle(null); ValidateStates(); } } private void ValidateStates() { foreach (var s in invalidatedStates) if (s.Working && !workingStates.Contains(s)) workingStates.Add(s); else if (!s.Working && workingStates.Contains(s)) workingStates.Remove(s); invalidatedStates.Clear(); } public void BroadcastPreInit() { RootState.BroadcastPreInit(); } public void BroadcastPostInit() { RootState.BroadcastPostInit(); } public void BroadcastEndUpdate() { RootState.BroadcastEndUpdate(); tempEvents.Clear(); } public void Anchor() { RootState.Anchor(); } } }