From a5a8075efb61baba880a31c5a81ff12be5b3b8ce Mon Sep 17 00:00:00 2001 From: ReinforceZwei <39955851+ReinforceZwei@users.noreply.github.com> Date: Sat, 8 May 2021 21:03:23 +0800 Subject: [PATCH] Add method to schedule main thread task (#1570) * Add method to schedule main thread task * ChatBot API: New delay task method * Add sealed attribute to ChatBot internal method --- MinecraftClient/ChatBot.cs | 108 ++++++++++++++++++++++++++++++++++++ MinecraftClient/McClient.cs | 39 ++++++++----- 2 files changed, 134 insertions(+), 13 deletions(-) diff --git a/MinecraftClient/ChatBot.cs b/MinecraftClient/ChatBot.cs index 6fa460a2..565f2395 100644 --- a/MinecraftClient/ChatBot.cs +++ b/MinecraftClient/ChatBot.cs @@ -41,6 +41,8 @@ namespace MinecraftClient private ChatBot master = null; private List registeredPluginChannels = new List(); private List registeredCommands = new List(); + private object delayTasksLock = new object(); + private List delayTasks = new List(); private McClient Handler { get @@ -53,6 +55,39 @@ namespace MinecraftClient } } + /// + /// Will be called every ~100ms. + /// + /// + /// method can be overridden by child class so need an extra update method + /// + public sealed void UpdateInternal() + { + lock (delayTasksLock) + { + if (delayTasks.Count > 0) + { + List tasksToRemove = new List(); + for (int i = 0; i < delayTasks.Count; i++) + { + if (delayTasks[i].Tick()) + { + Handler.ScheduleTask(delayTasks[i].Task); + tasksToRemove.Add(i); + } + } + if (tasksToRemove.Count > 0) + { + tasksToRemove.Sort((a, b) => b.CompareTo(a)); // descending sort + foreach (int index in tasksToRemove) + { + delayTasks.RemoveAt(index); + } + } + } + } + } + /* ================================================== */ /* Main methods to override for creating your bot */ /* ================================================== */ @@ -1354,6 +1389,38 @@ namespace MinecraftClient return Handler.GetProtocolVersion(); } + /// + /// Schedule a task to run on main thread + /// + /// Task to run + /// Run the task after X ticks (1 tick delay = ~100ms). 0 for no delay + /// + /// // Delay ~10 seconds + /// ScheduleTask(delegate () + /// { + /// /** Your code here **/ + /// Console.WriteLine("10 seconds has passed"); + /// }, 100); + /// + protected void ScheduleTask(Action task, int delayTicks = 0) + { + if (task != null) + { + if (delayTicks <= 0) + { + // Immediately schedule to run on next update + Handler.ScheduleTask(task); + } + else + { + lock (delayTasksLock) + { + delayTasks.Add(new DelayedTask(task, delayTicks)); + } + } + } + } + /// /// Command runner definition. /// Returned string will be the output of the command @@ -1398,5 +1465,46 @@ namespace MinecraftClient this.Runner = callback; } } + + private class DelayedTask + { + private Action task; + private int Counter; + + public Action Task { get { return task; } } + + public DelayedTask(Action task) + : this(task, 0) + { } + + public DelayedTask(Action task, int delayTicks) + { + this.task = task; + Counter = delayTicks; + } + + /// + /// Tick the counter + /// + /// Return true if counted to zero + public bool Tick() + { + Counter--; + if (Counter <= 0) + 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 6516c17a..4901b57e 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -28,11 +28,13 @@ namespace MinecraftClient private readonly Dictionary onlinePlayers = new Dictionary(); private static bool commandsLoaded = false; - private Queue commandQueue = new Queue(); private Queue chatQueue = new Queue(); private static DateTime nextMessageSendTime = DateTime.MinValue; + private Action threadTasks; + private object threadTasksLock = new object(); + private readonly List bots = new List(); private static readonly List botsOnHold = new List(); private static Dictionary inventories = new Dictionary(); @@ -310,10 +312,7 @@ namespace MinecraftClient while (client.Client.Connected) { string text = ConsoleIO.ReadLine(); - lock (commandQueue) - { - commandQueue.Enqueue(text); - } + ScheduleTask(delegate () { HandleCommandPromptText(text); }); } } catch (IOException) { } @@ -579,6 +578,7 @@ namespace MinecraftClient try { bot.Update(); + bot.UpdateInternal(); } catch (Exception e) { @@ -590,14 +590,6 @@ namespace MinecraftClient } } - lock (commandQueue) - { - if (commandQueue.Count > 0) - { - HandleCommandPromptText(commandQueue.Dequeue()); - } - } - lock (chatQueue) { if (chatQueue.Count > 0 && nextMessageSendTime < DateTime.Now) @@ -648,6 +640,15 @@ namespace MinecraftClient if (respawnTicks == 0) SendRespawnPacket(); } + + if (threadTasks != null) + { + lock (threadTasksLock) + { + threadTasks(); + threadTasks = null; + } + } } /// @@ -692,6 +693,18 @@ namespace MinecraftClient else return false; } + /// + /// Schedule a task to run on the main thread + /// + /// Task to run + public void ScheduleTask(Action task) + { + lock (threadTasksLock) + { + threadTasks += task; + } + } + #region Management: Load/Unload ChatBots and Enable/Disable settings ///