public async override ValueTask ProcessEffectOnTrackedObject(Key k, EffectTracker tracker) { try { (await this.mainSession.RMWAsync(ref k, ref tracker, token: this.terminationToken)).Complete(); } catch (Exception exception) when(this.terminationToken.IsCancellationRequested && !Utils.IsFatal(exception)) { throw new OperationCanceledException("Partition was terminated.", exception, this.terminationToken); } }
public Item(ItemSaveData saveData) { SkillNames = saveData.SkillNames; Effects = EffectTracker.IDStoEffects(saveData.EffectIDS); IsEquipped = saveData.IsEquipped; Damage = saveData.Damage; Weight = saveData.Weight; Name = saveData.Name; Value = saveData.Value; TL = saveData.TL; Quantity = saveData.Quantity; }
// read a tracked object on the main session and wait for the response (only one of these is executing at a time) public override async ValueTask <TrackedObject> ReadAsync(Key key, EffectTracker effectTracker) { try { var result = await this.mainSession.ReadAsync(key, effectTracker, context : null, token : this.terminationToken).ConfigureAwait(false); var(status, output) = result.Complete(); return(output); } catch (Exception exception) when(this.terminationToken.IsCancellationRequested && !Utils.IsFatal(exception)) { throw new OperationCanceledException("Partition was terminated.", exception, this.terminationToken); } }
//------------------------------------------------------ Private Audio Functions ----------------------------------------------\\ /// <summary> /// Plays an Audioclip. /// <para>If you don't have an AudioClip to play, use the FindAudioClip function.</para> /// </summary> /// <param name="toPlay">Audio clip to play</param> /// <param name="volume">volume of the audioSource</param> /// <param name="loop">Should the audio be looping?</param> /// <param name="pitch">pitch of the audio</param> /// <param name="spatialBlend">Should the audio be 3D?</param> /// <param name="audioPosition">Position of audio in worldspace. Only effective if the spatialBlend parameter is not 0</param> /// <param name="parent">The object that the audio source will be parented to. audioPosition will be ignored if this parameter is filled.</param> /// <param name="fade">Should the audio fade in?</param> /// <param name="step">Amount that will be added to volume when fading</param> /// <param name="audioMixerGroup">Thhe AudioMixerGroup that will be added to the AudioSource that will play the audioclip</param> /// <returns>Returns AudioSource ID used to stop that specific audioSource.</returns> private int PlayAudioFromRPC(AudioClip toPlay, float volume = 1, bool loop = false, float pitch = 1, float spatialBlend = 0, float minDistance = 1, float maxDistance = 20, Vector3 audioPosition = default, Transform parent = null, bool fade = false, float step = 0.1f, string audioMixerGroup = "Master") { EffectTracker <AudioSource> tracker = FindAvailableSource(SyncMode.Multiplayer); AudioSource source = tracker.reference; source.clip = toPlay; source.volume = fade ? 0 : volume; source.loop = loop; source.pitch = pitch; source.spatialBlend = spatialBlend; source.minDistance = minDistance; source.maxDistance = maxDistance; source.rolloffMode = AudioRolloffMode.Linear; if (parent != null) { source.transform.SetParent(parent); source.transform.position = parent.position; } else { source.transform.position = audioPosition; } if (audioMixer != null) { AudioMixerGroup[] audioMixerGroups = audioMixer.FindMatchingGroups(audioMixerGroup); if (audioMixerGroups.Length <= 0) { audioMixerGroups = audioMixer.FindMatchingGroups("Master"); } source.outputAudioMixerGroup = audioMixerGroups[0]; } source.Play(); if (fade) { StartCoroutine(AudioFadeIn(source, volume, step)); } return(tracker.ID); }
// kick off a read of a tracked object, completing asynchronously if necessary public override void ReadAsync(PartitionReadEvent readEvent, EffectTracker effectTracker) { this.partition.Assert(readEvent != null); try { if (readEvent.Prefetch.HasValue) { TryRead(readEvent.Prefetch.Value); } TryRead(readEvent.ReadTarget); void TryRead(Key key) { TrackedObject target = null; var status = this.mainSession.Read(ref key, ref effectTracker, ref target, readEvent, 0); switch (status) { case Status.NOTFOUND: case Status.OK: // fast path: we hit in the cache and complete the read this.StoreStats.HitCount++; effectTracker.ProcessReadResult(readEvent, key, target); break; case Status.PENDING: // slow path: read continuation will be called when complete this.StoreStats.MissCount++; break; case Status.ERROR: this.partition.ErrorHandler.HandleError(nameof(ReadAsync), "FASTER reported ERROR status", null, true, this.partition.ErrorHandler.IsTerminated); break; } } } catch (Exception exception) when(this.terminationToken.IsCancellationRequested && !Utils.IsFatal(exception)) { throw new OperationCanceledException("Partition was terminated.", exception, this.terminationToken); } }
public void Load(TileSaveData saveData) { color = saveData.Color; savedColor = color; effects = EffectTracker.IDStoEffects(saveData.EffectIDs); pins = saveData.Pins; items = new List <Item>(); foreach (ItemSaveData isd in saveData.Items) { items.Add(new Item(isd)); } hints = saveData.Hints; occupants = new List <CharacterSheet>(); foreach (CharacterSaveData csd in saveData.Occupants) { occupants.Add(new CharacterSheet(csd)); } isReveialed = saveData.isReveialed; isBlock = saveData.isBlock; isVisible = saveData.isVisible; }
private EffectTracker <AudioSource> CreateNewAudioSource(SyncMode syncMode) { List <EffectTracker <AudioSource> > audioSources = GetCorrectList(syncMode); string sync = syncMode == SyncMode.Singleplayer ? "Singleplayer" : "Multiplayer"; GameObject newSource = new GameObject { name = "AudioSource " + sync + " " + audioSources.Count }; newSource.transform.SetParent(transform); AudioSource source = newSource.AddComponent <AudioSource>(); source.playOnAwake = false; EffectTracker <AudioSource> toReturn = new EffectTracker <AudioSource>(audioSources.Count); toReturn.CreateReference(source); audioSources.Add(toReturn); return(toReturn); }
public Advantage(AdvantageSaveData saveData) { Name = saveData.Name; Description = saveData.Description; Effects = EffectTracker.IDStoEffects(saveData.EffectIDs); }
IAsyncEnumerable <OrchestrationState> ScanOrchestrationStates( EffectTracker effectTracker, PartitionQueryEvent queryEvent) { var instanceQuery = queryEvent.InstanceQuery; string queryId = queryEvent.EventIdString; this.partition.EventDetailTracer?.TraceEventProcessingDetail($"starting query {queryId}"); // we use a separate thread to iterate, since Faster can iterate synchronously only at the moment // and we don't want it to block thread pool worker threads var channel = Channel.CreateBounded <OrchestrationState>(500); var scanThread = new Thread(RunScan) { Name = $"QueryScan-{queryId}" }; scanThread.Start(); return(channel.Reader.ReadAllAsync()); void RunScan() { using var _ = EventTraceContext.MakeContext(0, queryId); // get the unique set of keys appearing in the log and emit them using var iter1 = this.fht.Iterate(); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); long scanned = 0; long deserialized = 0; long matched = 0; long lastReport; void ReportProgress() { this.partition.EventDetailTracer?.TraceEventProcessingDetail( $"query {queryId} scan position={iter1.CurrentAddress} elapsed={stopwatch.Elapsed.TotalSeconds:F2}s scanned={scanned} deserialized={deserialized} matched={matched}"); lastReport = stopwatch.ElapsedMilliseconds; } ReportProgress(); while (iter1.GetNext(out RecordInfo recordInfo) && !recordInfo.Tombstone) { if (stopwatch.ElapsedMilliseconds - lastReport > 5000) { ReportProgress(); } TrackedObjectKey key = iter1.GetKey().Val; if (key.ObjectType == TrackedObjectKey.TrackedObjectType.Instance) { scanned++; //this.partition.EventDetailTracer?.TraceEventProcessingDetail($"found instance {key.InstanceId}"); if (string.IsNullOrEmpty(instanceQuery?.InstanceIdPrefix) || key.InstanceId.StartsWith(instanceQuery.InstanceIdPrefix)) { //this.partition.EventDetailTracer?.TraceEventProcessingDetail($"reading instance {key.InstanceId}"); object val = iter1.GetValue().Val; //this.partition.EventDetailTracer?.TraceEventProcessingDetail($"read instance {key.InstanceId}, is {(val == null ? "null" : val.GetType().Name)}"); InstanceState instanceState; if (val is byte[] bytes) { instanceState = (InstanceState)Serializer.DeserializeTrackedObject(bytes); deserialized++; } else { instanceState = (InstanceState)val; } // reading the orchestrationState may race with updating the orchestration state // but it is benign because the OrchestrationState object is immutable var orchestrationState = instanceState?.OrchestrationState; if (orchestrationState != null && instanceQuery.Matches(orchestrationState)) { matched++; this.partition.EventDetailTracer?.TraceEventProcessingDetail($"match instance {key.InstanceId}"); var value = orchestrationState.ClearFieldsImmutably(!instanceQuery.FetchInput, false); var task = channel.Writer.WriteAsync(value); if (!task.IsCompleted) { task.AsTask().Wait(); } } } } } ReportProgress(); channel.Writer.Complete(); this.partition.EventDetailTracer?.TraceEventProcessingDetail($"finished query {queryId}"); } }
// kick off a prefetch public override async Task RunPrefetchSession(IAsyncEnumerable <TrackedObjectKey> keys) { int maxConcurrency = 500; using SemaphoreSlim prefetchSemaphore = new SemaphoreSlim(maxConcurrency); Guid sessionId = Guid.NewGuid(); this.blobManager.TraceHelper.FasterProgress($"PrefetchSession {sessionId} started (maxConcurrency={maxConcurrency})"); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); long numberIssued = 0; long numberMisses = 0; long numberHits = 0; long lastReport = 0; void ReportProgress(int elapsedMillisecondsThreshold) { if (stopwatch.ElapsedMilliseconds - lastReport >= elapsedMillisecondsThreshold) { this.blobManager.TraceHelper.FasterProgress( $"FasterKV PrefetchSession {sessionId} elapsed={stopwatch.Elapsed.TotalSeconds:F2}s issued={numberIssued} pending={maxConcurrency-prefetchSemaphore.CurrentCount} hits={numberHits} misses={numberMisses}"); lastReport = stopwatch.ElapsedMilliseconds; } } try { // these are disposed after the prefetch thread is done using var prefetchSession = this.CreateASession(); // for each key, issue a prefetch await foreach (TrackedObjectKey key in keys) { // wait for an available prefetch semaphore token while (!await prefetchSemaphore.WaitAsync(50, this.terminationToken)) { prefetchSession.CompletePending(); ReportProgress(1000); } FasterKV.Key k = key; EffectTracker noInput = null; TrackedObject ignoredOutput = null; var status = prefetchSession.Read(ref k, ref noInput, ref ignoredOutput, userContext: prefetchSemaphore, 0); numberIssued++; switch (status) { case Status.NOTFOUND: case Status.OK: // fast path: we hit in the cache and complete the read numberHits++; prefetchSemaphore.Release(); break; case Status.PENDING: // slow path: upon completion numberMisses++; break; case Status.ERROR: this.partition.ErrorHandler.HandleError(nameof(RunPrefetchSession), "FASTER reported ERROR status", null, true, this.partition.ErrorHandler.IsTerminated); break; } this.terminationToken.ThrowIfCancellationRequested(); prefetchSession.CompletePending(); ReportProgress(1000); } ReportProgress(0); this.blobManager.TraceHelper.FasterProgress($"PrefetchSession {sessionId} is waiting for completion"); // all prefetches were issued; now we wait for them all to complete // by acquiring ALL the semaphore tokens for (int i = 0; i < maxConcurrency; i++) { while (!await prefetchSemaphore.WaitAsync(50, this.terminationToken)) { prefetchSession.CompletePending(); ReportProgress(1000); } } ReportProgress(0); this.blobManager.TraceHelper.FasterProgress($"PrefetchSession {sessionId} completed"); } catch (OperationCanceledException) when(this.terminationToken.IsCancellationRequested) { // partition is terminating } catch (Exception e) when(!Utils.IsFatal(e)) { this.partition.ErrorHandler.HandleError(nameof(RunPrefetchSession), "PrefetchSession {sessionId} encountered exception", e, false, this.partition.ErrorHandler.IsTerminated); } }
// perform a query public override async Task QueryAsync(PartitionQueryEvent queryEvent, EffectTracker effectTracker) { try { var instanceQuery = queryEvent.InstanceQuery; #if FASTER_SUPPORTS_PSF IAsyncEnumerable <OrchestrationState> queryPSFsAsync(ClientSession <Key, Value, EffectTracker, TrackedObject, PartitionReadEvent, Functions> session) { // Issue the PSF query. Note that pending operations will be completed before this returns. var querySpec = new List <(IPSF, IEnumerable <PSFKey>)>(); if (instanceQuery.HasRuntimeStatus) { querySpec.Add((this.RuntimeStatusPsf, instanceQuery.RuntimeStatus.Select(s => new PSFKey(s)))); } if (instanceQuery.CreatedTimeFrom.HasValue || instanceQuery.CreatedTimeTo.HasValue) { IEnumerable <PSFKey> enumerateDateBinKeys() { var to = instanceQuery.CreatedTimeTo ?? DateTime.UtcNow; var from = instanceQuery.CreatedTimeFrom ?? to.AddDays(-7); // TODO Some default so we don't have to iterate from the first possible date for (var dt = from; dt <= to; dt += PSFKey.DateBinInterval) { yield return(new PSFKey(dt)); } } querySpec.Add((this.CreatedTimePsf, enumerateDateBinKeys())); } if (!string.IsNullOrWhiteSpace(instanceQuery.InstanceIdPrefix)) { querySpec.Add((this.InstanceIdPrefixPsf, new[] { new PSFKey(instanceQuery.InstanceIdPrefix) })); } var querySettings = new PSFQuerySettings { // This is a match-all-PSFs enumeration so do not continue after any PSF has hit EOS OnStreamEnded = (unusedPsf, unusedIndex) => false }; OrchestrationState getOrchestrationState(ref Value v) { if (v.Val is byte[] serialized) { var result = ((InstanceState)Serializer.DeserializeTrackedObject(serialized))?.OrchestrationState; if (result != null && !instanceQuery.FetchInput) { result.Input = null; } return(result); } else { var state = ((InstanceState)((TrackedObject)v))?.OrchestrationState; var result = state?.ClearFieldsImmutably(instanceQuery.FetchInput, true); return(result); } } return(session.QueryPSFAsync(querySpec, matches => matches.All(b => b), querySettings) .Select(providerData => getOrchestrationState(ref providerData.GetValue())) .Where(orchestrationState => orchestrationState != null)); } #else IAsyncEnumerable <OrchestrationState> queryPSFsAsync(ClientSession <Key, Value, EffectTracker, TrackedObject, object, IFunctions <Key, Value, EffectTracker, TrackedObject, object> > session) => this.ScanOrchestrationStates(effectTracker, queryEvent); #endif // create an individual session for this query so the main session can be used // while the query is progressing. using (var session = this.CreateASession()) { var orchestrationStates = (this.partition.Settings.UsePSFQueries && instanceQuery.IsSet) ? queryPSFsAsync(session) : this.ScanOrchestrationStates(effectTracker, queryEvent); await effectTracker.ProcessQueryResultAsync(queryEvent, orchestrationStates); } } catch (Exception exception) when(this.terminationToken.IsCancellationRequested && !Utils.IsFatal(exception)) { throw new OperationCanceledException("Partition was terminated.", exception, this.terminationToken); } }
public abstract ValueTask ProcessEffectOnTrackedObject(FasterKV.Key k, EffectTracker tracker);
// read a tracked object on the main session and wait for the response (only one of these is executing at a time) public abstract ValueTask <TrackedObject> ReadAsync(FasterKV.Key key, EffectTracker effectTracker);
// kick off a read of a tracked object, completing asynchronously if necessary public abstract void ReadAsync(PartitionReadEvent readEvent, EffectTracker effectTracker);
// perform a query public abstract Task QueryAsync(PartitionQueryEvent queryEvent, EffectTracker effectTracker);