Fix: Bot stack overflow after teleporting to an unwalkable map#844
Open
therpr wants to merge 1 commit into
Open
Fix: Bot stack overflow after teleporting to an unwalkable map#844therpr wants to merge 1 commit into
therpr wants to merge 1 commit into
Conversation
therpr
force-pushed
the
master
branch
3 times, most recently
from
July 24, 2026 07:17
4b052c3 to
d8505e6
Compare
therpr
marked this pull request as draft
July 24, 2026 07:25
therpr
marked this pull request as ready for review
July 24, 2026 08:12
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A bot can crash the whole game server with a stack overflow when it warps to a map whose spawn gate places it on a
blocked (non-walkable) tile.
Root cause
Bots are connection-less (BotPlayer : OfflinePlayer). When a warp lands a player on a blocked tile,
ClientReadyAfterMapChangeAsync recovers by calling WarpToSafezoneAsync:
ClientReadyAfterMapChangeAsync
→ WarpToSafezoneAsync
→ WarpToAsync
→ IMapChangePlugIn.MapChangeAsync
→ (offline bot) OfflineMapChangePlugIn.MapChangeAsync
→ ClientReadyAfterMapChangeAsync // synchronous, inline — no network round-trip
For a real player the map-change plugin sends a packet and returns; the next ClientReadyAfterMapChangeAsync arrives
later on a fresh stack from the client's F3 12 ack — no recursion.
For a bot, OfflineMapChangePlugIn.MapChangeAsync calls ClientReadyAfterMapChangeAsync inline. If the destination's
safezone spawn gate is itself blocked, the recovery re-enters on the same growing stack and recurses until it
overflows — crashing the process.
A reactive "remember broken maps, skip them next tick" fix cannot work here: the overflow kills the process on the
first bad warp, before the bot's next navigator tick ever runs.
Fix (proactive, bot-side only)
A bot is never offered a map it cannot stand in, so the recovery recursion can never start.
BotNavigator.HasWalkableSpawnGate(map) parses the destination map's terrain and checks that its safezone spawn gate
(the gate WarpToSafezoneAsync recovers to) contains at least one walkable tile. The two candidate pickers —
TryPickEasierMap and TryPickBetterMapCore — now drop any map that fails this check, alongside the existing legal-warp
and affordability filters:
if (!candidate.TryGetRequirementError(this._player, out _)
&& this.TryGetLegalWarp(candidate, out var candidateWarp)
&& this.CanAffordWarp(candidateWarp)
&& this.HasWalkableSpawnGate(candidate)) // new
Terrain is static configuration, so the verdict is parsed once per map and cached in a static
ConcurrentDictionary<int, bool> shared across all bots — no per-tick terrain parsing, no per-bot duplication.