diff --git a/apps/openmw-mp/Script/Functions/Objects.cpp b/apps/openmw-mp/Script/Functions/Objects.cpp index 3a23b90e9..803989d3b 100644 --- a/apps/openmw-mp/Script/Functions/Objects.cpp +++ b/apps/openmw-mp/Script/Functions/Objects.cpp @@ -58,6 +58,21 @@ unsigned char ObjectFunctions::GetObjectListContainerSubAction() noexcept return readObjectList->containerSubAction; } +bool ObjectFunctions::IsObjectPlayer(unsigned int i) noexcept +{ + return readObjectList->baseObjects.at(i).isPlayer; +} + +int ObjectFunctions::GetObjectPid(unsigned int i) noexcept +{ + Player *player = Players::getPlayer(readObjectList->baseObjects.at(i).guid); + + if (player != nullptr) + return player->getId(); + + return -1; +} + const char *ObjectFunctions::GetObjectRefId(unsigned int i) noexcept { return readObjectList->baseObjects.at(i).refId.c_str(); @@ -113,6 +128,41 @@ int ObjectFunctions::GetObjectLockLevel(unsigned int i) noexcept return readObjectList->baseObjects.at(i).lockLevel; } +bool ObjectFunctions::DoesObjectHavePlayerActivating(unsigned int i) noexcept +{ + return readObjectList->baseObjects.at(i).activatingActor.isPlayer; +} + +int ObjectFunctions::GetObjectActivatingPid(unsigned int i) noexcept +{ + Player *player = Players::getPlayer(readObjectList->baseObjects.at(i).activatingActor.guid); + + if (player != nullptr) + return player->getId(); + + return -1; +} + +const char *ObjectFunctions::GetObjectActivatingRefId(unsigned int i) noexcept +{ + return readObjectList->baseObjects.at(i).activatingActor.refId.c_str(); +} + +unsigned int ObjectFunctions::GetObjectActivatingRefNum(unsigned int i) noexcept +{ + return readObjectList->baseObjects.at(i).activatingActor.refNum; +} + +unsigned int ObjectFunctions::GetObjectActivatingMpNum(unsigned int i) noexcept +{ + return readObjectList->baseObjects.at(i).activatingActor.mpNum; +} + +const char *ObjectFunctions::GetObjectActivatingName(unsigned int i) noexcept +{ + return readObjectList->baseObjects.at(i).activatingActor.name.c_str(); +} + bool ObjectFunctions::GetObjectSummonState(unsigned int i) noexcept { return readObjectList->baseObjects.at(i).isSummon; @@ -399,6 +449,17 @@ void ObjectFunctions::AddContainerItem() noexcept tempContainerItem = emptyContainerItem; } +void ObjectFunctions::SendObjectActivate(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept +{ + mwmp::ObjectPacket *packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_OBJECT_ACTIVATE); + packet->setObjectList(&writeObjectList); + + if (!skipAttachedPlayer) + packet->Send(false); + if (sendToOtherPlayers) + packet->Send(true); +} + void ObjectFunctions::SendObjectPlace(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept { mwmp::ObjectPacket *packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_OBJECT_PLACE); diff --git a/apps/openmw-mp/Script/Functions/Objects.hpp b/apps/openmw-mp/Script/Functions/Objects.hpp index 061d87db4..849a93ece 100644 --- a/apps/openmw-mp/Script/Functions/Objects.hpp +++ b/apps/openmw-mp/Script/Functions/Objects.hpp @@ -13,6 +13,8 @@ {"GetObjectListAction", ObjectFunctions::GetObjectListAction},\ {"GetObjectListContainerSubAction", ObjectFunctions::GetObjectListContainerSubAction},\ \ + {"IsObjectPlayer", ObjectFunctions::IsObjectPlayer},\ + {"GetObjectPid", ObjectFunctions::GetObjectPid},\ {"GetObjectRefId", ObjectFunctions::GetObjectRefId},\ {"GetObjectRefNum", ObjectFunctions::GetObjectRefNum},\ {"GetObjectMpNum", ObjectFunctions::GetObjectMpNum},\ @@ -25,6 +27,13 @@ {"GetObjectDoorState", ObjectFunctions::GetObjectDoorState},\ {"GetObjectLockLevel", ObjectFunctions::GetObjectLockLevel},\ \ + {"DoesObjectHavePlayerActivating", ObjectFunctions::DoesObjectHavePlayerActivating},\ + {"GetObjectActivatingPid", ObjectFunctions::GetObjectActivatingPid},\ + {"GetObjectActivatingRefId", ObjectFunctions::GetObjectActivatingRefId},\ + {"GetObjectActivatingRefNum", ObjectFunctions::GetObjectActivatingRefNum},\ + {"GetObjectActivatingMpNum", ObjectFunctions::GetObjectActivatingMpNum},\ + {"GetObjectActivatingName", ObjectFunctions::GetObjectActivatingName},\ + \ {"GetObjectSummonState", ObjectFunctions::GetObjectSummonState},\ {"GetObjectSummonDuration", ObjectFunctions::GetObjectSummonDuration},\ {"DoesObjectHavePlayerSummoner", ObjectFunctions::DoesObjectHavePlayerSummoner},\ @@ -89,6 +98,7 @@ {"AddObject", ObjectFunctions::AddObject},\ {"AddContainerItem", ObjectFunctions::AddContainerItem},\ \ + {"SendObjectActivate", ObjectFunctions::SendObjectActivate},\ {"SendObjectPlace", ObjectFunctions::SendObjectPlace},\ {"SendObjectSpawn", ObjectFunctions::SendObjectSpawn},\ {"SendObjectDelete", ObjectFunctions::SendObjectDelete},\ @@ -174,9 +184,35 @@ public: */ static unsigned char GetObjectListContainerSubAction() noexcept; + /** + * \brief Check whether the object at a certain index in the read object list is a + * player. + * + * Note: Although most player data and events are dealt with in Player packets, + * object activation is general enough for players themselves to be included + * as objects in ObjectActivate packets. + * + * \param i The index of the object. + * \return Whether the object is a player. + */ + static bool IsObjectPlayer(unsigned int i) noexcept; + + /** + * \brief Get the player ID of the object at a certain index in the read object list, + * only valid if the object is a player. + * + * Note: Currently, players can only be objects in ObjectActivate and ConsoleCommand + * packets. + * + * \param i The index of the object. + * \return The player ID of the object. + */ + static int GetObjectPid(unsigned int i) noexcept; + /** * \brief Get the refId of the object at a certain index in the read object list. * + * \param i The index of the object. * \return The refId. */ static const char *GetObjectRefId(unsigned int i) noexcept; @@ -263,6 +299,60 @@ public: */ static int GetObjectLockLevel(unsigned int i) noexcept; + /** + * \brief Check whether the object at a certain index in the read object list has been + * activated by a player. + * + * \param i The index of the object. + * \return Whether the object has been activated by a player. + */ + static bool DoesObjectHavePlayerActivating(unsigned int i) noexcept; + + /** + * \brief Get the player ID of the player activating the object at a certain index in the + * read object list. + * + * \param i The index of the object. + * \return The player ID of the activating player. + */ + static int GetObjectActivatingPid(unsigned int i) noexcept; + + /** + * \brief Get the refId of the actor activating the object at a certain index in the read + * object list. + * + * \param i The index of the object. + * \return The refId of the activating actor. + */ + static const char *GetObjectActivatingRefId(unsigned int i) noexcept; + + /** + * \brief Get the refNum of the actor activating the object at a certain index in the read + * object list. + * + * \param i The index of the object. + * \return The refNum of the activating actor. + */ + static unsigned int GetObjectActivatingRefNum(unsigned int i) noexcept; + + /** + * \brief Get the mpNum of the actor activating the object at a certain index in the read + * object list. + * + * \param i The index of the object. + * \return The mpNum of the activating actor. + */ + static unsigned int GetObjectActivatingMpNum(unsigned int i) noexcept; + + /** + * \brief Get the name of the actor activating the object at a certain index in the read + * object list. + * + * \param i The index of the object. + * \return The name of the activating actor. + */ + static const char *GetObjectActivatingName(unsigned int i) noexcept; + /** * \brief Check whether the object at a certain index in the read object list is a * summon. @@ -767,6 +857,17 @@ public: */ static void AddContainerItem() noexcept; + /** + * \brief Send an ObjectActivate packet. + * + * \param sendToOtherPlayers Whether this packet should be sent to players other than the + * player attached to the packet (false by default). + * \param skipAttachedPlayer Whether the packet should skip being sent to the player attached + * to the packet (false by default). + * \return void + */ + static void SendObjectActivate(bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept; + /** * \brief Send an ObjectPlace packet. * diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 0d567c042..da0a9bb2c 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -122,12 +122,16 @@ add_openmw_dir (mwmp/processors/player ProcessorChatMessage ProcessorGUIMessageB ProcessorPlayerSpellbook ProcessorPlayerStatsDynamic ProcessorPlayerTopic ) -add_openmw_dir (mwmp/processors/object BaseObjectProcessor ProcessorConsoleCommand ProcessorContainer - ProcessorDoorDestination ProcessorDoorState ProcessorMusicPlay ProcessorVideoPlay ProcessorObjectAnimPlay - ProcessorObjectAttach ProcessorObjectCollision ProcessorObjectDelete ProcessorObjectLock ProcessorObjectMove - ProcessorObjectPlace ProcessorObjectReset ProcessorObjectRotate ProcessorObjectScale ProcessorObjectSpawn - ProcessorObjectState ProcessorObjectTrap ProcessorScriptLocalShort ProcessorScriptLocalFloat - ProcessorScriptMemberShort ProcessorScriptMemberFloat ProcessorScriptGlobalShort ProcessorScriptGlobalFloat +add_openmw_dir (mwmp/processors/object BaseObjectProcessor + + ProcessorConsoleCommand ProcessorContainer ProcessorDoorDestination ProcessorDoorState ProcessorMusicPlay + ProcessorVideoPlay + + ProcessorObjectActivate ProcessorObjectAnimPlay ProcessorObjectAttach ProcessorObjectCollision ProcessorObjectDelete + ProcessorObjectLock ProcessorObjectMove ProcessorObjectPlace ProcessorObjectReset ProcessorObjectRotate + ProcessorObjectScale ProcessorObjectSpawn ProcessorObjectState ProcessorObjectTrap ProcessorScriptLocalShort + ProcessorScriptLocalFloat ProcessorScriptMemberShort ProcessorScriptMemberFloat ProcessorScriptGlobalShort + ProcessorScriptGlobalFloat ) add_openmw_dir (mwmp/processors/worldstate ProcessorCellCreate ProcessorCellReplace ProcessorRecordDynamic diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index 1677e56f2..1d4602a6f 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -2,6 +2,18 @@ #include +/* + Start of tes3mp addition + + Include additional headers for multiplayer purposes +*/ +#include "../mwmp/Main.hpp" +#include "../mwmp/Networking.hpp" +#include "../mwmp/ObjectList.hpp" +/* + End of tes3mp addition +*/ + #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -63,7 +75,31 @@ namespace MWMechanics if (pathTo(actor, dest, duration, MWBase::Environment::get().getWorld()->getMaxActivationDistance())) //Stop when you get in activation range { // activate when reached - MWBase::Environment::get().getWorld()->activate(target, actor); + + /* + Start of tes3mp change (major) + + Disable unilateral activation on this client and expect the server's reply to our + packet to do it instead + */ + //MWBase::Environment::get().getWorld()->activate(target, actor); + /* + End of tes3mp change (major) + */ + + /* + Start of tes3mp addition + + Send an ID_OBJECT_ACTIVATE packet every time an object is activated here + */ + mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList(); + objectList->reset(); + objectList->addObjectActivate(target, actor); + objectList->sendObjectActivate(); + /* + End of tes3mp addition + */ + return true; } diff --git a/apps/openmw/mwmp/ObjectList.cpp b/apps/openmw/mwmp/ObjectList.cpp index 97ea60a20..97ba418ab 100644 --- a/apps/openmw/mwmp/ObjectList.cpp +++ b/apps/openmw/mwmp/ObjectList.cpp @@ -262,6 +262,58 @@ void ObjectList::editContainers(MWWorld::CellStore* cellStore) } } +void ObjectList::activateObjects(MWWorld::CellStore* cellStore) +{ + for (const auto &baseObject : baseObjects) + { + MWWorld::Ptr ptrFound; + + if (baseObject.isPlayer) + { + if (baseObject.guid == Main::get().getLocalPlayer()->guid) + { + LOG_APPEND(Log::LOG_VERBOSE, "-- Running on local player"); + ptrFound = Main::get().getLocalPlayer()->getPlayerPtr(); + } + else + { + DedicatedPlayer *player = PlayerList::getPlayer(baseObject.guid); + + if (player != 0) + { + LOG_APPEND(Log::LOG_VERBOSE, "-- Running on player %s", player->npc.mName.c_str()); + ptrFound = player->getPtr(); + } + else + { + LOG_APPEND(Log::LOG_VERBOSE, "-- Could not find target player!"); + } + } + } + else + { + LOG_APPEND(Log::LOG_VERBOSE, "-- Running on cellRef: %s %i-%i", baseObject.refId.c_str(), baseObject.refNum, baseObject.mpNum); + ptrFound = cellStore->searchExact(baseObject.refNum, baseObject.mpNum); + } + + if (ptrFound) + { + MWWorld::Ptr activatingActorPtr; + + if (baseObject.activatingActor.isPlayer) + activatingActorPtr = MechanicsHelper::getPlayerPtr(baseObject.activatingActor); + else + activatingActorPtr = cellStore->searchExact(baseObject.activatingActor.refNum, baseObject.activatingActor.mpNum); + + if (activatingActorPtr) + { + LOG_APPEND(Log::LOG_VERBOSE, "-- Object has activating actor: %s", activatingActorPtr.getCellRef().getRefId().c_str()); + MWBase::Environment::get().getWorld()->activate(ptrFound, activatingActorPtr); + } + } + } +} + void ObjectList::placeObjects(MWWorld::CellStore* cellStore) { MWBase::World *world = MWBase::Environment::get().getWorld(); @@ -621,7 +673,7 @@ void ObjectList::runConsoleCommands(MWWorld::CellStore* cellStore) { if (baseObject.guid == Main::get().getLocalPlayer()->guid) { - LOG_APPEND(Log::LOG_VERBOSE, "-- running on local player"); + LOG_APPEND(Log::LOG_VERBOSE, "-- Running on local player"); windowManager->setConsolePtr(Main::get().getLocalPlayer()->getPlayerPtr()); windowManager->executeCommandInConsole(consoleCommand); } @@ -631,7 +683,7 @@ void ObjectList::runConsoleCommands(MWWorld::CellStore* cellStore) if (player != 0) { - LOG_APPEND(Log::LOG_VERBOSE, "-- running on player %s", player->npc.mName.c_str()); + LOG_APPEND(Log::LOG_VERBOSE, "-- Running on player %s", player->npc.mName.c_str()); windowManager->setConsolePtr(player->getPtr()); windowManager->executeCommandInConsole(consoleCommand); } @@ -639,7 +691,7 @@ void ObjectList::runConsoleCommands(MWWorld::CellStore* cellStore) } else { - LOG_APPEND(Log::LOG_VERBOSE, "-- running on cellRef: %s %i-%i", baseObject.refId.c_str(), baseObject.refNum, baseObject.mpNum); + LOG_APPEND(Log::LOG_VERBOSE, "-- Running on cellRef: %s %i-%i", baseObject.refId.c_str(), baseObject.refNum, baseObject.mpNum); MWWorld::Ptr ptrFound = cellStore->searchExact(baseObject.refNum, baseObject.mpNum); @@ -792,6 +844,35 @@ void ObjectList::addRequestedContainers(MWWorld::CellStore* cellStore, const std } } +void ObjectList::addObjectActivate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& activatingActor) +{ + cell = *ptr.getCell()->getCell(); + + mwmp::BaseObject baseObject; + + if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr()) + { + baseObject.isPlayer = true; + baseObject.guid = mwmp::Main::get().getLocalPlayer()->guid; + } + else if (mwmp::PlayerList::isDedicatedPlayer(ptr)) + { + baseObject.isPlayer = true; + baseObject.guid = mwmp::PlayerList::getPlayer(ptr)->guid; + } + else + { + baseObject.isPlayer = false; + baseObject.refId = ptr.getCellRef().getRefId(); + baseObject.refNum = ptr.getCellRef().getRefNum().mIndex; + baseObject.mpNum = ptr.getCellRef().getMpNum(); + } + + baseObject.activatingActor = MechanicsHelper::getTarget(activatingActor); + + addObject(baseObject); +} + void ObjectList::addObjectPlace(const MWWorld::Ptr& ptr, bool droppedByPlayer) { if (ptr.getCellRef().getRefId().find("$dynamic") != string::npos) @@ -1011,6 +1092,12 @@ void ObjectList::addScriptGlobalShort(std::string varName, int shortVal) addObject(baseObject); } +void ObjectList::sendObjectActivate() +{ + mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_ACTIVATE)->setObjectList(this); + mwmp::Main::get().getNetworking()->getObjectPacket(ID_OBJECT_ACTIVATE)->Send(); +} + void ObjectList::sendObjectPlace() { if (baseObjects.size() == 0) diff --git a/apps/openmw/mwmp/ObjectList.hpp b/apps/openmw/mwmp/ObjectList.hpp index af8f50dd3..449abaa39 100644 --- a/apps/openmw/mwmp/ObjectList.hpp +++ b/apps/openmw/mwmp/ObjectList.hpp @@ -24,6 +24,7 @@ namespace mwmp void editContainers(MWWorld::CellStore* cellStore); + void activateObjects(MWWorld::CellStore* cellStore); void placeObjects(MWWorld::CellStore* cellStore); void spawnObjects(MWWorld::CellStore* cellStore); void deleteObjects(MWWorld::CellStore* cellStore); @@ -49,6 +50,7 @@ namespace mwmp void addAllContainers(MWWorld::CellStore* cellStore); void addRequestedContainers(MWWorld::CellStore* cellStore, const std::vector& requestObjects); + void addObjectActivate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& activatingActor); void addObjectPlace(const MWWorld::Ptr& ptr, bool droppedByPlayer = false); void addObjectSpawn(const MWWorld::Ptr& ptr); void addObjectSpawn(const MWWorld::Ptr& ptr, const MWWorld::Ptr& master, float spawnDuration); @@ -67,6 +69,7 @@ namespace mwmp void addScriptMemberShort(std::string refId, int index, int shortVal); void addScriptGlobalShort(std::string varName, int shortVal); + void sendObjectActivate(); void sendObjectPlace(); void sendObjectSpawn(); void sendObjectDelete(); diff --git a/apps/openmw/mwmp/processors/object/ProcessorObjectActivate.hpp b/apps/openmw/mwmp/processors/object/ProcessorObjectActivate.hpp index ba96702fc..0ec7d8146 100644 --- a/apps/openmw/mwmp/processors/object/ProcessorObjectActivate.hpp +++ b/apps/openmw/mwmp/processors/object/ProcessorObjectActivate.hpp @@ -17,7 +17,7 @@ namespace mwmp { BaseObjectProcessor::Do(packet, objectList); - //objectList.activateObjects(ptrCellStore); + objectList.activateObjects(ptrCellStore); } }; diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 5439447fd..7220464ef 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -3,6 +3,18 @@ #include #include +/* + Start of tes3mp addition + + Include additional headers for multiplayer purposes +*/ +#include "../mwmp/Main.hpp" +#include "../mwmp/Networking.hpp" +#include "../mwmp/ObjectList.hpp" +/* + End of tes3mp addition +*/ + #include #include #include @@ -231,7 +243,29 @@ namespace MWWorld if (!toActivate.getClass().canBeActivated(toActivate)) return; - MWBase::Environment::get().getWorld()->activate(toActivate, player); + /* + Start of tes3mp change (major) + + Disable unilateral activation on this client and expect the server's reply to our + packet to do it instead + */ + //MWBase::Environment::get().getWorld()->activate(toActivate, player); + /* + End of tes3mp change (major) + */ + + /* + Start of tes3mp addition + + Send an ID_OBJECT_ACTIVATE packet every time an object is activated here + */ + mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList(); + objectList->reset(); + objectList->addObjectActivate(toActivate, player); + objectList->sendObjectActivate(); + /* + End of tes3mp addition + */ } bool Player::wasTeleported() const diff --git a/components/openmw-mp/Base/BaseObject.hpp b/components/openmw-mp/Base/BaseObject.hpp index d358af474..34937e43f 100644 --- a/components/openmw-mp/Base/BaseObject.hpp +++ b/components/openmw-mp/Base/BaseObject.hpp @@ -59,6 +59,8 @@ namespace mwmp bool isDisarmed; bool droppedByPlayer; + Target activatingActor; + bool isSummon; float summonDuration; Target master; diff --git a/components/openmw-mp/Packets/Object/PacketObjectActivate.cpp b/components/openmw-mp/Packets/Object/PacketObjectActivate.cpp index 62f626169..e9e42d324 100644 --- a/components/openmw-mp/Packets/Object/PacketObjectActivate.cpp +++ b/components/openmw-mp/Packets/Object/PacketObjectActivate.cpp @@ -9,7 +9,40 @@ PacketObjectActivate::PacketObjectActivate(RakNet::RakPeerInterface *peer) : Obj hasCellData = true; } -void PacketObjectActivate::Object(BaseObject &baseObject, bool send) +void PacketObjectActivate::Packet(RakNet::BitStream *bs, bool send) { - ObjectPacket::Object(baseObject, send); + if (!PacketHeader(bs, send)) + return; + + BaseObject baseObject; + for (unsigned int i = 0; i < objectList->baseObjectCount; i++) + { + if (send) + baseObject = objectList->baseObjects.at(i); + + RW(baseObject.isPlayer, send); + + if (baseObject.isPlayer) + RW(baseObject.guid, send); + else + Object(baseObject, send); + + RW(baseObject.activatingActor.isPlayer, send); + + if (baseObject.activatingActor.isPlayer) + { + RW(baseObject.activatingActor.guid, send); + } + else + { + RW(baseObject.activatingActor.refId, send, true); + RW(baseObject.activatingActor.refNum, send); + RW(baseObject.activatingActor.mpNum, send); + + RW(baseObject.activatingActor.name, send); + } + + if (!send) + objectList->baseObjects.push_back(baseObject); + } } diff --git a/components/openmw-mp/Packets/Object/PacketObjectActivate.hpp b/components/openmw-mp/Packets/Object/PacketObjectActivate.hpp index c2ad5fdbd..771f2042c 100644 --- a/components/openmw-mp/Packets/Object/PacketObjectActivate.hpp +++ b/components/openmw-mp/Packets/Object/PacketObjectActivate.hpp @@ -10,7 +10,7 @@ namespace mwmp public: PacketObjectActivate(RakNet::RakPeerInterface *peer); - virtual void Object(BaseObject &baseObject, bool send); + virtual void Packet(RakNet::BitStream *bs, bool send); }; }