diff --git a/MinecraftClient/ChatBot.cs b/MinecraftClient/ChatBot.cs
index aafc2942..e15bd771 100644
--- a/MinecraftClient/ChatBot.cs
+++ b/MinecraftClient/ChatBot.cs
@@ -1402,12 +1402,13 @@ namespace MinecraftClient
/// Console.WriteLine("10 seconds has passed");
/// }), 100);
///
- protected void ScheduleTaskDelayed(Delegate task, int delayTicks = 0)
+ // TODO: Adapt to new IMinecraftComHandler API
+ /*protected void ScheduleTaskDelayed(Delegate task, int delayTicks = 0)
{
if (delayTicks <= 0)
{
// Immediately schedule to run on next update
- Handler.ScheduleTask(task);
+ Handler.InvokeOnMainThread(task);
}
else
{
@@ -1416,17 +1417,19 @@ namespace MinecraftClient
delayTasks.Add(new DelayedTask(task, delayTicks));
}
}
- }
+ }*/
///
/// Schedule a task to run on main thread.
///
/// Task to run
/// Any value returned from the task
+ // TODO: Adapt to new IMinecraftComHandler API
+ /*
protected object ScheduleTask(Delegate task)
{
- return Handler.ScheduleTask(task);
- }
+ return Handler.InvokeOnMainThread(task);
+ }*/
///
/// Command runner definition.
diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs
index a19763d4..d825c95d 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 Queue threadTasks = new Queue();
+ private Queue threadTasks = new Queue();
private object threadTasksLock = new object();
private readonly List bots = new List();
@@ -302,7 +302,6 @@ namespace MinecraftClient
///
/// Allows the user to send chat messages, commands, and leave the server.
- /// Enqueue text typed in the command prompt for processing on the main thread.
///
private void CommandPrompt()
{
@@ -312,7 +311,7 @@ namespace MinecraftClient
while (client.Client.Connected)
{
string text = ConsoleIO.ReadLine();
- ScheduleTask(new Action(() => { HandleCommandPromptText(text); }));
+ InvokeOnMainThread(() => HandleCommandPromptText(text));
}
}
catch (IOException) { }
@@ -641,14 +640,12 @@ namespace MinecraftClient
SendRespawnPacket();
}
-
lock (threadTasksLock)
{
while (threadTasks.Count > 0)
{
- var taskToRun = threadTasks.Dequeue();
- taskToRun.Execute();
- taskToRun.Release();
+ Action taskToRun = threadTasks.Dequeue();
+ taskToRun();
}
}
}
@@ -696,28 +693,43 @@ namespace MinecraftClient
}
///
- /// Schedule a task to run on the main thread
+ /// Invoke a task on the main thread, wait for completion and retrieve return value.
///
- /// Task to run
- /// Any result returned from delegate
- public object ScheduleTask(Delegate task)
+ /// Task to run with any type or return value
+ /// Any result returned from task, result type is inferred from the task
+ /// bool result = InvokeOnMainThread(methodThatReturnsAbool);
+ /// bool result = InvokeOnMainThread(() => methodThatReturnsAbool(argument));
+ /// int result = InvokeOnMainThread(() => { yourCode(); return 42; });
+ /// Type of the return value
+ public T InvokeOnMainThread(Func task)
{
if (!InvokeRequired())
{
- return task.DynamicInvoke();
+ return task();
}
else
{
- var taskAndResult = new TaskWithResult(task);
+ TaskWithResult taskWithResult = new TaskWithResult(task);
lock (threadTasksLock)
{
- threadTasks.Enqueue(taskAndResult);
+ threadTasks.Enqueue(taskWithResult.ExecuteSynchronously);
}
- taskAndResult.Block();
- return taskAndResult.Result;
+ return taskWithResult.WaitGetResult();
}
}
+ ///
+ /// Invoke a task on the main thread and wait for completion
+ ///
+ /// Task to run without return value
+ /// InvokeOnMainThread(methodThatReturnsNothing);
+ /// InvokeOnMainThread(() => methodThatReturnsNothing(argument));
+ /// InvokeOnMainThread(() => { yourCode(); });
+ public void InvokeOnMainThread(Action task)
+ {
+ InvokeOnMainThread(() => { task(); return true; });
+ }
+
///
/// Check if calling thread is main thread or other thread
///
diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs
index 7a1e087b..5018080b 100644
--- a/MinecraftClient/Protocol/Handlers/Protocol18.cs
+++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs
@@ -423,10 +423,9 @@ namespace MinecraftClient.Protocol.Handlers
int compressedDataSize = dataTypes.ReadNextInt(packetData);
byte[] compressed = dataTypes.ReadData(compressedDataSize, packetData);
byte[] decompressed = ZlibUtils.Decompress(compressed);
- new Task(new Action(() =>
- {
+ new Thread(() => {
pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, addBitmap, currentDimension == 0, chunksContinuous, currentDimension, new Queue(decompressed));
- })).Start();
+ }).Start();
}
else
{
@@ -450,10 +449,9 @@ namespace MinecraftClient.Protocol.Handlers
else dataTypes.ReadData(1024 * 4, packetData); // Biomes - 1.15 and above
}
int dataSize = dataTypes.ReadNextVarInt(packetData);
- new Task(new Action(() =>
- {
+ new Thread(() => {
pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, 0, false, chunksContinuous, currentDimension, packetData);
- })).Start();
+ }).Start();
}
}
break;
diff --git a/MinecraftClient/Protocol/Handlers/Protocol18Terrain.cs b/MinecraftClient/Protocol/Handlers/Protocol18Terrain.cs
index 8a0f92a9..14202599 100644
--- a/MinecraftClient/Protocol/Handlers/Protocol18Terrain.cs
+++ b/MinecraftClient/Protocol/Handlers/Protocol18Terrain.cs
@@ -162,12 +162,12 @@ namespace MinecraftClient.Protocol.Handlers
}
//We have our chunk, save the chunk into the world
- handler.ScheduleTask(new Action(() =>
+ handler.InvokeOnMainThread(() =>
{
if (handler.GetWorld()[chunkX, chunkZ] == null)
handler.GetWorld()[chunkX, chunkZ] = new ChunkColumn();
handler.GetWorld()[chunkX, chunkZ][chunkY] = chunk;
- }));
+ });
//Pre-1.14 Lighting data
if (protocolversion < Protocol18Handler.MC114Version)
@@ -192,10 +192,10 @@ namespace MinecraftClient.Protocol.Handlers
if (chunksContinuous && chunkMask == 0)
{
//Unload the entire chunk column
- handler.ScheduleTask(new Action(() =>
+ handler.InvokeOnMainThread(() =>
{
handler.GetWorld()[chunkX, chunkZ] = null;
- }));
+ });
}
else
{
@@ -214,12 +214,12 @@ namespace MinecraftClient.Protocol.Handlers
chunk[blockX, blockY, blockZ] = new Block(queue.Dequeue());
//We have our chunk, save the chunk into the world
- handler.ScheduleTask(new Action(() =>
+ handler.InvokeOnMainThread(() =>
{
if (handler.GetWorld()[chunkX, chunkZ] == null)
handler.GetWorld()[chunkX, chunkZ] = new ChunkColumn();
handler.GetWorld()[chunkX, chunkZ][chunkY] = chunk;
- }));
+ });
}
}
@@ -248,10 +248,10 @@ namespace MinecraftClient.Protocol.Handlers
if (chunksContinuous && chunkMask == 0)
{
//Unload the entire chunk column
- handler.ScheduleTask(new Action(() =>
+ handler.InvokeOnMainThread(() =>
{
handler.GetWorld()[chunkX, chunkZ] = null;
- }));
+ });
}
else
{
@@ -297,12 +297,12 @@ namespace MinecraftClient.Protocol.Handlers
for (int blockX = 0; blockX < Chunk.SizeX; blockX++)
chunk[blockX, blockY, blockZ] = new Block(blockTypes.Dequeue(), blockMeta.Dequeue());
- handler.ScheduleTask(new Action(() =>
+ handler.InvokeOnMainThread(() =>
{
if (handler.GetWorld()[chunkX, chunkZ] == null)
handler.GetWorld()[chunkX, chunkZ] = new ChunkColumn();
handler.GetWorld()[chunkX, chunkZ][chunkY] = chunk;
- }));
+ });
}
}
}
diff --git a/MinecraftClient/Protocol/IMinecraftComHandler.cs b/MinecraftClient/Protocol/IMinecraftComHandler.cs
index e34dc490..6b4072a2 100644
--- a/MinecraftClient/Protocol/IMinecraftComHandler.cs
+++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs
@@ -41,11 +41,24 @@ namespace MinecraftClient.Protocol
ILogger GetLogger();
///
- /// Schedule a task to run on the main thread
+ /// Invoke a task on the main thread, wait for completion and retrieve return value.
///
- /// Task to run
- /// Any result returned from delegate
- object ScheduleTask(Delegate task);
+ /// Task to run with any type or return value
+ /// Any result returned from task, result type is inferred from the task
+ /// bool result = InvokeOnMainThread(methodThatReturnsAbool);
+ /// bool result = InvokeOnMainThread(() => methodThatReturnsAbool(argument));
+ /// int result = InvokeOnMainThread(() => { yourCode(); return 42; });
+ /// Type of the return value
+ T InvokeOnMainThread(Func task);
+
+ ///
+ /// Invoke a task on the main thread and wait for completion
+ ///
+ /// Task to run without return value
+ /// InvokeOnMainThread(methodThatReturnsNothing);
+ /// InvokeOnMainThread(() => methodThatReturnsNothing(argument));
+ /// InvokeOnMainThread(() => { yourCode(); });
+ void InvokeOnMainThread(Action task);
///
/// Called when a network packet received or sent
diff --git a/MinecraftClient/TaskWithResult.cs b/MinecraftClient/TaskWithResult.cs
index c68a5014..2762fb28 100644
--- a/MinecraftClient/TaskWithResult.cs
+++ b/MinecraftClient/TaskWithResult.cs
@@ -6,42 +6,121 @@ using System.Threading;
namespace MinecraftClient
{
- public class TaskWithResult
+ ///
+ /// Holds an asynchronous task with return value
+ ///
+ /// Type of the return value
+ public class TaskWithResult
{
- private Delegate Task;
- private AutoResetEvent ResultEvent = new AutoResetEvent(false);
+ private AutoResetEvent resultEvent = new AutoResetEvent(false);
+ private Func task;
+ private T result = default(T);
+ private Exception exception = null;
+ private bool taskRun = false;
+ private object taskRunLock = new object();
- public object Result;
-
- public TaskWithResult(Delegate task)
+ ///
+ /// Create a new asynchronous task with return value
+ ///
+ /// Delegate with return value
+ public TaskWithResult(Func task)
{
- Task = task;
+ this.task = task;
}
///
- /// Execute the delegate and set the property to the returned value
+ /// Check whether the task has finished running
///
- /// Value returned from delegate
- public object Execute()
+ public bool HasRun
{
- Result = Task.DynamicInvoke();
- return Result;
+ get
+ {
+ return taskRun;
+ }
}
///
- /// Block the program execution
+ /// Get the task result (return value of the inner delegate)
///
- public void Block()
+ /// Thrown if the task is not finished yet
+ public T Result
{
- ResultEvent.WaitOne();
+ get
+ {
+ if (taskRun)
+ {
+ return result;
+ }
+ else throw new InvalidOperationException("Attempting to retrieve the result of an unfinished task");
+ }
}
///
- /// Resume the program execution
+ /// Get the exception thrown by the inner delegate, if any
///
- public void Release()
+ public Exception Exception
{
- ResultEvent.Set();
+ get
+ {
+ return exception;
+ }
+ }
+
+ ///
+ /// Execute the task in the current thread and set the property or to the returned value
+ ///
+ public void ExecuteSynchronously()
+ {
+ // Make sur the task will not run twice
+ lock (taskRunLock)
+ {
+ if (taskRun)
+ {
+ throw new InvalidOperationException("Attempting to run a task twice");
+ }
+ }
+
+ // Run the task
+ try
+ {
+ result = task();
+ }
+ catch (Exception e)
+ {
+ exception = e;
+ }
+
+ // Mark task as complete and release wait event
+ lock (taskRunLock)
+ {
+ taskRun = true;
+ }
+ resultEvent.Set();
+ }
+
+ ///
+ /// Wait until the task has run from another thread and get the returned value or exception thrown by the task
+ ///
+ /// Task result once available
+ /// Any exception thrown by the task
+ public T WaitGetResult()
+ {
+ // Wait only if the result is not available yet
+ bool mustWait = false;
+ lock (taskRunLock)
+ {
+ mustWait = !taskRun;
+ }
+ if (mustWait)
+ {
+ resultEvent.WaitOne();
+ }
+
+ // Receive exception from task
+ if (exception != null)
+ throw exception;
+
+ return result;
}
}
}