From 294f64d12de963405a829522bd034712cca3f1db Mon Sep 17 00:00:00 2001 From: David Cernat Date: Sun, 4 Jul 2021 12:54:11 +0200 Subject: [PATCH] [General] Track timestamps for spells in SpellsActive packets This makes it possible to remove a specific effect in effect stacks by checking its timestamp. --- apps/openmw/mwmechanics/activespells.cpp | 72 +++++++++++++++++-- apps/openmw/mwmechanics/activespells.hpp | 22 ++++++ apps/openmw/mwmp/DedicatedActor.cpp | 15 +++- apps/openmw/mwmp/DedicatedPlayer.cpp | 15 +++- apps/openmw/mwmp/LocalActor.cpp | 9 ++- apps/openmw/mwmp/LocalActor.hpp | 5 +- apps/openmw/mwmp/LocalPlayer.cpp | 30 ++++++-- apps/openmw/mwmp/LocalPlayer.hpp | 5 +- components/openmw-mp/Base/BaseStructs.hpp | 2 + .../Packets/Actor/PacketActorSpellsActive.cpp | 2 + .../Player/PacketPlayerSpellsActive.cpp | 2 + 11 files changed, 160 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 0a47e9ab4..800295318 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -45,13 +45,15 @@ namespace MWMechanics Whenever a player loses an active spell, send an ID_PLAYER_SPELLS_ACTIVE packet to the server with it */ + bool isStackingSpell = !MWBase::Environment::get().getWorld()->getStore().get().search(iter->first); + if (this == &MWMechanics::getPlayer().getClass().getCreatureStats(MWMechanics::getPlayer()).getActiveSpells()) { - mwmp::Main::get().getLocalPlayer()->sendSpellsActiveRemoval(iter->first); + mwmp::Main::get().getLocalPlayer()->sendSpellsActiveRemoval(iter->first, isStackingSpell, iter->second.mTimeStamp); } else if (mwmp::Main::get().getCellController()->isLocalActor(MechanicsHelper::getCurrentActor())) { - mwmp::Main::get().getCellController()->getLocalActor(MechanicsHelper::getCurrentActor())->sendSpellsActiveRemoval(iter->first); + mwmp::Main::get().getCellController()->getLocalActor(MechanicsHelper::getCurrentActor())->sendSpellsActiveRemoval(iter->first, isStackingSpell, iter->second.mTimeStamp); } /* End of tes3mp addition @@ -173,8 +175,16 @@ namespace MWMechanics return mSpells; } + /* + Start of tes3mp change (major) + + Add a timestamp argument so spells received from other clients can have the same timestamps they had there + */ void ActiveSpells::addSpell(const std::string &id, bool stack, std::vector effects, - const std::string &displayName, int casterActorId) + const std::string &displayName, int casterActorId, MWWorld::TimeStamp timestamp) + /* + End of tes3mp change (major) + */ { TContainer::iterator it(mSpells.find(id)); @@ -183,6 +193,16 @@ namespace MWMechanics params.mDisplayName = displayName; params.mCasterActorId = casterActorId; + /* + Start of tes3mp addition + + Track the timestamp of this active spell so that, if spells are stacked, the correct one can be removed + */ + params.mTimeStamp = timestamp; + /* + End of tes3mp addition + */ + if (it == end() || stack) { mSpells.insert(std::make_pair(id, params)); @@ -211,11 +231,11 @@ namespace MWMechanics if (this == &MWMechanics::getPlayer().getClass().getCreatureStats(MWMechanics::getPlayer()).getActiveSpells()) { - mwmp::Main::get().getLocalPlayer()->sendSpellsActiveAddition(id, isStackingSpell, esmParams); + mwmp::Main::get().getLocalPlayer()->sendSpellsActiveAddition(id, isStackingSpell, esmParams, params.mTimeStamp); } else if (mwmp::Main::get().getCellController()->isLocalActor(MechanicsHelper::getCurrentActor())) { - mwmp::Main::get().getCellController()->getLocalActor(MechanicsHelper::getCurrentActor())->sendSpellsActiveAddition(id, isStackingSpell, esmParams); + mwmp::Main::get().getCellController()->getLocalActor(MechanicsHelper::getCurrentActor())->sendSpellsActiveAddition(id, isStackingSpell, esmParams, params.mTimeStamp); } /* End of tes3mp addition @@ -224,6 +244,23 @@ namespace MWMechanics mSpellsChanged = true; } + /* + Start of tes3mp addition + + Declare addSpell() without the timestamp argument and make it call the version with that argument, + using the current time for the timestamp + */ + void ActiveSpells::addSpell(const std::string& id, bool stack, std::vector effects, + const std::string& displayName, int casterActorId) + { + MWWorld::TimeStamp timestamp = MWBase::Environment::get().getWorld()->getTimeStamp(); + + addSpell(id, stack, effects, displayName, casterActorId, timestamp); + } + /* + End of tes3mp addition + */ + void ActiveSpells::mergeEffects(std::vector& addTo, const std::vector& from) { for (std::vector::const_iterator effect(from.begin()); effect != from.end(); ++effect) @@ -257,6 +294,31 @@ namespace MWMechanics } } + /* + Start of tes3mp addition + + Remove the spell with a certain ID and a certain timestamp, useful + when there are stacked spells with the same ID + */ + void ActiveSpells::removeSpellByTimestamp(const std::string& id, MWWorld::TimeStamp timestamp) + { + for (TContainer::iterator spell = mSpells.begin(); spell != mSpells.end(); ++spell) + { + if (spell->first == id) + { + if (spell->second.mTimeStamp == timestamp) + { + spell->second.mEffects.clear(); + mSpellsChanged = true; + break; + } + } + } + } + /* + End of tes3mp addition + */ + void ActiveSpells::visitEffectSources(EffectSourceVisitor &visitor) const { for (TContainer::const_iterator it = begin(); it != end(); ++it) diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index 164d4ded1..dedec5f68 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -77,9 +77,31 @@ namespace MWMechanics void addSpell (const std::string& id, bool stack, std::vector effects, const std::string& displayName, int casterActorId); + /* + Start of tes3mp addition + + Add a separate addSpell() with a timestamp argument + */ + void addSpell (const std::string& id, bool stack, std::vector effects, + const std::string& displayName, int casterActorId, MWWorld::TimeStamp timestamp); + /* + End of tes3mp addition + */ + /// Removes the active effects from this spell/potion/.. with \a id void removeEffects (const std::string& id); + /* + Start of tes3mp addition + + Remove the spell with a certain ID and a certain timestamp, useful + when there are stacked spells with the same ID + */ + void removeSpellByTimestamp(const std::string& id, MWWorld::TimeStamp timestamp); + /* + End of tes3mp addition + */ + /// Remove all active effects with this effect id void purgeEffect (short effectId); diff --git a/apps/openmw/mwmp/DedicatedActor.cpp b/apps/openmw/mwmp/DedicatedActor.cpp index 625593b18..0140a787d 100644 --- a/apps/openmw/mwmp/DedicatedActor.cpp +++ b/apps/openmw/mwmp/DedicatedActor.cpp @@ -365,8 +365,10 @@ void DedicatedActor::addSpellsActive() for (const auto& activeSpell : spellsActiveChanges.activeSpells) { + MWWorld::TimeStamp timestamp = MWWorld::TimeStamp(activeSpell.timestampHour, activeSpell.timestampDay); + // Don't do a check for a spell's existence, because active effects from potions need to be applied here too - activeSpells.addSpell(activeSpell.id, activeSpell.isStackingSpell, activeSpell.params.mEffects, activeSpell.params.mDisplayName, 1); + activeSpells.addSpell(activeSpell.id, activeSpell.isStackingSpell, activeSpell.params.mEffects, activeSpell.params.mDisplayName, 1, timestamp); } reloadPtr(); @@ -378,7 +380,16 @@ void DedicatedActor::removeSpellsActive() for (const auto& activeSpell : spellsActiveChanges.activeSpells) { - activeSpells.removeEffects(activeSpell.id); + // Remove stacking spells based on their timestamps + if (activeSpell.isStackingSpell) + { + MWWorld::TimeStamp timestamp = MWWorld::TimeStamp(activeSpell.timestampHour, activeSpell.timestampDay); + activeSpells.removeSpellByTimestamp(activeSpell.id, timestamp); + } + else + { + activeSpells.removeEffects(activeSpell.id); + } } } diff --git a/apps/openmw/mwmp/DedicatedPlayer.cpp b/apps/openmw/mwmp/DedicatedPlayer.cpp index f4e9a6b90..287fbeee2 100644 --- a/apps/openmw/mwmp/DedicatedPlayer.cpp +++ b/apps/openmw/mwmp/DedicatedPlayer.cpp @@ -490,8 +490,10 @@ void DedicatedPlayer::addSpellsActive() for (const auto& activeSpell : spellsActiveChanges.activeSpells) { + MWWorld::TimeStamp timestamp = MWWorld::TimeStamp(activeSpell.timestampHour, activeSpell.timestampDay); + // Don't do a check for a spell's existence, because active effects from potions need to be applied here too - activeSpells.addSpell(activeSpell.id, activeSpell.isStackingSpell, activeSpell.params.mEffects, activeSpell.params.mDisplayName, 1); + activeSpells.addSpell(activeSpell.id, activeSpell.isStackingSpell, activeSpell.params.mEffects, activeSpell.params.mDisplayName, 1, timestamp); } } @@ -501,7 +503,16 @@ void DedicatedPlayer::removeSpellsActive() for (const auto& activeSpell : spellsActiveChanges.activeSpells) { - activeSpells.removeEffects(activeSpell.id); + // Remove stacking spells based on their timestamps + if (activeSpell.isStackingSpell) + { + MWWorld::TimeStamp timestamp = MWWorld::TimeStamp(activeSpell.timestampHour, activeSpell.timestampDay); + activeSpells.removeSpellByTimestamp(activeSpell.id, timestamp); + } + else + { + activeSpells.removeEffects(activeSpell.id); + } } } diff --git a/apps/openmw/mwmp/LocalActor.cpp b/apps/openmw/mwmp/LocalActor.cpp index 24483527b..9132c433f 100644 --- a/apps/openmw/mwmp/LocalActor.cpp +++ b/apps/openmw/mwmp/LocalActor.cpp @@ -285,7 +285,7 @@ void LocalActor::sendEquipment() Main::get().getNetworking()->getActorPacket(ID_ACTOR_EQUIPMENT)->Send(); } -void LocalActor::sendSpellsActiveAddition(const std::string id, bool isStackingSpell, ESM::ActiveSpells::ActiveSpellParams params) +void LocalActor::sendSpellsActiveAddition(const std::string id, bool isStackingSpell, ESM::ActiveSpells::ActiveSpellParams params, MWWorld::TimeStamp timestamp) { // Skip any bugged spells that somehow have clientside-only dynamic IDs if (id.find("$dynamic") != std::string::npos) @@ -296,6 +296,8 @@ void LocalActor::sendSpellsActiveAddition(const std::string id, bool isStackingS mwmp::ActiveSpell spell; spell.id = id; spell.isStackingSpell = isStackingSpell; + spell.timestampDay = timestamp.getDay(); + spell.timestampHour = timestamp.getHour(); spell.params = params; spellsActiveChanges.activeSpells.push_back(spell); @@ -308,7 +310,7 @@ void LocalActor::sendSpellsActiveAddition(const std::string id, bool isStackingS Main::get().getNetworking()->getActorPacket(ID_ACTOR_SPELLS_ACTIVE)->Send(); } -void LocalActor::sendSpellsActiveRemoval(const std::string id) +void LocalActor::sendSpellsActiveRemoval(const std::string id, bool isStackingSpell, MWWorld::TimeStamp timestamp) { // Skip any bugged spells that somehow have clientside-only dynamic IDs if (id.find("$dynamic") != std::string::npos) @@ -318,6 +320,9 @@ void LocalActor::sendSpellsActiveRemoval(const std::string id) mwmp::ActiveSpell spell; spell.id = id; + spell.isStackingSpell = isStackingSpell; + spell.timestampDay = timestamp.getDay(); + spell.timestampHour = timestamp.getHour(); spellsActiveChanges.activeSpells.push_back(spell); spellsActiveChanges.action = mwmp::SpellsActiveChanges::REMOVE; diff --git a/apps/openmw/mwmp/LocalActor.hpp b/apps/openmw/mwmp/LocalActor.hpp index c77deafef..d888d7036 100644 --- a/apps/openmw/mwmp/LocalActor.hpp +++ b/apps/openmw/mwmp/LocalActor.hpp @@ -4,6 +4,7 @@ #include #include "../mwmechanics/creaturestats.hpp" #include "../mwworld/manualref.hpp" +#include "../mwworld/timestamp.hpp" namespace mwmp { @@ -26,8 +27,8 @@ namespace mwmp void updateAttackOrCast(); void sendEquipment(); - void sendSpellsActiveAddition(const std::string id, bool isStackingSpell, ESM::ActiveSpells::ActiveSpellParams params); - void sendSpellsActiveRemoval(const std::string id); + void sendSpellsActiveAddition(const std::string id, bool isStackingSpell, ESM::ActiveSpells::ActiveSpellParams params, MWWorld::TimeStamp timestamp); + void sendSpellsActiveRemoval(const std::string id, bool isStackingSpell, MWWorld::TimeStamp timestamp); void sendDeath(char newDeathState); MWWorld::Ptr getPtr(); diff --git a/apps/openmw/mwmp/LocalPlayer.cpp b/apps/openmw/mwmp/LocalPlayer.cpp index 4b97d0617..99d8406f1 100644 --- a/apps/openmw/mwmp/LocalPlayer.cpp +++ b/apps/openmw/mwmp/LocalPlayer.cpp @@ -718,8 +718,10 @@ void LocalPlayer::addSpellsActive() for (const auto& activeSpell : spellsActiveChanges.activeSpells) { + MWWorld::TimeStamp timestamp = MWWorld::TimeStamp(activeSpell.timestampHour, activeSpell.timestampDay); + // Don't do a check for a spell's existence, because active effects from potions need to be applied here too - activeSpells.addSpell(activeSpell.id, activeSpell.isStackingSpell, activeSpell.params.mEffects, activeSpell.params.mDisplayName, 1); + activeSpells.addSpell(activeSpell.id, activeSpell.isStackingSpell, activeSpell.params.mEffects, activeSpell.params.mDisplayName, 1, timestamp); } } @@ -821,7 +823,16 @@ void LocalPlayer::removeSpellsActive() for (const auto& activeSpell : spellsActiveChanges.activeSpells) { - activeSpells.removeEffects(activeSpell.id); + // Remove stacking spells based on their timestamps + if (activeSpell.isStackingSpell) + { + MWWorld::TimeStamp timestamp = MWWorld::TimeStamp(activeSpell.timestampHour, activeSpell.timestampDay); + activeSpells.removeSpellByTimestamp(activeSpell.id, timestamp); + } + else + { + activeSpells.removeEffects(activeSpell.id); + } } } @@ -1589,7 +1600,7 @@ void LocalPlayer::sendSpellsActive() getNetworking()->getPlayerPacket(ID_PLAYER_SPELLS_ACTIVE)->Send(); } -void LocalPlayer::sendSpellsActiveAddition(const std::string id, bool isStackingSpell, ESM::ActiveSpells::ActiveSpellParams params) +void LocalPlayer::sendSpellsActiveAddition(const std::string id, bool isStackingSpell, ESM::ActiveSpells::ActiveSpellParams params, MWWorld::TimeStamp timestamp) { // Skip any bugged spells that somehow have clientside-only dynamic IDs if (id.find("$dynamic") != std::string::npos) @@ -1600,15 +1611,20 @@ void LocalPlayer::sendSpellsActiveAddition(const std::string id, bool isStacking mwmp::ActiveSpell spell; spell.id = id; spell.isStackingSpell = isStackingSpell; + spell.timestampDay = timestamp.getDay(); + spell.timestampHour = timestamp.getHour(); spell.params = params; spellsActiveChanges.activeSpells.push_back(spell); + LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, "Sending active spell addition with stacking %s, timestamp %i %f", + spell.isStackingSpell ? "true" : "false", spell.timestampDay, spell.timestampHour); + spellsActiveChanges.action = mwmp::SpellsActiveChanges::ADD; getNetworking()->getPlayerPacket(ID_PLAYER_SPELLS_ACTIVE)->setPlayer(this); getNetworking()->getPlayerPacket(ID_PLAYER_SPELLS_ACTIVE)->Send(); } -void LocalPlayer::sendSpellsActiveRemoval(const std::string id) +void LocalPlayer::sendSpellsActiveRemoval(const std::string id, bool isStackingSpell, MWWorld::TimeStamp timestamp) { // Skip any bugged spells that somehow have clientside-only dynamic IDs if (id.find("$dynamic") != std::string::npos) @@ -1618,8 +1634,14 @@ void LocalPlayer::sendSpellsActiveRemoval(const std::string id) mwmp::ActiveSpell spell; spell.id = id; + spell.isStackingSpell = isStackingSpell; + spell.timestampDay = timestamp.getDay(); + spell.timestampHour = timestamp.getHour(); spellsActiveChanges.activeSpells.push_back(spell); + LOG_MESSAGE_SIMPLE(TimedLog::LOG_INFO, "Sending active spell removal with stacking %s, timestamp %i %f", + spell.isStackingSpell ? "true" : "false", spell.timestampDay, spell.timestampHour); + spellsActiveChanges.action = mwmp::SpellsActiveChanges::REMOVE; getNetworking()->getPlayerPacket(ID_PLAYER_SPELLS_ACTIVE)->setPlayer(this); getNetworking()->getPlayerPacket(ID_PLAYER_SPELLS_ACTIVE)->Send(); diff --git a/apps/openmw/mwmp/LocalPlayer.hpp b/apps/openmw/mwmp/LocalPlayer.hpp index 628e9abb6..f7c7936dc 100644 --- a/apps/openmw/mwmp/LocalPlayer.hpp +++ b/apps/openmw/mwmp/LocalPlayer.hpp @@ -3,6 +3,7 @@ #include #include "../mwworld/ptr.hpp" +#include "../mwworld/timestamp.hpp" #include namespace mwmp @@ -91,8 +92,8 @@ namespace mwmp void sendSpellbook(); void sendSpellChange(std::string id, unsigned int action); void sendSpellsActive(); - void sendSpellsActiveAddition(const std::string id, bool isStackingSpell, ESM::ActiveSpells::ActiveSpellParams params); - void sendSpellsActiveRemoval(const std::string id); + void sendSpellsActiveAddition(const std::string id, bool isStackingSpell, ESM::ActiveSpells::ActiveSpellParams params, MWWorld::TimeStamp timestamp); + void sendSpellsActiveRemoval(const std::string id, bool isStackingSpell, MWWorld::TimeStamp timestamp); void sendQuickKey(unsigned short slot, int type, const std::string& itemId = ""); void sendJournalEntry(const std::string& quest, int index, const MWWorld::Ptr& actor); void sendJournalIndex(const std::string& quest, int index); diff --git a/components/openmw-mp/Base/BaseStructs.hpp b/components/openmw-mp/Base/BaseStructs.hpp index 706c1d9ee..a50b41840 100644 --- a/components/openmw-mp/Base/BaseStructs.hpp +++ b/components/openmw-mp/Base/BaseStructs.hpp @@ -161,6 +161,8 @@ namespace mwmp { std::string id; bool isStackingSpell; + int timestampDay; + double timestampHour; ESM::ActiveSpells::ActiveSpellParams params; }; diff --git a/components/openmw-mp/Packets/Actor/PacketActorSpellsActive.cpp b/components/openmw-mp/Packets/Actor/PacketActorSpellsActive.cpp index 5b823ebd5..adbf99bf4 100644 --- a/components/openmw-mp/Packets/Actor/PacketActorSpellsActive.cpp +++ b/components/openmw-mp/Packets/Actor/PacketActorSpellsActive.cpp @@ -30,6 +30,8 @@ void PacketActorSpellsActive::Actor(BaseActor &actor, bool send) { RW(activeSpell.id, send, true); RW(activeSpell.isStackingSpell, send); + RW(activeSpell.timestampDay, send); + RW(activeSpell.timestampHour, send); RW(activeSpell.params.mDisplayName, send, true); uint32_t effectCount; diff --git a/components/openmw-mp/Packets/Player/PacketPlayerSpellsActive.cpp b/components/openmw-mp/Packets/Player/PacketPlayerSpellsActive.cpp index 5c7e84526..194440711 100644 --- a/components/openmw-mp/Packets/Player/PacketPlayerSpellsActive.cpp +++ b/components/openmw-mp/Packets/Player/PacketPlayerSpellsActive.cpp @@ -31,6 +31,8 @@ void PacketPlayerSpellsActive::Packet(RakNet::BitStream *newBitstream, bool send { RW(activeSpell.id, send, true); RW(activeSpell.isStackingSpell, send); + RW(activeSpell.timestampDay, send); + RW(activeSpell.timestampHour, send); RW(activeSpell.params.mDisplayName, send, true); uint32_t effectCount;