using Cryville.Common; using Cryville.Common.Pdt; using Cryville.Common.Unity.Input; using Cryville.Crtr.Config; using System; using System.Collections.Generic; using UnityEngine; namespace Cryville.Crtr { public class InputProxy : IDisposable { 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; } } _vecsrc = new PropSrc.Arbitrary(PdtInternalType.Vector, _vecbuf); _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"); static readonly PropOp.Arbitrary _arbop = new PropOp.Arbitrary(); readonly byte[] _vecbuf = new byte[3 * sizeof(float) + sizeof(int)]; readonly PropSrc.Arbitrary _vecsrc; readonly Dictionary _timeOrigins = new Dictionary(); readonly Dictionary _activeCounts = new Dictionary(); readonly Dictionary _vect = new Dictionary(); readonly Dictionary _vecs = new Dictionary(); static readonly PropSrc.Arbitrary _nullsrc = new PropSrc.Arbitrary(PdtInternalType.Null, new byte[0]); 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, _nullsrc); OnInput(id, proxy.Target, ft, tt, true); } else { fixed (byte* ptr = _vecbuf) { *(Vector3*)ptr = vec.Vector; } _vecsrc.Invalidate(); _etor.ContextCascadeUpdate(_var_value, _vecsrc); 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) { var def = _ruleset.inputs[target]; if (def.pass != null) { foreach (var p in def.pass) { _etor.ContextCascadeInsert(); _arbop.Name = _var_value; if (!nullflag) _etor.Evaluate(_arbop, p.Value); OnInput(id, p.Key, ft, tt, nullflag); _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 = _nullsrc; 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); } } }