From f4cdedd8cb15ed8ad2616f7c244f7738b2655e9a Mon Sep 17 00:00:00 2001 From: David Cernat Date: Tue, 30 May 2017 10:11:01 +0300 Subject: [PATCH] [General] Synchronize summoned creatures --- apps/openmw-mp/Script/Functions/World.cpp | 5 ++ apps/openmw-mp/Script/Functions/World.hpp | 2 + apps/openmw/mwmechanics/creaturestats.cpp | 34 ++++++++ apps/openmw/mwmechanics/creaturestats.hpp | 11 +++ apps/openmw/mwmechanics/summoning.cpp | 42 ++++++++++ apps/openmw/mwmp/MechanicsHelper.cpp | 13 ++++ apps/openmw/mwmp/MechanicsHelper.hpp | 2 + apps/openmw/mwmp/WorldEvent.cpp | 78 ++++++++++++++++++- apps/openmw/mwmp/WorldEvent.hpp | 1 + apps/openmw/mwworld/worldimp.cpp | 2 +- components/openmw-mp/Base/BaseEvent.hpp | 3 + .../Packets/World/PacketObjectSpawn.cpp | 9 ++- 12 files changed, 199 insertions(+), 3 deletions(-) diff --git a/apps/openmw-mp/Script/Functions/World.cpp b/apps/openmw-mp/Script/Functions/World.cpp index 97264023b..f9724b5ed 100644 --- a/apps/openmw-mp/Script/Functions/World.cpp +++ b/apps/openmw-mp/Script/Functions/World.cpp @@ -208,6 +208,11 @@ void WorldFunctions::SetObjectDisarmState(bool disarmState) noexcept tempWorldObject.isDisarmed = disarmState; } +void WorldFunctions::SetObjectMasterState(bool masterState) noexcept +{ + tempWorldObject.hasMaster = masterState; +} + void WorldFunctions::SetObjectPosition(double x, double y, double z) noexcept { tempWorldObject.position.pos[0] = x; diff --git a/apps/openmw-mp/Script/Functions/World.hpp b/apps/openmw-mp/Script/Functions/World.hpp index 9bb683142..61e53da48 100644 --- a/apps/openmw-mp/Script/Functions/World.hpp +++ b/apps/openmw-mp/Script/Functions/World.hpp @@ -43,6 +43,7 @@ {"SetObjectDoorState", WorldFunctions::SetObjectDoorState},\ {"SetObjectLockLevel", WorldFunctions::SetObjectLockLevel},\ {"SetObjectDisarmState", WorldFunctions::SetObjectDisarmState},\ + {"SetObjectMasterState", WorldFunctions::SetObjectMasterState},\ {"SetObjectPosition", WorldFunctions::SetObjectPosition},\ {"SetObjectRotation", WorldFunctions::SetObjectRotation},\ \ @@ -111,6 +112,7 @@ public: static void SetObjectDoorState(int doorState) noexcept; static void SetObjectLockLevel(int lockLevel) noexcept; static void SetObjectDisarmState(bool disarmState) noexcept; + static void SetObjectMasterState(bool masterState) noexcept; static void SetObjectPosition(double x, double y, double z) noexcept; static void SetObjectRotation(double x, double y, double z) noexcept; diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 31724bcbc..ce329dbfe 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -2,6 +2,17 @@ #include +/* + Start of tes3mp addition + + Include additional headers for multiplayer purposes +*/ +#include +#include "spellcasting.hpp" +/* + End of tes3mp addition +*/ + #include #include #include @@ -668,4 +679,27 @@ namespace MWMechanics { return mSummonGraveyard; } + + /* + Start of tes3mp addition + + Make it possible to set a new actorId for summoned creatures, necessary for properly + initializing them after syncing them across players + */ + void CreatureStats::setSummonedCreatureActorId(std::string refId, int actorId) + { + for (std::map::iterator it = mSummonedCreatures.begin(); it != mSummonedCreatures.end(); ) + { + if (Misc::StringUtils::ciEqual(getSummonedCreature(it->first.first), refId)) + { + it->second = actorId; + break; + } + else + ++it; + } + } + /* + End of tes3mp addition + */ } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 057a6f602..a3f245918 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -222,6 +222,17 @@ namespace MWMechanics std::map& getSummonedCreatureMap(); // std::vector& getSummonedCreatureGraveyard(); // ActorIds + /* + Start of tes3mp addition + + Make it possible to set a new actorId for summoned creatures, necessary for properly + initializing them after syncing them across players + */ + void setSummonedCreatureActorId(std::string refId, int actorId); + /* + End of tes3mp addition + */ + enum Flag { Flag_ForceRun = 1, diff --git a/apps/openmw/mwmechanics/summoning.cpp b/apps/openmw/mwmechanics/summoning.cpp index e4a825efc..0ba76ca81 100644 --- a/apps/openmw/mwmechanics/summoning.cpp +++ b/apps/openmw/mwmechanics/summoning.cpp @@ -2,6 +2,20 @@ #include +/* + Start of tes3mp addition + + Include additional headers for multiplayer purposes +*/ +#include +#include "../mwmp/Main.hpp" +#include "../mwmp/Networking.hpp" +#include "../mwmp/CellController.hpp" +#include "../mwmp/WorldEvent.hpp" +/* + End of tes3mp addition +*/ + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/mechanicsmanager.hpp" @@ -68,19 +82,33 @@ namespace MWMechanics if (!creatureID.empty()) { int creatureActorId = -1; + + /* + Start of tes3mp change (major) + + Send an ID_OBJECT_SPAWN packet every time a creature is summoned in a cell that we hold + authority over, then delete the creature and wait for the server to send it back with a + unique mpNum of its own + + Comment out most of the code here except for the actual placement of the Ptr and the + creatureActorId insertion into the creatureMap + */ try { MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), creatureID, 1); + /* MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr()); // Make the summoned creature follow its master and help in fights AiFollow package(mActor.getCellRef().getRefId()); summonedCreatureStats.getAiSequence().stack(package, ref.getPtr()); creatureActorId = summonedCreatureStats.getActorId(); + */ MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), mActor, mActor.getCell(), 0, 120.f); + /* MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(placed); if (anim) { @@ -89,6 +117,17 @@ namespace MWMechanics if (fx) anim->addEffect("meshes\\" + fx->mModel, -1, false); } + */ + + if (mwmp::Main::get().getCellController()->hasLocalAuthority(*placed.getCell()->getCell())) + { + mwmp::WorldEvent *worldEvent = mwmp::Main::get().getNetworking()->getWorldEvent(); + worldEvent->reset(); + worldEvent->addObjectSpawn(placed, mActor); + worldEvent->sendObjectSpawn(); + } + + MWBase::Environment::get().getWorld()->deleteObject(placed); } catch (std::exception& e) { @@ -97,6 +136,9 @@ namespace MWMechanics } creatureMap.insert(std::make_pair(*it, creatureActorId)); + /* + End of tes3mp change (major) + */ } } } diff --git a/apps/openmw/mwmp/MechanicsHelper.cpp b/apps/openmw/mwmp/MechanicsHelper.cpp index 6ce4600a0..23f6d7b51 100644 --- a/apps/openmw/mwmp/MechanicsHelper.cpp +++ b/apps/openmw/mwmp/MechanicsHelper.cpp @@ -91,6 +91,19 @@ Attack *MechanicsHelper::getDedicatedAttack(const MWWorld::Ptr& ptr) return NULL; } +MWWorld::Ptr MechanicsHelper::getPlayerPtr(const Target& target) +{ + if (target.refId.empty()) + { + if (target.guid == mwmp::Main::get().getLocalPlayer()->guid) + return MWBase::Environment::get().getWorld()->getPlayerPtr(); + else if (PlayerList::getPlayer(target.guid) != NULL) + return PlayerList::getPlayer(target.guid)->getPtr(); + } + + return NULL; +} + void MechanicsHelper::assignAttackTarget(Attack* attack, const MWWorld::Ptr& target) { if (target == MWBase::Environment::get().getWorld()->getPlayerPtr()) diff --git a/apps/openmw/mwmp/MechanicsHelper.hpp b/apps/openmw/mwmp/MechanicsHelper.hpp index 02584686f..a70fa162b 100644 --- a/apps/openmw/mwmp/MechanicsHelper.hpp +++ b/apps/openmw/mwmp/MechanicsHelper.hpp @@ -21,6 +21,8 @@ namespace mwmp Attack *getLocalAttack(const MWWorld::Ptr& ptr); Attack *getDedicatedAttack(const MWWorld::Ptr& ptr); + MWWorld::Ptr getPlayerPtr(const Target& target); + void assignAttackTarget(Attack* attack, const MWWorld::Ptr& target); void resetAttack(Attack* attack); diff --git a/apps/openmw/mwmp/WorldEvent.cpp b/apps/openmw/mwmp/WorldEvent.cpp index 9b1d191af..89a881f21 100644 --- a/apps/openmw/mwmp/WorldEvent.cpp +++ b/apps/openmw/mwmp/WorldEvent.cpp @@ -1,8 +1,10 @@ #include "WorldEvent.hpp" #include "Main.hpp" #include "Networking.hpp" +#include "MechanicsHelper.hpp" #include "LocalPlayer.hpp" #include "DedicatedPlayer.hpp" +#include "PlayerList.hpp" #include @@ -12,7 +14,11 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwmechanics/aifollow.hpp" #include "../mwmechanics/spellcasting.hpp" +#include "../mwmechanics/summoning.hpp" + +#include "../mwrender/animation.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" @@ -184,10 +190,42 @@ void WorldEvent::spawnObjects(MWWorld::CellStore* cellStore) newPtr.getCellRef().setMpNum(worldObject.mpNum); newPtr = MWBase::Environment::get().getWorld()->placeObject(newPtr, cellStore, worldObject.position); + + if (worldObject.hasMaster) + { + MWWorld::Ptr masterPtr; + + if (worldObject.master.refId.empty()) + masterPtr = mwmp::Main::get().getMechanicsHelper()->getPlayerPtr(worldObject.master); + else + masterPtr = cellStore->searchExact(worldObject.master.refNumIndex, worldObject.master.mpNum); + + if (masterPtr) + { + LOG_APPEND(Log::LOG_VERBOSE, "-- Actor has master: %s", masterPtr.getCellRef().getRefId().c_str()); + + MWMechanics::AiFollow package(masterPtr.getCellRef().getRefId()); + newPtr.getClass().getCreatureStats(newPtr).getAiSequence().stack(package, newPtr); + + MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(newPtr); + if (anim) + { + const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get() + .search("VFX_Summon_Start"); + if (fx) + anim->addEffect("meshes\\" + fx->mModel, -1, false); + } + + int creatureActorId = newPtr.getClass().getCreatureStats(newPtr).getActorId(); + + MWMechanics::CreatureStats& masterCreatureStats = masterPtr.getClass().getCreatureStats(masterPtr); + masterCreatureStats.setSummonedCreatureActorId(worldObject.refId, creatureActorId); + } + } } else { - LOG_APPEND(Log::LOG_VERBOSE, "-- Object already existed!"); + LOG_APPEND(Log::LOG_VERBOSE, "-- Actor already existed!"); } } } @@ -547,6 +585,44 @@ void WorldEvent::addObjectSpawn(const MWWorld::Ptr& ptr) worldObject.refId = ptr.getCellRef().getRefId(); worldObject.refNumIndex = ptr.getCellRef().getRefNum().mIndex; worldObject.mpNum = 0; + worldObject.hasMaster = false; + + // Make sure we send the RefData position instead of the CellRef one, because that's what + // we actually see on this client + worldObject.position = ptr.getRefData().getPosition(); + + addObject(worldObject); +} + +void WorldEvent::addObjectSpawn(const MWWorld::Ptr& ptr, const MWWorld::Ptr& master) +{ + cell = *ptr.getCell()->getCell(); + + mwmp::WorldObject worldObject; + worldObject.refId = ptr.getCellRef().getRefId(); + worldObject.refNumIndex = ptr.getCellRef().getRefNum().mIndex; + worldObject.mpNum = 0; + + worldObject.hasMaster = true; + + if (master == MWBase::Environment::get().getWorld()->getPlayerPtr()) + { + worldObject.master.guid = mwmp::Main::get().getLocalPlayer()->guid; + worldObject.master.refId.clear(); + } + else if (mwmp::PlayerList::isDedicatedPlayer(master)) + { + worldObject.master.guid = mwmp::PlayerList::getPlayer(master)->guid; + worldObject.master.refId.clear(); + } + else + { + MWWorld::CellRef *masterRef = &master.getCellRef(); + + worldObject.master.refId = masterRef->getRefId(); + worldObject.master.refNumIndex = masterRef->getRefNum().mIndex; + worldObject.master.mpNum = masterRef->getMpNum(); + } // Make sure we send the RefData position instead of the CellRef one, because that's what // we actually see on this client diff --git a/apps/openmw/mwmp/WorldEvent.hpp b/apps/openmw/mwmp/WorldEvent.hpp index c7a674d8a..7ed6bd435 100644 --- a/apps/openmw/mwmp/WorldEvent.hpp +++ b/apps/openmw/mwmp/WorldEvent.hpp @@ -41,6 +41,7 @@ namespace mwmp void addObjectPlace(const MWWorld::Ptr& ptr); void addObjectSpawn(const MWWorld::Ptr& ptr); + void addObjectSpawn(const MWWorld::Ptr& ptr, const MWWorld::Ptr& master); void addObjectDelete(const MWWorld::Ptr& ptr); void addObjectLock(const MWWorld::Ptr& ptr, int lockLevel); void addObjectTrap(const MWWorld::Ptr& ptr, const ESM::Position& pos, bool isDisarmed); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 68474b359..7368d06b6 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3395,7 +3395,7 @@ namespace MWWorld /* Start of tes3mp change (major) - Send an ID_OBJECT_PLACE packet every time a random creature is spawned, then delete + Send an ID_OBJECT_SPAWN packet every time a random creature is spawned, then delete the creature and wait for the server to send it back with a unique mpNum of its own */ MWWorld::Ptr ptr = safePlaceObject(ref.getPtr(), getPlayerPtr(), getPlayerPtr().getCell(), 0, 220.f); diff --git a/components/openmw-mp/Base/BaseEvent.hpp b/components/openmw-mp/Base/BaseEvent.hpp index 9624992c8..464fe22ff 100644 --- a/components/openmw-mp/Base/BaseEvent.hpp +++ b/components/openmw-mp/Base/BaseEvent.hpp @@ -49,6 +49,9 @@ namespace mwmp bool isDisarmed; + Target master; + bool hasMaster; + std::vector containerItems; unsigned int containerItemCount; }; diff --git a/components/openmw-mp/Packets/World/PacketObjectSpawn.cpp b/components/openmw-mp/Packets/World/PacketObjectSpawn.cpp index 34ed8cff0..848f95703 100644 --- a/components/openmw-mp/Packets/World/PacketObjectSpawn.cpp +++ b/components/openmw-mp/Packets/World/PacketObjectSpawn.cpp @@ -38,7 +38,14 @@ void PacketObjectSpawn::Packet(RakNet::BitStream *bs, bool send) RW(worldObject.mpNum, send); RW(worldObject.position, send); - // Placeholder to be filled in later + RW(worldObject.hasMaster, send); + + if (worldObject.hasMaster) + { + RW(worldObject.master.refNumIndex, send); + RW(worldObject.master.mpNum, send); + RW(worldObject.master.guid, send); + } if (!send) {