using Cryville.Common; using Cryville.Common.Pdt; using Cryville.Common.Unity.Input; using System; using System.Collections.Generic; using UnityEngine; namespace Cryville.Crtr { public class InputProxy { readonly PdtEvaluator _etor; readonly PdtRuleset _ruleset; readonly Judge _judge; public InputProxy(PdtRuleset ruleset, Judge judge) { unsafe { fixed (byte* ptr = _vecbuf) { *(int*)(ptr + 3 * sizeof(float)) = PdtInternalType.Number; } } _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 Set(InputProxyEntry proxy) { var name = proxy.Target; if (_tproxies.ContainsKey(name)) Remove(proxy); if (_use[proxy.Target] > 0) throw new InvalidOperationException("Input already assigned"); if (proxy.Source != null) { proxy.Source.Value.Handler.OnInput -= OnInput; // Prevent duplicated hooks, no exception will be thrown proxy.Source.Value.Handler.OnInput += OnInput; _tproxies.Add(proxy.Target, proxy); _sproxies.Add(proxy.Source.Value, proxy); IncrementUseRecursive(name); IncrementReversedUseRecursive(name); } } void Remove(InputProxyEntry proxy) { var name = proxy.Target; proxy.Source.Value.Handler.OnInput -= OnInput; _sproxies.Remove(_tproxies[name].Source.Value); _tproxies.Remove(name); DecrementUseRecursive(name); DecrementReversedUseRecursive(name); } public bool IsUsed(InputSource src) { return _sproxies.ContainsKey(src); } void IncrementUseRecursive(string 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(string name) { foreach (var p in _rev[name]) { _use[p]++; BroadcastProxyChanged(p); IncrementReversedUseRecursive(p); } } void DecrementUseRecursive(string 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(string name) { foreach (var p in _rev[name]) { _use[p]--; BroadcastProxyChanged(p); DecrementReversedUseRecursive(p); } } void BroadcastProxyChanged(string name) { var del = ProxyChanged; if (del != null) del(this, new ProxyChangedEventArgs(name, _tproxies.ContainsKey(name) ? _tproxies[name].Source : null, _use[name] > 0)); } #endregion #region Handling public void Activate() { foreach (var src in _sproxies.Keys) src.Handler.Activate(); } public void Deactivate() { foreach (var src in _sproxies.Keys) src.Handler.Deactivate(); } readonly object _lock = new object(); static readonly int _var_value = IdentifierManager.SharedInstance.Request("value"); static readonly PropOp.Arbitrary _arbop = new PropOp.Arbitrary(); readonly byte[] _vecbuf = new byte[3 * sizeof(float) + sizeof(int)]; readonly Dictionary _vect = new Dictionary(); readonly Dictionary _vecs = new Dictionary(); static readonly byte[] _nullvalue = new byte[0]; unsafe void OnInput(InputIdentifier id, InputVector vec) { lock (_lock) { InputProxyEntry proxy; if (_sproxies.TryGetValue(id.Source, out proxy)) { _etor.ContextCascadeInsert(); float ft, tt = (float)vec.Time; if (!_vect.TryGetValue(id, out ft)) ft = tt; if (vec.IsNull) { _etor.ContextCascadeUpdate(_var_value, new PropSrc.Arbitrary(PdtInternalType.Null, new byte[0])); } else { fixed (byte* ptr = _vecbuf) { *(Vector3*)ptr = vec.Vector; } _etor.ContextCascadeUpdate(_var_value, new PropSrc.Arbitrary(PdtInternalType.Vector, _vecbuf)); } OnInput(id, proxy.Target, ft, tt); _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) { var def = _ruleset.inputs[target]; if (def.pass != null) { foreach (var p in def.pass) { _etor.ContextCascadeInsert(); _arbop.Name = _var_value; _etor.Evaluate(_arbop, p.Value); OnInput(id, p.Key, ft, tt); _etor.ContextCascadeDiscard(); } } else { var pid = new ProxiedInputIdentifier { Source = id, Target = target }; PropSrc.Arbitrary fv, tv = _etor.ContextCascadeLookup(_var_value); if (!_vecs.TryGetValue(pid, out fv)) fv = new PropSrc.Arbitrary(PdtInternalType.Null, new byte[0]); if (fv.Type != PdtInternalType.Null || tv.Type != PdtInternalType.Null) { _etor.ContextCascadeInsert(); _etor.ContextCascadeUpdate(_var_fv, fv); _etor.ContextCascadeUpdate(_var_tv, tv); _judge.Feed(target, ft, tt); _etor.ContextCascadeDiscard(); } _judge.Cleanup(target, ft, tt); _vecs[pid] = tv; } } #endregion } public class ProxyChangedEventArgs : EventArgs { public string Name { get; private set; } public InputSource? Proxy { get; private set; } public bool Used { get; private set; } public ProxyChangedEventArgs(string name, InputSource? src, bool used) { Name = name; Proxy = src; Used = used; } } public class InputProxyEntry { public InputSource? Source { get; set; } public string 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); } } }