Skip to content

Reduced network polling and unblocked bot stop#14

Merged
kis1yi merged 6 commits into
Silkroad-Developer-Community:mainfrom
kis1yi:cpu
Jun 3, 2026
Merged

Reduced network polling and unblocked bot stop#14
kis1yi merged 6 commits into
Silkroad-Developer-Community:mainfrom
kis1yi:cpu

Conversation

@kis1yi

@kis1yi kis1yi commented Jun 3, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • Performance Improvements

    • Reduced busy-waiting in the bot worker loop for lower CPU use.
    • Switched to a signal-driven packet dispatcher for faster, more responsive networking.
    • Action cancellation now supports a configurable timeout to reduce unnecessary waits.
  • Bug Fixes

    • More reliable and concurrency-safe shutdown sequence with improved cleanup ordering.
    • Shutdown now stops network workers more deterministically to avoid teardown races.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@kis1yi, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 13 minutes and 1 second. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 47fdd9a2-d45b-48cc-aa4a-a3f1649af76c

📥 Commits

Reviewing files that changed from the base of the PR and between 09ee881 and a80600a.

📒 Files selected for processing (1)
  • Library/RSBot.Core/Bot.cs
📝 Walkthrough

Walkthrough

Refactors packet processing to a signal-driven dispatcher in NetBase, updates Client/Server to signal the dispatcher, adds a CancelAction(int timeout) with immediate-send behavior for timeout<=0, and rewrites Bot start/stop to an async worker with coordinated shutdown and repeated immediate cancel attempts.

Changes

Signal-Driven Packet Processing Architecture

Layer / File(s) Summary
SkillManager cancellation and immediate usage
Library/RSBot.Core/Components/SkillManager.cs, Botbases/RSBot.Training/TrainingBase.cs
CancelAction(int timeout = 500) added with immediate-send when timeout <= 0; CreateCancelActionPacket() helper introduced. TrainingBase.Stop() switched to CancelAction(0) for immediate cancel inside lock.
Bot async worker and shutdown flow
Library/RSBot.Core/Bot.cs
Start replaced by async RunAsync loop with Task.Delay(100, token); Stop refactored with lock-protected checks, sets Running=false before token cancel, schedules CancelActionOnStop() attempts, clears selection, stops managers, and calls Botbase.Stop().
NetBase signal-driven dispatcher core
Library/RSBot.Core/Network/Socket/NetBase.cs
Adds _packetSignal AutoResetEvent, backing fields for IsClosing and EnablePacketDispatcher that signal on set, updates Start/Stop worker lifecycle to avoid duplicates and join thread on stop, rewrites dispatcher loop to wait on signal when disabled and call ProcessQueuedPackets when enabled, and makes ProcessQueuedPackets return processed status.
Client/Server dispatcher integration and lifecycle tweaks
Library/RSBot.Core/Network/Socket/Client.cs, Library/RSBot.Core/Network/Socket/Server.cs
Client/Server call SignalPacketDispatcher() after protocol Recv/Send; Shutdown/Disconnect call StopNetWorker() earlier; Client connect order adjusted to create/secure protocol before enabling dispatcher; IsClosing clearing moved into finally in Server.Disconnect.

Sequence Diagram(s)

sequenceDiagram
  participant Client/Server
  participant Protocol
  participant NetBase
  participant DispatcherThread
  participant Socket
  Client/Server->>Protocol: Recv()/Send(packet)
  Protocol->>NetBase: Enqueue packet / complete send
  NetBase->>DispatcherThread: SignalPacketDispatcher()
  DispatcherThread->>NetBase: ProcessQueuedPackets()
  NetBase->>Socket: _socket.Send(outgoing)  // only if connected & dispatch enabled
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I tapped the signal, packets wake from sleep,

Threads hush and wait, no busy loops to keep.
A cancel sent quick when shutdown comes near,
Bots bow and stop, their actions disappear.
Hops of code—soft, tidy—now all clear.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Reduced network polling and unblocked bot stop' directly and concisely summarizes the main changes in the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Library/RSBot.Core/Bot.cs (1)

56-79: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fix Bot.Start/Stop lifecycle race and async Task.Factory.StartNew usage in Library/RSBot.Core/Bot.cs

  • Stop() can return early because Running is set only inside the scheduled delegate; a Start→Stop right after Start() can leave the newly created TokenSource uncanceled and the worker running.
  • The loop checks the mutable TokenSource field, and both Task.Delay(100) calls are not cancellation-aware, so a stop/start can swap TokenSource while an old loop is mid-delay, allowing multiple loops to tick.
  • Task.Factory.StartNew(async ...) with an async lambda is a known pitfall: it produces a nested task shape that should be Unwrap’d, LongRunning only applies to the synchronous portion before the first await, and the returned task being ignored can hide exceptions/teardown.
Suggested fix
-        TokenSource = new CancellationTokenSource();
-
-        Task.Factory.StartNew(
-            async e =&gt;
+        var tokenSource = new CancellationTokenSource();
+        TokenSource = tokenSource;
+        Running = true;
+
+        _ = Task.Run(
+            async () =&gt;
             {
-                Running = true;
+                var token = tokenSource.Token;
 
                 EventManager.FireEvent("OnStartBot");
                 Botbase.Start();
 
-                while (!TokenSource.IsCancellationRequested)
+                while (!token.IsCancellationRequested)
                 {
                     if (!Game.Ready)
                     {
-                        await Task.Delay(100);
+                        await Task.Delay(100, token);
                         continue;
                     }
 
                     Botbase.Tick();
-                    await Task.Delay(100);
+                    await Task.Delay(100, token);
                 }
-            },
-            TokenSource.Token,
-            TaskCreationOptions.LongRunning
+            },
+            tokenSource.Token
         );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Library/RSBot.Core/Bot.cs` around lines 56 - 79, Start/Stop races and misuse
of Task.Factory.StartNew are causing orphaned background loops and lost
exceptions: set Running true before scheduling, create and store a
CancellationTokenSource and the resulting worker Task (e.g., TokenSource and
WorkerTask) atomically, capture the token in a local variable inside the worker
(var token = TokenSource.Token) and use cancellation-aware waits (await
Task.Delay(100, token)), avoid async void/nested Task by using Task.Run(async
token => { ... }, token) or Task.Factory.StartNew(() => WorkerLoopAsync(token),
token).Ensure Stop() cancels the TokenSource, awaits WorkerTask to completion,
and clears Running only after the worker finishes; also reference Botbase.Tick,
EventManager.FireEvent("OnStartBot") and check token.IsCancellationRequested in
the loop to exit promptly so a Stop→Start cannot leave old loops running.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Library/RSBot.Core/Components/SkillManager.cs`:
- Around line 639-643: The CancelAction fast-path sends the 0xB074 cancel packet
and returns without registering an AwaitCallback, but 0xB074 responses are
currently matched purely by ResponseOpcode and can satisfy unrelated waiters
(e.g., CastSkillOld/CastSkillAt/CastBuff/SpawnedItem.Pickup); modify
CancelAction (and the AwaitCallback registration for 0xB074) so responses are
request-correlated instead of opcode-only — e.g., include the action/skill id or
a unique request token from the outgoing packet in the AwaitCallback predicate
(or attach it to the AwaitCallback as a filter) so only callbacks whose request
id matches the incoming 0xB074 packet are completed, or alternatively serialize
0xB074 waiters by ensuring only one outstanding 0xB074 waiter at a time in
SkillManager to prevent cross-completion.

In `@Library/RSBot.Core/Network/Socket/NetBase.cs`:
- Around line 149-159: The dispatcher thread is permanently lost after a
transient disconnect because Server.Disconnect() flips IsClosing true causing
the loop in NetBase.Dispatcher to exit but never joins/clears _dispatcherThread,
so StartNetWorker() won't start a new one; fix this by ensuring the dispatcher
lifecycle is properly cleaned up and recreated: in the disconnect/shutdown path
(Server.Disconnect()/NetBase) join/abort the running _dispatcherThread and set
_dispatcherThread = null (or otherwise signal and wait for thread exit) so
StartNetWorker() can create a fresh thread, or alternatively adjust
StartNetWorker() to check thread.IsAlive before skipping creation; reference
symbols: _dispatcherThread, IsClosing, Server.Disconnect(), StartNetWorker(),
ProcessQueuedPackets(), _packetSignal.

---

Outside diff comments:
In `@Library/RSBot.Core/Bot.cs`:
- Around line 56-79: Start/Stop races and misuse of Task.Factory.StartNew are
causing orphaned background loops and lost exceptions: set Running true before
scheduling, create and store a CancellationTokenSource and the resulting worker
Task (e.g., TokenSource and WorkerTask) atomically, capture the token in a local
variable inside the worker (var token = TokenSource.Token) and use
cancellation-aware waits (await Task.Delay(100, token)), avoid async void/nested
Task by using Task.Run(async token => { ... }, token) or
Task.Factory.StartNew(() => WorkerLoopAsync(token), token).Ensure Stop() cancels
the TokenSource, awaits WorkerTask to completion, and clears Running only after
the worker finishes; also reference Botbase.Tick,
EventManager.FireEvent("OnStartBot") and check token.IsCancellationRequested in
the loop to exit promptly so a Stop→Start cannot leave old loops running.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 88d3f549-841b-481b-a1b4-3067fbe0bc98

📥 Commits

Reviewing files that changed from the base of the PR and between 745afe0 and da1db2e.

📒 Files selected for processing (6)
  • Botbases/RSBot.Training/TrainingBase.cs
  • Library/RSBot.Core/Bot.cs
  • Library/RSBot.Core/Components/SkillManager.cs
  • Library/RSBot.Core/Network/Socket/Client.cs
  • Library/RSBot.Core/Network/Socket/NetBase.cs
  • Library/RSBot.Core/Network/Socket/Server.cs

Comment thread Library/RSBot.Core/Components/SkillManager.cs
Comment thread Library/RSBot.Core/Network/Socket/NetBase.cs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Library/RSBot.Core/Bot.cs`:
- Around line 135-151: CancelActionOnStop can throw a NullReferenceException
because Game.Player may become null while the background Task checks
Game.Player.InAction; update the async loop in CancelActionOnStop to safely
handle a null player by capturing a local reference or using a null-conditional
check (e.g., check Game?.Player != null or use Game.Player?.InAction) before
accessing InAction, and keep the existing Running and
SkillManager.CancelAction(0) behavior so the loop returns when the bot is
running or there is no player.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cf03f317-f927-4f5f-9d9c-2960d455fdf9

📥 Commits

Reviewing files that changed from the base of the PR and between da1db2e and 09ee881.

📒 Files selected for processing (6)
  • Botbases/RSBot.Training/TrainingBase.cs
  • Library/RSBot.Core/Bot.cs
  • Library/RSBot.Core/Components/SkillManager.cs
  • Library/RSBot.Core/Network/Socket/Client.cs
  • Library/RSBot.Core/Network/Socket/NetBase.cs
  • Library/RSBot.Core/Network/Socket/Server.cs
💤 Files with no reviewable changes (1)
  • Botbases/RSBot.Training/TrainingBase.cs
🚧 Files skipped from review as they are similar to previous changes (2)
  • Library/RSBot.Core/Components/SkillManager.cs
  • Library/RSBot.Core/Network/Socket/NetBase.cs

Comment thread Library/RSBot.Core/Bot.cs
@kis1yi kis1yi merged commit a9deb4c into Silkroad-Developer-Community:main Jun 3, 2026
1 of 2 checks passed
@kis1yi kis1yi deleted the cpu branch June 3, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant