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);
                }
        }
示例#6
0
 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);