From a3a341fee64843985be9c7a10c105ab65d2ac416 Mon Sep 17 00:00:00 2001 From: David Cernat Date: Sat, 10 Mar 2018 22:18:33 +0200 Subject: [PATCH 01/22] [Server] Reimplement 4ebfcc4a21051702425c9f0ad9a710814e7cca90 for 0.6 --- apps/openmw-mp/Networking.cpp | 3 ++- apps/openmw-mp/Player.cpp | 22 +++++++++++++------ apps/openmw-mp/Player.hpp | 3 ++- .../processors/player/ProcessorHandshake.hpp | 2 +- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/apps/openmw-mp/Networking.cpp b/apps/openmw-mp/Networking.cpp index 7a0339c81..9c7eb949f 100644 --- a/apps/openmw-mp/Networking.cpp +++ b/apps/openmw-mp/Networking.cpp @@ -117,7 +117,8 @@ void Networking::processPlayerPacket(RakNet::Packet *packet) if (!player->isHandshaked()) { LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Have not completed handshake with player %d", player->getId()); - //KickPlayer(player->guid); + if (player->handshakeAttempts() > 5) + kickPlayer(player->guid); return; } diff --git a/apps/openmw-mp/Player.cpp b/apps/openmw-mp/Player.cpp index 4dea0b4a6..18e51a89a 100644 --- a/apps/openmw-mp/Player.cpp +++ b/apps/openmw-mp/Player.cpp @@ -8,6 +8,8 @@ TPlayers Players::players; TSlots Players::slots; +using namespace std; + void Players::deletePlayer(RakNet::RakNetGUID guid) { LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Deleting player with guid %lu", guid.g); @@ -69,7 +71,7 @@ unsigned short Players::getLastPlayerId() Player::Player(RakNet::RakNetGUID guid) : BasePlayer(guid) { - handshakeState = false; + handshakeCounter = 0; loadState = NOTLOADED; } @@ -88,16 +90,22 @@ void Player::setId(unsigned short id) this->id = id; } -void Player::setHandshake() -{ - handshakeState = true; -} - bool Player::isHandshaked() { - return handshakeState; + return handshakeCounter == numeric_limits::max(); } +void Player::setHandshake() +{ + handshakeCounter = numeric_limits::max(); +} + +int Player::handshakeAttempts() +{ + return handshakeCounter++; +} + + void Player::setLoadState(int state) { loadState = state; diff --git a/apps/openmw-mp/Player.hpp b/apps/openmw-mp/Player.hpp index 8dfd23c0f..aa52d7574 100644 --- a/apps/openmw-mp/Player.hpp +++ b/apps/openmw-mp/Player.hpp @@ -58,6 +58,7 @@ public: void setId(unsigned short id); bool isHandshaked(); + int handshakeAttempts(); void setHandshake(); void setLoadState(int state); @@ -72,7 +73,7 @@ public: private: CellController::TContainer cells; - bool handshakeState; + int handshakeCounter; int loadState; }; diff --git a/apps/openmw/mwmp/processors/player/ProcessorHandshake.hpp b/apps/openmw/mwmp/processors/player/ProcessorHandshake.hpp index 78652b225..abbf9431c 100644 --- a/apps/openmw/mwmp/processors/player/ProcessorHandshake.hpp +++ b/apps/openmw/mwmp/processors/player/ProcessorHandshake.hpp @@ -19,7 +19,7 @@ namespace mwmp virtual void Do(PlayerPacket &packet, BasePlayer *player) { - packet.setPlayer(getLocalPlayer()); // player is 0 because myGuid will be setted after ProcessUserMyID + packet.setPlayer(getLocalPlayer()); // player is 0 because myGuid will be set after ProcessUserMyID packet.Send(serverAddr); } }; From b249162ca1bcda0bde82bf6decbfe3702734571a Mon Sep 17 00:00:00 2001 From: David Cernat Date: Sun, 11 Mar 2018 04:50:59 +0200 Subject: [PATCH 02/22] [General] Implement setting of enforced client log level in GameSettings Certain servers do not want the players to have debug information about the locations and actions of other players, so a client's log level can now be enforced by the server via the GameSettings packet. --- apps/openmw-mp/Script/Functions/Settings.cpp | 8 ++++++++ apps/openmw-mp/Script/Functions/Settings.hpp | 19 +++++++++++++++++++ apps/openmw/mwmp/LocalPlayer.cpp | 1 + .../player/ProcessorGameSettings.hpp | 15 +++++++++++++++ components/openmw-mp/Base/BasePlayer.hpp | 1 + components/openmw-mp/Log.cpp | 5 +++++ components/openmw-mp/Log.hpp | 1 + .../Packets/Player/PacketGameSettings.cpp | 3 ++- 8 files changed, 52 insertions(+), 1 deletion(-) diff --git a/apps/openmw-mp/Script/Functions/Settings.cpp b/apps/openmw-mp/Script/Functions/Settings.cpp index eaee5a56c..f021e7338 100644 --- a/apps/openmw-mp/Script/Functions/Settings.cpp +++ b/apps/openmw-mp/Script/Functions/Settings.cpp @@ -17,6 +17,14 @@ void SettingFunctions::SetDifficulty(unsigned short pid, int difficulty) player->difficulty = difficulty; } +void SettingFunctions::SetEnforcedLogLevel(unsigned short pid, int enforcedLogLevel) +{ + Player *player; + GET_PLAYER(pid, player, ); + + player->enforcedLogLevel = enforcedLogLevel; +} + void SettingFunctions::SetPhysicsFramerate(unsigned short pid, double physicsFramerate) { Player *player; diff --git a/apps/openmw-mp/Script/Functions/Settings.hpp b/apps/openmw-mp/Script/Functions/Settings.hpp index 763a5e69e..48437028d 100644 --- a/apps/openmw-mp/Script/Functions/Settings.hpp +++ b/apps/openmw-mp/Script/Functions/Settings.hpp @@ -5,6 +5,7 @@ #define SETTINGSAPI \ {"SetDifficulty", SettingFunctions::SetDifficulty},\ + {"SetEnforcedLogLevel", SettingFunctions::SetEnforcedLogLevel},\ {"SetPhysicsFramerate", SettingFunctions::SetPhysicsFramerate},\ \ {"SetConsoleAllowed", SettingFunctions::SetConsoleAllowed},\ @@ -30,6 +31,24 @@ public: */ static void SetDifficulty(unsigned short pid, int difficulty); + /** + * \brief Set the client log level enforced for a player. + * + * This changes the enforced log level for that player in the server memory, but does not by itself + * send a packet. + * + * Enforcing a certain log level is necessary to prevent players from learning information from + * their console window that they are otherwise unable to obtain, such as the locations of + * other players. + * + * If you do not wish to enforce a log level, simply set enforcedLogLevel to -1 + * + * \param pid The player ID. + * \param bool The enforced log level. + * \return void + */ + static void SetEnforcedLogLevel(unsigned short pid, int enforcedLogLevel); + /** * \brief Set the physics framerate for a player. * diff --git a/apps/openmw/mwmp/LocalPlayer.cpp b/apps/openmw/mwmp/LocalPlayer.cpp index d9d0fc665..c70b2e499 100644 --- a/apps/openmw/mwmp/LocalPlayer.cpp +++ b/apps/openmw/mwmp/LocalPlayer.cpp @@ -48,6 +48,7 @@ LocalPlayer::LocalPlayer() charGenStage.end = 1; difficulty = 0; + enforcedLogLevel = -1; physicsFramerate = 60.0; consoleAllowed = false; bedRestAllowed = true; diff --git a/apps/openmw/mwmp/processors/player/ProcessorGameSettings.hpp b/apps/openmw/mwmp/processors/player/ProcessorGameSettings.hpp index dc3027bdd..1e48d5308 100644 --- a/apps/openmw/mwmp/processors/player/ProcessorGameSettings.hpp +++ b/apps/openmw/mwmp/processors/player/ProcessorGameSettings.hpp @@ -19,8 +19,12 @@ namespace mwmp virtual void Do(PlayerPacket &packet, BasePlayer *player) { + static const int initialLogLevel = Log::GetLevel(); + if (isLocal()) { + LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Received ID_GAME_SETTINGS"); + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) { if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Console && !player->consoleAllowed) @@ -30,6 +34,17 @@ namespace mwmp MWBase::Environment::get().getWindowManager()->popGuiMode(); } + if (player->enforcedLogLevel > -1) + { + LOG_APPEND(Log::LOG_INFO, "- server is enforcing log level %i", player->enforcedLogLevel); + Log::SetLevel(player->enforcedLogLevel); + } + else if (initialLogLevel != Log::GetLevel()) + { + LOG_APPEND(Log::LOG_INFO, "- log level has been reset to initial value %i", initialLogLevel); + Log::SetLevel(initialLogLevel); + } + MWBase::Environment::get().getWorld()->setPhysicsFramerate(player->physicsFramerate); } } diff --git a/components/openmw-mp/Base/BasePlayer.hpp b/components/openmw-mp/Base/BasePlayer.hpp index 04aafb4d4..717ddd266 100644 --- a/components/openmw-mp/Base/BasePlayer.hpp +++ b/components/openmw-mp/Base/BasePlayer.hpp @@ -253,6 +253,7 @@ namespace mwmp CurrentContainer currentContainer; int difficulty; + int enforcedLogLevel; float physicsFramerate; bool consoleAllowed; bool bedRestAllowed; diff --git a/components/openmw-mp/Log.cpp b/components/openmw-mp/Log.cpp index 0ed414337..6efbd56f8 100644 --- a/components/openmw-mp/Log.cpp +++ b/components/openmw-mp/Log.cpp @@ -41,6 +41,11 @@ const Log &Log::Get() return *sLog; } +int Log::GetLevel() +{ + return sLog->logLevel; +} + void Log::SetLevel(int level) { sLog->logLevel = level; diff --git a/components/openmw-mp/Log.hpp b/components/openmw-mp/Log.hpp index 7483a3663..58b296c7f 100644 --- a/components/openmw-mp/Log.hpp +++ b/components/openmw-mp/Log.hpp @@ -44,6 +44,7 @@ public: static void Create(int logLevel); static void Delete(); static const Log &Get(); + static int GetLevel(); static void SetLevel(int level); void print(int level, bool hasPrefix, const char *file, int line, const char *message, ...) const; diff --git a/components/openmw-mp/Packets/Player/PacketGameSettings.cpp b/components/openmw-mp/Packets/Player/PacketGameSettings.cpp index 24c275e71..e83031322 100644 --- a/components/openmw-mp/Packets/Player/PacketGameSettings.cpp +++ b/components/openmw-mp/Packets/Player/PacketGameSettings.cpp @@ -14,9 +14,10 @@ void PacketGameSettings::Packet(RakNet::BitStream *bs, bool send) PlayerPacket::Packet(bs, send); RW(player->difficulty, send); - RW(player->physicsFramerate, send); RW(player->consoleAllowed, send); RW(player->bedRestAllowed, send); RW(player->wildernessRestAllowed, send); RW(player->waitAllowed, send); + RW(player->enforcedLogLevel, send); + RW(player->physicsFramerate, send); } From e0aa5e8e79fc1c717f87bc3871bb44888fd9df96 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 11 Mar 2018 16:19:37 +0400 Subject: [PATCH 03/22] Prevent overlapping for journal and books scrolling --- apps/openmw/mwbase/windowmanager.hpp | 2 +- apps/openmw/mwgui/bookwindow.cpp | 4 ++-- apps/openmw/mwgui/journalwindow.cpp | 4 ++-- apps/openmw/mwgui/windowmanagerimp.cpp | 9 +++++++-- apps/openmw/mwgui/windowmanagerimp.hpp | 2 +- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index d454067c8..8a9eb2ea6 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -337,7 +337,7 @@ namespace MWBase /// Cycle to next or previous weapon virtual void cycleWeapon(bool next) = 0; - virtual void playSound(const std::string& soundId, float volume = 1.f, float pitch = 1.f) = 0; + virtual void playSound(const std::string& soundId, bool preventOverlapping = false, float volume = 1.f, float pitch = 1.f) = 0; // In WindowManager for now since there isn't a VFS singleton virtual std::string correctIconPath(const std::string& path) = 0; diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index c18548dad..cb902cadf 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -200,7 +200,7 @@ namespace MWGui { if ((mCurrentPage+1)*2 < mPages.size()) { - MWBase::Environment::get().getWindowManager()->playSound("book page2"); + MWBase::Environment::get().getWindowManager()->playSound("book page2", true); ++mCurrentPage; @@ -211,7 +211,7 @@ namespace MWGui { if (mCurrentPage > 0) { - MWBase::Environment::get().getWindowManager()->playSound("book page"); + MWBase::Environment::get().getWindowManager()->playSound("book page", true); --mCurrentPage; diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 4b7a789e8..0776706d8 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -616,7 +616,7 @@ namespace if (page+2 < book->pageCount()) { - MWBase::Environment::get().getWindowManager()->playSound("book page"); + MWBase::Environment::get().getWindowManager()->playSound("book page", true); page += 2; updateShowingPages (); @@ -634,7 +634,7 @@ namespace if(page >= 2) { - MWBase::Environment::get().getWindowManager()->playSound("book page"); + MWBase::Environment::get().getWindowManager()->playSound("book page", true); page -= 2; updateShowingPages (); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 300f8e39f..0bb5e4137 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1916,11 +1916,16 @@ namespace MWGui mInventoryWindow->cycle(next); } - void WindowManager::playSound(const std::string& soundId, float volume, float pitch) + void WindowManager::playSound(const std::string& soundId, bool preventOverlapping, float volume, float pitch) { if (soundId.empty()) return; - MWBase::Environment::get().getSoundManager()->playSound(soundId, volume, pitch, MWSound::Type::Sfx, MWSound::PlayMode::NoEnv); + + MWBase::SoundManager *sndmgr = MWBase::Environment::get().getSoundManager(); + if (preventOverlapping && sndmgr->getSoundPlaying(MWWorld::Ptr(), soundId)) + return; + + sndmgr->playSound(soundId, volume, pitch, MWSound::Type::Sfx, MWSound::PlayMode::NoEnv); } void WindowManager::updateSpellWindow() diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 1d250f6d4..56ccea33b 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -366,7 +366,7 @@ namespace MWGui /// Cycle to next or previous weapon virtual void cycleWeapon(bool next); - virtual void playSound(const std::string& soundId, float volume = 1.f, float pitch = 1.f); + virtual void playSound(const std::string& soundId, bool preventOverlapping = false, float volume = 1.f, float pitch = 1.f); // In WindowManager for now since there isn't a VFS singleton virtual std::string correctIconPath(const std::string& path); From e4531a6910fd73ad281714ee454395836d40cbd9 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 12 Mar 2018 19:05:57 +0300 Subject: [PATCH 04/22] Use middle gray instead of pure black as default fallback color (Fixes #2841) --- components/fallback/fallback.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/fallback/fallback.cpp b/components/fallback/fallback.cpp index 11a577a45..f33509ec9 100644 --- a/components/fallback/fallback.cpp +++ b/components/fallback/fallback.cpp @@ -53,7 +53,7 @@ namespace Fallback { std::string sum=getFallbackString(fall); if(sum.empty()) - return osg::Vec4f(0.f,0.f,0.f,1.f); + return osg::Vec4f(0.5f,0.5f,0.5f,1.f); else { std::string ret[3]; From 20caea083aa9af84ae723be15eae7151dab7614f Mon Sep 17 00:00:00 2001 From: David Cernat Date: Mon, 12 Mar 2018 23:31:37 +0200 Subject: [PATCH 05/22] [Client] Use correct count for items in equipment packets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, throwing weapon sync was completely broken for players, as the count for their equipped throwing weapons was never set and – as a result – defaulted to a count of 1 on other clients. As a result, any time a player threw a dart, they would then appear as having switched to hand-to-hand for other players. Moreover, the count of equipped items was mistakenly based on the total count of items with that refId in the inventory. As a result, if – for example – I equipped 1 Daedric Longsword and had 4 others in my inventory, my DedicatedPlayer on other clients would equip a Daedric Longsword with a count of 5. If I was overencumbered by having that many Daedric Longswords on me and then dropped 4 of them, allowing myself to move again, my DedicatedPlayer would still walk around with 5 Daedric Longswords and lack animations due to still being overencumbered on the other clients. These problems were less prevalent for actors, but their equipment updating code has also been changed to match that of players. --- apps/openmw/mwmechanics/actors.cpp | 2 +- apps/openmw/mwmp/LocalActor.cpp | 15 ++++----------- apps/openmw/mwmp/LocalPlayer.cpp | 12 +----------- 3 files changed, 6 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 26efa0a99..e1030c4a4 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -964,7 +964,7 @@ namespace MWMechanics Start of tes3mp change (major) We need DedicatedPlayers and DedicatedActors to not automatically - equip their light-emitting items, so additions conditions have been + equip their light-emitting items, so additional conditions have been added for them */ if (!isPlayer && !mwmp::PlayerList::isDedicatedPlayer(ptr) && !mwmp::Main::get().getCellController()->isDedicatedActor(ptr)) diff --git a/apps/openmw/mwmp/LocalActor.cpp b/apps/openmw/mwmp/LocalActor.cpp index 7c4143517..ac474776e 100644 --- a/apps/openmw/mwmp/LocalActor.cpp +++ b/apps/openmw/mwmp/LocalActor.cpp @@ -193,8 +193,8 @@ void LocalActor::updateEquipment(bool forceUpdate) MWWorld::InventoryStore &invStore = ptr.getClass().getInventoryStore(ptr); for (int slot = 0; slot < MWWorld::InventoryStore::Slots; slot++) { - MWWorld::ContainerStoreIterator it = invStore.getSlot(slot); auto &item = equipedItems[slot]; + MWWorld::ContainerStoreIterator it = invStore.getSlot(slot); if (it != invStore.end()) { @@ -205,16 +205,8 @@ void LocalActor::updateEquipment(bool forceUpdate) item.refId = cellRef.getRefId(); item.charge = cellRef.getCharge(); - if (slot == MWWorld::InventoryStore::Slot_CarriedRight) - { - MWMechanics::WeaponType weaptype; - auto &_class = ptr.getClass(); - MWMechanics::getActiveWeapon(_class.getCreatureStats(ptr), _class.getInventoryStore(ptr), &weaptype); - if (weaptype != MWMechanics::WeapType_Thrown) - item.count = 1; - } - else - item.count = invStore.count(cellRef.getRefId()); + item.enchantmentCharge = it->getCellRef().getEnchantmentCharge(); + item.count = it->getRefData().getCount(); } } else if (!item.refId.empty()) @@ -223,6 +215,7 @@ void LocalActor::updateEquipment(bool forceUpdate) item.refId = ""; item.count = 0; item.charge = 0; + item.enchantmentCharge = -1; } } diff --git a/apps/openmw/mwmp/LocalPlayer.cpp b/apps/openmw/mwmp/LocalPlayer.cpp index c70b2e499..28439a773 100644 --- a/apps/openmw/mwmp/LocalPlayer.cpp +++ b/apps/openmw/mwmp/LocalPlayer.cpp @@ -471,17 +471,7 @@ void LocalPlayer::updateEquipment(bool forceUpdate) item.refId = it->getCellRef().getRefId(); item.charge = it->getCellRef().getCharge(); item.enchantmentCharge = it->getCellRef().getEnchantmentCharge(); - - if (slot == MWWorld::InventoryStore::Slot_CarriedRight) - { - MWMechanics::WeaponType weaptype; - MWMechanics::getActiveWeapon(ptrPlayer.getClass().getCreatureStats(ptrPlayer), - ptrPlayer.getClass().getInventoryStore(ptrPlayer), &weaptype); - if (weaptype != MWMechanics::WeapType_Thrown) - item.count = 1; - } - else - item.count = invStore.count(it->getCellRef().getRefId()); + item.count = it->getRefData().getCount(); } } else if (!item.refId.empty()) From 2f9b6b536bbcee59dcc5a579cfd68054c0a54cc3 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 20 Aug 2017 22:30:10 +0300 Subject: [PATCH 06/22] Label gtest directories as system To hide all warnings when use custom GTEST_ROOT. --- apps/openmw_test_suite/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw_test_suite/CMakeLists.txt b/apps/openmw_test_suite/CMakeLists.txt index 9b09bc41f..48c8be4d8 100644 --- a/apps/openmw_test_suite/CMakeLists.txt +++ b/apps/openmw_test_suite/CMakeLists.txt @@ -1,7 +1,7 @@ find_package(GTest REQUIRED) if (GTEST_FOUND) - include_directories(${GTEST_INCLUDE_DIRS}) + include_directories(SYSTEM ${GTEST_INCLUDE_DIRS}) file(GLOB UNITTEST_SRC_FILES ../openmw/mwworld/store.cpp From 26df0e6ebd4d08b6780d8dd0b85e837706b980ef Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 5 Aug 2017 15:39:00 +0300 Subject: [PATCH 07/22] Remove duplicated include --- apps/openmw/mwphysics/physicssystem.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 174560e6b..f3c34bc4e 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include From a26483ab26d3cf3ebf87203063acab10afafec7d Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 10 Mar 2018 22:18:47 +0300 Subject: [PATCH 08/22] Fix memory leak There is no delete for TextFormat objects in PageDisplay destructor. --- apps/openmw/mwgui/bookpage.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index 5a9237cea..6f16cf076 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -892,7 +892,7 @@ protected: public: typedef TypesetBookImpl::StyleImpl Style; - typedef std::map ActiveTextFormats; + typedef std::map > ActiveTextFormats; int mViewTop; int mViewBottom; @@ -1048,7 +1048,7 @@ public: { if (mNode != NULL) i->second->destroyDrawItem (mNode); - delete i->second; + i->second.reset(); } mActiveTextFormats.clear (); @@ -1115,11 +1115,11 @@ public: if (j == this_->mActiveTextFormats.end ()) { - TextFormat * textFormat = new TextFormat (Font, this_); + std::unique_ptr textFormat(new TextFormat (Font, this_)); textFormat->mTexture = Font->getTextureFont (); - j = this_->mActiveTextFormats.insert (std::make_pair (Font, textFormat)).first; + j = this_->mActiveTextFormats.insert (std::make_pair (Font, std::move(textFormat))).first; } j->second->mCountVertex += run.mPrintableChars * 6; From 002ad9ae1b85e98086b82004bb1a62fb91614619 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 13 Mar 2018 23:59:01 +0300 Subject: [PATCH 09/22] Print a warning in case a fallback value wasn't found --- components/fallback/fallback.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/components/fallback/fallback.cpp b/components/fallback/fallback.cpp index f33509ec9..ce6cba313 100644 --- a/components/fallback/fallback.cpp +++ b/components/fallback/fallback.cpp @@ -1,5 +1,7 @@ #include "fallback.hpp" +#include + #include @@ -17,6 +19,7 @@ namespace Fallback std::map::const_iterator it; if((it = mFallbackMap.find(fall)) == mFallbackMap.end()) { + std::cerr << "Warning: fallback value " << fall << " not found." << std::endl; return ""; } return it->second; @@ -25,7 +28,7 @@ namespace Fallback float Map::getFallbackFloat(const std::string& fall) const { std::string fallback=getFallbackString(fall); - if(fallback.empty()) + if (fallback.empty()) return 0; else return boost::lexical_cast(fallback); @@ -34,7 +37,7 @@ namespace Fallback int Map::getFallbackInt(const std::string& fall) const { std::string fallback=getFallbackString(fall); - if(fallback.empty()) + if (fallback.empty()) return 0; else return std::stoi(fallback); @@ -43,7 +46,7 @@ namespace Fallback bool Map::getFallbackBool(const std::string& fall) const { std::string fallback=getFallbackString(fall); - if(fallback.empty()) + if (fallback.empty()) return false; else return stob(fallback); @@ -52,7 +55,7 @@ namespace Fallback osg::Vec4f Map::getFallbackColour(const std::string& fall) const { std::string sum=getFallbackString(fall); - if(sum.empty()) + if (sum.empty()) return osg::Vec4f(0.5f,0.5f,0.5f,1.f); else { From 5b49248d6e93dc6de755a28414998be47467a9ae Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 18 Mar 2018 17:24:27 +0300 Subject: [PATCH 10/22] Restrict opening doors to bipedal actors (Fixes #4313) --- apps/openmw/mwmechanics/aipackage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 398e84448..38f641b94 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -189,7 +189,7 @@ void MWMechanics::AiPackage::evadeObstacles(const MWWorld::Ptr& actor, float dur static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance(); MWWorld::Ptr door = getNearbyDoor(actor, distance); - if (door != MWWorld::Ptr()) + if (door != MWWorld::Ptr() && actor.getClass().isBipedal(actor)) { // note: AiWander currently does not open doors if (getTypeId() != TypeIdWander && !door.getCellRef().getTeleport() && door.getClass().getDoorState(door) == 0) @@ -224,7 +224,7 @@ void MWMechanics::AiPackage::evadeObstacles(const MWWorld::Ptr& actor, float dur MWBase::Environment::get().getWorld()->activate(door, actor); } } - else // any other obstacle (NPC, crate, etc.) + else { mObstacleCheck.takeEvasiveAction(movement); } From d626d89c494d26c8c952af0a745ab78876edf627 Mon Sep 17 00:00:00 2001 From: RoadTrain Date: Mon, 19 Mar 2018 00:50:50 +0300 Subject: [PATCH 11/22] OpenMW-CS: Fix a typo in startup warning. --- apps/opencs/view/doc/startup.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/doc/startup.cpp b/apps/opencs/view/doc/startup.cpp index 67ff50dab..78f18b3a1 100644 --- a/apps/opencs/view/doc/startup.cpp +++ b/apps/opencs/view/doc/startup.cpp @@ -106,7 +106,7 @@ CSVDoc::StartupDialogue::StartupDialogue() : mWidth (0), mColumn (2) /// \todo remove this label once we are feature complete and convinced that this thing is /// working properly. - QLabel *warning = new QLabel ("WARNING: OpenMW-CS is in alpha stage.

The editor is not feature complete and not sufficiently tested.
In theory your data should be safe. But we strongly advice to make backups regularly if you are working with live data.
"); + QLabel *warning = new QLabel ("WARNING: OpenMW-CS is in alpha stage.

The editor is not feature complete and not sufficiently tested.
In theory your data should be safe. But we strongly advise to make backups regularly if you are working with live data.
"); QFont font; font.setPointSize (12); From bd6c7de5793b86c1f86b8af155792607a7e14b7f Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 3 Mar 2018 14:16:02 +0400 Subject: [PATCH 12/22] Do not unequip two-handed weapon when equipping torch --- apps/openmw/mwclass/light.cpp | 18 ------------------ apps/openmw/mwmechanics/actors.cpp | 7 +------ 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index f9056b75d..aeeb89a72 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -233,24 +233,6 @@ namespace MWClass if (!(ref->mBase->mData.mFlags & ESM::Light::Carry)) return std::make_pair(0,""); - const MWWorld::InventoryStore& invStore = npc.getClass().getInventoryStore(npc); - MWWorld::ConstContainerStoreIterator weapon = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); - - if(weapon == invStore.end()) - return std::make_pair(1,""); - - /// \todo the 2h check is repeated many times; put it in a function - if(weapon->getTypeName() == typeid(ESM::Weapon).name() && - (weapon->get()->mBase->mData.mType == ESM::Weapon::LongBladeTwoHand || - weapon->get()->mBase->mData.mType == ESM::Weapon::BluntTwoClose || - weapon->get()->mBase->mData.mType == ESM::Weapon::BluntTwoWide || - weapon->get()->mBase->mData.mType == ESM::Weapon::SpearTwoWide || - weapon->get()->mBase->mData.mType == ESM::Weapon::AxeTwoHand || - weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanBow || - weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)) - { - return std::make_pair(3,""); - } return std::make_pair(1,""); } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 7a1e7270d..7fd1e6447 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -931,16 +931,11 @@ namespace MWMechanics // For non-hostile NPCs, unequip whatever is in the left slot in favor of a light. if (heldIter != inventoryStore.end() && heldIter->getTypeName() != typeid(ESM::Light).name()) inventoryStore.unequipItem(*heldIter, ptr); - - // Also unequip twohanded weapons which conflict with anything in CarriedLeft - if (torch->getClass().canBeEquipped(*torch, ptr).first == 3) - inventoryStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, ptr); } heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - // If we have a torch and can equip it (left slot free, no - // twohanded weapon in right slot), then equip it now. + // If we have a torch and can equip it, then equip it now. if (heldIter == inventoryStore.end() && torch->getClass().canBeEquipped(*torch, ptr).first == 1) { From a0a30cdbf5b9920b613510f01437cb6c25cea57a Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 3 Mar 2018 17:18:40 +0400 Subject: [PATCH 13/22] AI: hide torches during bad weather (bug #4334) --- apps/openmw/mwbase/world.hpp | 4 ++-- apps/openmw/mwmechanics/actors.cpp | 9 ++++++--- apps/openmw/mwmechanics/actors.hpp | 2 +- apps/openmw/mwworld/weather.cpp | 13 +++++++++---- apps/openmw/mwworld/weather.hpp | 4 ++-- apps/openmw/mwworld/worldimp.cpp | 6 ++++-- apps/openmw/mwworld/worldimp.hpp | 7 +++---- 7 files changed, 27 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 038535939..d0c329361 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -495,8 +495,8 @@ namespace MWBase virtual void breakInvisibility (const MWWorld::Ptr& actor) = 0; - // Are we in an exterior or pseudo-exterior cell and it's night? - virtual bool isDark() const = 0; + // Allow NPCs to use torches? + virtual bool useTorches() const = 0; virtual bool findInteriorPositionInWorldSpace(const MWWorld::CellStore* cell, osg::Vec3f& result) = 0; diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 7fd1e6447..ba90752fb 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -900,7 +900,7 @@ namespace MWMechanics stats.setTimeToStartDrowning(fHoldBreathTime); } - void Actors::updateEquippedLight (const MWWorld::Ptr& ptr, float duration) + void Actors::updateEquippedLight (const MWWorld::Ptr& ptr, float duration, bool mayEquip) { bool isPlayer = (ptr == getPlayer()); @@ -922,7 +922,7 @@ namespace MWMechanics } } - if (MWBase::Environment::get().getWorld()->isDark()) + if (mayEquip) { if (torch != inventoryStore.end()) { @@ -1199,6 +1199,9 @@ namespace MWMechanics if (mTimerDisposeSummonsCorpses >= 0.2f) mTimerDisposeSummonsCorpses = 0; if (timerUpdateEquippedLight >= updateEquippedLightInterval) timerUpdateEquippedLight = 0; + // show torches only when there are darkness and no precipitations + bool showTorches = MWBase::Environment::get().getWorld()->useTorches(); + MWWorld::Ptr player = getPlayer(); /// \todo move update logic to Actor class where appropriate @@ -1297,7 +1300,7 @@ namespace MWMechanics updateNpc(iter->first, duration); if (timerUpdateEquippedLight == 0) - updateEquippedLight(iter->first, updateEquippedLightInterval); + updateEquippedLight(iter->first, updateEquippedLightInterval, showTorches); } } } diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 13641abf4..18046aa5d 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -39,7 +39,7 @@ namespace MWMechanics void updateDrowning (const MWWorld::Ptr& ptr, float duration); - void updateEquippedLight (const MWWorld::Ptr& ptr, float duration); + void updateEquippedLight (const MWWorld::Ptr& ptr, float duration, bool mayEquip); void updateCrimePersuit (const MWWorld::Ptr& ptr, float duration); diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 2f0a2f8cf..e9f07bf77 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -520,6 +520,7 @@ WeatherManager::WeatherManager(MWRender::RenderingManager& rendering, const Fall , mSecunda("Secunda", fallback) , mWindSpeed(0.f) , mIsStorm(false) + , mPrecipitation(false) , mStormDirection(0,1,0) , mCurrentRegion() , mTimePassed(0) @@ -660,6 +661,10 @@ void WeatherManager::update(float duration, bool paused) mWindSpeed = mResult.mWindSpeed; mIsStorm = mResult.mIsStorm; + // For some reason Ash Storm is not considered as a precipitation weather in game + mPrecipitation = !(mResult.mParticleEffect.empty() && mResult.mRainEffect.empty()) + && mResult.mParticleEffect != "meshes\\ashcloud.nif"; + if (mIsStorm) { osg::Vec3f playerPos (player.getRefData().getPosition().asVec3()); @@ -777,12 +782,12 @@ unsigned int WeatherManager::getWeatherID() const return mCurrentWeather; } -bool WeatherManager::isDark() const +bool WeatherManager::useTorches() const { TimeStamp time = MWBase::Environment::get().getWorld()->getTimeStamp(); - bool exterior = (MWBase::Environment::get().getWorld()->isCellExterior() - || MWBase::Environment::get().getWorld()->isCellQuasiExterior()); - return exterior && (time.getHour() < mSunriseTime || time.getHour() > mTimeSettings.mNightStart - 1); + bool isDark = time.getHour() < mSunriseTime || time.getHour() > mTimeSettings.mNightStart - 1; + + return isDark && !mPrecipitation; } void WeatherManager::write(ESM::ESMWriter& writer, Loading::Listener& progress) diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 84a6c5105..f023044ef 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -240,8 +240,7 @@ namespace MWWorld unsigned int getWeatherID() const; - /// @see World::isDark - bool isDark() const; + bool useTorches() const; void write(ESM::ESMWriter& writer, Loading::Listener& progress); @@ -275,6 +274,7 @@ namespace MWWorld float mWindSpeed; bool mIsStorm; + bool mPrecipitation; osg::Vec3f mStormDirection; std::string mCurrentRegion; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 7786b6823..8e4030382 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2847,11 +2847,13 @@ namespace MWWorld MWBase::Environment::get().getMechanicsManager()->updateMagicEffects(actor); } - bool World::isDark() const + bool World::useTorches() const { + // If we are in exterior, check the weather manager. + // In interiors there are no precipitations and sun, so check the ambient MWWorld::CellStore* cell = mPlayer->getPlayer().getCell(); if (cell->isExterior()) - return mWeatherManager->isDark(); + return mWeatherManager->useTorches(); else { uint32_t ambient = cell->getCell()->mAmbi.mAmbient; diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 02b3756d5..3115dcdb9 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -604,12 +604,11 @@ namespace MWWorld void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) override; - const std::vector& getContentFiles() const override; - void breakInvisibility (const MWWorld::Ptr& actor) override; - // Are we in an exterior or pseudo-exterior cell and it's night? - bool isDark() const override; + + // Allow NPCs to use torches? + bool useTorches() const override; bool findInteriorPositionInWorldSpace(const MWWorld::CellStore* cell, osg::Vec3f& result) override; From 53ef3456808476a69ee26518f08225e80fd0cd9f Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 17 Mar 2018 13:41:13 +0400 Subject: [PATCH 14/22] Update magic effect particles after building new animation (bug #2254) --- apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwmechanics/spellcasting.cpp | 22 ++++++++++++++++++++-- apps/openmw/mwmechanics/spellcasting.hpp | 17 +++++++++++++++++ apps/openmw/mwworld/scene.cpp | 3 +++ apps/openmw/mwworld/worldimp.cpp | 15 +++++++++++++++ apps/openmw/mwworld/worldimp.hpp | 1 + 6 files changed, 58 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 038535939..dc11189d1 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -491,6 +491,8 @@ namespace MWBase virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) = 0; + virtual void applyLoopingParticles(const MWWorld::Ptr& ptr) = 0; + virtual const std::vector& getContentFiles() const = 0; virtual void breakInvisibility (const MWWorld::Ptr& actor) = 0; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index d864dc619..99b46db10 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -24,7 +24,6 @@ #include "../mwrender/animation.hpp" -#include "magiceffects.hpp" #include "npcstats.hpp" #include "actorutil.hpp" #include "aifollow.hpp" @@ -609,7 +608,6 @@ namespace MWMechanics std::string texture = magicEffect->mParticle; - // TODO: VFX are no longer active after saving/reloading the game bool loop = (magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx) != 0; // Note: in case of non actor, a free effect should be fine as well MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target); @@ -1314,4 +1312,24 @@ namespace MWMechanics return MWBase::Environment::get().getWorld()->getStore().get().find(it->second)->getString(); } + void ApplyLoopingParticlesVisitor::visit (MWMechanics::EffectKey key, + const std::string& /*sourceName*/, const std::string& /*sourceId*/, int /*casterActorId*/, + float /*magnitude*/, float /*remainingTime*/, float /*totalTime*/) + { + const ESM::MagicEffect *magicEffect = + MWBase::Environment::get().getWorld()->getStore().get().find(key.mId); + + const ESM::Static* castStatic; + if (!magicEffect->mHit.empty()) + castStatic = MWBase::Environment::get().getWorld()->getStore().get().find (magicEffect->mHit); + else + castStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_DefaultHit"); + + std::string texture = magicEffect->mParticle; + + bool loop = (magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx) != 0; + MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(mActor); + if (anim && loop) + anim->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, "", texture); + } } diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 1f5ef45bd..07c5b8477 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -6,6 +6,8 @@ #include "../mwworld/ptr.hpp" +#include "magiceffects.hpp" + namespace ESM { struct Spell; @@ -119,6 +121,21 @@ namespace MWMechanics bool applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const MWMechanics::EffectKey& effect, float magnitude); }; + class ApplyLoopingParticlesVisitor : public EffectSourceVisitor + { + private: + MWWorld::Ptr mActor; + + public: + ApplyLoopingParticlesVisitor(const MWWorld::Ptr& actor) + : mActor(actor) + { + } + + virtual void visit (MWMechanics::EffectKey key, + const std::string& /*sourceName*/, const std::string& /*sourceId*/, int /*casterActorId*/, + float /*magnitude*/, float /*remainingTime*/ = -1, float /*totalTime*/ = -1); + }; } #endif diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 0a27cf257..4b844f464 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -79,6 +79,9 @@ namespace if (ptr.getClass().isActor()) rendering.addWaterRippleEmitter(ptr); + + // Restore effect particles + MWBase::Environment::get().getWorld()->applyLoopingParticles(ptr); } void updateObjectRotation (const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 7786b6823..1ddeac903 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2244,6 +2244,8 @@ namespace MWWorld model = Misc::ResourceHelpers::correctActorModelPath(model, mResourceSystem->getVFS()); mPhysics->remove(getPlayerPtr()); mPhysics->addActor(getPlayerPtr(), model); + + applyLoopingParticles(player); } int World::canRest () @@ -2831,6 +2833,19 @@ namespace MWWorld mProjectileManager->launchMagicBolt(spellId, caster, fallbackDirection); } + void World::applyLoopingParticles(const MWWorld::Ptr& ptr) + { + const MWWorld::Class &cls = ptr.getClass(); + if (cls.isActor()) + { + MWMechanics::ApplyLoopingParticlesVisitor visitor(ptr); + cls.getCreatureStats(ptr).getActiveSpells().visitEffectSources(visitor); + cls.getCreatureStats(ptr).getSpells().visitEffectSources(visitor); + if (cls.hasInventoryStore(ptr)) + cls.getInventoryStore(ptr).visitEffectSources(visitor); + } + } + const std::vector& World::getContentFiles() const { return mContentFiles; diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 02b3756d5..6c44a62e7 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -604,6 +604,7 @@ namespace MWWorld void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) override; + void applyLoopingParticles(const MWWorld::Ptr& ptr); const std::vector& getContentFiles() const override; From fed3e56fc1cd1a37c856dbb130d70d344381a4ef Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 19 Mar 2018 23:08:15 +0400 Subject: [PATCH 15/22] Weather manager: get rid of World dependency --- apps/openmw/mwworld/weather.cpp | 18 +++++------------- apps/openmw/mwworld/weather.hpp | 6 +++--- apps/openmw/mwworld/worldimp.cpp | 14 +++++++++++--- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index e9f07bf77..cf6cb1df8 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -9,7 +9,6 @@ #include #include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwmechanics/actorutil.hpp" @@ -609,14 +608,11 @@ void WeatherManager::modRegion(const std::string& regionID, const std::vectorisCellExterior() || world->isCellQuasiExterior()) { - std::string playerRegion = Misc::StringUtils::lowerCase(world->getPlayerPtr().getCell()->getCell()->mRegion); std::map::iterator it = mRegions.find(playerRegion); if(it != mRegions.end() && playerRegion != mCurrentRegion) { @@ -626,11 +622,9 @@ void WeatherManager::playerTeleported() } } -void WeatherManager::update(float duration, bool paused) +void WeatherManager::update(float duration, bool paused, const TimeStamp& time, bool isExterior) { MWWorld::ConstPtr player = MWMechanics::getPlayer(); - MWBase::World& world = *MWBase::Environment::get().getWorld(); - TimeStamp time = world.getTimeStamp(); if(!paused || mFastForward) { @@ -648,8 +642,7 @@ void WeatherManager::update(float duration, bool paused) updateWeatherTransitions(duration); } - const bool exterior = (world.isCellExterior() || world.isCellQuasiExterior()); - if(!exterior) + if(!isExterior) { mRendering.setSkyEnabled(false); stopSounds(); @@ -782,10 +775,9 @@ unsigned int WeatherManager::getWeatherID() const return mCurrentWeather; } -bool WeatherManager::useTorches() const +bool WeatherManager::useTorches(float hour) const { - TimeStamp time = MWBase::Environment::get().getWorld()->getTimeStamp(); - bool isDark = time.getHour() < mSunriseTime || time.getHour() > mTimeSettings.mNightStart - 1; + bool isDark = hour < mSunriseTime || hour > mTimeSettings.mNightStart - 1; return isDark && !mPrecipitation; } diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index f023044ef..540c2f15d 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -218,14 +218,14 @@ namespace MWWorld */ void changeWeather(const std::string& regionID, const unsigned int weatherID); void modRegion(const std::string& regionID, const std::vector& chances); - void playerTeleported(); + void playerTeleported(const std::string& playerRegion, bool isExterior); /** * Per-frame update * @param duration * @param paused */ - void update(float duration, bool paused = false); + void update(float duration, bool paused, const TimeStamp& time, bool isExterior); void stopSounds(); @@ -240,7 +240,7 @@ namespace MWWorld unsigned int getWeatherID() const; - bool useTorches() const; + bool useTorches(float hour) const; void write(ESM::ESMWriter& writer, Loading::Listener& progress); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8e4030382..ed653fd5e 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2851,9 +2851,13 @@ namespace MWWorld { // If we are in exterior, check the weather manager. // In interiors there are no precipitations and sun, so check the ambient + // Looks like pseudo-exteriors considered as interiors in this case MWWorld::CellStore* cell = mPlayer->getPlayer().getCell(); if (cell->isExterior()) - return mWeatherManager->useTorches(); + { + float hour = getTimeStamp().getHour(); + return mWeatherManager->useTorches(hour); + } else { uint32_t ambient = cell->getCell()->mAmbi.mAmbient; @@ -3014,13 +3018,17 @@ namespace MWWorld void World::updateWeather(float duration, bool paused) { + bool isExterior = isCellExterior() || isCellQuasiExterior(); if (mPlayer->wasTeleported()) { mPlayer->setTeleported(false); - mWeatherManager->playerTeleported(); + + const std::string playerRegion = Misc::StringUtils::lowerCase(getPlayerPtr().getCell()->getCell()->mRegion); + mWeatherManager->playerTeleported(playerRegion, isExterior); } - mWeatherManager->update(duration, paused); + const TimeStamp time = getTimeStamp(); + mWeatherManager->update(duration, paused, time, isExterior); } struct AddDetectedReferenceVisitor From 14f0299322fe58d3079edb6c58c119b09e2d4e6a Mon Sep 17 00:00:00 2001 From: David Cernat Date: Tue, 20 Mar 2018 06:41:04 +0200 Subject: [PATCH 16/22] [Client] Don't log InputBox inputs for client --- apps/openmw/mwmp/GUIController.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/openmw/mwmp/GUIController.cpp b/apps/openmw/mwmp/GUIController.cpp index 37e358388..2d5fb1a97 100644 --- a/apps/openmw/mwmp/GUIController.cpp +++ b/apps/openmw/mwmp/GUIController.cpp @@ -175,7 +175,6 @@ void mwmp::GUIController::showInputBox(const BasePlayer::GUIMessageBox &guiMessa void mwmp::GUIController::onInputBoxDone(MWGui::WindowBase *parWindow) { //MWBase::WindowManager *windowManager = MWBase::Environment::get().getWindowManager(); - LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "GUIController::onInputBoxDone: %s.",mInputBox->getTextInput().c_str()); Main::get().getLocalPlayer()->guiMessageBox.data = mInputBox->getTextInput(); Main::get().getNetworking()->getPlayerPacket(ID_GUI_MESSAGEBOX)->setPlayer(Main::get().getLocalPlayer()); From bbdc30628bc0c46196d5e0236ead080b4f0634c1 Mon Sep 17 00:00:00 2001 From: David Cernat Date: Wed, 21 Mar 2018 06:08:04 +0200 Subject: [PATCH 17/22] [Client] Use the correct guid for a received WorldEvent or ActorList --- apps/openmw/mwmp/processors/ActorProcessor.cpp | 3 ++- apps/openmw/mwmp/processors/WorldProcessor.cpp | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmp/processors/ActorProcessor.cpp b/apps/openmw/mwmp/processors/ActorProcessor.cpp index 6f61d6766..42c46881c 100644 --- a/apps/openmw/mwmp/processors/ActorProcessor.cpp +++ b/apps/openmw/mwmp/processors/ActorProcessor.cpp @@ -15,6 +15,7 @@ bool ActorProcessor::Process(RakNet::Packet &packet, ActorList &actorList) { RakNet::BitStream bsIn(&packet.data[1], packet.length, false); bsIn.Read(guid); + actorList.guid = guid; ActorPacket *myPacket = Main::get().getNetworking()->getActorPacket(packet.data[0]); @@ -44,4 +45,4 @@ bool ActorProcessor::Process(RakNet::Packet &packet, ActorList &actorList) } } return false; -} \ No newline at end of file +} diff --git a/apps/openmw/mwmp/processors/WorldProcessor.cpp b/apps/openmw/mwmp/processors/WorldProcessor.cpp index a1fc95bb5..67689e55b 100644 --- a/apps/openmw/mwmp/processors/WorldProcessor.cpp +++ b/apps/openmw/mwmp/processors/WorldProcessor.cpp @@ -15,6 +15,7 @@ bool WorldProcessor::Process(RakNet::Packet &packet, WorldEvent &event) { RakNet::BitStream bsIn(&packet.data[1], packet.length, false); bsIn.Read(guid); + event.guid = guid; WorldPacket *myPacket = Main::get().getNetworking()->getWorldPacket(packet.data[0]); From 63e34a55755f6b1a063a0b2caf8819a2544ad167 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Fri, 23 Mar 2018 14:10:43 +0200 Subject: [PATCH 18/22] Typofix: updateCrimePersuit -> ...Pursuit --- apps/openmw/mwmechanics/actors.cpp | 4 ++-- apps/openmw/mwmechanics/actors.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index ba90752fb..f10fd10eb 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -992,7 +992,7 @@ namespace MWMechanics } } - void Actors::updateCrimePersuit(const MWWorld::Ptr& ptr, float duration) + void Actors::updateCrimePursuit(const MWWorld::Ptr& ptr, float duration) { MWWorld::Ptr player = getPlayer(); if (ptr != player && ptr.getClass().isNpc()) @@ -1285,7 +1285,7 @@ namespace MWMechanics } if (iter->first.getClass().isNpc() && iter->first != player) - updateCrimePersuit(iter->first, duration); + updateCrimePursuit(iter->first, duration); if (iter->first != player) { diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 18046aa5d..15f2d3dc8 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -41,7 +41,7 @@ namespace MWMechanics void updateEquippedLight (const MWWorld::Ptr& ptr, float duration, bool mayEquip); - void updateCrimePersuit (const MWWorld::Ptr& ptr, float duration); + void updateCrimePursuit (const MWWorld::Ptr& ptr, float duration); void killDeadActors (); From bb15ee921555813d55981d1d8add128903d5e92b Mon Sep 17 00:00:00 2001 From: David Cernat Date: Sat, 24 Mar 2018 09:00:15 +0200 Subject: [PATCH 19/22] [Client] Make it possible to get ContainerWindow from elsewhere in code --- apps/openmw/mwbase/windowmanager.hpp | 11 ++++++++++ apps/openmw/mwgui/windowmanagerimp.cpp | 28 ++++++++++++++++++++++---- apps/openmw/mwgui/windowmanagerimp.hpp | 22 ++++++++++++++++++++ 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 11d96f16c..2a332e94c 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -145,6 +145,17 @@ namespace MWBase virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0; virtual MWGui::TradeWindow* getTradeWindow() = 0; + /* + Start of tes3mp addition + + Make it possible to get the ContainerWindow from elsewhere + in the code + */ + virtual MWGui::ContainerWindow* getContainerWindow() = 0; + /* + End of tes3mp addition + */ + /// Make the player use an item, while updating GUI state accordingly virtual void useItem(const MWWorld::Ptr& item) = 0; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 6b7bf2e9d..20d30cdf1 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -384,10 +384,19 @@ namespace MWGui mGuiModeStates[GM_Dialogue] = GuiModeState(mDialogueWindow); mTradeWindow->eventTradeDone += MyGUI::newDelegate(mDialogueWindow, &DialogueWindow::onTradeComplete); - ContainerWindow* containerWindow = new ContainerWindow(mDragAndDrop); - mWindows.push_back(containerWindow); - trackWindow(containerWindow, "container"); - mGuiModeStates[GM_Container] = GuiModeState({containerWindow, mInventoryWindow}); + /* + Start of tes3mp change (major) + + Use a member variable (mContainerWIndow) instead of a local one so + we can access it from elsewhere + */ + mContainerWindow = new ContainerWindow(mDragAndDrop); + mWindows.push_back(mContainerWindow); + trackWindow(mContainerWindow, "container"); + mGuiModeStates[GM_Container] = GuiModeState({mContainerWindow, mInventoryWindow}); + /* + End of tes3mp change (major) + */ mHud = new HUD(mCustomMarkers, mDragAndDrop, mLocalMapRender); mWindows.push_back(mHud); @@ -1399,6 +1408,17 @@ namespace MWGui MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; } MWGui::TradeWindow* WindowManager::getTradeWindow() { return mTradeWindow; } + /* + Start of tes3mp addition + + Make it possible to get the ContainerWindow from elsewhere + in the code + */ + MWGui::ContainerWindow* WindowManager::getContainerWindow() { return mContainerWindow; } + /* + End of tes3mp addition + */ + void WindowManager::useItem(const MWWorld::Ptr &item) { if (mInventoryWindow) diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index f1fa3e7c5..0c5d93e5b 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -184,6 +184,17 @@ namespace MWGui virtual MWGui::ConfirmationDialog* getConfirmationDialog(); virtual MWGui::TradeWindow* getTradeWindow(); + /* + Start of tes3mp addition + + Make it possible to get the ContainerWindow from elsewhere + in the code + */ + virtual MWGui::ContainerWindow* getContainerWindow(); + /* + End of tes3mp addition + */ + /// Make the player use an item, while updating GUI state accordingly virtual void useItem(const MWWorld::Ptr& item); @@ -510,6 +521,17 @@ namespace MWGui DebugWindow* mDebugWindow; JailScreen* mJailScreen; + /* + Start of tes3mp addition + + Keep a pointer to the container window because of its usefulness + in multiplayer for container sync + */ + ContainerWindow* mContainerWindow; + /* + End of tes3mp addition + */ + std::vector mWindows; Translation::Storage& mTranslationDataStorage; From 9165b12d78a13a7f4f3f1cf9c67cbf9fb9f52604 Mon Sep 17 00:00:00 2001 From: David Cernat Date: Mon, 26 Mar 2018 10:01:26 +0300 Subject: [PATCH 20/22] [Client] Update inventory views when receiving inventory or equipment --- apps/openmw/mwmp/LocalPlayer.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwmp/LocalPlayer.cpp b/apps/openmw/mwmp/LocalPlayer.cpp index 28439a773..15ffd0707 100644 --- a/apps/openmw/mwmp/LocalPlayer.cpp +++ b/apps/openmw/mwmp/LocalPlayer.cpp @@ -694,6 +694,8 @@ void LocalPlayer::addItems() LOG_APPEND(Log::LOG_INFO, "- Ignored addition of invalid inventory item %s", item.refId.c_str()); } } + + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); } void LocalPlayer::addSpells() @@ -1079,6 +1081,8 @@ void LocalPlayer::setEquipment() else ptrInventory.unequipSlot(slot, ptrPlayer); } + + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updatePlayer(); } void LocalPlayer::setInventory() From 5f6ddcfc59f383d704d0883da89c9d543e662a7c Mon Sep 17 00:00:00 2001 From: David Cernat Date: Mon, 26 Mar 2018 19:27:36 +0300 Subject: [PATCH 21/22] [General] Rework container sync to prevent item duping A main priority in TES3MP development is to avoid making major changes to OpenMW code, so as to avoid merge conflicts in the future. Whenever avoiding potential conflicts seems especially difficult for the proper implementation of a particular multiplayer feature, that multiplayer feature is often put off until later or partially implemented with the intent of being revisited in the future. Container sync is the perfect example. Previously, the OpenMW code for container actions was kept exactly as it was, with clients unilaterally accepting their own container changes as per singleplayer-specific code, with only the addition that clients sent container packets every time they made a change in a container, packets which were then forwarded unquestioningly by the server to other players. This meant that two players clicking on the same item in a container at the same time both managed to take it, thus duplicating the item. Immediately after the packets were already forwarded, server scripts were able to check for incorrect changes, such as the removal of more items than should have existed in a container, but they had to send their own packets that attempted to fix what had already been accepted on the initial client and then forwarded to all clients, which was quite onerous in some scenarios, such as when a player on a slow connection immediately dropped items in the world after taking them from a container (which is why the default TES3MP serverside scripts made no attempt at sending corrective packets at all, preferring to expect the matter to be solved in a later C++ implementation). This commit fixes item duping in containers by preventing container actions from initially running on clients and by ending the automatic forwarding of container packets by the server. Instead, clients now send container packets that act as requests for container actions, and serverside scripts have to forward these requests themselves. In other words, without a matching Container event in the server's Lua scripts, players are completely unable to affect containers for themselves or for others. To forward a received Container packet, the following line must be used in a Container event in the Lua scripts: tes3mp.SendContainer(true, true) When an invalid action count is used in a container request, the serverside scripts can amend it using the following new function: tes3mp.SetReceivedContainerItemActionCount(objectIndex, itemIndex, actionCount) Thus, the serverside scripts are able to allow only container actions that are correct based on their own recorded contents for that container. The OpenMW code allowing unilateral container actions in mwgui/container.cpp is now prevented from executing. When a player's container request is returned to them, code in mwmp/WorldEvent.cpp simulates those container actions instead. --- apps/openmw-mp/Script/Functions/World.cpp | 19 +- apps/openmw-mp/Script/Functions/World.hpp | 27 ++- .../processors/world/ProcessorContainer.hpp | 6 +- apps/openmw/mwgui/container.cpp | 168 ++++++++++++------ apps/openmw/mwgui/container.hpp | 23 +++ apps/openmw/mwgui/draganddrop.cpp | 27 ++- apps/openmw/mwgui/draganddrop.hpp | 15 +- apps/openmw/mwmp/LocalActor.cpp | 2 +- apps/openmw/mwmp/WorldEvent.cpp | 142 ++++++++++++--- apps/openmw/mwmp/WorldEvent.hpp | 7 +- .../processors/world/ProcessorContainer.hpp | 4 +- components/openmw-mp/Base/BaseEvent.hpp | 9 + .../Packets/World/PacketContainer.cpp | 1 + 13 files changed, 358 insertions(+), 92 deletions(-) diff --git a/apps/openmw-mp/Script/Functions/World.cpp b/apps/openmw-mp/Script/Functions/World.cpp index 1fbbf297e..ba7dba1a5 100644 --- a/apps/openmw-mp/Script/Functions/World.cpp +++ b/apps/openmw-mp/Script/Functions/World.cpp @@ -44,6 +44,11 @@ unsigned char WorldFunctions::GetEventAction() noexcept return readEvent->action; } +unsigned char WorldFunctions::GetEventContainerSubAction() noexcept +{ + return readEvent->containerSubAction; +} + const char *WorldFunctions::GetObjectRefId(unsigned int i) noexcept { return readEvent->worldObjects.at(i).refId.c_str(); @@ -287,6 +292,11 @@ void WorldFunctions::SetContainerItemEnchantmentCharge(double enchantmentCharge) tempContainerItem.enchantmentCharge = enchantmentCharge; } +void WorldFunctions::SetReceivedContainerItemActionCount(unsigned int objectIndex, unsigned int itemIndex, int actionCount) noexcept +{ + readEvent->worldObjects.at(objectIndex).containerItems.at(itemIndex).actionCount = actionCount; +} + void WorldFunctions::AddWorldObject() noexcept { tempWorldObject.droppedByPlayer = false; @@ -382,10 +392,15 @@ void WorldFunctions::SendDoorState(bool broadcast) noexcept packet->Send(true); } -void WorldFunctions::SendContainer(bool broadcast) noexcept +void WorldFunctions::SendContainer(bool broadcast, bool useLastReadEvent) noexcept { mwmp::WorldPacket *packet = mwmp::Networking::get().getWorldPacketController()->GetPacket(ID_CONTAINER); - packet->setEvent(&writeEvent); + + if (useLastReadEvent) + packet->setEvent(readEvent); + else + packet->setEvent(&writeEvent); + packet->Send(false); if (broadcast) diff --git a/apps/openmw-mp/Script/Functions/World.hpp b/apps/openmw-mp/Script/Functions/World.hpp index 7667254a5..95d8af7cd 100644 --- a/apps/openmw-mp/Script/Functions/World.hpp +++ b/apps/openmw-mp/Script/Functions/World.hpp @@ -7,6 +7,7 @@ \ {"GetObjectChangesSize", WorldFunctions::GetObjectChangesSize},\ {"GetEventAction", WorldFunctions::GetEventAction},\ + {"GetEventContainerSubAction", WorldFunctions::GetEventContainerSubAction},\ \ {"GetObjectRefId", WorldFunctions::GetObjectRefId},\ {"GetObjectRefNumIndex", WorldFunctions::GetObjectRefNumIndex},\ @@ -59,6 +60,8 @@ {"SetContainerItemCharge", WorldFunctions::SetContainerItemCharge},\ {"SetContainerItemEnchantmentCharge", WorldFunctions::SetContainerItemEnchantmentCharge},\ \ + {"SetReceivedContainerItemActionCount", WorldFunctions::SetReceivedContainerItemActionCount},\ + \ {"AddWorldObject", WorldFunctions::AddWorldObject},\ {"AddContainerItem", WorldFunctions::AddContainerItem},\ \ @@ -112,6 +115,13 @@ public: */ static unsigned char GetEventAction() noexcept; + /** + * \brief Get the container subaction type used in the read event. + * + * \return The action type (0 for NONE, 1 for DRAG, 2 for DROP, 3 for TAKE_ALL). + */ + static unsigned char GetEventContainerSubAction() noexcept; + /** * \brief Get the refId of the object at a certain index in the read event's object changes. * @@ -544,6 +554,21 @@ public: */ static void SetContainerItemEnchantmentCharge(double enchantmentCharge) noexcept; + /** + * \brief Set the action count of the container item at a certain itemIndex in the container + * changes of the object at a certain objectIndex in the read event's object changes. + * + * When resending a received Container packet, this allows you to correct the amount of items + * removed from a container by a player when it conflicts with what other players have already + * taken. + * + * \param objectIndex The index of the object. + * \param itemIndex The index of the container item. + * \param actionCount The action count. + * \return void + */ + static void SetReceivedContainerItemActionCount(unsigned int objectIndex, unsigned int itemIndex, int actionCount) noexcept; + /** * \brief Add a copy of the server's temporary world object to the server's temporary event. * @@ -653,7 +678,7 @@ public: * * \return void */ - static void SendContainer(bool broadcast = false) noexcept; + static void SendContainer(bool broadcast = false, bool useLastReadEvent = false) noexcept; /** * \brief Send a ConsoleCommand packet. diff --git a/apps/openmw-mp/processors/world/ProcessorContainer.hpp b/apps/openmw-mp/processors/world/ProcessorContainer.hpp index 8a6dcd819..19d0247f2 100644 --- a/apps/openmw-mp/processors/world/ProcessorContainer.hpp +++ b/apps/openmw-mp/processors/world/ProcessorContainer.hpp @@ -28,8 +28,10 @@ namespace mwmp if (serverCell != nullptr) serverCell->sendToLoaded(&packet, &event); } - else - packet.Send(true); + + // Otherwise, don't have any hardcoded sync and expect Lua scripts to forward + // container packets to ensure their integrity based on what exists in the + // server data Script::Call(player.getId(), event.cell.getDescription().c_str()); diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 82fcde95c..5fd46f9b3 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -115,34 +115,34 @@ namespace MWGui worldEvent->reset(); worldEvent->cell = *mPtr.getCell()->getCell(); worldEvent->action = mwmp::BaseEvent::REMOVE; + worldEvent->containerSubAction = mwmp::BaseEvent::DRAG; - mwmp::WorldObject worldObject; - worldObject.refId = mPtr.getCellRef().getRefId(); - worldObject.refNumIndex = mPtr.getCellRef().getRefNum().mIndex; - worldObject.mpNum = mPtr.getCellRef().getMpNum(); - + mwmp::WorldObject worldObject = worldEvent->getWorldObject(mPtr); MWWorld::Ptr itemPtr = mModel->getItem(mSelectedItem).mBase; - - mwmp::ContainerItem containerItem; - containerItem.refId =itemPtr.getCellRef().getRefId(); - containerItem.count = itemPtr.getRefData().getCount(); - containerItem.charge = itemPtr.getCellRef().getCharge(); - containerItem.enchantmentCharge = itemPtr.getCellRef().getEnchantmentCharge(); - containerItem.actionCount = count; - - worldObject.containerItems.push_back(containerItem); + worldEvent->addContainerItem(worldObject, itemPtr, count); worldEvent->addObject(worldObject); mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->setEvent(worldEvent); mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->Send(); LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_CONTAINER about\n- Ptr cellRef: %s, %i\n- cell: %s\n- item: %s, %i", - worldObject.refId.c_str(), worldObject.refNumIndex, worldEvent->cell.getDescription().c_str(), - containerItem.refId.c_str(), containerItem.count); + worldObject.refId.c_str(), worldObject.refNumIndex, worldEvent->cell.getDescription().c_str(), + itemPtr.getCellRef().getRefId().c_str(), itemPtr.getRefData().getCount()); /* End of tes3mp addition */ + /* + Start of tes3mp change (major) + + Avoid running any of the original code for dragging items, to prevent possibilities + for item duping or interaction with restricted containers + */ + return; + /* + End of tes3mp change (major) + */ + mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count); } @@ -161,17 +161,13 @@ namespace MWGui worldEvent->reset(); worldEvent->cell = *mPtr.getCell()->getCell(); worldEvent->action = mwmp::BaseEvent::ADD; + worldEvent->containerSubAction = mwmp::BaseEvent::DROP; - mwmp::WorldObject worldObject; - worldObject.refId = mPtr.getCellRef().getRefId(); - worldObject.refNumIndex = mPtr.getCellRef().getRefNum().mIndex; - worldObject.mpNum = mPtr.getCellRef().getMpNum(); - + mwmp::WorldObject worldObject = worldEvent->getWorldObject(mPtr); MWWorld::Ptr itemPtr = mDragAndDrop->mItem.mBase; - mwmp::ContainerItem containerItem; containerItem.refId = itemPtr.getCellRef().getRefId(); - + // Make sure we get the drag and drop count, not the count of the original item containerItem.count = mDragAndDrop->mDraggedCount; @@ -184,16 +180,28 @@ namespace MWGui mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->setEvent(worldEvent); mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->Send(); - LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_CONTAINER about\n- Ptr cellRef: %s, %i\n- cell: %s\n- item: %s, %i", - worldObject.refId.c_str(), worldObject.refNumIndex, worldEvent->cell.getDescription().c_str(), - containerItem.refId.c_str(), containerItem.count); + LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_CONTAINER about\n- Ptr cellRef: %s, %i\n- cell: %s\n- item: %s, %i, %i", + worldObject.refId.c_str(), worldObject.refNumIndex, worldEvent->cell.getDescription().c_str(), + containerItem.refId.c_str(), containerItem.count, containerItem.charge); } /* End of tes3mp addition */ - if (success) - mDragAndDrop->drop(mModel, mItemView); + /* + Start of tes3mp change (major) + + Avoid running any of the original code for dropping items, to prevent possibilities + for item duping or interaction with restricted containers + + Instead, finish the drag in a way that removes the items in it + */ + //if (success) + // mDragAndDrop->drop(mModel, mItemView); + mDragAndDrop->finish(true); + /* + End of tes3mp change (major) + */ } void ContainerWindow::onBackgroundSelected() @@ -282,6 +290,40 @@ namespace MWGui if(mDragAndDrop != NULL && mDragAndDrop->mIsOnDragAndDrop) return; + /* + Start of tes3mp addition + + Send an ID_CONTAINER packet every time the Take All button is used on + a container + */ + mwmp::WorldEvent *worldEvent = mwmp::Main::get().getNetworking()->getWorldEvent(); + worldEvent->reset(); + worldEvent->cell = *mPtr.getCell()->getCell(); + worldEvent->action = mwmp::BaseEvent::REMOVE; + worldEvent->containerSubAction = mwmp::BaseEvent::TAKE_ALL; + worldEvent->addEntireContainer(mPtr); + + mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->setEvent(worldEvent); + mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->Send(); + + LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_CONTAINER about\n- Ptr cellRef: %s, %i-%i\n- cell: %s", + mPtr.getCellRef().getRefId().c_str(), mPtr.getCellRef().getRefNum().mIndex, mPtr.getCellRef().getMpNum(), + worldEvent->cell.getDescription().c_str()); + /* + End of tes3mp addition + */ + + /* + Start of tes3mp change (major) + + Avoid running any of the original code for taking all items, to prevent + possibilities for item duping or interaction with restricted containers + */ + return; + /* + End of tes3mp change (major) + */ + // transfer everything into the player's inventory ItemModel* playerModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getModel(); mModel->update(); @@ -321,32 +363,6 @@ namespace MWGui } MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); - - /* - Start of tes3mp addition - - Send an ID_CONTAINER packet every time the Take All button is used on - a container - */ - mwmp::WorldEvent *worldEvent = mwmp::Main::get().getNetworking()->getWorldEvent(); - worldEvent->reset(); - worldEvent->cell = *mPtr.getCell()->getCell(); - worldEvent->action = mwmp::BaseEvent::SET; - - mwmp::WorldObject worldObject; - worldObject.refId = mPtr.getCellRef().getRefId(); - worldObject.refNumIndex = mPtr.getCellRef().getRefNum().mIndex; - worldObject.mpNum = mPtr.getCellRef().getMpNum(); - worldEvent->addObject(worldObject); - - mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->setEvent(worldEvent); - mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->Send(); - - LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_CONTAINER about\n- Ptr cellRef: %s, %i\n- cell: %s", - worldObject.refId.c_str(), worldObject.refNumIndex, worldEvent->cell.getDescription().c_str()); - /* - End of tes3mp addition - */ } void ContainerWindow::onDisposeCorpseButtonClicked(MyGUI::Widget *sender) @@ -390,4 +406,48 @@ namespace MWGui return mModel->onTakeItem(item.mBase, count); } + /* + Start of tes3mp addition + + Make it possible to check from elsewhere whether there is currently an + item being dragged in the container window + */ + bool ContainerWindow::isOnDragAndDrop() + { + return mDragAndDrop->mIsOnDragAndDrop; + } + /* + End of tes3mp addition + */ + + /* + Start of tes3mp addition + + Make it possible to drag a specific item Ptr instead of having to rely + on an index that may have changed in the meantime, for drags that + require approval from the server + */ + bool ContainerWindow::dragItemByPtr(const MWWorld::Ptr& itemPtr, int dragCount) + { + ItemModel::ModelIndex newIndex = -1; + for (unsigned int i = 0; i < mModel->getItemCount(); ++i) + { + if (mModel->getItem(i).mBase == itemPtr) + { + newIndex = i; + break; + } + } + + if (newIndex != -1) + { + mDragAndDrop->startDrag(newIndex, mSortModel, mModel, mItemView, dragCount); + return true; + } + + return false; + } + /* + End of tes3mp addition + */ } diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index cf02d165d..427647fd6 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -41,6 +41,29 @@ namespace MWGui virtual void resetReference(); + /* + Start of tes3mp addition + + Make it possible to check from elsewhere whether there is currently an + item being dragged in the container window + */ + bool isOnDragAndDrop(); + /* + End of tes3mp addition + */ + + /* + Start of tes3mp addition + + Make it possible to drag a specific item Ptr instead of having to rely + on an index that may have changed in the meantime, for drags that + require approval from the server + */ + bool dragItemByPtr(const MWWorld::Ptr& itemPtr, int dragCount); + /* + End of tes3mp addition + */ + private: DragAndDrop* mDragAndDrop; diff --git a/apps/openmw/mwgui/draganddrop.cpp b/apps/openmw/mwgui/draganddrop.cpp index d81b2ed00..075393dc6 100644 --- a/apps/openmw/mwgui/draganddrop.cpp +++ b/apps/openmw/mwgui/draganddrop.cpp @@ -127,10 +127,35 @@ void DragAndDrop::onFrame() finish(); } -void DragAndDrop::finish() +/* + Start of tes3mp change (minor) + + Add a deleteDragItems argument that allows the deletion of the + items in the drag as oppposed to the regular behavior of returning + them to their source model + + This is required to reduce unpredictable behavior for drags approved + or rejected by the server +*/ +void DragAndDrop::finish(bool deleteDragItems) +/* + End of tes3mp change (minor) +*/ { mIsOnDragAndDrop = false; mSourceSortModel->clearDragItems(); + + /* + Start of tes3mp addition + + Make it possible to entirely delete the items in the drag + */ + if (deleteDragItems) + mSourceModel->removeItem(mItem, mDraggedCount); + /* + End of tes3mp addition + */ + // since mSourceView doesn't get updated in drag() MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); diff --git a/apps/openmw/mwgui/draganddrop.hpp b/apps/openmw/mwgui/draganddrop.hpp index dff8cd73c..20f753fbc 100644 --- a/apps/openmw/mwgui/draganddrop.hpp +++ b/apps/openmw/mwgui/draganddrop.hpp @@ -31,7 +31,20 @@ namespace MWGui void drop (ItemModel* targetModel, ItemView* targetView); void onFrame(); - void finish(); + /* + Start of tes3mp change (minor) + + Add a deleteDragItems argument that allows the deletion of the + items in the drag as oppposed to the regular behavior of returning + them to their source model + + This is required to reduce unpredictable behavior for drags approved + or rejected by the server + */ + void finish(bool deleteDragItems = false); + /* + End of tes3mp change (minor) + */ }; } diff --git a/apps/openmw/mwmp/LocalActor.cpp b/apps/openmw/mwmp/LocalActor.cpp index ac474776e..9d86d886d 100644 --- a/apps/openmw/mwmp/LocalActor.cpp +++ b/apps/openmw/mwmp/LocalActor.cpp @@ -214,7 +214,7 @@ void LocalActor::updateEquipment(bool forceUpdate) equipmentChanged = true; item.refId = ""; item.count = 0; - item.charge = 0; + item.charge = -1; item.enchantmentCharge = -1; } } diff --git a/apps/openmw/mwmp/WorldEvent.cpp b/apps/openmw/mwmp/WorldEvent.cpp index c62aed004..5a5352805 100644 --- a/apps/openmw/mwmp/WorldEvent.cpp +++ b/apps/openmw/mwmp/WorldEvent.cpp @@ -15,6 +15,8 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwgui/container.hpp" + #include "../mwmechanics/aifollow.hpp" #include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/summoning.hpp" @@ -50,6 +52,9 @@ void WorldEvent::reset() cell.blank(); worldObjects.clear(); guid = mwmp::Main::get().getNetworking()->getLocalPlayer()->guid; + + action = -1; + containerSubAction = 0; } void WorldEvent::addObject(WorldObject worldObject) @@ -59,13 +64,17 @@ void WorldEvent::addObject(WorldObject worldObject) void WorldEvent::editContainers(MWWorld::CellStore* cellStore) { + bool isLocalEvent = guid == Main::get().getLocalPlayer()->guid; + + LOG_APPEND(Log::LOG_VERBOSE, "- isLocalEvent? %s", isLocalEvent ? "true" : "false"); + WorldObject worldObject; for (unsigned int i = 0; i < worldObjectCount; i++) { worldObject = worldObjects.at(i); - //LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i, %i", worldObject.refId.c_str(), worldObject.refNumIndex, worldObject.mpNum); + //LOG_APPEND(Log::LOG_VERBOSE, "- container cellRef: %s %i-%i", worldObject.refId.c_str(), worldObject.refNumIndex, worldObject.mpNum); MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refNumIndex, worldObject.mpNum); @@ -74,19 +83,41 @@ void WorldEvent::editContainers(MWWorld::CellStore* cellStore) //LOG_APPEND(Log::LOG_VERBOSE, "-- Found %s, %i, %i", ptrFound.getCellRef().getRefId().c_str(), // ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum()); + bool isCurrentContainer = false; + + // If we are in a container, and it happens to be this container, keep track of that + if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Container)) + { + CurrentContainer *currentContainer = &mwmp::Main::get().getLocalPlayer()->currentContainer; + + if (currentContainer->refNumIndex == ptrFound.getCellRef().getRefNum().mIndex && + currentContainer->mpNum == ptrFound.getCellRef().getMpNum()) + { + isCurrentContainer = true; + } + } + MWWorld::ContainerStore& containerStore = ptrFound.getClass().getContainerStore(ptrFound); // If we are setting the entire contents, clear the current ones if (action == BaseEvent::SET) containerStore.clear(); + bool isLocalDrop = isLocalEvent && containerSubAction == BaseEvent::DROP; + bool isLocalDrag = isLocalEvent && containerSubAction == BaseEvent::DRAG; + bool isLocalTakeAll = isLocalEvent && containerSubAction == BaseEvent::TAKE_ALL; + std::string takeAllSound = ""; + MWWorld::Ptr ownerPtr = MWBase::Environment::get().getWorld()->getPlayerPtr(); for (const auto &containerItem : worldObject.containerItems) { + //LOG_APPEND(Log::LOG_VERBOSE, "-- containerItem cellRef: %s, count: %i, actionCount: %i", + // containerItem.refId.c_str(), containerItem.count, containerItem.actionCount); + if (containerItem.refId.find("$dynamic") != string::npos) continue; - if (action == BaseEvent::ADD || action == BaseEvent::SET) + if (action == BaseEvent::SET || action == BaseEvent::ADD) { // Create a ManualRef to be able to set item charge MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), containerItem.refId, 1); @@ -102,29 +133,55 @@ void WorldEvent::editContainers(MWWorld::CellStore* cellStore) newPtr.getCellRef().setEnchantmentCharge(containerItem.enchantmentCharge); containerStore.add(newPtr, containerItem.count, ownerPtr, true); - } - else if (action == BaseEvent::REMOVE) + } + + else if (action == BaseEvent::REMOVE && containerItem.actionCount > 0) { // We have to find the right item ourselves because ContainerStore has no method // accounting for charge - for (const auto ptr : containerStore) + for (const auto itemPtr : containerStore) { - if (Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), containerItem.refId)) + if (Misc::StringUtils::ciEqual(itemPtr.getCellRef().getRefId(), containerItem.refId)) { - if (ptr.getRefData().getCount() == containerItem.count && - ptr.getCellRef().getCharge() == containerItem.charge && - ptr.getCellRef().getEnchantmentCharge() == containerItem.enchantmentCharge) + if (itemPtr.getCellRef().getCharge() == containerItem.charge && + itemPtr.getCellRef().getEnchantmentCharge() == containerItem.enchantmentCharge) { + // Store the sound of the first item in a TAKE_ALL + if (isLocalTakeAll && takeAllSound.empty()) + takeAllSound = itemPtr.getClass().getUpSoundId(itemPtr); + // Is this an actor's container? If so, unequip this item if it was equipped if (ptrFound.getClass().isActor()) { MWWorld::InventoryStore& invStore = ptrFound.getClass().getInventoryStore(ptrFound); - if (invStore.isEquipped(ptr)) - invStore.unequipItemQuantity(ptr, ptrFound, containerItem.count); + if (invStore.isEquipped(itemPtr)) + invStore.unequipItemQuantity(itemPtr, ptrFound, containerItem.count); } - containerStore.remove(ptr, containerItem.actionCount, ownerPtr); + bool isResolved = false; + + if (isLocalDrag && isCurrentContainer) + { + MWGui::ContainerWindow* containerWindow = MWBase::Environment::get().getWindowManager()->getContainerWindow(); + + if (!containerWindow->isOnDragAndDrop()) + { + isResolved = containerWindow->dragItemByPtr(itemPtr, containerItem.actionCount); + } + } + + if (!isResolved) + { + containerStore.remove(itemPtr, containerItem.actionCount, ownerPtr); + + if (isLocalDrag || isLocalTakeAll) + { + MWWorld::Ptr ptrPlayer = MWBase::Environment::get().getWorld()->getPlayerPtr(); + MWWorld::ContainerStore &playerStore = ptrPlayer.getClass().getContainerStore(ptrPlayer); + *playerStore.add(containerItem.refId, containerItem.actionCount, ptrPlayer); + } + } } } } @@ -140,16 +197,18 @@ void WorldEvent::editContainers(MWWorld::CellStore* cellStore) invStore.autoEquip(ptrFound); } - // If we are in a container, and it happens to be this container, update its view - if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Container)) + // If this container was open for us, update its view + if (isCurrentContainer) { - CurrentContainer *currentContainer = &mwmp::Main::get().getLocalPlayer()->currentContainer; - - if (currentContainer->refNumIndex == ptrFound.getCellRef().getRefNum().mIndex && - currentContainer->mpNum == ptrFound.getCellRef().getMpNum()) + if (isLocalTakeAll) { MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); - MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Container, ptrFound); + MWBase::Environment::get().getWindowManager()->playSound(takeAllSound); + } + else + { + MWGui::ContainerWindow* containerWindow = MWBase::Environment::get().getWindowManager()->getContainerWindow(); + containerWindow->setPtr(ptrFound); } } } @@ -618,6 +677,41 @@ void WorldEvent::playVideo() } } +WorldObject WorldEvent::getWorldObject(const MWWorld::Ptr& ptr) +{ + mwmp::WorldObject worldObject; + worldObject.refId = ptr.getCellRef().getRefId(); + worldObject.refNumIndex = ptr.getCellRef().getRefNum().mIndex; + worldObject.mpNum = ptr.getCellRef().getMpNum(); + return worldObject; +} + +void WorldEvent::addContainerItem(mwmp::WorldObject& worldObject, const MWWorld::Ptr& itemPtr, int actionCount) +{ + mwmp::ContainerItem containerItem; + containerItem.refId = itemPtr.getCellRef().getRefId(); + containerItem.count = itemPtr.getRefData().getCount(); + containerItem.charge = itemPtr.getCellRef().getCharge(); + containerItem.enchantmentCharge = itemPtr.getCellRef().getEnchantmentCharge(); + containerItem.actionCount = actionCount; + + worldObject.containerItems.push_back(containerItem); +} + +void WorldEvent::addEntireContainer(const MWWorld::Ptr& ptr) +{ + MWWorld::ContainerStore& containerStore = ptr.getClass().getContainerStore(ptr); + + mwmp::WorldObject worldObject = getWorldObject(ptr); + + for (const auto itemPtr : containerStore) + { + addContainerItem(worldObject, itemPtr, itemPtr.getRefData().getCount()); + } + + addObject(worldObject); +} + void WorldEvent::addObjectPlace(const MWWorld::Ptr& ptr, bool droppedByPlayer) { if (ptr.getCellRef().getRefId().find("$dynamic") != string::npos) @@ -988,7 +1082,7 @@ void WorldEvent::sendScriptGlobalShort() mwmp::Main::get().getNetworking()->getWorldPacket(ID_SCRIPT_GLOBAL_SHORT)->Send(); } -void WorldEvent::sendContainers(MWWorld::CellStore* cellStore) +void WorldEvent::sendCellContainers(MWWorld::CellStore* cellStore) { reset(); cell = *cellStore->getCell(); @@ -1007,13 +1101,7 @@ void WorldEvent::sendContainers(MWWorld::CellStore* cellStore) for (const auto itemPtr : containerStore) { - mwmp::ContainerItem containerItem; - containerItem.refId = itemPtr.getCellRef().getRefId(); - containerItem.count = itemPtr.getRefData().getCount(); - containerItem.charge = itemPtr.getCellRef().getCharge(); - containerItem.enchantmentCharge = itemPtr.getCellRef().getEnchantmentCharge(); - - worldObject.containerItems.push_back(containerItem); + addContainerItem(worldObject, itemPtr, 0); } addObject(worldObject); diff --git a/apps/openmw/mwmp/WorldEvent.hpp b/apps/openmw/mwmp/WorldEvent.hpp index 783744b24..416a0efb8 100644 --- a/apps/openmw/mwmp/WorldEvent.hpp +++ b/apps/openmw/mwmp/WorldEvent.hpp @@ -41,6 +41,10 @@ namespace mwmp void playMusic(); void playVideo(); + WorldObject getWorldObject(const MWWorld::Ptr& ptr); + void addContainerItem(mwmp::WorldObject& worldObject, const MWWorld::Ptr& itemPtr, int actionCount); + void addEntireContainer(const MWWorld::Ptr& ptr); + 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); @@ -50,6 +54,7 @@ namespace mwmp void addObjectScale(const MWWorld::Ptr& ptr, float scale); void addObjectState(const MWWorld::Ptr& ptr, bool objectState); void addObjectAnimPlay(const MWWorld::Ptr& ptr, std::string group, int mode); + void addDoorState(const MWWorld::Ptr& ptr, int state); void addMusicPlay(std::string filename); void addVideoPlay(std::string filename, bool allowSkipping); @@ -74,7 +79,7 @@ namespace mwmp void sendScriptMemberShort(); void sendScriptGlobalShort(); - void sendContainers(MWWorld::CellStore* cellStore); + void sendCellContainers(MWWorld::CellStore* cellStore); private: Networking *getNetworking(); diff --git a/apps/openmw/mwmp/processors/world/ProcessorContainer.hpp b/apps/openmw/mwmp/processors/world/ProcessorContainer.hpp index cb2a3b4c8..fa6c5668f 100644 --- a/apps/openmw/mwmp/processors/world/ProcessorContainer.hpp +++ b/apps/openmw/mwmp/processors/world/ProcessorContainer.hpp @@ -17,11 +17,11 @@ namespace mwmp { BaseObjectProcessor::Do(packet, event); - LOG_APPEND(Log::LOG_VERBOSE, "- action: %i", event.action); + LOG_APPEND(Log::LOG_VERBOSE, "- action: %i, containerSubAction: %i", event.action, event.containerSubAction); // If we've received a request for information, comply with it if (event.action == mwmp::BaseEvent::REQUEST) - event.sendContainers(ptrCellStore); + event.sendCellContainers(ptrCellStore); // Otherwise, edit containers based on the information received else event.editContainers(ptrCellStore); diff --git a/components/openmw-mp/Base/BaseEvent.hpp b/components/openmw-mp/Base/BaseEvent.hpp index 0bed7eb6c..b2a66c9fd 100644 --- a/components/openmw-mp/Base/BaseEvent.hpp +++ b/components/openmw-mp/Base/BaseEvent.hpp @@ -85,6 +85,14 @@ namespace mwmp REQUEST = 3 }; + enum CONTAINER_SUBACTION + { + NONE = 0, + DRAG = 1, + DROP = 2, + TAKE_ALL = 3 + }; + RakNet::RakNetGUID guid; std::vector worldObjects; @@ -94,6 +102,7 @@ namespace mwmp std::string consoleCommand; unsigned char action; // 0 - Clear and set in entirety, 1 - Add item, 2 - Remove item, 3 - Request items + unsigned char containerSubAction; // 0 - None, 1 - Drag, 2 - Take all bool isValid; }; diff --git a/components/openmw-mp/Packets/World/PacketContainer.cpp b/components/openmw-mp/Packets/World/PacketContainer.cpp index 2e272b523..54f7a68c4 100644 --- a/components/openmw-mp/Packets/World/PacketContainer.cpp +++ b/components/openmw-mp/Packets/World/PacketContainer.cpp @@ -16,6 +16,7 @@ void PacketContainer::Packet(RakNet::BitStream *bs, bool send) return; RW(event->action, send); + RW(event->containerSubAction, send); WorldObject worldObject; for (unsigned int i = 0; i < event->worldObjectCount; i++) From 1c340568d2d373b40b04ad6bc163caeabaac4d0d Mon Sep 17 00:00:00 2001 From: David Cernat Date: Thu, 29 Mar 2018 06:04:20 +0300 Subject: [PATCH 22/22] [Client] Disallow resting if player has not finished chargen Previously, players pressing T rapidly before logging in had the Wait screen show up for them. --- apps/openmw/mwinput/inputmanagerimp.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 5c203ab9b..4b6824cbb 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -1066,6 +1066,17 @@ namespace MWInput return; } + /* + Start of tes3mp addition + + Ignore attempts to rest if the player has not finished character generation yet + */ + if (!mwmp::Main::get().getLocalPlayer()->hasFinishedCharGen()) + return; + /* + End of tes3mp addition + */ + /* Start of tes3mp addition