using Cryville.Common; using Cryville.Common.Pdt; using Cryville.Common.Unity.Input; using Cryville.Crtr.Config; using System; using System.Collections.Generic; using System.Runtime.Serialization; using RVector3 = UnityEngine.Vector3; namespace Cryville.Crtr { public class InputProxy : IDisposable { readonly PdtEvaluator _etor; readonly PdtRuleset _ruleset; readonly Judge _judge; public InputProxy(PdtRuleset ruleset, Judge judge) { for (int i = 0; i <= MAX_DEPTH; i++) { var vecsrc = new InputVectorSrc(); _vecsrcs[i] = vecsrc; _vecops[i] = new InputVectorOp(vecsrc); } _etor = ChartPlayer.etor; _ruleset = ruleset; _judge = judge; foreach (var i in ruleset.inputs) { _use.Add(i.Key, 0); _rev.Add(i.Key, new List()); } foreach (var i in ruleset.inputs) { if (i.Value.pass != null) { foreach (var p in i.Value.pass) _rev[p.Key].Add(i.Key); } } } #region Settings readonly Dictionary _tproxies = new Dictionary(); readonly Dictionary _sproxies = new Dictionary(); readonly Dictionary _use = new Dictionary(); readonly Dictionary> _rev = new Dictionary>(); public event EventHandler ProxyChanged; public void LoadFrom(Dictionary config) { foreach (var cfg in config) { Set(new InputProxyEntry { Target = new Identifier(cfg.Key), Source = new InputSource { Handler = Game.InputManager.GetHandler(cfg.Value.handler), Type = cfg.Value.type } }); } } public void SaveTo(Dictionary config) { config.Clear(); foreach (var p in _tproxies) { config.Add((string)p.Key.Name, new RulesetConfig.InputEntry { handler = ReflectionHelper.GetNamespaceQualifiedName(p.Value.Source.Value.Handler.GetType()), type = p.Value.Source.Value.Type }); } } public void Set(InputProxyEntry proxy) { var target = proxy.Target; if (!_ruleset.inputs.ContainsKey(target)) throw new ArgumentException("Invalid input name"); if (_tproxies.ContainsKey(target)) Remove(proxy); if (_use[target] > 0) throw new InvalidOperationException("Input already assigned"); if (proxy.Source != null) { _tproxies.Add(target, proxy); _sproxies.Add(proxy.Source.Value, proxy); IncrementUseRecursive(target); IncrementReversedUseRecursive(target); } } void Remove(InputProxyEntry proxy) { var target = proxy.Target; _sproxies.Remove(_tproxies[target].Source.Value); _tproxies.Remove(target); DecrementUseRecursive(target); DecrementReversedUseRecursive(target); } public bool IsUsed(InputSource src) { return _sproxies.ContainsKey(src); } public bool IsCompleted() { foreach (var i in _use) if (!IsCompleted(i.Key)) return false; return true; } bool IsCompleted(Identifier name) { return name.Key == _var_pause || _use[name] != 0 || _tproxies.ContainsKey(name); } static readonly int _var_pause = IdentifierManager.SharedInstance.Request("pause"); void IncrementUseRecursive(Identifier name) { BroadcastProxyChanged(name); var passes = _ruleset.inputs[name].pass; if (passes != null) { foreach (var p in _ruleset.inputs[name].pass) { _use[p.Key]++; IncrementUseRecursive(p.Key); } } } void IncrementReversedUseRecursive(Identifier name) { foreach (var p in _rev[name]) { _use[p]++; BroadcastProxyChanged(p); IncrementReversedUseRecursive(p); } } void DecrementUseRecursive(Identifier name) { BroadcastProxyChanged(name); var passes = _ruleset.inputs[name].pass; if (passes != null) { foreach (var p in _ruleset.inputs[name].pass) { _use[p.Key]--; DecrementUseRecursive(p.Key); } } } void DecrementReversedUseRecursive(Identifier name) { foreach (var p in _rev[name]) { _use[p]--; BroadcastProxyChanged(p); DecrementReversedUseRecursive(p); } } void BroadcastProxyChanged(Identifier name) { var del = ProxyChanged; if (del != null) del(this, this[name]); } public ProxyChangedEventArgs this[Identifier name] { get { return new ProxyChangedEventArgs(name, _tproxies.ContainsKey(name) ? _tproxies[name].Source : null, _use[name] > 0, !IsCompleted(name)); } } #endregion #region Handling public void Activate() { _activeCounts.Clear(); _vect.Clear(); _vecs.Clear(); foreach (var src in _sproxies) { _activeCounts.Add(src.Key, 0); var isrc = src.Value.Source; if (isrc != null) { isrc.Value.Handler.OnInput += OnInput; } } } public void Deactivate() { foreach (var src in _sproxies) { var isrc = src.Value.Source; if (isrc != null) { isrc.Value.Handler.OnInput -= OnInput; } } } ~InputProxy() { Dispose(false); GC.SuppressFinalize(this); } public void Dispose() { Dispose(true); } protected void Dispose(bool disposing) { if (disposing) { Deactivate(); foreach (var proxy in _tproxies) { proxy.Value.Source.Value.Handler.OnInput -= OnInput; } } } readonly object _lock = new object(); static readonly int _var_value = IdentifierManager.SharedInstance.Request("value"); const int MAX_DEPTH = 15; const int MAX_DIMENSION = 3; readonly InputVectorSrc[] _vecsrcs = new InputVectorSrc[MAX_DEPTH + 1]; readonly InputVectorOp[] _vecops = new InputVectorOp[MAX_DEPTH + 1]; unsafe class InputVectorSrc : PropSrc.FixedBuffer { public InputVectorSrc() : base(PdtInternalType.Vector, MAX_DIMENSION * sizeof(float) + sizeof(int)) { fixed (byte* ptr = buf) { *(int*)(ptr + MAX_DIMENSION * sizeof(float)) = PdtInternalType.Number; } } public bool IsNull { get; set; } public void Set(RVector3 vec) { fixed (byte* _ptr = buf) { *(RVector3*)_ptr = vec; } Invalidate(); } } class InputVectorOp : PropOp { readonly InputVectorSrc _src; public InputVectorOp(InputVectorSrc src) { _src = src; } protected override void Execute() { var op = GetOperand(0); if (op.Type == PdtInternalType.Null) { _src.IsNull = true; } else { var vec = new RVector3(); int dim; if (op.Type == PdtInternalType.Number) dim = 1; else if (op.Type == PdtInternalType.Vector) { int arrtype, _; op.GetArraySuffix(out arrtype, out _); if (arrtype != PdtInternalType.Number) throw new InvalidCastException("Not a vector of numbers"); dim = Math.Min(3, (op.Length - sizeof(int)) / sizeof(float)); } else throw new InvalidCastException("Invalid vector"); for (int i = 0; i < dim; i++) { vec[i] = op.AsNumber(i * sizeof(float)); } _src.IsNull = false; _src.Set(vec); } } } readonly Dictionary _timeOrigins = new Dictionary(); readonly Dictionary _activeCounts = new Dictionary(); readonly Dictionary _vect = new Dictionary(); readonly Dictionary _vecs = new Dictionary(); double? _lockTime = null; unsafe void OnInput(InputIdentifier id, InputVector vec) { lock (_lock) { InputProxyEntry proxy; if (_sproxies.TryGetValue(id.Source, out proxy)) { _etor.ContextCascadeInsert(); float ft, tt = (float)(_lockTime != null ? _lockTime.Value : (vec.Time - _timeOrigins[id.Source.Handler])); if (!_vect.TryGetValue(id, out ft)) ft = tt; if (vec.IsNull) { _etor.ContextCascadeUpdate(_var_value, PropSrc.Null); OnInput(id, proxy.Target, ft, tt, true); } else { _vecsrcs[0].Set(vec.Vector); _etor.ContextCascadeUpdate(_var_value, _vecsrcs[0]); OnInput(id, proxy.Target, ft, tt, false); } _vect[id] = tt; _etor.ContextCascadeDiscard(); } } } static readonly int _var_fv = IdentifierManager.SharedInstance.Request("fv"); static readonly int _var_tv = IdentifierManager.SharedInstance.Request("tv"); unsafe void OnInput(InputIdentifier id, Identifier target, float ft, float tt, bool nullflag, int depth = 0) { if (depth >= MAX_DEPTH) throw new InputProxyException("Input propagation limit reached\nThe ruleset has invalid input definitions"); var def = _ruleset.inputs[target]; if (def.pass != null) { foreach (var p in def.pass) { _etor.ContextCascadeInsert(); bool newNullFlag = nullflag; if (!newNullFlag) { ChartPlayer.etor.Evaluate(_vecops[depth + 1], p.Value); newNullFlag = _vecsrcs[depth + 1].IsNull; if (newNullFlag) ChartPlayer.etor.ContextCascadeUpdate(_var_value, PropSrc.Null); else ChartPlayer.etor.ContextCascadeUpdate(_var_value, _vecsrcs[depth + 1]); } OnInput(id, p.Key, ft, tt, newNullFlag, depth + 1); _etor.ContextCascadeDiscard(); } } else { var pid = new ProxiedInputIdentifier { Source = id, Target = target }; PropSrc fv, tv = _etor.ContextCascadeLookup(_var_value); if (!_vecs.TryGetValue(pid, out fv)) fv = PropSrc.Null; if (fv.Type != PdtInternalType.Null || tv.Type != PdtInternalType.Null) { if (fv.Type == PdtInternalType.Null) _activeCounts[id.Source]++; _etor.ContextCascadeInsert(); _etor.ContextCascadeUpdate(_var_fv, fv); _etor.ContextCascadeUpdate(_var_tv, tv); _judge.Feed(target, ft, tt); _etor.ContextCascadeDiscard(); if (tv.Type == PdtInternalType.Null) _activeCounts[id.Source]--; } _judge.Cleanup(target, tt); _vecs[pid] = tv; } } public void SyncTime(double time) { foreach (var s in _sproxies) { var h = s.Key.Handler; _timeOrigins[h] = h.GetCurrentTimestamp() - time; } } public void ForceTick() { foreach (var s in _sproxies) { var src = s.Key; if (_activeCounts[src] == 0) { OnInput(new InputIdentifier { Source = src, Id = 0 }, new InputVector(_lockTime != null ? _lockTime.Value : src.Handler.GetCurrentTimestamp())); } } } public double GetTimestampAverage() { double result = 0; foreach (var s in _sproxies) { var src = s.Key; result += src.Handler.GetCurrentTimestamp() - _timeOrigins[src.Handler]; } return result / _sproxies.Count; } public void LockTime() { _lockTime = GetTimestampAverage(); } public void UnlockTime() { _lockTime = null; } #endregion } public class ProxyChangedEventArgs : EventArgs { public Identifier Name { get; private set; } public InputSource? Proxy { get; private set; } public bool Used { get; private set; } public bool Required { get; private set; } public ProxyChangedEventArgs(Identifier name, InputSource? src, bool used, bool required) { Name = name; Proxy = src; Used = used; Required = required; } } public class InputProxyEntry { public InputSource? Source { get; set; } public Identifier Target { get; set; } public byte[] Mapping { get; private set; } } public struct ProxiedInputIdentifier : IEquatable { public InputIdentifier Source { get; set; } public Identifier Target { get; set; } public override bool Equals(object obj) { if (obj == null || !(obj is ProxiedInputIdentifier)) return false; return Equals((ProxiedInputIdentifier)obj); } public bool Equals(ProxiedInputIdentifier other) { return Source == other.Source && Target == other.Target; } public override int GetHashCode() { return Source.GetHashCode() ^ Target.GetHashCode(); } public override string ToString() { return string.Format("{0}->{1}", Source, Target); } public static bool operator ==(ProxiedInputIdentifier lhs, ProxiedInputIdentifier rhs) { return lhs.Equals(rhs); } public static bool operator !=(ProxiedInputIdentifier lhs, ProxiedInputIdentifier rhs) { return !lhs.Equals(rhs); } } [Serializable] public class InputProxyException : Exception { public InputProxyException() { } public InputProxyException(string message) : base(message) { } public InputProxyException(string message, Exception inner) : base(message, inner) { } protected InputProxyException(SerializationInfo info, StreamingContext context) : base(info, context) { } } }