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); }
/// <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; } }
/// <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); } }
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); }
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)); }