244 lines
8.4 KiB
C#
244 lines
8.4 KiB
C#
using Cryville.Common;
|
|
using Cryville.Common.Pdt;
|
|
using Cryville.Common.Unity.Input;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using Logger = Cryville.Common.Logger;
|
|
|
|
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<string>());
|
|
}
|
|
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<string, InputProxyEntry> _tproxies = new Dictionary<string, InputProxyEntry>();
|
|
readonly Dictionary<InputSource, InputProxyEntry> _sproxies = new Dictionary<InputSource, InputProxyEntry>();
|
|
readonly Dictionary<string, int> _use = new Dictionary<string, int>();
|
|
readonly Dictionary<string, List<string>> _rev = new Dictionary<string, List<string>>();
|
|
public event EventHandler<ProxyChangedEventArgs> 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) {
|
|
if (_judge != 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;
|
|
if (_judge != null) _tproxies[name].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() {
|
|
_activeCounts.Clear();
|
|
_vect.Clear(); _vecs.Clear();
|
|
foreach (var src in _sproxies.Keys) {
|
|
_activeCounts.Add(src, 0);
|
|
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<InputHandler, double> _timeOrigins = new Dictionary<InputHandler, double>();
|
|
readonly Dictionary<InputSource, int> _activeCounts = new Dictionary<InputSource, int>();
|
|
readonly Dictionary<InputIdentifier, float> _vect = new Dictionary<InputIdentifier, float>();
|
|
readonly Dictionary<ProxiedInputIdentifier, PropSrc.Arbitrary> _vecs = new Dictionary<ProxiedInputIdentifier, PropSrc.Arbitrary>();
|
|
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 - _timeOrigins[id.Source.Handler]);
|
|
if (!_vect.TryGetValue(id, out ft)) ft = tt;
|
|
if (vec.IsNull) {
|
|
_etor.ContextCascadeUpdate(_var_value, new PropSrc.Arbitrary(PdtInternalType.Null, _nullvalue));
|
|
}
|
|
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, _nullvalue);
|
|
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, ft, tt);
|
|
_vecs[pid] = tv;
|
|
}
|
|
}
|
|
public void SyncTime(double time) {
|
|
foreach (var s in _sproxies.Keys) {
|
|
var h = s.Handler;
|
|
if (!_timeOrigins.ContainsKey(h))
|
|
_timeOrigins.Add(h, h.GetCurrentTimestamp() - time);
|
|
}
|
|
}
|
|
public void ForceTick() {
|
|
foreach (var src in _sproxies.Keys) {
|
|
if (_activeCounts[src] == 0) {
|
|
OnInput(new InputIdentifier { Source = src, Id = 0 }, new InputVector(src.Handler.GetCurrentTimestamp()));
|
|
}
|
|
}
|
|
}
|
|
public double GetTimestampAverage() {
|
|
double result = 0;
|
|
foreach (var src in _sproxies.Keys) {
|
|
result += src.Handler.GetCurrentTimestamp() - _timeOrigins[src.Handler];
|
|
}
|
|
return result / _sproxies.Count;
|
|
}
|
|
#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<ProxiedInputIdentifier> {
|
|
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);
|
|
}
|
|
}
|
|
}
|