diff --git a/Server/Components/GangZones/gangzone.cpp b/Server/Components/GangZones/gangzone.cpp index a73d23481..f9c632d25 100644 --- a/Server/Components/GangZones/gangzone.cpp +++ b/Server/Components/GangZones/gangzone.cpp @@ -155,13 +155,28 @@ class GangZonesComponent final : public IGangZonesComponent, public PlayerConnec // Only go through those that are added to our checking list using IGangZonesComponent::useGangZoneCheck if (checkingList.entries().size()) { + // Snapshot IDs: destroying a gangzone during dispatch below must not mutate + // checkingList while we're iterating over it. + DynamicArray ids; + ids.reserve(checkingList.entries().size()); + for (auto gangzone : checkingList.entries()) + { + ids.push_back(gangzone->getID()); + } + const Vector3& playerPos = player.getPosition(); - DynamicArray enteredList; + DynamicArray enteredList; // Guarantee a single allocation - enteredList.reserve(checkingList.entries().size()); + enteredList.reserve(ids.size()); - for (auto gangzone : checkingList.entries()) + for (int id : ids) { + IGangZone* gangzone = get(id); + if (!gangzone || !checkingList.valid(id)) + { + continue; + } + // Only check visible gangzones if (!gangzone->isShownForPlayer(player)) { @@ -175,7 +190,7 @@ class GangZonesComponent final : public IGangZonesComponent, public PlayerConnec if (isPlayerInZoneArea && !isPlayerInInsideList) { // Collect entered gangzones to call events with them later after exit events - enteredList.push_back(gangzone); + enteredList.push_back(id); } else if (!isPlayerInZoneArea && isPlayerInInsideList) { @@ -190,8 +205,14 @@ class GangZonesComponent final : public IGangZonesComponent, public PlayerConnec } // Call enter gangzone events for all the gangzones in entered gangzone list - for (auto gangzone : enteredList) + for (int id : enteredList) { + IGangZone* gangzone = get(id); + if (!gangzone || !checkingList.valid(id) || !gangzone->isShownForPlayer(player)) + { + continue; + } + ScopedPoolReleaseLock lock(*this, *gangzone); static_cast(gangzone)->setPlayerInside(player, true); eventDispatcher.dispatch( @@ -319,8 +340,21 @@ class GangZonesComponent final : public IGangZonesComponent, public PlayerConnec void onPlayerClickMap(IPlayer& player, Vector3 clickPos) override { // Only go through those that are added to our checking list using IGangZonesComponent::toggleGangZoneCheck + DynamicArray ids; + ids.reserve(checkingList.entries().size()); for (auto gangzone : checkingList.entries()) { + ids.push_back(gangzone->getID()); + } + + for (int id : ids) + { + IGangZone* gangzone = get(id); + if (!gangzone || !checkingList.valid(id)) + { + continue; + } + // only check visible gangzones if (!gangzone->isShownForPlayer(player)) {