Пример #1
0
        public static async Task <IComposedObject <T> > LookupOrRetrieveAsync <T>(
            DbRef reference,
            [NotNull] ICacheClient redis,
            [NotNull] Func <DbRef, CancellationToken, Task <T> > retrieveFunction,
            CancellationToken cancellationToken) where T : ObjectBase
        {
            if (reference.Equals(DbRef.Ambiguous) || reference.Equals(DbRef.FailedMatch) ||
                reference.Equals(DbRef.Nothing))
            {
                return(null);
            }

            if (redis == null)
            {
                throw new ArgumentNullException(nameof(redis));
            }

            if (retrieveFunction == null)
            {
                throw new ArgumentNullException(nameof(retrieveFunction));
            }

            if (reference.Equals(DbRef.Nothing))
            {
                return(null);
            }

            if (Cache.Contains(reference))
            {
                return((IComposedObject <T>)Cache.Get(reference));
            }

            var obj = await retrieveFunction.Invoke(reference, cancellationToken);

            if (obj == null)
            {
                return(null);
            }

            var composition = await ComposedObject <T> .CreateAsync(redis, obj, cancellationToken);

            if (composition.Item1)
            {
                // The composition was perfect!  No unresolved references, so cache it as-is.
                Cache.Add(reference, composition.Item2, Policy);
            }

            return(composition.Item2);
        }
Пример #2
0
        /// <inheritdoc />
        public async Task MoveAsync(DbRef newLocation, ICacheClient redis, CancellationToken cancellationToken)
        {
            if (redis == null)
            {
                throw new ArgumentNullException(nameof(redis));
            }

            Debug.Assert(!newLocation.Equals(DbRef.Nothing), "!newLocation.Equals(DbRef.NOTHING)");
            Debug.Assert(!newLocation.Equals(DbRef.Ambiguous), "!newLocation.Equals(DbRef.AMBIGUOUS)");
            Debug.Assert(!newLocation.Equals(DbRef.FailedMatch), "!newLocation.Equals(DbRef.FAILED_MATCH)");

            if (redis == null)
            {
                throw new ArgumentNullException(nameof(redis));
            }

            if (this.Location.Equals(newLocation))
            {
                return;
            }

            var oldLocationObject = this.Location.Equals(DbRef.Nothing) ? null : await CacheManager.LookupOrRetrieveAsync(this.Location, redis, async (d, token) => await GetAsync(redis, d, token), cancellationToken);

            var newLocationObject = await CacheManager.LookupOrRetrieveAsync(newLocation, redis, async (d, token) => await GetAsync(redis, d, token), cancellationToken);

            if (newLocationObject != null)
            {
                if (oldLocationObject != null)
                {
                    oldLocationObject.DataObject.RemoveContents(this.DbRef);
                    await oldLocationObject.DataObject.SaveAsync(redis, cancellationToken);
                }

                newLocationObject.DataObject.AddContents(this.DbRef);
                await newLocationObject.DataObject.SaveAsync(redis, cancellationToken);

                this.Location = newLocation;
                await this.SaveAsync(redis, cancellationToken);

                var genericUpdateAsync = typeof(CacheManager).GetMethod(nameof(CacheManager.UpdateAsync)).MakeGenericMethod(newLocationObject.DataObject.GetType());
                var task = (Task)genericUpdateAsync.Invoke(null, new object[] { newLocation, redis, newLocationObject.DataObject, cancellationToken });
                Debug.Assert(task != null, "task != null");
                await task;
            }
        }
Пример #3
0
        /// <inheritdoc />
        public async Task ReparentAsync(DbRef newParent, ICacheClient redis, CancellationToken cancellationToken)
        {
            Debug.Assert(!newParent.Equals(DbRef.Nothing), "!newParent.Equals(DbRef.NOTHING)");
            Debug.Assert(!newParent.Equals(DbRef.Ambiguous), "!newParent.Equals(DbRef.AMBIGUOUS)");
            Debug.Assert(!newParent.Equals(DbRef.FailedMatch), "!newParent.Equals(DbRef.FAILED_MATCH)");

            if (redis == null)
            {
                throw new ArgumentNullException(nameof(redis));
            }

            var newParentObject = await CacheManager.LookupOrRetrieveAsync(newParent, redis, async (d, token) => await GetAsync(redis, d, token), cancellationToken);

            if (newParentObject != null)
            {
                this.Parent = newParent;
                await this.SaveAsync(redis, cancellationToken);
            }
        }
Пример #4
0
        public async Task <ProgramContext <T> > RunProgramAsync <T>(
            DbRef programRef,
            [CanBeNull] Connection connection,
            [NotNull] ObjectBase thisObject,
            [NotNull] ObjectBase caller,
            [CanBeNull] string verb,
            [CanBeNull] string argString,
            [CanBeNull] string[] args,
            [CanBeNull] string directObjectString,
            [CanBeNull] ObjectBase directObject,
            [CanBeNull] string prepositionString,
            [CanBeNull] string indirectObjectString,
            [CanBeNull] ObjectBase indirectObject,
            CancellationToken cancellationToken)
        {
            if (thisObject == null)
            {
                throw new ArgumentNullException(nameof(thisObject));
            }

            if (caller == null)
            {
                throw new ArgumentNullException(nameof(caller));
            }

            if (programRef.Equals(DbRef.Nothing))
            {
                return(ProgramContext <T> .Error(null, ContextErrorNumber.ProgramNotSpecified, "No program was supplied"));
            }

            var program = await Program.GetAsync(this.redis, programRef, cancellationToken);

            if (program == null)
            {
                return(ProgramContext <T> .Error(
                           null,
                           ContextErrorNumber.ProgramNotFound,
                           $"Unable to locate program {programRef}"));
            }

            // ReSharper disable once ConditionIsAlwaysTrueOrFalse
            if (connection?.Identity == null && !program.UnauthenticatedExecution)
            {
                return(ProgramContext <T> .Error(program, ContextErrorNumber.AuthenticationRequired, "No trigger was supplied"));
            }

            var context = new ProgramContext <T>(program);

            using (var outputStream = new MemoryStream(2048))
                using (var outputStreamReader = new StreamReader(outputStream))
                    using (var outputStreamWriter = new StreamWriter(outputStream))

                        // Don't try to combine this with the method parameter version
                        using (var outputCancellationTokenSource = new CancellationTokenSource())
                        {
                            Debug.Assert(outputStream != null, "outputStream != null");
                            Debug.Assert(outputStreamWriter != null, "outputStreamWriter != null");

                            var scriptGlobals = new ProgramContextGlobals(thisObject, caller, outputStreamWriter, new Libraries.DatabaseLibrary(caller, this.redis))
                            {
                                ArgString            = argString,
                                Args                 = args,
                                DirectObject         = directObject,
                                DirectObjectString   = directObjectString,
                                IndirectObject       = indirectObject,
                                IndirectObjectString = indirectObjectString,
                                Player               = connection?.Identity,
                                PlayerLocation       = connection?.Identity == null ? null : (await CacheManager.LookupOrRetrieveAsync <ObjectBase>(connection.Identity.Location, this.Redis, async(d, token) => await Room.GetAsync(this.Redis, d, token), cancellationToken))?.DataObject,
                                PrepositionString    = prepositionString,
                                Verb                 = verb
                            };

                            var outputLastPositionRead = 0L;

                            // OUTPUT
                            var appendOutputTask = new Task(
                                async() =>
                            {
                                while (context.State == ContextState.Loaded || context.State == ContextState.Running)
                                {
                                    if (context.State == ContextState.Running)
                                    {
                                        // Input
                                        await scriptGlobals.PlayerInputWriterInternal.FlushAsync();

                                        // Output
                                        await scriptGlobals.PlayerOutput.FlushAsync();
                                        outputStream.Position = Interlocked.Read(ref outputLastPositionRead);
                                        var outputString      = await outputStreamReader.ReadToEndAsync();
                                        if (!string.IsNullOrEmpty(outputString))
                                        {
                                            context.AppendFeedback(outputString);
                                        }

                                        Interlocked.Exchange(ref outputLastPositionRead, outputStream.Position);

                                        // Send output to trigger, if capable of receiving.
                                        while (connection != null && context.Output.Count > 0)
                                        {
                                            string nextOutput;
                                            if (context.Output.TryDequeue(out nextOutput) && nextOutput != null)
                                            {
                                                await connection.SendAsync(nextOutput, cancellationToken);
                                            }
                                            else
                                            {
                                                break;
                                            }
                                        }
                                    }

                                    Thread.Sleep(100);
                                }
                            },
                                outputCancellationTokenSource.Token);
                            appendOutputTask.Start();

                            // INPUT
                            if (program.Interactive)
                            {
                                connection?.RedirectInputToProgram(
                                    async input =>
                                {
                                    await scriptGlobals.PlayerInputWriterInternal.WriteAsync(input);
                                    await scriptGlobals.PlayerInputWriterInternal.FlushAsync();
                                });
                            }

                            try
                            {
                                await context.RunAsync(scriptGlobals, cancellationToken);
                            }
                            finally
                            {
                                connection?.ResetInputRedirection();
                            }

                            outputCancellationTokenSource.Cancel();

                            // Do one last time to get any last feedback

                            // Input
                            await scriptGlobals.PlayerInputWriterInternal.FlushAsync();

                            // Output
                            await scriptGlobals.PlayerOutput.FlushAsync();

                            outputStream.Position = Interlocked.Read(ref outputLastPositionRead);
                            var feedbackString2 = await outputStreamReader.ReadToEndAsync();

                            Interlocked.Exchange(ref outputLastPositionRead, outputStream.Position);
                            if (!string.IsNullOrEmpty(feedbackString2))
                            {
                                context.AppendFeedback(feedbackString2);
                            }

                            // Send output to trigger, if capable of receiving.
                            while (connection != null && context.Output.Count > 0)
                            {
                                string nextOutput;
                                if (context.Output.TryDequeue(out nextOutput) && nextOutput != null)
                                {
                                    await connection.SendAsync(nextOutput, cancellationToken);
                                }
                                else
                                {
                                    break;
                                }
                            }
                        }

            return(context);
        }
Пример #5
0
        public static async Task <Tuple <DbRef, ObjectBase> > MatchVerbAsync([CanBeNull] Player player, [NotNull] ICacheClient redis, [NotNull] string text, DbRef directObjectRef, DbRef indirectObjectRef, CancellationToken cancellationToken)
        {
            var matched = await MatchTypeAsync <Link>(player, redis, text, cancellationToken);

            if (!matched.Item1.Equals(DbRef.FailedMatch))
            {
                return(matched);
            }

            // We didn't find a verb, so try hunting on the Direct Object
            if (!directObjectRef.Equals(DbRef.Ambiguous) && !directObjectRef.Equals(DbRef.FailedMatch) && !directObjectRef.Equals(DbRef.Nothing))
            {
                var        exactMatch             = DbRef.FailedMatch;
                ObjectBase lastExactMatchObject   = null;
                var        partialMatch           = DbRef.FailedMatch;
                ObjectBase lastPartialMatchObject = null;

                var directObject = await CacheManager.LookupOrRetrieveAsync(directObjectRef, redis, async (d, token) => await ObjectBase.GetAsync(redis, d, token), cancellationToken);

                if (directObject == null)
                {
                    return(new Tuple <DbRef, ObjectBase>(exactMatch, null));
                }

                MatchTypeOnObject <Link, ObjectBase>(text, directObject, ref exactMatch, ref lastExactMatchObject, ref partialMatch, ref lastPartialMatchObject);

                if (exactMatch > 0)
                {
                    return(exactMatch > 0
                        ? new Tuple <DbRef, ObjectBase>(exactMatch, lastExactMatchObject)
                        : new Tuple <DbRef, ObjectBase>(exactMatch, null));
                }

                if (partialMatch > 0)
                {
                    return(partialMatch > 0
                        ? new Tuple <DbRef, ObjectBase>(partialMatch, lastPartialMatchObject)
                        : new Tuple <DbRef, ObjectBase>(partialMatch, null));
                }

                return(new Tuple <DbRef, ObjectBase>(exactMatch + partialMatch, null));
            }

            // We didn't find a verb, so try hunting on the Indirect Object
            if (!indirectObjectRef.Equals(DbRef.Ambiguous) && !indirectObjectRef.Equals(DbRef.FailedMatch) && !indirectObjectRef.Equals(DbRef.Nothing))
            {
                var        exactMatch             = DbRef.FailedMatch;
                ObjectBase lastExactMatchObject   = null;
                var        partialMatch           = DbRef.FailedMatch;
                ObjectBase lastPartialMatchObject = null;

                var indirectObject = await CacheManager.LookupOrRetrieveAsync(indirectObjectRef, redis, async (d, token) => await ObjectBase.GetAsync(redis, d, token), cancellationToken);

                if (indirectObject == null)
                {
                    return(new Tuple <DbRef, ObjectBase>(exactMatch, null));
                }

                MatchTypeOnObject <Link, ObjectBase>(text, indirectObject, ref exactMatch, ref lastExactMatchObject, ref partialMatch, ref lastPartialMatchObject);

                if (exactMatch > 0)
                {
                    return(exactMatch > 0
                        ? new Tuple <DbRef, ObjectBase>(exactMatch, lastExactMatchObject)
                        : new Tuple <DbRef, ObjectBase>(exactMatch, null));
                }

                if (partialMatch > 0)
                {
                    return(partialMatch > 0
                        ? new Tuple <DbRef, ObjectBase>(partialMatch, lastPartialMatchObject)
                        : new Tuple <DbRef, ObjectBase>(partialMatch, null));
                }

                return(new Tuple <DbRef, ObjectBase>(exactMatch + partialMatch, null));
            }

            return(new Tuple <DbRef, ObjectBase>(DbRef.FailedMatch, null));
        }