From f848495243b167686d820be1939f1e7183df15e2 Mon Sep 17 00:00:00 2001 From: ReinforceZwei <39955851+ReinforceZwei@users.noreply.github.com> Date: Wed, 12 May 2021 12:20:13 +0800 Subject: [PATCH] Implement schedule main thread task with return value (#1579) * Implement schedule main thread task with return value * Revert change of TestBot.cs --- MinecraftClient/ChatBot.cs | 58 ++++++++++++-------------- MinecraftClient/McClient.cs | 21 ++++++---- MinecraftClient/MinecraftClient.csproj | 1 + MinecraftClient/TaskWithResult.cs | 47 +++++++++++++++++++++ 4 files changed, 88 insertions(+), 39 deletions(-) create mode 100644 MinecraftClient/TaskWithResult.cs diff --git a/MinecraftClient/ChatBot.cs b/MinecraftClient/ChatBot.cs index e32c9458..aafc2942 100644 --- a/MinecraftClient/ChatBot.cs +++ b/MinecraftClient/ChatBot.cs @@ -72,7 +72,7 @@ namespace MinecraftClient { if (delayTasks[i].Tick()) { - Handler.ScheduleTask(delayTasks[i].Task); + delayTasks[i].Task.DynamicInvoke(); tasksToRemove.Add(i); } } @@ -1390,37 +1390,44 @@ namespace MinecraftClient } /// - /// Schedule a task to run on main thread + /// Schedule a task to run on main thread. Returned value cannot be retrieved /// /// Task to run /// Run the task after X ticks (1 tick delay = ~100ms). 0 for no delay /// /// // Delay ~10 seconds - /// ScheduleTask(delegate () + /// ScheduleTaskDelayed(new Action(() => /// { /// /** Your code here **/ /// Console.WriteLine("10 seconds has passed"); - /// }, 100); + /// }), 100); /// - protected void ScheduleTask(Action task, int delayTicks = 0) + protected void ScheduleTaskDelayed(Delegate task, int delayTicks = 0) { - if (task != null) + if (delayTicks <= 0) { - if (delayTicks <= 0) + // Immediately schedule to run on next update + Handler.ScheduleTask(task); + } + else + { + lock (delayTasksLock) { - // Immediately schedule to run on next update - Handler.ScheduleTask(task); - } - else - { - lock (delayTasksLock) - { - delayTasks.Add(new DelayedTask(task, delayTicks)); - } + delayTasks.Add(new DelayedTask(task, delayTicks)); } } } + /// + /// Schedule a task to run on main thread. + /// + /// Task to run + /// Any value returned from the task + protected object ScheduleTask(Delegate task) + { + return Handler.ScheduleTask(task); + } + /// /// Command runner definition. /// Returned string will be the output of the command @@ -1468,16 +1475,16 @@ namespace MinecraftClient private class DelayedTask { - private Action task; + private Delegate task; private int Counter; - public Action Task { get { return task; } } + public Delegate Task { get { return task; } } - public DelayedTask(Action task) + public DelayedTask(Delegate task) : this(task, 0) { } - public DelayedTask(Action task, int delayTicks) + public DelayedTask(Delegate task, int delayTicks) { this.task = task; Counter = delayTicks; @@ -1494,17 +1501,6 @@ namespace MinecraftClient return true; return false; } - - /// - /// Execute the task - /// - public void Execute() - { - if (task != null) - { - task(); - } - } } } } diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index 4901b57e..f55b5c65 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -32,7 +32,7 @@ namespace MinecraftClient private Queue chatQueue = new Queue(); private static DateTime nextMessageSendTime = DateTime.MinValue; - private Action threadTasks; + private Queue threadTasks = new Queue(); private object threadTasksLock = new object(); private readonly List bots = new List(); @@ -312,7 +312,7 @@ namespace MinecraftClient while (client.Client.Connected) { string text = ConsoleIO.ReadLine(); - ScheduleTask(delegate () { HandleCommandPromptText(text); }); + ScheduleTask(new Action(() => { HandleCommandPromptText(text); })); } } catch (IOException) { } @@ -641,12 +641,14 @@ namespace MinecraftClient SendRespawnPacket(); } - if (threadTasks != null) + + lock (threadTasksLock) { - lock (threadTasksLock) + while (threadTasks.Count > 0) { - threadTasks(); - threadTasks = null; + var taskToRun = threadTasks.Dequeue(); + taskToRun.Execute(); + taskToRun.Release(); } } } @@ -697,12 +699,15 @@ namespace MinecraftClient /// Schedule a task to run on the main thread /// /// Task to run - public void ScheduleTask(Action task) + public object ScheduleTask(Delegate task) { + var taskAndResult = new TaskWithResult(task); lock (threadTasksLock) { - threadTasks += task; + threadTasks.Enqueue(taskAndResult); } + taskAndResult.Block(); + return taskAndResult.Result; } #region Management: Load/Unload ChatBots and Enable/Disable settings diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index 117b619f..8850677f 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -222,6 +222,7 @@ + diff --git a/MinecraftClient/TaskWithResult.cs b/MinecraftClient/TaskWithResult.cs new file mode 100644 index 00000000..c68a5014 --- /dev/null +++ b/MinecraftClient/TaskWithResult.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; + +namespace MinecraftClient +{ + public class TaskWithResult + { + private Delegate Task; + private AutoResetEvent ResultEvent = new AutoResetEvent(false); + + public object Result; + + public TaskWithResult(Delegate task) + { + Task = task; + } + + /// + /// Execute the delegate and set the property to the returned value + /// + /// Value returned from delegate + public object Execute() + { + Result = Task.DynamicInvoke(); + return Result; + } + + /// + /// Block the program execution + /// + public void Block() + { + ResultEvent.WaitOne(); + } + + /// + /// Resume the program execution + /// + public void Release() + { + ResultEvent.Set(); + } + } +}