Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/map/battlefield.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1031,6 +1042,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)
Expand Down
30 changes: 30 additions & 0 deletions src/map/zone.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint16>(PChar->getCharVar("battlefieldRecoveryId"));
PChar->StatusEffectContainer->AddStatusEffectSilent(
xi::StatusEffect::Battlefield, static_cast<uint16>(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))
{
Expand Down Expand Up @@ -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()))
Expand Down