diff --git a/apps/openmw-mp/Script/Functions/Worldstate.cpp b/apps/openmw-mp/Script/Functions/Worldstate.cpp index 456c7fac9..3afec8de3 100644 --- a/apps/openmw-mp/Script/Functions/Worldstate.cpp +++ b/apps/openmw-mp/Script/Functions/Worldstate.cpp @@ -3,8 +3,11 @@ #include #include #include +#include #include +#include + #include "Worldstate.hpp" using namespace std; @@ -262,6 +265,12 @@ void WorldstateFunctions::ClearSynchronizedClientGlobalIds() noexcept writeWorldstate.synchronizedClientGlobalIds.clear(); } +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(); @@ -272,6 +281,11 @@ void WorldstateFunctions::ClearDestinationOverrides() noexcept writeWorldstate.destinationOverrides.clear(); } +void WorldstateFunctions::ClearCellsToReset() noexcept +{ + writeWorldstate.cellsToReset.clear(); +} + void WorldstateFunctions::SaveMapTileImageFile(unsigned int index, const char *filePath) noexcept { if (index >= readWorldstate->mapTiles.size()) @@ -449,6 +463,41 @@ 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); + + 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); + } +} + // 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 aa411221f..8f5d4782f 100644 --- a/apps/openmw-mp/Script/Functions/Worldstate.hpp +++ b/apps/openmw-mp/Script/Functions/Worldstate.hpp @@ -81,6 +81,10 @@ {"SendWorldDestinationOverride", WorldstateFunctions::SendWorldDestinationOverride},\ {"SendWorldRegionAuthority", WorldstateFunctions::SendWorldRegionAuthority},\ \ + {"AddCellToReset", WorldstateFunctions::AddCellToReset},\ + {"ClearCellsToReset", WorldstateFunctions::ClearCellsToReset},\ + {"SendCellReset", WorldstateFunctions::SendCellReset},\ + \ {"ReadLastWorldstate", WorldstateFunctions::ReadLastWorldstate},\ {"CopyLastWorldstateToStore", WorldstateFunctions::CopyLastWorldstateToStore} @@ -485,6 +489,14 @@ public: /** * \brief Clear the list of refIds for which collision should be enforced irrespective + * \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. * * \return void @@ -498,6 +510,13 @@ public: */ static void ClearDestinationOverrides() 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. @@ -569,6 +588,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/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 23707d190..6ac1add24 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -620,6 +620,16 @@ namespace MWBase 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 + */ + virtual void getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector &actors) = 0; ///< get a list of actors standing on \a object virtual bool getPlayerStandingOn (const MWWorld::ConstPtr& object) = 0; ///< @return true if the player is standing on \a object virtual bool getActorStandingOn (const MWWorld::ConstPtr& object) = 0; ///< @return true if any actor is standing on \a object diff --git a/apps/openmw/mwmp/processors/worldstate/ProcessorCellReset.hpp b/apps/openmw/mwmp/processors/worldstate/ProcessorCellReset.hpp index 36d461323..b732d4f6b 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 { @@ -15,7 +16,19 @@ namespace mwmp virtual void Do(WorldstatePacket &packet, Worldstate &worldstate) { - // Placeholder + LOG_MESSAGE_SIMPLE(TimedLog::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) + { + 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 d789594a1..5e9fd9d24 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -621,6 +621,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(TimedLog::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()) @@ -1320,4 +1355,66 @@ namespace MWWorld || enchantment->mData.mType == ESM::Enchantment::WhenStrikes) mRechargingItems.emplace_back(ptr.getBase(), static_cast(enchantment->mData.mCharge)); } + + /* + 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(); + mDoors.mList.clear(); + mIngreds.mList.clear(); + mCreatureLists.mList.clear(); + mItemLists.mList.clear(); + mLights.mList.clear(); + mLockpicks.mList.clear(); + mMiscItems.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(); + + mFogState = NULL; + } + } + /* + End of tes3mp addition + */ } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 72dcfb245..7b0a45e96 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -302,6 +302,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; bool movedHere(const MWWorld::Ptr& ptr) const; @@ -450,6 +470,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/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ddf9183b8..16f0bf1ac 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2986,6 +2986,87 @@ namespace MWWorld 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(TimedLog::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 + */ + bool World::getPlayerStandingOn (const MWWorld::ConstPtr& object) { MWWorld::Ptr player = getPlayerPtr(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index e636d5d43..b5a9c8a3a 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -706,6 +706,16 @@ namespace MWWorld 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 + */ + /* Start of tes3mp addition diff --git a/components/openmw-mp/Base/BaseWorldstate.hpp b/components/openmw-mp/Base/BaseWorldstate.hpp index d7705575c..6ff653803 100644 --- a/components/openmw-mp/Base/BaseWorldstate.hpp +++ b/components/openmw-mp/Base/BaseWorldstate.hpp @@ -396,6 +396,8 @@ namespace mwmp std::vector staticRecords; 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 6a10a894a..72394be37 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 *newBitstream, bool send) { WorldstatePacket::Packet(newBitstream, 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); + } }