Files
crtr/Assets/Cryville/Crtr/InputProxy.cs
2023-03-08 20:36:18 +08:00

319 lines
11 KiB
C#

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 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<Identifier>());
}
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<Identifier, InputProxyEntry> _tproxies = new Dictionary<Identifier, InputProxyEntry>();
readonly Dictionary<InputSource, InputProxyEntry> _sproxies = new Dictionary<InputSource, InputProxyEntry>();
readonly Dictionary<Identifier, int> _use = new Dictionary<Identifier, int>();
readonly Dictionary<Identifier, List<Identifier>> _rev = new Dictionary<Identifier, List<Identifier>>();
public event EventHandler<ProxyChangedEventArgs> ProxyChanged;
public void LoadFrom(Dictionary<string, RulesetConfig.InputEntry> 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<string, RulesetConfig.InputEntry> 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<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> _vecs = new Dictionary<ProxiedInputIdentifier, PropSrc>();
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 {
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, int depth = 0) {
if (depth >= 16) 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();
_arbop.Name = _var_value;
if (!nullflag) _etor.Evaluate(_arbop, p.Value);
OnInput(id, p.Key, ft, tt, nullflag, 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<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);
}
}
[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) { }
}
}