///<summary>Registers a callback to the dependent soul that only occurs if the necessary soul doesn't die first, ensuring everything is cleaned up properly.</summary> public static RegistrationRemover DependentRegister(this ISoul soul, Action action, ISoul necessarySoul) { if (soul == null) throw new ArgumentNullException("soul"); if (action == null) throw new ArgumentNullException("action"); if (necessarySoul == null) throw new ArgumentNullException("necessarySoul"); // when the necessary soul is the same soul as the dependent soul, assume the callback invocation will beat the registration removal if (ReferenceEquals(soul, necessarySoul)) necessarySoul = ImmortalSoul.Instance; // avoid wrapping when possible if (necessarySoul.Phase == Phase.Dead) return EmptyRemover; if (soul.Phase != Phase.Mortal) { action(); return EmptyRemover; } if (necessarySoul.Phase == Phase.Immortal) return soul.Register(action); return soul.InterdependentRegister( () => { action(); return true; }, necessarySoul, () => necessarySoul.Phase == Phase.Dead); }
///<summary>Registers callbacks to each soul, ensuring everything is cleaned up properly upon completion.</summary> public static RegistrationRemover InterdependentRegister(this ISoul soul1, Func<bool> tryComplete1, ISoul soul2, Func<bool> tryComplete2) { if (soul1 == null) throw new ArgumentNullException("soul1"); if (tryComplete1 == null) throw new ArgumentNullException("tryComplete1"); if (soul2 == null) throw new ArgumentNullException("soul2"); if (tryComplete2 == null) throw new ArgumentNullException("tryComplete2"); // forward declare the second registration canceller, so it can be referenced by the first registration RegistrationRemover cancelRegistration2 = null; var callCount = 0; Action skipOnceCancelRegistration2 = () => { if (Interlocked.Increment(ref callCount) == 2 && cancelRegistration2 != null) cancelRegistration2(); }; // register the callbacks, linking their cancellation to each other var cancelRegistration1 = soul1.Register(() => { if (tryComplete1()) skipOnceCancelRegistration2(); }); cancelRegistration2 = soul2.Register(() => { if (tryComplete2()) cancelRegistration1(); }); // now that cancelRegistration2 has been initialized, we can allow it to be run skipOnceCancelRegistration2(); // outside can force cleanup return () => { cancelRegistration1(); cancelRegistration2(); }; }
void IBootable.Launch() { _This = _Binder.Bind <IPlayer>(this); _Chatter = _Room.RegistChatter(this); _Chatters.Items.Clear(); _Room.Chatters.Supply += _Add; _Room.Chatters.Unsupply += _Leave; }
public CollapsingSoul(ISoul subSoul) { this._subSoul = subSoul; // flatten multiple levels of wrapping var r = subSoul as CollapsingSoul; if (r != null) this._subSoul = r._subSoul; // ensure collapse occurs once the sub soul becomes fixed Register(() => TryOptimize()); }
private Phase TryOptimize() { var phase = _subSoul.Phase; if (_collapsed) return phase; // already optimized if (phase == Phase.Mortal) return phase; // can't optimize yet // the following is idempotent, so it's fine if multiple writers race _collapsed = true; _subSoul = Phase.AsPermanentSoul(); return phase; }
///<summary>Returns a lifetime for the given soul, collapsing it to a simpler soul when possible.</summary> public static Lifetime AsCollapsingLifetime(this ISoul soul) { // avoid any wrapping if possible var p = soul.Phase; if (p != Phase.Mortal) { return(p.AsPermanentLifetime()); } return(new Lifetime(new CollapsingSoul(soul))); }
public CollapsingSoul(ISoul subSoul) { _subSoul = subSoul; // flatten multiple levels of wrapping if (subSoul is CollapsingSoul cS) { _subSoul = cS._subSoul; } // ensure collapse occurs once the sub soul becomes fixed Register(() => tryOptimize()); }
///<summary>Registers callbacks to each soul, ensuring everything is cleaned up properly upon completion.</summary> public static RegistrationRemover InterdependentRegister(this ISoul soul1, Func <bool> tryComplete1, ISoul soul2, Func <bool> tryComplete2) { if (soul1 == null) { throw new ArgumentNullException("soul1"); } if (tryComplete1 == null) { throw new ArgumentNullException("tryComplete1"); } if (soul2 == null) { throw new ArgumentNullException("soul2"); } if (tryComplete2 == null) { throw new ArgumentNullException("tryComplete2"); } // forward declare the second registration canceller, so it can be referenced by the first registration RegistrationRemover cancelRegistration2 = null; var callCount = 0; Action skipOnceCancelRegistration2 = () => { if (Interlocked.Increment(ref callCount) == 2 && cancelRegistration2 != null) { cancelRegistration2(); } }; // register the callbacks, linking their cancellation to each other var cancelRegistration1 = soul1.Register(() => { if (tryComplete1()) { skipOnceCancelRegistration2(); } }); cancelRegistration2 = soul2.Register(() => { if (tryComplete2()) { cancelRegistration1(); } }); // now that cancelRegistration2 has been initialized, we can allow it to be run skipOnceCancelRegistration2(); // outside can force cleanup return(() => { cancelRegistration1(); cancelRegistration2(); }); }
public CollapsingSoul(ISoul subSoul) { this._subSoul = subSoul; // flatten multiple levels of wrapping var r = subSoul as CollapsingSoul; if (r != null) { this._subSoul = r._subSoul; } // ensure collapse occurs once the sub soul becomes fixed Register(() => TryOptimize()); }
private Phase TryOptimize() { var phase = _subSoul.Phase; if (_collapsed) { return(phase); // already optimized } if (phase == Phase.Mortal) { return(phase); // can't optimize yet } // the following is idempotent, so it's fine if multiple writers race _collapsed = true; _subSoul = Phase.AsPermanentSoul(); return(phase); }
private void _Unbind(ISoul soul) { SoulProxy soulInfo; //Regulus.Utility.Log.Instance.WriteInfoImmediate($"unbind i:{soul.Instance.GetHashCode()} t:{soul.Instance.GetType()} id:{soul.Id}."); if (!_Souls.TryRemove(soul.Id, out soulInfo)) { throw new Exception($"can't find the soul {soul.Id} to delete."); } soulInfo.Release(); soulInfo.SupplySoulEvent -= _PropertyBind; soulInfo.UnsupplySoulEvent -= _PropertyUnbind; _UnloadSoul(soulInfo.InterfaceId, soulInfo.Id); _IdLandlord.Return(soulInfo.Id); }
///<summary>Combines two souls by using a custom function to combine their phases.</summary> public static ISoul Combine(this ISoul soul1, ISoul soul2, Func <Phase, Phase, Phase> phaseCombiner) { Func <Phase> getPhase = () => phaseCombiner(soul1.Phase, soul2.Phase); return(new AnonymousSoul( getPhase, action => { Func <bool> tryComplete = () => { var hasPhase = getPhase() != Phase.Mortal; if (hasPhase) { action(); } return hasPhase; }; return soul1.InterdependentRegister(tryComplete, soul2, tryComplete); })); }
///<summary>Registers a callback to the dependent soul that only occurs if the necessary soul doesn't die first, ensuring everything is cleaned up properly.</summary> public static RegistrationRemover DependentRegister(this ISoul soul, Action action, ISoul necessarySoul) { if (soul == null) { throw new ArgumentNullException("soul"); } if (action == null) { throw new ArgumentNullException("action"); } if (necessarySoul == null) { throw new ArgumentNullException("necessarySoul"); } // when the necessary soul is the same soul as the dependent soul, assume the callback invocation will beat the registration removal if (ReferenceEquals(soul, necessarySoul)) { necessarySoul = ImmortalSoul.Instance; } // avoid wrapping when possible if (necessarySoul.Phase == Phase.Dead) { return(EmptyRemover); } if (soul.Phase != Phase.Mortal) { action(); return(EmptyRemover); } if (necessarySoul.Phase == Phase.Immortal) { return(soul.Register(action)); } return(soul.InterdependentRegister( () => { action(); return true; }, necessarySoul, () => necessarySoul.Phase == Phase.Dead)); }
static async Task Summon(Soul soul, string[] args = null) { try { ISoul soulHost = soul switch { Soul.Anubis => new AnubisSoul(), Soul.Horus => new HorusSoul(), _ => throw new UnknownSoulException() }; tcsSummon = new TaskCompletionSource(); Speak($"{soul} has been summoned"); soulHost.WaitForCommand(args); await tcsSummon.Task; } catch (UnknownSoulException) { Speak("Soul is not known or cannot be summoned"); } catch (TaskCanceledException) { tcsSummon = null; var response = Ask($"{soul} dismissed. Do you want to summon {soul} again?", true); if (response == "y") { await Summon(soul); } } catch (SummonFailedException ex) { Speak($"{soul} was unable to executed your command: {ex.Message}"); var response = Ask($"Do you want to summon {soul} again?", true); if (response == "y") { await Summon(soul); } } }
void IBinder.Unbind(ISoul soul) { _Unbind(soul); }
///<summary>Combines two souls by using a custom function to combine their phases.</summary> public static ISoul Combine(this ISoul soul1, ISoul soul2, Func<Phase, Phase, Phase> phaseCombiner) { Func<Phase> getPhase = () => phaseCombiner(soul1.Phase, soul2.Phase); return new AnonymousSoul( getPhase, action => { Func<bool> tryComplete = () => { var hasPhase = getPhase() != Phase.Mortal; if (hasPhase) action(); return hasPhase; }; return soul1.InterdependentRegister(tryComplete, soul2, tryComplete); }); }
void IBootable.Launch() { _Login = _Binder.Bind <ILogin>(this); }
internal Lifetime(ISoul soul) { _defSoul = soul; }
internal Lifetime(ISoul soul) { this._defSoul = soul; }