Code structure cleanup.

This commit is contained in:
2023-08-24 15:47:34 +08:00
parent e40c98ae1b
commit 1f58390298
137 changed files with 439 additions and 362 deletions

View File

@@ -0,0 +1,466 @@
using Cryville.Common;
using Cryville.Common.Pdt;
using Cryville.Crtr.Config;
using Cryville.Input;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Threading;
using UnityEngine;
using UnityEngine.Profiling;
using Logger = Cryville.Common.Logging.Logger;
using RVector4 = UnityEngine.Vector4;
namespace Cryville.Crtr.Ruleset {
public class InputProxy : IDisposable {
readonly PdtEvaluator _etor;
readonly PdtRuleset _ruleset;
readonly Judge _judge;
readonly InputVector _screenSize;
public InputProxy(PdtRuleset ruleset, Judge judge, Vector2 screenSize) {
for (int i = 0; i <= MAX_DEPTH; i++) {
var vecsrc = new InputVectorSrc();
_vecsrcs[i] = vecsrc;
_vecops[i] = new InputVectorOp(vecsrc);
}
_etor = judge != null ? judge._etor : PdtEvaluator.Instance;
_ruleset = ruleset;
_judge = judge;
_screenSize = new InputVector(screenSize.x, screenSize.y);
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) {
var handler = Game.InputManager.GetHandlerByTypeName(cfg.Value.handler);
if (handler == null) {
Logger.Log("main", 3, "Input", "Uninitialized or unknown handler in ruleset config: {0}", cfg.Value.handler);
continue;
}
Set(new InputProxyEntry {
Target = new Identifier(cfg.Key),
Source = new InputSource {
Handler = 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 = p.Value.Source.Value.Handler.GetType().AssemblyQualifiedName,
type = p.Value.Source.Value.Type
});
}
}
public void Clear() {
_tproxies.Clear();
_sproxies.Clear();
foreach (var i in _rev) _use[i.Key] = 0;
}
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.Shared.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() {
_vect.Clear(); _vecs.Clear();
foreach (var src in _sproxies) {
var isrc = src.Value.Source;
if (isrc != null) {
isrc.Value.Handler.OnInput += OnInput;
isrc.Value.Handler.OnBatch += OnBatch;
}
}
_targetActiveCount.Clear();
foreach (var i in _ruleset.inputs) {
if (i.Value.pass == null) _targetActiveCount.Add(i.Key, 0);
}
}
public void Deactivate() {
foreach (var src in _sproxies) {
var isrc = src.Value.Source;
if (isrc != null) {
isrc.Value.Handler.OnInput -= OnInput;
isrc.Value.Handler.OnBatch -= OnBatch;
}
}
}
~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;
}
}
}
static readonly int _var_input_vec = IdentifierManager.Shared.Request("input_vec");
const int MAX_DEPTH = 15;
const int MAX_DIMENSION = 4;
readonly InputVectorSrc[] _vecsrcs = new InputVectorSrc[MAX_DEPTH + 1];
readonly InputVectorOp[] _vecops = new InputVectorOp[MAX_DEPTH + 1];
unsafe class InputVectorSrc : PropSrc.FixedBuffer<RVector4> {
public InputVectorSrc() : base(PdtInternalType.Vector, MAX_DIMENSION * sizeof(float) + sizeof(int), null) {
fixed (byte* ptr = buf) {
*(int*)(ptr + MAX_DIMENSION * sizeof(float)) = PdtInternalType.Number;
}
}
public bool IsNull { get; set; }
public RVector4 Get() {
fixed (byte* _ptr = buf) {
return *(RVector4*)_ptr;
}
}
public void Set(RVector4 vec) {
fixed (byte* _ptr = buf) {
*(RVector4*)_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 RVector4();
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<InputHandler, double> _timeOrigins = new Dictionary<InputHandler, double>();
readonly Dictionary<Identifier, int> _targetActiveCount = new Dictionary<Identifier, int>();
readonly Dictionary<InputIdentifier, float> _vect = new Dictionary<InputIdentifier, float>();
readonly Dictionary<ProxiedInputIdentifier, Vector4> _vecs = new Dictionary<ProxiedInputIdentifier, Vector4>();
double? _lockTime = null;
unsafe void OnInput(InputIdentifier id, InputFrame frame) {
var rc = id.Source.Handler.ReferenceCue;
if (rc.RelativeUnit == RelativeUnit.Pixel) {
frame = rc.InverseTransform(frame, _screenSize);
var vec = frame.Vector;
vec.X /= _screenSize.X; vec.Y /= _screenSize.Y;
vec.X -= 0.5f; vec.Y -= 0.5f;
vec.X *= ChartPlayer.hitRect.width; vec.Y *= ChartPlayer.hitRect.height;
frame.Vector = vec;
}
else frame = rc.InverseTransform(frame);
Monitor.Enter(_etor);
try {
Profiler.BeginSample("InputProxy.OnInput");
InputProxyEntry proxy;
if (_sproxies.TryGetValue(id.Source, out proxy)) {
float ft, tt = (float)GetSyncedTime(frame.Time, id.Source.Handler);
if (!_vect.TryGetValue(id, out ft)) ft = tt;
_etor.ContextCascadeInsert();
try {
if (frame.IsNull) {
_etor.ContextCascadeUpdate(_var_input_vec, PropSrc.Null);
OnInput(id, proxy.Target, ft, tt, true);
}
else {
_vecsrcs[0].Set(new RVector4(frame.Vector.X, frame.Vector.Y, frame.Vector.Z, frame.Vector.W));
_etor.ContextCascadeUpdate(_var_input_vec, _vecsrcs[0]);
OnInput(id, proxy.Target, ft, tt, false);
}
}
finally {
_etor.ContextCascadeDiscard();
}
_vect[id] = tt;
}
}
finally {
Profiler.EndSample();
Monitor.Exit(_etor);
}
}
static readonly int _var_fv = IdentifierManager.Shared.Request("input_vec_from");
static readonly int _var_tv = IdentifierManager.Shared.Request("input_vec_to");
readonly InputVectorSrc _vecsrc = new InputVectorSrc();
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();
try {
bool newNullFlag = nullFlag;
if (!newNullFlag) {
_etor.Evaluate(_vecops[depth + 1], p.Value);
newNullFlag = _vecsrcs[depth + 1].IsNull;
if (newNullFlag) _etor.ContextCascadeUpdate(_var_input_vec, PropSrc.Null);
else _etor.ContextCascadeUpdate(_var_input_vec, _vecsrcs[depth + 1]);
}
OnInput(id, p.Key, ft, tt, newNullFlag, depth + 1);
}
finally {
_etor.ContextCascadeDiscard();
}
}
}
else {
var pid = new ProxiedInputIdentifier { Source = id, Target = target };
PropSrc fv, tv = _etor.ContextCascadeLookup(_var_input_vec);
bool hfv; Vector4 ifv;
if (hfv = _vecs.TryGetValue(pid, out ifv)) {
_vecsrc.Set(ifv);
fv = _vecsrc;
}
else fv = PropSrc.Null;
if (fv.Type != PdtInternalType.Null || tv.Type != PdtInternalType.Null) {
if (fv.Type == PdtInternalType.Null) {
_targetActiveCount[target]++;
}
_etor.ContextCascadeInsert();
try {
_etor.ContextCascadeUpdate(_var_fv, fv);
_etor.ContextCascadeUpdate(_var_tv, tv);
_judge.Feed(target, ft, tt);
}
finally {
_etor.ContextCascadeDiscard();
}
if (tv.Type == PdtInternalType.Null) {
_vecs.Remove(pid);
_targetActiveCount[target]--;
}
else {
var itv = ((InputVectorSrc)tv).Get();
if (!hfv || ifv != itv) _vecs[pid] = itv;
}
}
}
}
void OnBatch(InputHandler handler, double time) {
lock (_etor) {
foreach (var vec in _vecs) {
if (vec.Key.Source.Source.Handler != handler) continue;
InputProxyEntry proxy;
if (!_sproxies.TryGetValue(vec.Key.Source.Source, out proxy)) continue;
float ft, tt = (float)GetSyncedTime(time, handler);
if (_vect.TryGetValue(vec.Key.Source, out ft) && ft < tt) {
_etor.ContextCascadeInsert();
_vecsrcs[0].Set(vec.Value);
_etor.ContextCascadeUpdate(_var_input_vec, _vecsrcs[0]);
OnInput(vec.Key.Source, proxy.Target, ft, tt, false);
_etor.ContextCascadeDiscard();
_vect[vec.Key.Source] = tt;
}
Cleanup(proxy.Target, tt, true);
}
}
}
void Cleanup(Identifier target, float tt, bool batching = false, 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) {
Cleanup(p.Key, tt, batching, depth + 1);
}
}
else if (batching || _targetActiveCount[target] == 0) {
_judge.Cleanup(target, tt);
}
}
public void SyncTime(double time) {
foreach (var s in _sproxies) {
var h = s.Key.Handler;
_timeOrigins[h] = h.GetCurrentTimestamp() - time;
}
}
const double batchDelayTolerance = 0.02;
public void ForceTick() {
lock (_etor) {
foreach (var s in _sproxies) {
var handler = s.Key.Handler;
Cleanup(s.Value.Target, (float)GetSyncedTime(handler.GetCurrentTimestamp() - batchDelayTolerance, handler));
}
}
}
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;
}
double GetSyncedTime(double time, InputHandler handler) {
return _lockTime != null ? _lockTime.Value : (time - _timeOrigins[handler]);
}
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) { }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: af16092e83ce23d46b46254e2d9798f9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,460 @@
using Cryville.Common;
using Cryville.Common.Buffers;
using Cryville.Common.Collections.Generic;
using Cryville.Common.Collections.Specialized;
using Cryville.Common.Pdt;
using Cryville.Crtr.Event;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.Serialization;
using System.Text.Formatting;
using UnityEngine;
using UnsafeIL;
namespace Cryville.Crtr.Ruleset {
internal struct JudgeResult {
public float? Time { get; set; }
public Vector4 Vector { get; set; }
}
internal class JudgeEvent {
public double StartTime { get; set; }
public double EndTime { get; set; }
public double StartClip { get; set; }
public double EndClip { get; set; }
public Chart.Judge BaseEvent { get; set; }
public JudgeDefinition Definition { get; set; }
public NoteHandler Handler { get; set; }
public JudgeResult JudgeResult { get; set; }
public JudgeCallContext CallContext { get; set; }
}
internal struct JudgeCallContext {
public bool CalledOnMiss { get; set; }
public float CallTime { get; set; }
public JudgeEvent ReturnEvent { get; set; }
public int ReturnIndex { get; set; }
}
internal interface IJudge {
void Call(JudgeEvent ev, float time, Identifier id, bool onMiss, int index);
bool Pass(JudgeEvent ev, float time, Identifier[] ids, bool onMiss, int depth);
void UpdateScore(ScoreOperation op, PdtExpression exp);
}
public class Judge : IJudge {
#region Data
readonly ChartPlayer _sys;
internal readonly PdtEvaluator _etor;
readonly PdtRuleset _rs;
internal Dictionary<Identifier, PdtExpression> _areaFuncs;
readonly Dictionary<Identifier, List<JudgeEvent>> evs
= new Dictionary<Identifier, List<JudgeEvent>>();
readonly Dictionary<Identifier, List<JudgeEvent>> activeEvs
= new Dictionary<Identifier, List<JudgeEvent>>();
static readonly int _var_pause = IdentifierManager.Shared.Request("pause");
readonly JudgeDefinition _judgePause;
static readonly IComparer<JudgeEvent> _stcmp = new JudgeEventStartTimeComparer();
class JudgeEventStartTimeComparer : IComparer<JudgeEvent> {
public int Compare(JudgeEvent x, JudgeEvent y) {
return x.StartClip.CompareTo(y.StartClip);
}
}
public Judge(ChartPlayer sys, PdtRuleset rs) {
_sys = sys;
_etor = new PdtEvaluator();
_etor.ContextJudge = this;
_rs = rs;
_areaFuncs = rs.areas;
_identop = new PropOp.Identifier(v => _identbuf = new Identifier(v));
_clipop = new PropOp.Clip(v => _clipbuf = v);
_rs.judges.TryGetValue(new Identifier(_var_pause), out _judgePause);
foreach (var i in rs.inputs) {
var id = i.Key;
var l = new List<JudgeEvent>();
evs.Add(id, l);
activeEvs.Add(id, new List<JudgeEvent>());
if (_judgePause != null && id.Key == _var_pause) {
l.Add(new JudgeEvent {
StartTime = double.NegativeInfinity, EndTime = double.PositiveInfinity,
StartClip = double.NegativeInfinity, EndClip = double.PositiveInfinity,
Definition = _judgePause,
});
}
}
InitJudges();
InitScores();
}
Identifier _identbuf; readonly PropOp _identop;
Clip _clipbuf; readonly PropOp _clipop;
public void Prepare(StampedEvent sev, NoteHandler handler) {
var tev = (Chart.Judge)sev.Unstamped;
InsertEvent(tev, new Clip((float)sev.Time, (float)(sev.Time + sev.Duration)), tev.Id, handler);
}
void InsertEvent(Chart.Judge ev, Clip clip, Identifier id, NoteHandler handler, JudgeCallContext call = default(JudgeCallContext)) {
if (id.Key == _var_pause) throw new InvalidOperationException("Cannot assign the special judge \"pause\" to notes");
var def = _rs.judges[id];
_etor.Evaluate(_identop, def.input);
_etor.Evaluate(_clipop, def.clip);
var list = evs[_identbuf];
var jev = new JudgeEvent {
StartTime = clip.Behind,
EndTime = clip.Ahead,
StartClip = clip.Behind + _clipbuf.Behind,
EndClip = clip.Ahead + _clipbuf.Ahead,
BaseEvent = ev,
Definition = def,
Handler = handler,
CallContext = call,
};
var index = list.BinarySearch(jev, _stcmp);
if (index < 0) index = ~index;
list.Insert(index, jev);
}
#endregion
#region Judge
internal readonly IntKeyedDictionary<int> judgeMap = new IntKeyedDictionary<int>();
void InitJudges() {
foreach (var i in _rs.judges) {
var id = i.Key;
judgeMap.Add(id.Key, IdentifierManager.Shared.Request("judge_" + id.Name));
}
}
static bool _flag;
static readonly PropOp.Boolean _flagop = new PropOp.Boolean(v => _flag = v);
static readonly HitOp _hitop = new HitOp();
class HitOp : PdtOperator {
const int MAX_SORTS = 16;
readonly float[] _buf = new float[MAX_SORTS];
readonly float[] _sorts = new float[MAX_SORTS];
public bool LastHit { get; private set; }
public JudgeResult JudgeResult { get; private set; }
public HitOp() : base(MAX_SORTS) { }
protected override unsafe void Execute() {
LastHit = false;
var judgeResult = new JudgeResult();
var judgeVector = new Vector4();
for (int i = 0; i < LoadedOperandCount; i++) {
var op = GetOperand(i);
var sort = op.AsNumber();
if (sort <= 0) return;
if (!LastHit) {
if (sort < _sorts[i]) return;
if (sort > _sorts[i]) LastHit = true;
}
_buf[i] = sort;
if (op.Type == PdtInternalType.Vector) {
var len = (op.Length - sizeof(int)) / sizeof(float);
if (len > 1) {
judgeResult.Time = op.AsNumber(sizeof(float));
for (int j = 0; j < 4; j++) {
if (len > j + 2) judgeVector[j] = op.AsNumber((j + 2) * sizeof(float));
}
judgeResult.Vector = judgeVector;
}
}
}
if (!LastHit) return;
Array.Clear(_buf, LoadedOperandCount, MAX_SORTS - LoadedOperandCount);
fixed (float* ptrsrc = _buf, ptrdest = _sorts) {
Unsafe.CopyBlock(ptrdest, ptrsrc, MAX_SORTS * sizeof(float));
}
JudgeResult = judgeResult;
}
public void Clear() { Array.Clear(_sorts, 0, MAX_SORTS); }
}
static readonly int _var_fn = IdentifierManager.Shared.Request("judge_time_from");
static readonly int _var_tn = IdentifierManager.Shared.Request("judge_time_to");
static readonly int _var_ft = IdentifierManager.Shared.Request("input_time_from");
static readonly int _var_tt = IdentifierManager.Shared.Request("input_time_to");
readonly PropStores.Float
_numst1 = new PropStores.Float(),
_numst2 = new PropStores.Float(),
_numst3 = new PropStores.Float(),
_numst4 = new PropStores.Float();
static readonly int _var_jt = IdentifierManager.Shared.Request("hit_time");
static readonly int _var_jdt = IdentifierManager.Shared.Request("hit_delta_time");
static readonly int _var_jv = IdentifierManager.Shared.Request("hit_vec");
readonly PropStores.Float
_jnumst = new PropStores.Float(),
_jdnumst = new PropStores.Float();
readonly PropStores.Vector4 _jvecst = new PropStores.Vector4();
// Adopted from System.Collections.Generic.ArraySortHelper<T>.InternalBinarySearch(T[] array, int index, int length, T value, IComparer<T> comparer)
int BinarySearch(List<JudgeEvent> list, float time, int stack) {
int num = 0;
int num2 = list.Count - 1;
while (num <= num2) {
int num3 = num + (num2 - num >> 1);
int num4 = -list[num3].Definition.stack.CompareTo(stack);
if (num4 == 0) num4 = list[num3].StartClip.CompareTo(time);
if (num4 == 0) return num3;
else if (num4 < 0) num = num3 + 1;
else num2 = num3 - 1;
}
return ~num;
}
int BinarySearchFirst(List<JudgeEvent> list, int stack) {
if (list[0].Definition.stack == stack) return 0;
int num = 0;
int num2 = list.Count - 1;
while (num <= num2) {
int num3 = num + (num2 - num >> 1);
int num4 = -list[num3].Definition.stack.CompareTo(stack);
if (num4 > 0) num2 = num3 - 1;
else if (num4 < 0) num = num3 + 1;
else if (num != num3) num2 = num3;
else return num;
}
return ~num;
}
void UpdateContextJudgeEvent(JudgeEvent ev) {
_numst1.Value = (float)ev.StartTime; _etor.ContextCascadeUpdate(_var_fn, _numst1.Source);
_numst2.Value = (float)ev.EndTime; _etor.ContextCascadeUpdate(_var_tn, _numst2.Source);
if (ev.BaseEvent != null) {
_etor.ContextEvent = ev.BaseEvent;
_etor.ContextState = ev.Handler.cs;
}
var call = ev.CallContext;
if (call.ReturnEvent != null) {
JudgeResult judgeResult = call.ReturnEvent.JudgeResult;
_jnumst.Value = judgeResult.Time.Value; _etor.ContextCascadeUpdate(_var_jt, _jnumst.Source);
_jdnumst.Value = (float)(judgeResult.Time.Value - call.ReturnEvent.StartTime); _etor.ContextCascadeUpdate(_var_jdt, _jdnumst.Source);
_jvecst.Value = judgeResult.Vector; _etor.ContextCascadeUpdate(_var_jv, _jvecst.Source);
}
else {
_etor.ContextCascadeUpdate(_var_jt, PropSrc.Null);
_etor.ContextCascadeUpdate(_var_jv, PropSrc.Null);
}
}
public void Feed(Identifier target, float ft, float tt) {
Forward(target, tt);
var actlist = activeEvs[target];
if (actlist.Count > 0) {
_numst3.Value = ft; _etor.ContextCascadeUpdate(_var_ft, _numst3.Source);
_numst4.Value = tt; _etor.ContextCascadeUpdate(_var_tt, _numst4.Source);
int index = 0, iter = 0;
while (index < actlist.Count) {
if (iter++ >= 16) throw new JudgePropagationException();
_hitop.Clear();
int cstack = actlist[index].Definition.stack;
int hitIndex = -1;
while (index >= 0 && index < actlist.Count) {
var ev = actlist[index];
if (ev.Definition.stack != cstack) break;
UpdateContextJudgeEvent(ev);
var def = ev.Definition;
if (def.hit != null) {
_etor.Evaluate(_hitop, def.hit);
if (_hitop.LastHit) {
hitIndex = index;
ev.JudgeResult = _hitop.JudgeResult;
}
}
else if (hitIndex == -1) hitIndex = index;
index++;
}
if (hitIndex != -1) {
var hitEvent = actlist[hitIndex];
UpdateContextJudgeEvent(hitEvent);
var def = hitEvent.Definition;
if (def == _judgePause) _sys.TogglePause();
if (def.persist != null) _etor.Evaluate(_flagop, def.persist);
else _flag = false;
Execute(hitEvent, (ft + tt) / 2, def.on_hit, false);
if (!_flag) {
actlist.RemoveAt(hitIndex);
--index;
}
if (def.prop != 0 && actlist.Count > 0) {
index = BinarySearchFirst(actlist, def.stack - def.prop);
if (index < 0) index = ~index;
}
}
}
_etor.ContextState = null;
_etor.ContextEvent = null;
}
}
public void Cleanup(Identifier target, float tt) {
lock (_etor) {
Forward(target, tt);
var actlist = activeEvs[target];
for (int i = actlist.Count - 1; i >= 0; i--) {
JudgeEvent ev = actlist[i];
if (tt > ev.EndClip) {
actlist.RemoveAt(i);
Execute(ev, tt, ev.Definition.on_miss, true);
}
}
}
}
void Forward(Identifier target, float tt) {
var list = evs[target];
var actlist = activeEvs[target];
JudgeEvent ev;
while (list.Count > 0 && (ev = list[0]).StartClip <= tt) {
list.RemoveAt(0);
var index = BinarySearch(actlist, (float)ev.StartClip, ev.Definition.stack);
if (index < 0) index = ~index;
actlist.Insert(index, ev);
}
}
void Execute(JudgeEvent ev, float time, PairList<JudgeAction, PdtExpression> actions, bool onMiss, int depth = 0, int index = 0) {
JudgeResult judgeResult = ev.JudgeResult;
if (!onMiss && judgeResult.Time != null) {
_jnumst.Value = judgeResult.Time.Value; _etor.ContextCascadeUpdate(_var_jt, _jnumst.Source);
_jdnumst.Value = (float)(judgeResult.Time.Value - ev.StartTime); _etor.ContextCascadeUpdate(_var_jdt, _jdnumst.Source);
_jvecst.Value = judgeResult.Vector; _etor.ContextCascadeUpdate(_var_jv, _jvecst.Source);
}
if (actions != null) {
// Ensure that all actions that modifies judge result sources break the execution
for (int i = index; i < actions.Count; i++) {
var a = actions[i];
if (a.Key.Execute(this, ev, time, a.Value, onMiss, depth, i).BreakExecution) break;
}
}
else {
var call = ev.CallContext;
if (call.ReturnEvent != null) {
// TODO
if (onMiss)
Execute(call.ReturnEvent, time, call.ReturnEvent.Definition.on_miss, true, depth + 1, 0);
else
Execute(call.ReturnEvent, time, call.ReturnEvent.Definition.on_hit, false, depth + 1, call.ReturnIndex);
}
}
}
void IJudge.Call(JudgeEvent ev, float time, Identifier id, bool onMiss, int index) {
InsertEvent(ev.BaseEvent, new Clip((float)ev.StartTime, (float)ev.EndTime), id, ev.Handler, new JudgeCallContext {
CalledOnMiss = onMiss,
CallTime = time,
ReturnEvent = ev,
ReturnIndex = index + 1,
}); // TODO optimize GC
}
bool IJudge.Pass(JudgeEvent ev, float time, Identifier[] ids, bool onMiss, int depth) {
if (depth >= 16) throw new JudgePropagationException();
foreach (var i in ids) {
var def = _rs.judges[i];
bool hitFlag;
if (def.hit != null) {
_hitop.Clear();
_etor.Evaluate(_hitop, def.hit);
hitFlag = _hitop.LastHit;
}
else hitFlag = true;
if (hitFlag) {
Execute(ev, time, def.on_hit, onMiss, depth + 1);
ev.Handler.ReportJudge(ev, time, i);
return true;
}
}
return false;
}
void IJudge.UpdateScore(ScoreOperation op, PdtExpression exp) {
_etor.ContextSelfValue = scoreSrcs[op.name.Key];
_etor.Evaluate(scoreOps[op.name.Key], exp);
InvalidateScore(op.name.Key);
foreach (var s in _rs.scores) {
if (s.Value.value != null) {
_etor.ContextSelfValue = scoreSrcs[s.Key.Key];
_etor.Evaluate(scoreOps[s.Key.Key], s.Value.value);
InvalidateScore(s.Key.Key);
}
}
}
#endregion
#region Score
readonly IntKeyedDictionary<int> scoreStringKeys = new IntKeyedDictionary<int>();
readonly IntKeyedDictionary<int> scoreStringKeysRev = new IntKeyedDictionary<int>();
readonly IntKeyedDictionary<PropSrc> scoreSrcs = new IntKeyedDictionary<PropSrc>();
readonly IntKeyedDictionary<PropOp> scoreOps = new IntKeyedDictionary<PropOp>();
readonly IntKeyedDictionary<ScoreDefinition> scoreDefs = new IntKeyedDictionary<ScoreDefinition>();
readonly IntKeyedDictionary<float> scores = new IntKeyedDictionary<float>();
readonly IntKeyedDictionary<string> scoreStringCache = new IntKeyedDictionary<string>();
readonly ArrayPool<byte> scoreStringPool = new ArrayPool<byte>();
readonly IntKeyedDictionary<string> scoreFormatCache = new IntKeyedDictionary<string>();
readonly TargetString scoreFullStr = new TargetString();
readonly StringBuffer scoreFullBuf = new StringBuffer();
void InitScores() {
foreach (var s in _rs.scores) {
var key = s.Key.Key;
var strkey = IdentifierManager.Shared.Request("_score_" + (string)s.Key.Name);
scoreStringKeys.Add(key, strkey);
scoreStringKeysRev.Add(strkey, key);
scoreSrcs.Add(key, new PropSrc.Float(() => scores[key]));
scoreOps.Add(key, new PropOp.Float(v => scores[key] = v));
scoreDefs.Add(key, s.Value);
scores.Add(key, s.Value.init);
scoreStringCache.Add(scoreStringKeys[key], null);
scoreSrcs.Add(scoreStringKeys[key], new ScoreStringSrc(scoreStringPool, () => scores[key], scoreDefs[key].format));
scoreFormatCache[key] = string.Format("{{0:{0}}}", s.Value.format);
}
}
void InvalidateScore(int key) {
scoreSrcs[key].Invalidate();
scoreStringCache[scoreStringKeys[key]] = null;
scoreSrcs[scoreStringKeys[key]].Invalidate();
}
public bool TryGetScoreSrc(int key, out PropSrc value) {
return scoreSrcs.TryGetValue(key, out value);
}
public TargetString GetFullFormattedScoreString() {
lock (_etor) {
bool flag = false;
scoreFullBuf.Clear();
foreach (var s in scores) {
var id = s.Key;
scoreFullBuf.AppendFormat(flag ? "\n{0}: " : "{0}: ", (string)IdentifierManager.Shared.Retrieve(id));
scoreFullBuf.AppendFormat(scoreFormatCache[id], scores[id]);
flag = true;
}
scoreFullStr.Length = scoreFullBuf.Count;
var arr = scoreFullStr.TrustedAsArray();
scoreFullBuf.CopyTo(0, arr, 0, scoreFullBuf.Count);
return scoreFullStr;
}
}
class ScoreStringSrc : PropSrc {
readonly Func<float> _cb;
readonly string _format;
readonly ArrayPool<byte> _pool;
readonly StringBuffer _buf = new StringBuffer() { Culture = CultureInfo.InvariantCulture };
public ScoreStringSrc(ArrayPool<byte> pool, Func<float> cb, string format)
: base(PdtInternalType.String) {
_pool = pool;
_cb = cb;
_format = string.Format("{{0:{0}}}", format);
}
public override void Invalidate() {
if (buf != null) {
_pool.Return(buf);
base.Invalidate();
}
}
protected override unsafe void InternalGet() {
var src = _cb();
_buf.Clear();
_buf.AppendFormat(_format, src);
int strlen = _buf.Count;
buf = _pool.Rent(sizeof(int) + strlen * sizeof(char));
fixed (byte* _ptr = buf) {
*(int*)_ptr = strlen;
char* ptr = (char*)(_ptr + sizeof(int));
_buf.CopyTo(ptr, 0, strlen);
}
}
}
#endregion
}
[Serializable]
public class JudgePropagationException : Exception {
public JudgePropagationException() : base("Judge propagation limit reached\nThe ruleset has invalid judge definitions") { }
public JudgePropagationException(string message) : base(message) { }
public JudgePropagationException(string message, Exception inner) : base(message, inner) { }
protected JudgePropagationException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c3063149610959f4e853ced6341a9d36
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,81 @@
using Cryville.Common;
using Cryville.Common.Pdt;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Cryville.Crtr.Ruleset {
internal struct JudgeActionResult {
public bool BreakExecution;
public bool PreventRecycle;
}
public abstract class JudgeAction {
public static JudgeAction Construct(HashSet<string> a, string k) {
if (a.Remove("pass")) {
return new Pass(a, from i in k.Split(',') select new Identifier(i.Trim()));
}
else if (a.Remove("call")) {
return new Call(a, new Identifier(k));
}
else if (a.Remove("score")) {
return new Score(a, k);
}
else if (a.Remove("var")) {
return new Variable(a, new Identifier(k));
}
throw new FormatException("Invalid judge action format.");
}
public readonly HashSet<string> annotations;
public JudgeAction(IEnumerable<string> a) {
annotations = a.ToHashSet();
}
public virtual void Optimize(PdtEvaluatorBase etor, PdtExpression value) { etor.Optimize(value); }
internal abstract JudgeActionResult Execute(IJudge judge, JudgeEvent ev, float time, PdtExpression exp, bool onMiss, int depth, int index);
public class Call : JudgeAction {
readonly Identifier _target;
public Call(IEnumerable<string> a, Identifier k) : base(a) {
_target = k;
}
internal override JudgeActionResult Execute(IJudge judge, JudgeEvent ev, float time, PdtExpression exp, bool onMiss, int depth, int index) {
judge.Call(ev, time, _target, onMiss, index);
return new JudgeActionResult { BreakExecution = true, PreventRecycle = true };
}
}
public class Pass : JudgeAction {
readonly Identifier[] _targets;
public Pass(IEnumerable<string> a, IEnumerable<Identifier> k) : base(a) {
_targets = k.ToArray();
}
internal override JudgeActionResult Execute(IJudge judge, JudgeEvent ev, float time, PdtExpression exp, bool onMiss, int depth, int index) {
return new JudgeActionResult { BreakExecution = judge.Pass(ev, time, _targets, onMiss, depth) };
}
}
public class Score : JudgeAction {
readonly ScoreOperation _op;
public Score(IEnumerable<string> a, string k) : base(a) {
_op = new ScoreOperation(k);
}
public Score(ScoreOperation op) : base(Enumerable.Empty<string>()) {
_op = op;
}
public override void Optimize(PdtEvaluatorBase etor, PdtExpression value) {
base.Optimize(etor, value);
if (_op.op != default(Identifier)) PdtExpression.PatchCompound(_op.name.Key, _op.op.Key, value);
}
internal override JudgeActionResult Execute(IJudge judge, JudgeEvent ev, float time, PdtExpression exp, bool onMiss, int depth, int index) {
judge.UpdateScore(_op, exp);
return new JudgeActionResult();
}
}
public class Variable : JudgeAction {
readonly Identifier _target;
public Variable(IEnumerable<string> a, Identifier k) : base(a) {
_target = k;
}
internal override JudgeActionResult Execute(IJudge judge, JudgeEvent ev, float time, PdtExpression exp, bool onMiss, int depth, int index) {
// throw new NotImplementedException();
return new JudgeActionResult();
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 52c6416297266354999ce5ff46a568dc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,216 @@
using Cryville.Common;
using Cryville.Common.Collections.Generic;
using Cryville.Common.Pdt;
using Cryville.Crtr.Extension;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace Cryville.Crtr.Ruleset {
public class RulesetDefinition : MetaInfo {
public const long CURRENT_FORMAT = 2;
[JsonRequired]
public long format;
public string @base;
[JsonIgnore]
public PdtRuleset Root { get; private set; }
public void LoadPdt(DirectoryInfo dir) {
using (StreamReader pdtreader = new StreamReader(dir.FullName + "/" + data + ".pdt", Encoding.UTF8)) {
var src = pdtreader.ReadToEnd();
Root = (PdtRuleset)new RulesetInterpreter(src, null).Interpret(typeof(PdtRuleset));
}
}
}
[Binder(typeof(PdtBinder))]
public class PdtRuleset {
public Dictionary<Identifier, ConfigDefinition> configs;
public Dictionary<Identifier, MotionDefinition> motions;
public Dictionary<Identifier, InputDefinition> inputs;
public Dictionary<Identifier, PdtExpression> areas;
public Dictionary<Identifier, JudgeDefinition> judges;
public Dictionary<Identifier, ScoreDefinition> scores;
public Constraint constraints;
public void Optimize(PdtEvaluatorBase etor) {
foreach (var i in inputs) {
var input = i.Value;
if (input.pass != null) foreach (var e in input.pass) {
etor.Optimize(e.Value);
}
}
if (areas != null) foreach (var a in areas) {
etor.Optimize(a.Value);
}
foreach (var j in judges) {
var judge = j.Value;
if (judge.hit != null) etor.Optimize(judge.hit);
if (judge.on_hit != null) OptimizeJudgeActions(judge.on_hit, etor);
if (judge.on_miss != null) OptimizeJudgeActions(judge.on_miss, etor);
}
foreach (var s in scores) {
var score = s.Value;
if (score.value != null) etor.Optimize(score.value);
}
constraints.Optimize(etor);
}
void OptimizeJudgeActions(PairList<JudgeAction, PdtExpression> actions, PdtEvaluatorBase etor) {
foreach (var a in actions) a.Key.Optimize(etor, a.Value);
}
public void PrePatch(Chart chart) {
constraints.PrePatch(chart);
}
}
public class ConfigDefinition {
public string category;
public ConfigType type;
public PdtExpression @default;
public PdtExpression range;
public PdtExpression value;
}
public enum ConfigType {
unknown, number, number_stepped,
}
public class MotionDefinition {
// TODO
}
public class InputDefinition {
public int dim;
public string pdim;
public bool notnull;
public PairList<Identifier, PdtExpression> pass;
}
public class JudgeDefinition {
public int stack;
public int prop;
public PdtExpression clip;
public PdtExpression input;
public PdtExpression hit;
public PdtExpression persist;
public PairList<JudgeAction, PdtExpression> on_hit;
public PairList<JudgeAction, PdtExpression> on_miss;
#pragma warning disable IDE1006
public PairList<ScoreOperation, PdtExpression> scores {
set {
if (on_hit == null) on_hit = new PairList<JudgeAction, PdtExpression>();
int i = 0;
foreach (var s in value) {
on_hit.Insert(i++, new JudgeAction.Score(s.Key), s.Value);
}
}
}
public Identifier[] pass {
set {
if (on_hit == null) on_hit = new PairList<JudgeAction, PdtExpression>();
on_hit.Add(new JudgeAction.Pass(Enumerable.Empty<string>(), value), PdtExpression.Empty);
}
}
public Identifier[] miss {
set {
if (on_miss == null) on_miss = new PairList<JudgeAction, PdtExpression>();
on_miss.Add(new JudgeAction.Pass(Enumerable.Empty<string>(), value), PdtExpression.Empty);
}
}
#pragma warning restore IDE1006
}
public class ScoreOperation {
public Identifier name;
public Identifier op;
public ScoreOperation(Identifier name, Identifier op) {
this.name = name;
this.op = op;
}
public ScoreOperation(string str) {
var m = Regex.Match(str, @"^(\S+)\s*?(\S+)?$");
name = new Identifier(m.Groups[1].Value);
if (!m.Groups[2].Success) return;
op = new Identifier(m.Groups[2].Value);
}
public override string ToString() {
if (op == default(Identifier)) return name.ToString();
else return string.Format("{0} {1}", name, op);
}
}
public class ScoreDefinition {
public PdtExpression value;
public float init = 0;
public string format = "";
}
public class Constraint {
static readonly PropOp.Arbitrary _arbop = new PropOp.Arbitrary();
[ElementList]
public PairList<RulesetSelectors, Constraint> Elements = new PairList<RulesetSelectors, Constraint>();
[PropertyList]
public PairList<ConstraintKey, PdtExpression> Properties = new PairList<ConstraintKey, PdtExpression>();
public void Optimize(PdtEvaluatorBase etor) {
foreach (var e in Properties) {
etor.Optimize(e.Value);
}
foreach (var e in Elements) {
e.Key.Optimize(etor);
e.Value.Optimize(etor);
}
}
public void PrePatch(ChartEvent ev) {
var etor = PdtEvaluator.Instance;
PropSrc src;
etor.ContextCascadeInsert();
etor.ContextEvent = ev;
foreach (var prop in Properties) {
var name = prop.Key.Name;
switch (prop.Key.Type) {
case ConstraintType.Property:
if (ev.PropSrcs.TryGetValue(name, out src))
etor.ContextSelfValue = src;
etor.Evaluate(ev.PropOps[name], prop.Value);
etor.ContextSelfValue = null;
break;
case ConstraintType.Variable:
_arbop.Name = name;
etor.Evaluate(_arbop, prop.Value);
break;
default: throw new NotSupportedException("Unknown property key type");
}
}
etor.ContextEvent = null;
foreach (var el in Elements) {
var targets = el.Key.Match(ev);
if (targets == null) continue;
foreach (var target in targets)
el.Value.PrePatch(target);
}
etor.ContextCascadeDiscard();
}
}
public class ConstraintKey {
public ConstraintType Type { get; private set; }
public int Name { get; private set; }
public ConstraintKey(ConstraintType type, string name) {
Type = type;
Name = IdentifierManager.Shared.Request(name);
}
public override string ToString() {
switch (Type) {
case ConstraintType.Property: return (string)IdentifierManager.Shared.Retrieve(Name);
case ConstraintType.Variable: return string.Format("@var {0}", IdentifierManager.Shared.Retrieve(Name));
default: return string.Format("<{0}> {1}", Type, IdentifierManager.Shared.Retrieve(Name));
}
}
}
public enum ConstraintType {
Property,
Variable,
}
public class RulesetViolationException : Exception {
public RulesetViolationException() { }
public RulesetViolationException(string message) : base(message) { }
public RulesetViolationException(string message, Exception innerException) : base(message, innerException) { }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7d7a277a6e9217e4591c39d1a220cbcf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,78 @@
using Cryville.Common.Collections.Generic;
using Cryville.Common.Pdt;
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Cryville.Crtr.Ruleset {
internal class RulesetInterpreter : PdtInterpreter {
public RulesetInterpreter(string src, Binder binder) : base(src, binder) { }
readonly List<RulesetSelector> s = new List<RulesetSelector>();
readonly HashSet<string> a = new HashSet<string>();
protected override object InterpretKey(Type type) {
if (PairCollection<JudgeAction, PdtExpression>.IsPairCollection(type))
return InterpretJudgeAction();
else if (type == typeof(Constraint))
return InterpretConstraintKey();
else
return base.InterpretKey(type);
}
object InterpretJudgeAction() {
a.Clear();
while (true) {
int pp = Position;
switch (cc) {
case '@':
GetChar();
a.Add(GetIdentifier());
break;
default:
return JudgeAction.Construct(a, (string)base.InterpretKey(null));
}
ws();
if (Position == pp) throw new FormatException("Invalid judge action format.");
}
}
object InterpretConstraintKey() {
s.Clear(); a.Clear();
string key = "";
while (true) {
int pp = Position;
switch (cc) {
case '@':
GetChar();
a.Add(GetIdentifier());
break;
case '$':
GetChar();
s.Add(new RulesetSelector.CreateItem());
key = null;
break;
case '#':
GetChar();
s.Add(new RulesetSelector.Index());
break;
case '>':
GetChar();
s.Add(new RulesetSelector.Property(GetExp()));
break;
case ';':
case ':':
return new ConstraintKey(a.Contains("var") ? ConstraintType.Variable : ConstraintType.Property, key);
case '{':
return new RulesetSelectors(s);
case '}':
return null;
default:
var p4 = GetIdentifier();
s.Add(new RulesetSelector.EventType(p4));
if (key != null) key += p4;
break;
}
ws();
if (Position == pp) throw new FormatException("Invalid selector or key format.");
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 78904c737c51e254ab55b6686d964837
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,102 @@
using Cryville.Common.Pdt;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Cryville.Crtr.Ruleset {
public class RulesetSelectors {
readonly RulesetSelector[] selectors;
public RulesetSelectors(IEnumerable<RulesetSelector> s) {
selectors = s.ToArray();
}
public void Optimize(PdtEvaluatorBase etor) {
for (int i = 0; i < selectors.Length; i++) {
selectors[i].Optimize(etor);
}
}
public IEnumerable<ChartEvent> Match(ChartEvent ev) {
IEnumerable<ChartEvent> result = new ChartEvent[] { ev };
foreach (var sel in selectors) {
IEnumerable<ChartEvent> temp = new ChartEvent[0];
foreach (var e in result) {
var m = sel.Match(e);
if (m != null) temp = temp.Concat(m);
}
result = temp;
}
return result;
}
public override string ToString() {
if (selectors.Length == 0) return "";
bool flag = false;
string r = "";
foreach (var s in selectors) {
if (flag) r += " " + s.ToString();
else { r += s.ToString(); flag = true; }
}
return r;
}
}
public abstract class RulesetSelector {
public virtual void Optimize(PdtEvaluatorBase etor) { }
public abstract IEnumerable<ChartEvent> Match(ChartEvent ev);
public class CreateItem : RulesetSelector {
public override string ToString() {
return "$";
}
public override IEnumerable<ChartEvent> Match(ChartEvent ev) {
if (!(ev is EventList)) throw new ArgumentException("Event is not event list");
var tev = (EventList)ev;
var result = tev.Create();
tev.Events.Add(result); // TODO create at
return new ChartEvent[] { result };
}
}
public class EventType : RulesetSelector {
readonly string _type;
public EventType(string type) { _type = type; }
public override string ToString() {
return _type;
}
public override IEnumerable<ChartEvent> Match(ChartEvent ev) {
if (!(ev is EventContainer)) throw new ArgumentException("Event is not container");
var tev = (EventContainer)ev;
return new ChartEvent[] { tev.GetEventsOfType(_type) };
}
}
public class Index : RulesetSelector {
public override string ToString() {
return "#";
}
public override IEnumerable<ChartEvent> Match(ChartEvent ev) {
if (!(ev is EventList)) throw new ArgumentException("Event is not event list");
var tev = (EventList)ev;
return tev.Events; // TODO select at
}
}
public class Property : RulesetSelector {
readonly PdtExpression _exp;
readonly PdtOperator _op;
bool _flag;
public Property(PdtExpression exp) {
_exp = exp;
_op = new PropOp.Boolean(v => _flag = v);
}
public override string ToString() {
return string.Format("> {{{0}}}", _exp);
}
public override void Optimize(PdtEvaluatorBase etor) {
etor.Optimize(_exp);
}
public override IEnumerable<ChartEvent> Match(ChartEvent ev) {
PdtEvaluator.Instance.ContextEvent = ev;
if (!PdtEvaluator.Instance.Evaluate(_op, _exp))
throw new EvaluationFailureException();
PdtEvaluator.Instance.ContextEvent = null;
if (_flag) return new ChartEvent[] { ev };
else return null;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 770e66e317b9b3848a16736b68414b43
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: