diff --git a/MUNGA/ENTITY.cpp b/MUNGA/ENTITY.cpp index f74bbc4..26bf7f5 100644 --- a/MUNGA/ENTITY.cpp +++ b/MUNGA/ENTITY.cpp @@ -1483,6 +1483,45 @@ void // if (!IsCondemned()) { + // + //--------------------------------------------------------------- + // Stop being anybody's vehicle before going on the row. + // + // Player::playerVehicle is a bare pointer that nothing ever + // cleared. The task that empties this row deletes one entity per + // frame, and a replicant Player runs GoToVehicle EVERY frame - + // it reads playerVehicle->localOrigin to follow its pod around. + // So from the moment the vehicle was fried, every one of those + // frames read freed memory. It normally survives on the garbage + // still lying in the block, which is its own kind of bug: a + // remote pod following coordinates that are no longer anything. + // + // Caught by the podium rig under full page heap, where the block + // is decommitted instead of merely stale: an access violation in + // Player::GoToVehicle reading a VTV whose free stack was + // FryDeathRowTask::Execute -> VTV::`scalar deleting destructor'. + // + // Cleared here rather than in ~Entity: the entity is still whole + // at this point, the groups are all still standing, and it + // happens exactly once. A destructor doing this would run during + // Shutdown too, when the Players group may already be gone. + //--------------------------------------------------------------- + // + EntityGroup *player_group = entity_manager->FindGroup("Players"); + if (player_group != NULL) + { + ChainIteratorOf iterator(player_group->groupMembers); + Player *player; + + while ((player = (Player*) iterator.ReadAndNext()) != NULL) + { + if (player->GetPlayerVehicle() == this) + { + player->ClearPlayerVehicle(); + } + } + } + entity_manager->deathRow->Add(this); SetCondemnedFlag(); NeverExecute(); diff --git a/MUNGA/PLAYER.h b/MUNGA/PLAYER.h index 1794ce7..7f678a8 100644 --- a/MUNGA/PLAYER.h +++ b/MUNGA/PLAYER.h @@ -331,6 +331,16 @@ public: playerVehicle = player_vehicle; } + // + // The vehicle is going away. SetPlayerVehicle cannot say this - it + // Checks its argument, so it will not take NULL - and every reader + // of playerVehicle tests it for NULL first, so this is all they need + // to stop following a pod that no longer exists. + // + void + ClearPlayerVehicle() + {Check(this); playerVehicle = NULL;} + Mission* GetMission() {