Speeds up transcript building but not by much
This commit is contained in:
parent
b3710f26ae
commit
6a0d2830d6
60
Framework/OverwatchTranscript/ActionQueue.cs
Normal file
60
Framework/OverwatchTranscript/ActionQueue.cs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
namespace OverwatchTranscript
|
||||||
|
{
|
||||||
|
public class ActionQueue
|
||||||
|
{
|
||||||
|
private readonly object queueLock = new object();
|
||||||
|
private readonly AutoResetEvent signal = new AutoResetEvent(false);
|
||||||
|
private List<Action> queue = new List<Action>();
|
||||||
|
private Task queueWorker = null!;
|
||||||
|
private bool stopping = false;
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
queueWorker = Task.Run(QueueWorker);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count { get; private set; }
|
||||||
|
|
||||||
|
public void StopAndJoin()
|
||||||
|
{
|
||||||
|
stopping = true;
|
||||||
|
queueWorker.Wait();
|
||||||
|
if (queue.Count > 0) throw new Exception("not all acions handled");
|
||||||
|
queueWorker.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(Action action)
|
||||||
|
{
|
||||||
|
if (stopping) throw new Exception("queue stopping");
|
||||||
|
|
||||||
|
lock (queueLock)
|
||||||
|
{
|
||||||
|
queue.Add(action);
|
||||||
|
Count = queue.Count;
|
||||||
|
}
|
||||||
|
signal.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void QueueWorker()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
signal.WaitOne(10);
|
||||||
|
|
||||||
|
List<Action> work = null!;
|
||||||
|
lock (queueLock)
|
||||||
|
{
|
||||||
|
work = queue;
|
||||||
|
queue = new List<Action>();
|
||||||
|
Count = 0;
|
||||||
|
}
|
||||||
|
if (stopping && !work.Any()) return;
|
||||||
|
|
||||||
|
foreach (var action in work)
|
||||||
|
{
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,14 +5,12 @@ namespace OverwatchTranscript
|
|||||||
public class BucketSet
|
public class BucketSet
|
||||||
{
|
{
|
||||||
private const int numberOfActiveBuckets = 10;
|
private const int numberOfActiveBuckets = 10;
|
||||||
private readonly object queueLock = new object();
|
|
||||||
private List<Action> queue = new List<Action>();
|
|
||||||
private readonly Task queueWorker;
|
|
||||||
private readonly ILog log;
|
private readonly ILog log;
|
||||||
private readonly string workingDir;
|
private readonly string workingDir;
|
||||||
private readonly object _bucketLock = new object();
|
private readonly object _bucketLock = new object();
|
||||||
private readonly List<EventBucket> fullBuckets = new List<EventBucket>();
|
private readonly List<EventBucketWriter> fullBuckets = new List<EventBucketWriter>();
|
||||||
private readonly List<EventBucket> activeBuckets = new List<EventBucket>();
|
private readonly List<EventBucketWriter> activeBuckets = new List<EventBucketWriter>();
|
||||||
|
private readonly ActionQueue queue = new ActionQueue();
|
||||||
private int activeBucketIndex = 0;
|
private int activeBucketIndex = 0;
|
||||||
private bool closed = false;
|
private bool closed = false;
|
||||||
private string internalErrors = string.Empty;
|
private string internalErrors = string.Empty;
|
||||||
@ -27,20 +25,15 @@ namespace OverwatchTranscript
|
|||||||
AddNewBucket();
|
AddNewBucket();
|
||||||
}
|
}
|
||||||
|
|
||||||
queueWorker = Task.Run(QueueWorker);
|
queue.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Add(DateTime utc, object payload)
|
public void Add(DateTime utc, object payload)
|
||||||
{
|
{
|
||||||
if (closed) throw new Exception("Buckets already closed!");
|
if (closed) throw new Exception("Buckets already closed!");
|
||||||
int count = 0;
|
queue.Add(() => AddInternal(utc, payload));
|
||||||
lock (queueLock)
|
|
||||||
{
|
|
||||||
queue.Add(() => AddInternal(utc, payload));
|
|
||||||
count = queue.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count > 1000)
|
if (queue.Count > 1000)
|
||||||
{
|
{
|
||||||
Thread.Sleep(1);
|
Thread.Sleep(1);
|
||||||
}
|
}
|
||||||
@ -49,8 +42,7 @@ namespace OverwatchTranscript
|
|||||||
public IFinalizedBucket[] FinalizeBuckets()
|
public IFinalizedBucket[] FinalizeBuckets()
|
||||||
{
|
{
|
||||||
closed = true;
|
closed = true;
|
||||||
WaitForZeroQueue();
|
queue.StopAndJoin();
|
||||||
queueWorker.Wait();
|
|
||||||
|
|
||||||
if (IsEmpty()) throw new Exception("No entries have been added.");
|
if (IsEmpty()) throw new Exception("No entries have been added.");
|
||||||
if (!string.IsNullOrEmpty(internalErrors)) throw new Exception(internalErrors);
|
if (!string.IsNullOrEmpty(internalErrors)) throw new Exception(internalErrors);
|
||||||
@ -105,42 +97,7 @@ namespace OverwatchTranscript
|
|||||||
{
|
{
|
||||||
var size = bucketSizes[bucketSizeIndex];
|
var size = bucketSizes[bucketSizeIndex];
|
||||||
bucketSizeIndex = (bucketSizeIndex + 1) % bucketSizes.Length;
|
bucketSizeIndex = (bucketSizeIndex + 1) % bucketSizes.Length;
|
||||||
activeBuckets.Add(new EventBucket(log, Path.Combine(workingDir, Guid.NewGuid().ToString()), size));
|
activeBuckets.Add(new EventBucketWriter(log, Path.Combine(workingDir, Guid.NewGuid().ToString()), size));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void QueueWorker()
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
List<Action> work = null!;
|
|
||||||
lock (queueLock)
|
|
||||||
{
|
|
||||||
work = queue;
|
|
||||||
queue = new List<Action>();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (closed && !work.Any()) return;
|
|
||||||
foreach (var action in work)
|
|
||||||
{
|
|
||||||
action();
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread.Sleep(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void WaitForZeroQueue()
|
|
||||||
{
|
|
||||||
log.Debug("Wait for zero pending.");
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
lock (queueLock)
|
|
||||||
{
|
|
||||||
log.Debug("(wait) Pending: " + queue.Count);
|
|
||||||
if (queue.Count == 0) return;
|
|
||||||
}
|
|
||||||
Thread.Sleep(10);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
127
Framework/OverwatchTranscript/EventBucketReader.cs
Normal file
127
Framework/OverwatchTranscript/EventBucketReader.cs
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
using Logging;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace OverwatchTranscript
|
||||||
|
{
|
||||||
|
public interface IFinalizedBucket
|
||||||
|
{
|
||||||
|
bool IsEmpty { get; }
|
||||||
|
DateTime? SeeTopUtc();
|
||||||
|
BucketTop? TakeTop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BucketTop
|
||||||
|
{
|
||||||
|
public BucketTop(DateTime utc, OverwatchEvent[] events)
|
||||||
|
{
|
||||||
|
Utc = utc;
|
||||||
|
Events = events;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime Utc { get; }
|
||||||
|
public OverwatchEvent[] Events { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EventBucketReader : IFinalizedBucket
|
||||||
|
{
|
||||||
|
private readonly object topLock = new object();
|
||||||
|
private readonly string bucketFile;
|
||||||
|
private readonly AutoResetEvent topReadySignal = new AutoResetEvent(false);
|
||||||
|
private readonly AutoResetEvent topTakenSignal = new AutoResetEvent(true);
|
||||||
|
private BucketTop? top;
|
||||||
|
private DateTime? topUtc;
|
||||||
|
|
||||||
|
public EventBucketReader(ILog log, string bucketFile)
|
||||||
|
{
|
||||||
|
this.bucketFile = bucketFile;
|
||||||
|
if (!File.Exists(bucketFile)) throw new Exception("Doesn't exist: " + bucketFile);
|
||||||
|
|
||||||
|
log.Debug("Read Bucket open: " + bucketFile);
|
||||||
|
|
||||||
|
Task.Run(ReadBucket);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsEmpty { get; private set; }
|
||||||
|
|
||||||
|
public DateTime? SeeTopUtc()
|
||||||
|
{
|
||||||
|
if (IsEmpty) return null;
|
||||||
|
return topUtc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BucketTop? TakeTop()
|
||||||
|
{
|
||||||
|
if (IsEmpty) return null;
|
||||||
|
topReadySignal.WaitOne();
|
||||||
|
|
||||||
|
lock (topLock)
|
||||||
|
{
|
||||||
|
var t = top;
|
||||||
|
top = null;
|
||||||
|
topUtc = null;
|
||||||
|
topTakenSignal.Set();
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReadBucket()
|
||||||
|
{
|
||||||
|
using var file = File.OpenRead(bucketFile);
|
||||||
|
using var reader = new StreamReader(file);
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
topTakenSignal.WaitOne(10);
|
||||||
|
lock (topLock)
|
||||||
|
{
|
||||||
|
if (top == null)
|
||||||
|
{
|
||||||
|
top = CreateNewTop(reader);
|
||||||
|
if (top != null)
|
||||||
|
{
|
||||||
|
topUtc = top.Utc;
|
||||||
|
}
|
||||||
|
topReadySignal.Set();
|
||||||
|
}
|
||||||
|
if (top == null)
|
||||||
|
{
|
||||||
|
IsEmpty = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private EventBucketEntry? nextEntry = null;
|
||||||
|
private BucketTop? CreateNewTop(StreamReader reader)
|
||||||
|
{
|
||||||
|
if (nextEntry == null)
|
||||||
|
{
|
||||||
|
nextEntry = ReadEntry(reader);
|
||||||
|
if (nextEntry == null) return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var topEntry = nextEntry;
|
||||||
|
var entries = new List<EventBucketEntry>
|
||||||
|
{
|
||||||
|
topEntry
|
||||||
|
};
|
||||||
|
|
||||||
|
nextEntry = ReadEntry(reader);
|
||||||
|
while (nextEntry != null && nextEntry.Utc == topEntry.Utc)
|
||||||
|
{
|
||||||
|
entries.Add(nextEntry);
|
||||||
|
nextEntry = ReadEntry(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BucketTop(topEntry.Utc, entries.Select(e => e.Event).ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
private EventBucketEntry? ReadEntry(StreamReader reader)
|
||||||
|
{
|
||||||
|
var line = reader.ReadLine();
|
||||||
|
if (string.IsNullOrEmpty(line)) return null;
|
||||||
|
return JsonConvert.DeserializeObject<EventBucketEntry>(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,7 @@ using Newtonsoft.Json;
|
|||||||
|
|
||||||
namespace OverwatchTranscript
|
namespace OverwatchTranscript
|
||||||
{
|
{
|
||||||
public class EventBucket : IFinalizedBucket
|
public class EventBucketWriter
|
||||||
{
|
{
|
||||||
private const int MaxBuffer = 1000;
|
private const int MaxBuffer = 1000;
|
||||||
|
|
||||||
@ -13,16 +13,15 @@ namespace OverwatchTranscript
|
|||||||
private readonly string bucketFile;
|
private readonly string bucketFile;
|
||||||
private readonly int maxCount;
|
private readonly int maxCount;
|
||||||
private readonly List<EventBucketEntry> buffer = new List<EventBucketEntry>();
|
private readonly List<EventBucketEntry> buffer = new List<EventBucketEntry>();
|
||||||
private EventBucketEntry? topEntry;
|
|
||||||
|
|
||||||
public EventBucket(ILog log, string bucketFile, int maxCount)
|
public EventBucketWriter(ILog log, string bucketFile, int maxCount)
|
||||||
{
|
{
|
||||||
this.log = log;
|
this.log = log;
|
||||||
this.bucketFile = bucketFile;
|
this.bucketFile = bucketFile;
|
||||||
this.maxCount = maxCount;
|
this.maxCount = maxCount;
|
||||||
if (File.Exists(bucketFile)) throw new Exception("Already exists");
|
if (File.Exists(bucketFile)) throw new Exception("Already exists");
|
||||||
|
|
||||||
log.Debug("Bucket open: " + bucketFile);
|
log.Debug("Write Bucket open: " + bucketFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Count { get; private set; }
|
public int Count { get; private set; }
|
||||||
@ -47,29 +46,7 @@ namespace OverwatchTranscript
|
|||||||
SortFileByTimestamps();
|
SortFileByTimestamps();
|
||||||
}
|
}
|
||||||
log.Debug($"Finalized bucket with {Count} entries");
|
log.Debug($"Finalized bucket with {Count} entries");
|
||||||
return this;
|
return new EventBucketReader(log, bucketFile);
|
||||||
}
|
|
||||||
|
|
||||||
public EventBucketEntry? ViewTopEntry()
|
|
||||||
{
|
|
||||||
if (!closed) throw new Exception("Bucket not closed yet. FinalizeBucket first.");
|
|
||||||
return topEntry;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PopTopEntry()
|
|
||||||
{
|
|
||||||
var lines = File.ReadAllLines(bucketFile).ToList();
|
|
||||||
lines.RemoveAt(0);
|
|
||||||
File.WriteAllLines(bucketFile, lines);
|
|
||||||
|
|
||||||
if (lines.Any())
|
|
||||||
{
|
|
||||||
topEntry = JsonConvert.DeserializeObject<EventBucketEntry>(lines[0]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
topEntry = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
@ -124,19 +101,9 @@ namespace OverwatchTranscript
|
|||||||
|
|
||||||
File.Delete(bucketFile);
|
File.Delete(bucketFile);
|
||||||
File.WriteAllLines(bucketFile, entries.Select(JsonConvert.SerializeObject));
|
File.WriteAllLines(bucketFile, entries.Select(JsonConvert.SerializeObject));
|
||||||
|
|
||||||
topEntry = entries.FirstOrDefault();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IFinalizedBucket
|
|
||||||
{
|
|
||||||
int Count { get; }
|
|
||||||
bool IsFull { get; }
|
|
||||||
EventBucketEntry? ViewTopEntry();
|
|
||||||
void PopTopEntry();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class EventBucketEntry
|
public class EventBucketEntry
|
||||||
{
|
{
|
@ -15,17 +15,26 @@ namespace OverwatchTranscript
|
|||||||
this.workingDir = workingDir;
|
this.workingDir = workingDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OverwatchMomentReference[] Build(IFinalizedBucket[] buckets)
|
public OverwatchMomentReference[] Build(IFinalizedBucket[] finalizedBuckets)
|
||||||
{
|
{
|
||||||
var result = new List<OverwatchMomentReference>();
|
var result = new List<OverwatchMomentReference>();
|
||||||
var currentBuilder = new Builder(log, workingDir);
|
var currentBuilder = new Builder(log, workingDir);
|
||||||
|
|
||||||
log.Debug($"Building references for {buckets.Length} buckets.");
|
var buckets = finalizedBuckets.ToList();
|
||||||
while (EntriesRemaining(buckets))
|
log.Debug($"Building references for {buckets.Count} buckets.");
|
||||||
|
while (buckets.Any())
|
||||||
{
|
{
|
||||||
|
buckets.RemoveAll(b => b.IsEmpty);
|
||||||
|
if (!buckets.Any()) break;
|
||||||
|
|
||||||
var earliestUtc = GetEarliestUtc(buckets);
|
var earliestUtc = GetEarliestUtc(buckets);
|
||||||
var entries = CollectAllEntriesForUtc(earliestUtc, buckets);
|
if (earliestUtc == null)
|
||||||
var moment = ConvertEntriesToMoment(entries);
|
{
|
||||||
|
Thread.Sleep(1); continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tops = CollectAllTopsForUtc(earliestUtc.Value, buckets);
|
||||||
|
var moment = ConvertTopsToMoment(tops);
|
||||||
currentBuilder.Add(moment);
|
currentBuilder.Add(moment);
|
||||||
if (currentBuilder.NumberOfMoments == MaxMomentsPerReference)
|
if (currentBuilder.NumberOfMoments == MaxMomentsPerReference)
|
||||||
{
|
{
|
||||||
@ -42,56 +51,58 @@ namespace OverwatchTranscript
|
|||||||
return result.ToArray();
|
return result.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private OverwatchMoment ConvertEntriesToMoment(List<EventBucketEntry> entries)
|
private OverwatchMoment ConvertTopsToMoment(List<BucketTop> tops)
|
||||||
{
|
{
|
||||||
var discintUtc = entries.Select(e => e.Utc).Distinct().ToArray();
|
var discintUtc = tops.Select(e => e.Utc).Distinct().ToArray();
|
||||||
if (discintUtc.Length != 1) throw new Exception("UTC mixing in moment construction.");
|
if (discintUtc.Length != 1) throw new Exception("UTC mixing in moment construction.");
|
||||||
|
|
||||||
return new OverwatchMoment
|
return new OverwatchMoment
|
||||||
{
|
{
|
||||||
Utc = entries[0].Utc,
|
Utc = tops[0].Utc,
|
||||||
Events = entries.Select(e => e.Event).ToArray()
|
Events = tops.SelectMany(e => e.Events).ToArray()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<EventBucketEntry> CollectAllEntriesForUtc(DateTime earliestUtc, IFinalizedBucket[] buckets)
|
private List<BucketTop> CollectAllTopsForUtc(DateTime earliestUtc, List<IFinalizedBucket> buckets)
|
||||||
{
|
{
|
||||||
var result = new List<EventBucketEntry>();
|
var result = new List<BucketTop>();
|
||||||
|
|
||||||
foreach (var bucket in buckets)
|
foreach (var bucket in buckets)
|
||||||
{
|
{
|
||||||
var top = bucket.ViewTopEntry();
|
if (bucket.IsEmpty) continue;
|
||||||
while (top != null && top.Utc == earliestUtc)
|
|
||||||
|
var utc = bucket.SeeTopUtc();
|
||||||
|
if (utc == null) continue;
|
||||||
|
|
||||||
|
if (utc.Value == earliestUtc)
|
||||||
{
|
{
|
||||||
|
var top = bucket.TakeTop();
|
||||||
|
if (top == null) throw new Exception("top was null after top utc was not");
|
||||||
result.Add(top);
|
result.Add(top);
|
||||||
bucket.PopTopEntry();
|
|
||||||
top = bucket.ViewTopEntry();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private DateTime GetEarliestUtc(IFinalizedBucket[] buckets)
|
private DateTime? GetEarliestUtc(List<IFinalizedBucket> buckets)
|
||||||
{
|
{
|
||||||
var earliest = DateTime.MaxValue;
|
var earliest = DateTime.MaxValue;
|
||||||
foreach (var bucket in buckets)
|
foreach (var bucket in buckets)
|
||||||
{
|
{
|
||||||
var top = bucket.ViewTopEntry();
|
var utc = bucket.SeeTopUtc();
|
||||||
if (top != null && top.Utc < earliest) earliest = top.Utc;
|
if (utc == null) return null;
|
||||||
|
|
||||||
|
if (utc.Value < earliest) earliest = utc.Value;
|
||||||
}
|
}
|
||||||
return earliest;
|
return earliest;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool EntriesRemaining(IFinalizedBucket[] buckets)
|
|
||||||
{
|
|
||||||
return buckets.Any(b => b.ViewTopEntry() != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Builder
|
public class Builder
|
||||||
{
|
{
|
||||||
private readonly ILog log;
|
private readonly ILog log;
|
||||||
private OverwatchMomentReference reference;
|
private OverwatchMomentReference reference;
|
||||||
|
private readonly ActionQueue queue = new ActionQueue();
|
||||||
|
|
||||||
public Builder(ILog log, string workingDir)
|
public Builder(ILog log, string workingDir)
|
||||||
{
|
{
|
||||||
@ -104,25 +115,32 @@ namespace OverwatchTranscript
|
|||||||
NumberOfMoments = 0,
|
NumberOfMoments = 0,
|
||||||
};
|
};
|
||||||
this.log = log;
|
this.log = log;
|
||||||
|
|
||||||
|
queue.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int NumberOfMoments => reference.NumberOfMoments;
|
public int NumberOfMoments => reference.NumberOfMoments;
|
||||||
|
|
||||||
public void Add(OverwatchMoment moment)
|
public void Add(OverwatchMoment moment)
|
||||||
{
|
{
|
||||||
File.AppendAllLines(reference.MomentsFile, new[]
|
|
||||||
{
|
|
||||||
JsonConvert.SerializeObject(moment)
|
|
||||||
});
|
|
||||||
|
|
||||||
if (moment.Utc < reference.EarliestUtc) reference.EarliestUtc = moment.Utc;
|
if (moment.Utc < reference.EarliestUtc) reference.EarliestUtc = moment.Utc;
|
||||||
if (moment.Utc > reference.LatestUtc) reference.LatestUtc = moment.Utc;
|
if (moment.Utc > reference.LatestUtc) reference.LatestUtc = moment.Utc;
|
||||||
reference.NumberOfMoments++;
|
reference.NumberOfMoments++;
|
||||||
reference.NumberOfEvents += moment.Events.Length;
|
reference.NumberOfEvents += moment.Events.Length;
|
||||||
|
|
||||||
|
queue.Add(() =>
|
||||||
|
{
|
||||||
|
File.AppendAllLines(reference.MomentsFile, new[]
|
||||||
|
{
|
||||||
|
JsonConvert.SerializeObject(moment)
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public OverwatchMomentReference Build()
|
public OverwatchMomentReference Build()
|
||||||
{
|
{
|
||||||
|
queue.StopAndJoin();
|
||||||
|
|
||||||
log.Debug($"Created reference with {reference.NumberOfMoments} moments and {reference.NumberOfEvents} events...");
|
log.Debug($"Created reference with {reference.NumberOfMoments} moments and {reference.NumberOfEvents} events...");
|
||||||
var result = reference;
|
var result = reference;
|
||||||
reference = null!;
|
reference = null!;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user