From 63018abc564b0b9710d640a9735ee2992f89ba5d Mon Sep 17 00:00:00 2001 From: Mobpsycho Date: Thu, 23 Jul 2026 00:08:18 -0400 Subject: [PATCH 1/2] Eject players stranded in a battlefield after disconnecting --- src/map/battlefield.cpp | 10 ++++++++++ src/map/zone.cpp | 30 ++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/map/battlefield.cpp b/src/map/battlefield.cpp index 5f8570c37ae..50b7668cdb1 100644 --- a/src/map/battlefield.cpp +++ b/src/map/battlefield.cpp @@ -1031,6 +1031,16 @@ void CBattlefield::setPlayerEntered(CCharEntity* PChar, bool entered) } effect->SetTier(entered ? 1 : 0); + + // Persist the battlefield id when the player enters the arena. The Battlefield status + // effect is removed during the win/cleanup sequence, so if a player disconnects during + // the post-win loot window the effect no longer exists on relog and the normal eject + // logic in CZone::CharZoneIn cannot detect them. This charvar survives the relog and is + // used there to send them to the exit. It is cleared on any normal (non-disconnect) leave. + if (entered) + { + PChar->setCharVar("battlefieldRecoveryId", effect->GetPower()); + } } bool CBattlefield::hasPlayerEntered(CCharEntity* PChar) diff --git a/src/map/zone.cpp b/src/map/zone.cpp index 21827700952..debf5f1c749 100644 --- a/src/map/zone.cpp +++ b/src/map/zone.cpp @@ -1091,6 +1091,25 @@ void CZone::CharZoneIn(CCharEntity* PChar) if (m_BattlefieldHandler) { + // A player who disconnects inside a battlefield arena (for example during the post-win + // loot window) loses their Battlefield status effect during the cleanup sequence, so on + // relog the orphan handling below cannot detect them and they are left stuck in the arena. + // The recovery charvar set on entry survives the relog; restore a minimal Battlefield + // effect from it so the existing orphan-eject path can send them to the exit, then consume + // the marker. + if (!PChar->StatusEffectContainer->HasStatusEffectByFlag(xi::StatusEffectFlag::Confrontation) && + PChar->getCharVar("battlefieldRecoveryId") != 0) + { + const auto battlefieldId = static_cast(PChar->getCharVar("battlefieldRecoveryId")); + PChar->StatusEffectContainer->AddStatusEffectSilent( + xi::StatusEffect::Battlefield, static_cast(xi::StatusEffect::Battlefield), battlefieldId, 0s, 0s, 0, 0); + if (auto* effect = PChar->StatusEffectContainer->GetStatusEffect(xi::StatusEffect::Battlefield)) + { + effect->SetTier(1); + } + PChar->setCharVar("battlefieldRecoveryId", 0); + } + auto* PBattlefield = m_BattlefieldHandler->GetBattlefield(PChar, true); if (PBattlefield != nullptr && PChar->StatusEffectContainer->HasStatusEffectByFlag(xi::StatusEffectFlag::Confrontation)) { @@ -1174,6 +1193,17 @@ void CZone::CharZoneOut(CCharEntity* PChar) { TracyZoneScoped; + // Clear the battlefield relog-recovery marker when the player genuinely zones out + // (shuttingDown == 2 is a normal zone transition). On a disconnect/logout (shuttingDown + // == 1) we keep it, so a player stranded inside a battlefield arena can be ejected on + // their next zone-in (see CZone::CharZoneIn). This is the only signal that reliably + // separates a proper exit from a disconnect - the battlefield leavecode does not, since + // the WIN cleanup runs for both. + if (PChar->PSession != nullptr && PChar->PSession->shuttingDown == 2 && PChar->getCharVar("battlefieldRecoveryId") != 0) + { + PChar->setCharVar("battlefieldRecoveryId", 0); + } + for (const auto& triggerArea : m_triggerAreaList) { if (PChar->isInTriggerArea(triggerArea->getTriggerAreaID())) From 9fb41713021f73bd612d1a7bf37196537e1bc967 Mon Sep 17 00:00:00 2001 From: Mobpsycho Date: Thu, 23 Jul 2026 18:59:01 -0400 Subject: [PATCH 2/2] Clear battlefield recovery marker on a normal arena exit Clear battlefieldRecoveryId when a player leaves the arena under their own power. Every legitimate exit (running out, and leaving after a win or loss) routes through leavecode EXIT, so a player who leaves manually and then logs out at the burning circle is no longer wrongly ejected on relog. The disconnect teardown path uses WARPDC and keeps the marker, so a player who drops during the post-win loot window is still recovered. --- src/map/battlefield.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/map/battlefield.cpp b/src/map/battlefield.cpp index 50b7668cdb1..e66d41048c7 100644 --- a/src/map/battlefield.cpp +++ b/src/map/battlefield.cpp @@ -595,6 +595,17 @@ bool CBattlefield::RemoveEntity(CBaseEntity* PEntity, uint8 leavecode) m_EnteredPlayers.erase(PEntity->id); + // A player who leaves under their own power is back outside at the burning circle, so the + // battlefield recovery marker is no longer needed. Every legitimate exit (running out, + // and exiting after a win or loss) routes through EXIT, so clearing it here prevents a + // later relog from wrongly ejecting a player who already left. The disconnect teardown + // path uses WARPDC and deliberately keeps the marker, so a player who drops during the + // post-win loot window can still be recovered on relog. + if (leavecode == BATTLEFIELD_LEAVE_CODE_EXIT) + { + PChar->setCharVar("battlefieldRecoveryId", 0); + } + if (leavecode != 255) { // todo: probably shouldnt hardcode this