diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index b3267da54..9480e2bba 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -587,6 +587,16 @@ namespace MWBase End of tes3mp addition */ + /* + Start of tes3mp addition + + Make it possible to unload all active cells from elsewhere + */ + virtual void unloadActiveCells() = 0; + /* + End of tes3mp addition + */ + /* Start of tes3mp addition diff --git a/apps/openmw/mwmp/CellController.cpp b/apps/openmw/mwmp/CellController.cpp index c695ab1da..9bd34e9d1 100644 --- a/apps/openmw/mwmp/CellController.cpp +++ b/apps/openmw/mwmp/CellController.cpp @@ -107,7 +107,7 @@ void CellController::uninitializeCell(const ESM::Cell& cell) { std::string mapIndex = cell.getDescription(); - // If this key doesn't exist, create it + // If this key exists, erase the key-value pair from the map if (cellsInitialized.count(mapIndex) > 0) { mwmp::Cell* mpCell = cellsInitialized.at(mapIndex); @@ -118,6 +118,22 @@ void CellController::uninitializeCell(const ESM::Cell& cell) } } +void CellController::uninitializeCells() +{ + if (cellsInitialized.size() > 0) + { + for (auto it = cellsInitialized.cbegin(); it != cellsInitialized.cend(); it++) + { + mwmp::Cell* mpCell = it->second; + mpCell->uninitializeLocalActors(); + mpCell->uninitializeDedicatedActors(); + delete it->second; + } + + cellsInitialized.clear(); + } +} + void CellController::readPositions(ActorList& actorList) { std::string mapIndex = actorList.cell.getDescription(); diff --git a/apps/openmw/mwmp/CellController.hpp b/apps/openmw/mwmp/CellController.hpp index 79bc15651..68248a76e 100644 --- a/apps/openmw/mwmp/CellController.hpp +++ b/apps/openmw/mwmp/CellController.hpp @@ -21,6 +21,7 @@ namespace mwmp void initializeCell(const ESM::Cell& cell); void uninitializeCell(const ESM::Cell& cell); + void uninitializeCells(); void readPositions(mwmp::ActorList& actorList); void readAnimFlags(mwmp::ActorList& actorList); diff --git a/apps/openmw/mwmp/PlayerList.cpp b/apps/openmw/mwmp/PlayerList.cpp index 2646abea9..8ea5ac9e2 100644 --- a/apps/openmw/mwmp/PlayerList.cpp +++ b/apps/openmw/mwmp/PlayerList.cpp @@ -81,6 +81,24 @@ DedicatedPlayer *PlayerList::getPlayer(const MWWorld::Ptr &ptr) return nullptr; } +std::vector PlayerList::getPlayersInCell(const ESM::Cell& cell) +{ + std::vector playersInCell; + + for (auto& playerEntry : playerList) + { + if (playerEntry.first != RakNet::UNASSIGNED_CRABNET_GUID) + { + if (Main::get().getCellController()->isSameCell(cell, playerEntry.second->cell)) + { + playersInCell.push_back(playerEntry.first); + } + } + } + + return playersInCell; +} + bool PlayerList::isDedicatedPlayer(const MWWorld::Ptr &ptr) { if (ptr.mRef == nullptr) diff --git a/apps/openmw/mwmp/PlayerList.hpp b/apps/openmw/mwmp/PlayerList.hpp index face8781b..9967b2a7e 100644 --- a/apps/openmw/mwmp/PlayerList.hpp +++ b/apps/openmw/mwmp/PlayerList.hpp @@ -34,6 +34,7 @@ namespace mwmp static DedicatedPlayer *getPlayer(RakNet::RakNetGUID guid); static DedicatedPlayer *getPlayer(const MWWorld::Ptr &ptr); + static std::vector getPlayersInCell(const ESM::Cell& cell); static bool isDedicatedPlayer(const MWWorld::Ptr &ptr); diff --git a/apps/openmw/mwmp/Worldstate.cpp b/apps/openmw/mwmp/Worldstate.cpp index 33c74f310..f8199fe9e 100644 --- a/apps/openmw/mwmp/Worldstate.cpp +++ b/apps/openmw/mwmp/Worldstate.cpp @@ -12,7 +12,10 @@ #include "Worldstate.hpp" #include "Main.hpp" #include "Networking.hpp" +#include "PlayerList.hpp" +#include "DedicatedPlayer.hpp" #include "RecordHelper.hpp" +#include "CellController.hpp" using namespace mwmp; using namespace std; @@ -442,6 +445,64 @@ void Worldstate::setWeather() weather.queuedWeather, weather.transitionFactor, forceWeather); } +void Worldstate::resetCells(std::vector* cells) +{ + MWBase::World* world = MWBase::Environment::get().getWorld(); + + bool haveUnloadedActiveCells = false; + ESM::Cell playerCell = *world->getPlayerPtr().getCell()->getCell(); + ESM::Position playerPos = world->getPlayerPtr().getRefData().getPosition(); + std::vector playersInCell; + + for (auto cell : *cells) + { + if (!haveUnloadedActiveCells) + { + if (world->isCellActive(cell)) + { + playersInCell = mwmp::PlayerList::getPlayersInCell(cell); + + // If there are any DedicatedPlayers in this cell, also move them to the temporary holding interior cell + if (!playersInCell.empty()) + { + for (RakNet::RakNetGUID otherGuid : playersInCell) + { + DedicatedPlayer* dedicatedPlayer = mwmp::PlayerList::getPlayer(otherGuid); + dedicatedPlayer->cell = *world->getInterior(RecordHelper::getPlaceholderInteriorCellName())->getCell(); + dedicatedPlayer->setCell(); + } + } + + // Change to temporary holding interior cell + world->changeToInteriorCell(RecordHelper::getPlaceholderInteriorCellName(), playerPos, true, true); + + mwmp::Main::get().getCellController()->uninitializeCells(); + world->unloadActiveCells(); + + haveUnloadedActiveCells = true; + } + } + + world->clearCellStore(cell); + + for (RakNet::RakNetGUID otherGuid : playersInCell) + { + DedicatedPlayer* dedicatedPlayer = mwmp::PlayerList::getPlayer(otherGuid); + dedicatedPlayer->cell = cell; + dedicatedPlayer->setCell(); + } + } + + // Move the player from their temporary holding cell to their previous cell + if (haveUnloadedActiveCells) + { + if (playerCell.isExterior()) + world->changeToExteriorCell(playerPos, true, true); + else + world->changeToInteriorCell(playerCell.mName, playerPos, true, true); + } +} + void Worldstate::sendClientGlobal(std::string varName, int value, mwmp::VARIABLE_TYPE variableType) { clientGlobals.clear(); diff --git a/apps/openmw/mwmp/Worldstate.hpp b/apps/openmw/mwmp/Worldstate.hpp index c5cfaf1ee..1f301d283 100644 --- a/apps/openmw/mwmp/Worldstate.hpp +++ b/apps/openmw/mwmp/Worldstate.hpp @@ -23,6 +23,8 @@ namespace mwmp void setMapExplored(); void setWeather(); + void resetCells(std::vector* cells); + void sendClientGlobal(std::string varName, int value, mwmp::VARIABLE_TYPE variableType); void sendClientGlobal(std::string varName, float value); void sendMapExplored(int cellX, int cellY, const std::vector& imageData); diff --git a/apps/openmw/mwmp/processors/worldstate/ProcessorCellReset.hpp b/apps/openmw/mwmp/processors/worldstate/ProcessorCellReset.hpp index 76cf12c92..b326ddc22 100644 --- a/apps/openmw/mwmp/processors/worldstate/ProcessorCellReset.hpp +++ b/apps/openmw/mwmp/processors/worldstate/ProcessorCellReset.hpp @@ -21,7 +21,7 @@ namespace mwmp CellController* cellController = Main::get().getCellController(); MWBase::World * world = MWBase::Environment::get().getWorld(); - //world->reloadCells(&worldstate.cellsToReset); + mwmp::Main::get().getNetworking()->getWorldstate()->resetCells(&worldstate.cellsToReset); } }; } diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 7cb87541f..0650c6267 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -340,6 +340,37 @@ namespace MWWorld return MWWorld::Ptr(object.getBase(), cellToMoveTo); } + /* + Start of tes3mp addition + + Make it possible to clear the moves to other cells tracked for objects, allowing for + on-the-fly cell resets that don't cause crashes + */ + void CellStore::clearMovesToCells() + { + MWBase::World* world = MWBase::Environment::get().getWorld(); + + for (auto &reference : mMovedHere) + { + MWWorld::CellStore *otherCell = reference.second; + + otherCell->mMovedToAnotherCell.erase(reference.first); + } + + for (auto &reference : mMovedToAnotherCell) + { + MWWorld::CellStore *otherCell = reference.second; + + otherCell->mMovedHere.erase(reference.first); + } + + mMovedHere.empty(); + mMovedToAnotherCell.empty(); + } + /* + End of tes3mp addition + */ + struct MergeVisitor { MergeVisitor(std::vector& mergeTo, const std::map& movedHere, diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index c132d3b92..de3c291d0 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -194,6 +194,17 @@ namespace MWWorld /// @return updated MWWorld::Ptr with the new CellStore pointer set. MWWorld::Ptr moveTo(const MWWorld::Ptr& object, MWWorld::CellStore* cellToMoveTo); + /* + Start of tes3mp addition + + Make it possible to clear the moves to other cells tracked for objects, allowing for + on-the-fly cell resets that don't cause crashes + */ + void clearMovesToCells(); + /* + End of tes3mp addition + */ + void rest(double hours); void recharge(float duration); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 3764f2260..cfdf0a9c2 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -22,6 +22,7 @@ #include "../mwmp/LocalActor.hpp" #include "../mwmp/DedicatedActor.hpp" #include "../mwmp/ObjectList.hpp" +#include "../mwmp/RecordHelper.hpp" #include "../mwmp/CellController.hpp" #include "../mwmp/MechanicsHelper.hpp" /* @@ -2918,6 +2919,28 @@ namespace MWWorld End of tes3mp addition */ + /* + Start of tes3mp addition + + Make it possible to unload all active cells from elsewhere + */ + void World::unloadActiveCells() + { + const Scene::CellStoreCollection& activeCells = mWorldScene->getActiveCells(); + + for (auto it = activeCells.begin(); it != activeCells.end(); ++it) + { + // Ignore a placeholder interior that a player may currently be in + if ((*it)->getCell()->isExterior() || !Misc::StringUtils::ciEqual((*it)->getCell()->getDescription(), RecordHelper::getPlaceholderInteriorCellName())) + { + mWorldScene->unloadCell(it); + } + } + } + /* + End of tes3mp addition + */ + /* Start of tes3mp addition @@ -2925,6 +2948,11 @@ namespace MWWorld */ void World::clearCellStore(const ESM::Cell& cell) { + mwmp::CellController* cellController = mwmp::Main::get().getCellController(); + MWWorld::CellStore *cellStore = cellController->getCellStore(cell); + + if (cellStore != nullptr) + cellStore->clearMovesToCells(); mCells.clear(cell); } /* diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 8a00ea165..679f4ee4f 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -691,6 +691,16 @@ namespace MWWorld End of tes3mp addition */ + /* + Start of tes3mp addition + + Make it possible to unload all active cells from elsewhere + */ + void unloadActiveCells() override; + /* + End of tes3mp addition + */ + /* Start of tes3mp addition