diff --git a/Botbases/RSBot.Training/TrainingBase.cs b/Botbases/RSBot.Training/TrainingBase.cs
index 748da902..91b6f20f 100644
--- a/Botbases/RSBot.Training/TrainingBase.cs
+++ b/Botbases/RSBot.Training/TrainingBase.cs
@@ -79,9 +79,6 @@ public void Stop()
{
lock (Container.Lock)
{
- if (Game.Player.InAction)
- SkillManager.CancelAction();
-
Bundles.Stop();
}
}
diff --git a/Library/RSBot.Core/Bot.cs b/Library/RSBot.Core/Bot.cs
index df4703c2..30dd06e3 100644
--- a/Library/RSBot.Core/Bot.cs
+++ b/Library/RSBot.Core/Bot.cs
@@ -1,5 +1,6 @@
using System.Threading;
using System.Threading.Tasks;
+using System;
using RSBot.Core.Components;
using RSBot.Core.Event;
using RSBot.Core.Plugins;
@@ -8,6 +9,9 @@ namespace RSBot.Core;
public class Bot
{
+ private readonly object _lock = new();
+ private Task _workerTask;
+
///
/// Gets or sets a value indicating whether this is running.
///
@@ -50,31 +54,47 @@ public void SetBotbaseView(IBotbaseView botBaseView)
///
public void Start()
{
- if (Running || Botbase == null)
- return;
-
- TokenSource = new CancellationTokenSource();
-
- Task.Factory.StartNew(
- async e =>
- {
- Running = true;
+ CancellationTokenSource tokenSource;
+
+ lock (_lock)
+ {
+ if (Running || Botbase == null || (_workerTask != null && !_workerTask.IsCompleted))
+ return;
+
+ tokenSource = new CancellationTokenSource();
+ TokenSource = tokenSource;
+ Running = true;
+ _workerTask = Task.Run(() => RunAsync(tokenSource), tokenSource.Token);
+ }
+ }
- EventManager.FireEvent("OnStartBot");
- Botbase.Start();
+ private async Task RunAsync(CancellationTokenSource tokenSource)
+ {
+ var token = tokenSource.Token;
- while (!TokenSource.IsCancellationRequested)
- {
- if (!Game.Ready)
- continue;
+ try
+ {
+ EventManager.FireEvent("OnStartBot");
+ Botbase.Start();
+ while (!token.IsCancellationRequested)
+ {
+ if (Game.Ready)
Botbase.Tick();
- await Task.Delay(100);
- }
- },
- TokenSource.Token,
- TaskCreationOptions.LongRunning
- );
+
+ await Task.Delay(100, token);
+ }
+ }
+ catch (OperationCanceledException) when (token.IsCancellationRequested) { }
+ catch (Exception ex)
+ {
+ Log.Fatal(ex);
+ }
+ finally
+ {
+ if (ReferenceEquals(TokenSource, tokenSource))
+ Running = false;
+ }
}
///
@@ -82,27 +102,61 @@ public void Start()
///
public void Stop()
{
- ScriptManager.Stop();
- ShoppingManager.Stop();
- PickupManager.Stop();
+ CancellationTokenSource tokenSource;
- if (Botbase == null)
- return;
+ lock (_lock)
+ {
+ if (Botbase == null || !Running)
+ return;
- if (!Running)
- return;
+ Running = false;
+ tokenSource = TokenSource;
+ }
- if (!TokenSource.IsCancellationRequested)
- TokenSource.Cancel();
+ if (tokenSource != null && !tokenSource.IsCancellationRequested)
+ tokenSource.Cancel();
EventManager.FireEvent("OnStopBot");
Log.Notify($"Stopping bot {Botbase.Name}");
+ CancelActionOnStop();
+
Game.SelectedEntity = null;
+
+ ScriptManager.Stop();
+ ShoppingManager.Stop();
+ PickupManager.Stop();
Botbase.Stop();
- Running = false;
Log.Notify($"Stopped bot {Botbase.Name}");
Log.Status("Bot stopped");
}
+
+ private void CancelActionOnStop()
+ {
+ var player = Game.Player;
+ if (player == null || !player.InAction)
+ return;
+
+ _ = CancelActionOnStopAsync();
+
+ async Task CancelActionOnStopAsync()
+ {
+ const int attempts = 5;
+ const int retryDelay = 1000;
+
+ for (var i = 0; i < attempts; i++)
+ {
+ if (Running || !Game.Ready || !ReferenceEquals(Game.Player, player) || !player.InAction)
+ return;
+
+ SkillManager.CancelAction(0);
+
+ if (i == attempts - 1)
+ return;
+
+ await Task.Delay(retryDelay).ConfigureAwait(false);
+ }
+ }
+ }
}
diff --git a/Library/RSBot.Core/Components/SkillManager.cs b/Library/RSBot.Core/Components/SkillManager.cs
index 5f0f335b..145b732b 100644
--- a/Library/RSBot.Core/Components/SkillManager.cs
+++ b/Library/RSBot.Core/Components/SkillManager.cs
@@ -631,24 +631,42 @@ public static void CancelBuff(uint skillId)
/// Cancels the action.
///
///
- public static bool CancelAction()
+ public static bool CancelAction(int timeout = 500)
{
- var packet = new Packet(0x7074);
- packet.WriteByte(0x02); //Cancel
+ if (timeout <= 0)
+ {
+ PacketManager.SendPacket(CreateCancelActionPacket(), PacketDestination.Server);
+ return true;
+ }
var callback = new AwaitCallback(
response =>
{
- return response.ReadByte() == 0x02 && response.ReadByte() == 0x00
- ? AwaitCallbackResult.Success
- : AwaitCallbackResult.ConditionFailed;
+ var state = response.ReadByte();
+ var recurring = response.ReadByte();
+
+ if (state == 0x02 && recurring == 0x00)
+ return AwaitCallbackResult.Success;
+
+ if (state == 0x02)
+ return AwaitCallbackResult.Fail;
+
+ return AwaitCallbackResult.ConditionFailed;
},
0xB074
);
- PacketManager.SendPacket(packet, PacketDestination.Server, callback);
- callback.AwaitResponse();
+ PacketManager.SendPacket(CreateCancelActionPacket(), PacketDestination.Server, callback);
+ callback.AwaitResponse(timeout);
return callback.IsCompleted;
}
+
+ private static Packet CreateCancelActionPacket()
+ {
+ var packet = new Packet(0x7074);
+ packet.WriteByte(0x02); //Cancel
+
+ return packet;
+ }
}
diff --git a/Library/RSBot.Core/Network/Socket/Client.cs b/Library/RSBot.Core/Network/Socket/Client.cs
index 60125290..5f337e5b 100644
--- a/Library/RSBot.Core/Network/Socket/Client.cs
+++ b/Library/RSBot.Core/Network/Socket/Client.cs
@@ -58,10 +58,7 @@ public void Shutdown()
{
try
{
- EnablePacketDispatcher = false;
- IsClosing = true;
-
- _dispatcherThread?.Join();
+ StopNetWorker();
//Close Socket
if (_socket != null)
@@ -81,7 +78,6 @@ public void Shutdown()
}
_protocol = null;
- _dispatcherThread = null;
}
catch { }
}
@@ -97,12 +93,12 @@ private void OnClientConnect(IAsyncResult ar)
if (IsClosing)
return;
- EnablePacketDispatcher = true;
_socket = _listener.EndAccept(ar);
_protocol = new SecurityProtocol();
_protocol.GenerateSecurity(true, true, true);
+ EnablePacketDispatcher = true;
_socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, OnBeginReceiveCallback, null);
OnConnected();
@@ -136,6 +132,7 @@ private void OnBeginReceiveCallback(IAsyncResult ar)
}
_protocol.Recv(_buffer, 0, receivedSize);
+ SignalPacketDispatcher();
}
catch (SocketException se)
{
diff --git a/Library/RSBot.Core/Network/Socket/NetBase.cs b/Library/RSBot.Core/Network/Socket/NetBase.cs
index f308a4ce..db3ae740 100644
--- a/Library/RSBot.Core/Network/Socket/NetBase.cs
+++ b/Library/RSBot.Core/Network/Socket/NetBase.cs
@@ -31,13 +31,25 @@ public class NetBase(bool isClient = false)
///
private bool _isClient { get; set; } = isClient;
+ private readonly AutoResetEvent _packetSignal = new(false);
+ private volatile bool _enablePacketDispatcher;
+ private volatile bool _isClosing;
+
///
/// Gets or sets a value indicating whether this instance is closing.
///
///
/// true if this instance is closing; otherwise, false.
///
- public bool IsClosing { get; set; }
+ public bool IsClosing
+ {
+ get => _isClosing;
+ set
+ {
+ _isClosing = value;
+ _packetSignal.Set();
+ }
+ }
///
/// Gets or sets a value indicating whether [enable packet processor].
@@ -45,7 +57,15 @@ public class NetBase(bool isClient = false)
///
/// true if [enable packet processor]; otherwise, false.
///
- public bool EnablePacketDispatcher { get; set; }
+ public bool EnablePacketDispatcher
+ {
+ get => _enablePacketDispatcher;
+ set
+ {
+ _enablePacketDispatcher = value;
+ _packetSignal.Set();
+ }
+ }
///
/// Gets or sets the security protocol.
@@ -108,15 +128,30 @@ public virtual void OnPacketSent(Packet packet)
protected void StartNetWorker()
{
- if (_dispatcherThread == null)
+ if (_dispatcherThread != null && _dispatcherThread.IsAlive)
+ return;
+
+ _dispatcherThread = new Thread(ProcessPacketsThreaded)
{
- _dispatcherThread = new Thread(ProcessPacketsThreaded)
- {
- Name = "Network.PacketProcessor",
- IsBackground = true,
- };
- _dispatcherThread.Start();
- }
+ Name = "Network.PacketProcessor",
+ IsBackground = true,
+ };
+ _dispatcherThread.Start();
+ }
+
+ protected void StopNetWorker(int joinTimeout = 1000)
+ {
+ EnablePacketDispatcher = false;
+ IsClosing = true;
+
+ var dispatcherThread = _dispatcherThread;
+ var stopped = dispatcherThread == null || !dispatcherThread.IsAlive || dispatcherThread == Thread.CurrentThread;
+
+ if (dispatcherThread != null && dispatcherThread.IsAlive && dispatcherThread != Thread.CurrentThread)
+ stopped = dispatcherThread.Join(joinTimeout);
+
+ if (stopped)
+ _dispatcherThread = null;
}
///
@@ -126,19 +161,17 @@ private void ProcessPacketsThreaded()
{
try
{
- while (!EnablePacketDispatcher && !IsClosing)
- Thread.Sleep(1);
-
- while (EnablePacketDispatcher && !IsClosing)
+ while (!IsClosing)
{
- ProcessQueuedPackets();
- Thread.Sleep(1);
- }
-
- if (IsClosing)
- return;
+ if (!EnablePacketDispatcher)
+ {
+ _packetSignal.WaitOne(100);
+ continue;
+ }
- ProcessPacketsThreaded();
+ if (!ProcessQueuedPackets())
+ _packetSignal.WaitOne(15);
+ }
}
catch { }
}
@@ -146,18 +179,22 @@ private void ProcessPacketsThreaded()
///
/// Processes the packets.
///
- private void ProcessQueuedPackets()
+ private bool ProcessQueuedPackets()
{
try
{
if (IsClosing || !EnablePacketDispatcher)
- return;
+ return false;
+
+ var packets = _protocol?.TransferIncoming();
+ var processed = false;
- var packets = _protocol.TransferIncoming();
if (packets != null)
{
foreach (var packet in packets)
{
+ processed = true;
+
if (packet.Opcode == 0x5000 || packet.Opcode == 0x9000)
continue;
@@ -174,17 +211,23 @@ private void ProcessQueuedPackets()
var buffers = _protocol?.TransferOutgoing();
if (buffers == null)
- return;
+ return processed;
foreach (var buffer in buffers)
{
if (_socket == null || IsClosing || !EnablePacketDispatcher || !_socket.Connected)
- return;
+ return processed;
_socket.Send(buffer);
+ processed = true;
}
+
+ return processed;
+ }
+ catch
+ {
+ return false;
}
- catch { }
}
///
@@ -196,5 +239,11 @@ public void Send(Packet packet)
OnPacketSent(packet);
_protocol?.Send(packet);
+ _packetSignal.Set();
+ }
+
+ protected void SignalPacketDispatcher()
+ {
+ _packetSignal.Set();
}
}
diff --git a/Library/RSBot.Core/Network/Socket/Server.cs b/Library/RSBot.Core/Network/Socket/Server.cs
index c39a91e6..765b5cef 100644
--- a/Library/RSBot.Core/Network/Socket/Server.cs
+++ b/Library/RSBot.Core/Network/Socket/Server.cs
@@ -133,6 +133,7 @@ private void OnBeginReceiveCallback(IAsyncResult ar)
_receivedPostInitialHandshakePacket = true;
_protocol.Recv(_buffer, 0, receivedSize);
+ SignalPacketDispatcher();
}
catch (SocketException se)
{
@@ -166,6 +167,7 @@ public void Send(Packet packet)
{
OnPacketSent(packet);
_protocol.Send(packet);
+ SignalPacketDispatcher();
}
///
@@ -173,15 +175,11 @@ public void Send(Packet packet)
///
public void Disconnect()
{
- EnablePacketDispatcher = false;
- IsClosing = true;
+ StopNetWorker();
try
{
- if (_socket == null)
- return;
-
- if (_socket.Connected)
+ if (_socket != null && _socket.Connected)
{
_socket.Shutdown(SocketShutdown.Both);
_socket.Close();
@@ -192,8 +190,7 @@ public void Disconnect()
{
_socket = null;
OnDisconnected();
+ IsClosing = false;
}
-
- IsClosing = false;
}
}