From 6cb4d5ec35c3a35db3e72a6de939c5869c0f105f Mon Sep 17 00:00:00 2001 From: uramer Date: Sun, 24 Mar 2019 13:19:46 +0100 Subject: [PATCH 1/2] [General] Implement CellReset packet, stage 1 --- .../openmw-mp/Script/Functions/Worldstate.cpp | 27 +++++++++++ .../openmw-mp/Script/Functions/Worldstate.hpp | 26 +++++++++++ .../worldstate/ProcessorCellReset.hpp | 15 +++++- apps/openmw/mwworld/cellstore.cpp | 46 +++++++++++++++++++ apps/openmw/mwworld/cellstore.hpp | 10 ++++ components/openmw-mp/Base/BaseWorldstate.hpp | 2 + .../Packets/Worldstate/PacketCellReset.cpp | 19 +++++++- 7 files changed, 143 insertions(+), 2 deletions(-) diff --git a/apps/openmw-mp/Script/Functions/Worldstate.cpp b/apps/openmw-mp/Script/Functions/Worldstate.cpp index 28685577c..1186ad4be 100644 --- a/apps/openmw-mp/Script/Functions/Worldstate.cpp +++ b/apps/openmw-mp/Script/Functions/Worldstate.cpp @@ -5,6 +5,8 @@ #include #include +#include + #include "Worldstate.hpp" using namespace std; @@ -158,11 +160,22 @@ void WorldstateFunctions::AddEnforcedCollisionRefId(const char *refId) noexcept writeWorldstate.enforcedCollisionRefIds.push_back(refId); } +void WorldstateFunctions::AddCellToReset(const char *cellDescription) noexcept +{ + ESM::Cell cell = Utils::getCellFromDescription(cellDescription); + writeWorldstate.cellsToReset.push_back(cell); +} + void WorldstateFunctions::ClearEnforcedCollisionRefIds() noexcept { writeWorldstate.enforcedCollisionRefIds.clear(); } +void WorldstateFunctions::ClearCellsToReset() noexcept +{ + writeWorldstate.cellsToReset.clear(); +} + void WorldstateFunctions::SaveMapTileImageFile(unsigned int index, const char *filePath) noexcept { if (index >= readWorldstate->mapTiles.size()) @@ -276,6 +289,20 @@ void WorldstateFunctions::SendWorldRegionAuthority(unsigned short pid) noexcept packet->Send(true); } +void WorldstateFunctions::SendCellReset(unsigned short pid, bool sendToOtherPlayers) noexcept +{ + mwmp::WorldstatePacket *packet = mwmp::Networking::get().getWorldstatePacketController()->GetPacket(ID_CELL_RESET); + + Player *player; + GET_PLAYER(pid, player, ); + + writeWorldstate.guid = player->guid; + + packet->setWorldstate(&writeWorldstate); + + packet->Send(sendToOtherPlayers); +} + // All methods below are deprecated versions of methods from above diff --git a/apps/openmw-mp/Script/Functions/Worldstate.hpp b/apps/openmw-mp/Script/Functions/Worldstate.hpp index 3728d84a9..23eb31dc5 100644 --- a/apps/openmw-mp/Script/Functions/Worldstate.hpp +++ b/apps/openmw-mp/Script/Functions/Worldstate.hpp @@ -55,6 +55,10 @@ {"SendWorldCollisionOverride", WorldstateFunctions::SendWorldCollisionOverride},\ {"SendWorldRegionAuthority", WorldstateFunctions::SendWorldRegionAuthority},\ \ + {"AddCellToReset", WorldstateFunctions::AddCellToReset},\ + {"ClearCellsToReset", WorldstateFunctions::ClearCellsToReset},\ + {"SendCellsToReset", WorldstateFunctions::SendCellReset},\ + \ {"ReadLastWorldstate", WorldstateFunctions::ReadLastWorldstate},\ {"CopyLastWorldstateToStore", WorldstateFunctions::CopyLastWorldstateToStore} @@ -302,6 +306,13 @@ public: */ static void AddEnforcedCollisionRefId(const char* refId) noexcept; + /** + * \brief Add a cell with given cellDescription to the list of cells that should be reset on the client. + * + * \return void + */ + static void AddCellToReset(const char * cellDescription) noexcept; + /** * \brief Clear the list of refIdsd for which collision should be enforced irrespective * of other settings. @@ -310,6 +321,13 @@ public: */ static void ClearEnforcedCollisionRefIds() noexcept; + /** + * \brief Clear the list of cells which should be reset on the client. + * + * \return void + */ + static void ClearCellsToReset() noexcept; + /** * \brief Save the .png image data of the map tile at a certain index in the read worldstate's * map changes. @@ -342,6 +360,14 @@ public: */ static void SendWorldRegionAuthority(unsigned short pid) noexcept; + /** + * \brief Send a CellReset packet with a list of cells, + * + * \param pid The player ID attached to the packet. + * \return void + */ + static void SendCellReset(unsigned short pid, bool sendToOtherPlayers) noexcept; + /** * \brief Send a WorldMap packet with the current set of map changes in the write-only * worldstate. diff --git a/apps/openmw/mwmp/processors/worldstate/ProcessorCellReset.hpp b/apps/openmw/mwmp/processors/worldstate/ProcessorCellReset.hpp index 9cca7b3d0..1a24cb0e0 100644 --- a/apps/openmw/mwmp/processors/worldstate/ProcessorCellReset.hpp +++ b/apps/openmw/mwmp/processors/worldstate/ProcessorCellReset.hpp @@ -15,7 +15,20 @@ namespace mwmp virtual void Do(WorldstatePacket &packet, Worldstate &worldstate) { - // Placeholder + LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Received ID_CELL_RESET"); + CellController* cellController = Main::get().getCellController(); + + for (ESM::Cell cell : worldstate.cellsToReset) + { + MWWorld::CellStore * cellStore = cellController->getCellStore(cell); + if (cellStore != nullptr) + { + LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Resetting cell %s!", cell.getDescription().c_str()); + cellStore->clear(); + } + else + LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Attempted to reset an uninitialized cell %s!", cell.getDescription().c_str()); + } } }; } diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index fec2ac35c..5327a0898 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -1177,4 +1177,50 @@ namespace MWWorld } } } + + /* + Start of tes3mp addition + + Make it possible to clear cell data (e.g. to reset cells) + */ + void CellStore::clear() + { + if (mState != State_Unloaded) + { + mState = State_Unloaded; + + mIds.clear(); + + mActivators.mList.clear(); + mPotions.mList.clear(); + mAppas.mList.clear(); + mArmors.mList.clear(); + mBooks.mList.clear(); + mClothes.mList.clear(); + mContainers.mList.clear(); + mCreatures.mList.clear(); + mDoors.mList.clear(); + mIngreds.mList.clear(); + mCreatureLists.mList.clear(); + mItemLists.mList.clear(); + mLights.mList.clear(); + mLockpicks.mList.clear(); + mMiscItems.mList.clear(); + mNpcs.mList.clear(); + mProbes.mList.clear(); + mRepairs.mList.clear(); + mStatics.mList.clear(); + mWeapons.mList.clear(); + mBodyParts.mList.clear(); + + mMovedHere.clear(); + mMovedToAnotherCell.clear(); + mMergedRefs.clear(); + + mFogState = NULL; + } + } + /* + End of tes3mp addition + */ } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index f6cfc6c21..3975c698d 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -435,6 +435,16 @@ namespace MWWorld void respawn (); ///< Check mLastRespawn and respawn references if necessary. This is a no-op if the cell is not loaded. + /* + Start of tes3mp addition + + Make it possible to clear cell data (e.g. to reset cells). + */ + void clear (); + /* + End of tes3mp addition + */ + private: /// Run through references and store IDs diff --git a/components/openmw-mp/Base/BaseWorldstate.hpp b/components/openmw-mp/Base/BaseWorldstate.hpp index 8860a0ec8..0b8ca4e0c 100644 --- a/components/openmw-mp/Base/BaseWorldstate.hpp +++ b/components/openmw-mp/Base/BaseWorldstate.hpp @@ -232,6 +232,8 @@ namespace mwmp std::vector spellRecords; std::vector weaponRecords; + std::vector cellsToReset; + bool isValid; }; } diff --git a/components/openmw-mp/Packets/Worldstate/PacketCellReset.cpp b/components/openmw-mp/Packets/Worldstate/PacketCellReset.cpp index d89f789f6..f5f5968cd 100644 --- a/components/openmw-mp/Packets/Worldstate/PacketCellReset.cpp +++ b/components/openmw-mp/Packets/Worldstate/PacketCellReset.cpp @@ -13,5 +13,22 @@ void PacketCellReset::Packet(RakNet::BitStream *bs, bool send) { WorldstatePacket::Packet(bs, send); - // Placeholder + uint32_t cellCount; + + if (send) + cellCount = static_cast(worldstate->cellsToReset.size()); + + RW(cellCount, send); + + if (!send) + { + worldstate->cellsToReset.clear(); + worldstate->cellsToReset.resize(cellCount); + } + + for (auto &&cellToReset : worldstate->cellsToReset) + { + RW(cellToReset.mData, send, true); + RW(cellToReset.mName, send, true); + } } From c89efd251cd218c34297226301608e13954f2931 Mon Sep 17 00:00:00 2001 From: uramer Date: Sun, 7 Apr 2019 01:27:15 +0200 Subject: [PATCH 2/2] [General] Implement CellReset packet, stage 2 --- .../openmw-mp/Script/Functions/Worldstate.cpp | 24 ++++- .../openmw-mp/Script/Functions/Worldstate.hpp | 2 +- apps/openmw/mwbase/world.hpp | 12 ++- apps/openmw/mwmp/CellController.cpp | 8 +- .../worldstate/ProcessorCellReset.hpp | 20 ++-- apps/openmw/mwworld/cellstore.cpp | 55 +++++++++- apps/openmw/mwworld/cellstore.hpp | 20 ++++ apps/openmw/mwworld/worldimp.cpp | 101 +++++++++++++++++- apps/openmw/mwworld/worldimp.hpp | 12 ++- 9 files changed, 231 insertions(+), 23 deletions(-) diff --git a/apps/openmw-mp/Script/Functions/Worldstate.cpp b/apps/openmw-mp/Script/Functions/Worldstate.cpp index 1186ad4be..d963cbf4c 100644 --- a/apps/openmw-mp/Script/Functions/Worldstate.cpp +++ b/apps/openmw-mp/Script/Functions/Worldstate.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -299,8 +300,29 @@ void WorldstateFunctions::SendCellReset(unsigned short pid, bool sendToOtherPlay writeWorldstate.guid = player->guid; packet->setWorldstate(&writeWorldstate); - + packet->Send(sendToOtherPlayers); + + if (sendToOtherPlayers) + { + packet->Send(false); + } + + CellController * cellController = CellController::get(); + + for (ESM::Cell cell : writeWorldstate.cellsToReset) + { + if (sendToOtherPlayers) + { + TPlayers * players = Players::getPlayers(); + for (TPlayers::iterator iter = players->begin(); iter != players->end(); iter++) + { + cellController->getCell(&cell)->removePlayer((*iter).second, true); + } + } + else + cellController->getCell(&cell)->removePlayer(Players::getPlayer(pid), true); + } } diff --git a/apps/openmw-mp/Script/Functions/Worldstate.hpp b/apps/openmw-mp/Script/Functions/Worldstate.hpp index 23eb31dc5..1fe13bb94 100644 --- a/apps/openmw-mp/Script/Functions/Worldstate.hpp +++ b/apps/openmw-mp/Script/Functions/Worldstate.hpp @@ -57,7 +57,7 @@ \ {"AddCellToReset", WorldstateFunctions::AddCellToReset},\ {"ClearCellsToReset", WorldstateFunctions::ClearCellsToReset},\ - {"SendCellsToReset", WorldstateFunctions::SendCellReset},\ + {"SendCellReset", WorldstateFunctions::SendCellReset},\ \ {"ReadLastWorldstate", WorldstateFunctions::ReadLastWorldstate},\ {"CopyLastWorldstateToStore", WorldstateFunctions::CopyLastWorldstateToStore} diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 43b1d1af6..547ff2220 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -579,7 +579,17 @@ namespace MWBase Make it possible to check whether a cell is active */ - virtual bool isCellActive(MWWorld::CellStore* cell) = 0; + virtual bool isCellActive(ESM::Cell cell) = 0; + /* + End of tes3mp addition + */ + + /* + Start of tes3mp addition + + Make it possible to reload active cells (e.g. for CellReset) + */ + virtual void reloadCells(std::vector * cells) = 0; /* End of tes3mp addition */ diff --git a/apps/openmw/mwmp/CellController.cpp b/apps/openmw/mwmp/CellController.cpp index e2d07b084..64b96904d 100644 --- a/apps/openmw/mwmp/CellController.cpp +++ b/apps/openmw/mwmp/CellController.cpp @@ -35,7 +35,7 @@ void CellController::updateLocal(bool forceUpdate) { mwmp::Cell *mpCell = it->second; - if (!MWBase::Environment::get().getWorld()->isCellActive(mpCell->getCellStore())) + if (!MWBase::Environment::get().getWorld()->isCellActive(*(mpCell->getCellStore()->getCell()))) { mpCell->uninitializeLocalActors(); mpCell->uninitializeDedicatedActors(); @@ -312,11 +312,7 @@ bool CellController::isInitializedCell(const ESM::Cell& cell) bool CellController::isActiveWorldCell(const ESM::Cell& cell) { - MWWorld::CellStore *cellStore = getCellStore(cell); - - if (!cellStore) return false; - - return MWBase::Environment::get().getWorld()->isCellActive(cellStore); + return MWBase::Environment::get().getWorld()->isCellActive(cell); } Cell *CellController::getCell(const ESM::Cell& cell) diff --git a/apps/openmw/mwmp/processors/worldstate/ProcessorCellReset.hpp b/apps/openmw/mwmp/processors/worldstate/ProcessorCellReset.hpp index 1a24cb0e0..941ed6b30 100644 --- a/apps/openmw/mwmp/processors/worldstate/ProcessorCellReset.hpp +++ b/apps/openmw/mwmp/processors/worldstate/ProcessorCellReset.hpp @@ -2,6 +2,7 @@ #define OPENMW_PROCESSORCELLRESET_HPP #include "../WorldstateProcessor.hpp" +#include namespace mwmp { @@ -16,19 +17,20 @@ namespace mwmp virtual void Do(WorldstatePacket &packet, Worldstate &worldstate) { LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Received ID_CELL_RESET"); + CellController* cellController = Main::get().getCellController(); + MWBase::World * world = MWBase::Environment::get().getWorld(); + + world->reloadCells(&worldstate.cellsToReset); + + - for (ESM::Cell cell : worldstate.cellsToReset) + /*for (ESM::Cell cell : worldstate.cellsToReset) { - MWWorld::CellStore * cellStore = cellController->getCellStore(cell); - if (cellStore != nullptr) - { - LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Resetting cell %s!", cell.getDescription().c_str()); - cellStore->clear(); - } - else - LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Attempted to reset an uninitialized cell %s!", cell.getDescription().c_str()); + Main::get().getLocalPlayer()->storeCellState(cell, CellState::LOAD); } + Main::get().getLocalPlayer()->sendCellStates(); + Main::get().getLocalPlayer()->clearCellStates();*/ } }; } diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 5327a0898..76e1f9f85 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -605,6 +605,41 @@ namespace MWWorld End of tes3mp addition */ + /* + Start of tes3mp addition + + Make it possible to get mMovedHere in the CellStore from elsewhere in the code + */ + std::vector CellStore::getMovedHere() + { + std::vector hereVector; + for (CellStore::MovedRefTracker::iterator iter = mMovedHere.begin(); iter != mMovedHere.end(); ++iter) + { + hereVector.push_back(Ptr(iter->first, iter->second)); + } + return hereVector; + } + /* + End of tes3mp addition + */ + + /* + Start of tes3mp addition + + Make it possible to return all NPCs back to this cell from elsewhere in the code + */ + void CellStore::returnFromOtherCells() + { + for (CellStore::MovedRefTracker::iterator iter = mMovedToAnotherCell.begin(); iter != mMovedToAnotherCell.end(); ++iter) + { + LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Returning actor from %d, %d!", iter->second->getCell()->getGridX(), iter->second->getCell()->getGridY()); + iter->second->moveTo(iter->first, this); + } + } + /* + End of tes3mp addition + */ + float CellStore::getWaterLevel() const { if (isExterior()) @@ -1198,7 +1233,6 @@ namespace MWWorld mBooks.mList.clear(); mClothes.mList.clear(); mContainers.mList.clear(); - mCreatures.mList.clear(); mDoors.mList.clear(); mIngreds.mList.clear(); mCreatureLists.mList.clear(); @@ -1206,13 +1240,30 @@ namespace MWWorld mLights.mList.clear(); mLockpicks.mList.clear(); mMiscItems.mList.clear(); - mNpcs.mList.clear(); mProbes.mList.clear(); mRepairs.mList.clear(); mStatics.mList.clear(); mWeapons.mList.clear(); mBodyParts.mList.clear(); + mwmp::CellController * cellController = mwmp::Main::get().getCellController(); + + for (std::list>::iterator ref = mCreatures.mList.begin(); ref != mCreatures.mList.end(); ref++) + { + if (!cellController->isDedicatedActor(MWWorld::Ptr(&*ref, this))) + { + mCreatures.mList.erase(ref); + } + } + + for (std::list>::iterator ref = mNpcs.mList.begin(); ref != mNpcs.mList.end(); ref++) + { + if (!cellController->isDedicatedActor(MWWorld::Ptr(&*ref, this))) + { + mNpcs.mList.erase(ref); + } + } + mMovedHere.clear(); mMovedToAnotherCell.clear(); mMergedRefs.clear(); diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 3975c698d..6e7d9d585 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -289,6 +289,26 @@ namespace MWWorld End of tes3mp addition */ + /* + Start of tes3mp addition + + Make it possible to get the mContainers in the CellStore from elsewhere in the code + */ + std::vector getMovedHere(); + /* + End of tes3mp addition + */ + + /* + Start of tes3mp addition + + Make it possible to get mMovedHere in the CellStore from elsewhere in the code + */ + void returnFromOtherCells(); + /* + End of tes3mp addition + */ + float getWaterLevel() const; void setWaterLevel (float level); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c4ad67d53..286689991 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2691,9 +2691,106 @@ namespace MWWorld Make it possible to check whether a cell is active */ - bool World::isCellActive(MWWorld::CellStore* cell) + bool World::isCellActive(ESM::Cell cell) { - return mWorldScene->isCellActive(*cell); + MWWorld::Scene::CellStoreCollection activeCells = (*mWorldScene).getActiveCells(); + mwmp::CellController* cellController = mwmp::Main::get().getCellController(); + for (MWWorld::Scene::CellStoreCollection::iterator iter = activeCells.begin(); iter != activeCells.end(); iter++) + { + ESM::Cell iterCell = *(*iter)->getCell(); + if (cellController->isSameCell(iterCell, cell)) { + return true; + } + } + + return false; + } + /* + End of tes3mp addition + */ + + /* + */ + + /* + Start of tes3mp addition + + Make it possible to reload active cells (e.g. for CellReset) + */ + void World::reloadCells(std::vector * cells) + { + mwmp::CellController* cellController = mwmp::Main::get().getCellController(); + MWWorld::Scene::CellStoreCollection activeCells = (*mWorldScene).getActiveCells(); + + MWWorld::Scene::CellStoreCollection activeToReset; + + for (MWWorld::Scene::CellStoreCollection::iterator iter = activeCells.begin(); iter != activeCells.end(); iter++) + { + ESM::Cell iterCell = *(*iter)->getCell(); + for (ESM::Cell cell : *cells) + { + if (cellController->isSameCell(iterCell, cell)) + { + activeToReset.insert(*iter); + break; + } + } + } + + if (!activeToReset.empty()) + { + typedef std::pair returnPtr; + std::map moveBack; + for (MWWorld::Scene::CellStoreCollection::iterator iter = activeToReset.begin(); iter != activeToReset.end(); iter++) + { + ESM::Cell iterCell = *(*iter)->getCell(); + MWWorld::CellStore * cellStore = cellController->getCellStore(iterCell); + + cellStore->returnFromOtherCells(); + std::vector movedRefs = cellStore->getMovedHere(); + for (Ptr ref : movedRefs) + { + moveBack.insert(returnPtr(ref, cellStore)); + } + } + + for (MWWorld::Scene::CellStoreCollection::iterator iter = activeCells.begin(); iter != activeCells.end(); iter++) + { + ESM::Cell iterCell = *(*iter)->getCell(); + MWWorld::CellStore * cellStore = cellController->getCellStore(iterCell); + + mWorldScene->unloadCell(iter); + cellController->getCell(iterCell)->uninitializeLocalActors(); + cellController->getCell(iterCell)->uninitializeDedicatedActors(); + + if (activeToReset.count(*iter) > 0) + { + cellStore->clear(); + LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Resetting cell %s!", iterCell.getDescription().c_str()); + + } + + + } + + ESM::CellId pCellId = getPlayerPtr().getCell()->getCell()->getCellId(); + + changeToCell(pCellId, getPlayerPtr().getRefData().getPosition(), false, true); + + for (returnPtr ret : moveBack) + { + ret.first.getCell()->moveTo(ret.first, ret.second); + cellController->getCell(*ret.second->getCell())->initializeDedicatedActor(ret.first); + } + } + else + { + for (ESM::Cell cell : *cells) + { + MWWorld::CellStore * cellStore = cellController->getCellStore(cell); + cellStore->clear(); + } + } } /* End of tes3mp addition diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index f98b3473b..0ab306220 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -677,7 +677,17 @@ namespace MWWorld Make it possible to check whether a cell is active */ - bool isCellActive(MWWorld::CellStore* cell) override; + bool isCellActive(ESM::Cell cell) override; + /* + End of tes3mp addition + */ + + /* + Start of tes3mp addition + + Make it possible to reload active cells (e.g. for CellReset) + */ + void reloadCells(std::vector * cells) override; /* End of tes3mp addition */