Fixes bug [#4678](https://gitlab.com/OpenMW/openmw/issues/4678)
The `loadSCVR` method assumes that the list of variable
names won't be empty, which it might.
Instead of crashing, we show a warning and ignore the record.
This change aims to allow TES4/TE5 content to OpenMW. i.e. a standalone TES4 would be implemented quite differently. That said, the key changes are:
* Use pointers rather than references for ESM readers so that they can be switched to another variant on the fly.
* Content file dependencies to be checked within each group (only 3 groups for now, TES3/TES4/TES5)
* Implement hash based lookup for TES3 BSA files.
* Added TES4/TES5 BSA support.
* Implemented a hack (non-portable code) in an attempt to reduce startup time under Windows because Boost::filesystem seems to take forever on GetFileAttributeW. This implementation uses FindFirstFile/FindNextFile/FindClose instead.
Note, I suspect Rng::rollClosedProbability() is not needed. The only difference between it and rollProbability() is that one time in 37k (on Windows), it will give an output of 1.0.
On some versions of Linux, the value of 1.0 will occur about 1 time in 4 billion.
(cherry picked from commit 3f28634d1f)
# Conflicts:
# apps/openmw/mwclass/creature.cpp
# apps/openmw/mwclass/npc.cpp
# apps/openmw/mwgui/pickpocketitemmodel.cpp
# apps/openmw/mwgui/waitdialog.cpp
# apps/openmw/mwmechanics/combat.cpp
# apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
# components/CMakeLists.txt
# libs/openengine/misc/rng.cpp
Previously, the player's currently selected weapon was being used in ranged attacks as in the original melee-oriented attack sync, which meant that shooting one type of projectile and then equipping another while the old projectile was still in the air turned the old projectile into the new projectile upon impact.
Additionally, avoid running most of the code in MechanicsHelper::assignAttackTarget() for non-hitting melee and ranged attacks.
This is done by including the final attackStrength used for ranged attacks in packets and then applying it in WeaponAnimation::releaseArrow() on other clients.
Projectile hits now send Attack packets with RANGED attacks, and their success or failure is now synchronized.
Strike enchantments no longer require a valid victim to be synchronized.
Additional debug messages have been added for attacks.
Whenever an item is added to or removed from the player's ContainerStore, that player sends a PlayerInventory packet with just that addition or removal.
This eliminates all the unnecessary packet spam related to oversized PlayerInventory packets that had existed in one form or another since the initial implementation of inventory sync in 1b259e2d33
Additionally, move booleans from BasePlayer to LocalPlayer when they are only needed on the client, and make the usage of the isReceivingQuickKeys boolean consistent with the new isReceivingInventory boolean by having them both in the processors of their associated packets.
Fixes compile error encountered on OSX 10.9 with g++
sophie:build pineapple$ g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin13.4.0
Thread model: posix
The compilation error:
[ 24%] Building CXX object apps/openmw/CMakeFiles/openmw.dir/mwrender/renderingmanager.cpp.o
In file included from /Users/pineapple/git/openmw/apps/openmw/mwrender/renderingmanager.cpp:1:
In file included from /Users/pineapple/git/openmw/apps/openmw/mwrender/renderingmanager.hpp:4:
/Users/pineapple/git/openmw/openmw-deps/include/osg/ref_ptr:35:36: error: member access into incomplete type 'SceneUtil::UnrefWorkItem'
~ref_ptr() { if (_ptr) _ptr->unref(); _ptr = 0; }
^
/Users/pineapple/git/openmw/./components/sceneutil/unrefqueue.hpp:14:11: note: in instantiation of member function
'osg::ref_ptr<SceneUtil::UnrefWorkItem>::~ref_ptr' requested here
class UnrefQueue : public osg::Referenced
^
/Users/pineapple/git/openmw/./components/sceneutil/unrefqueue.hpp:10:11: note: forward declaration of 'SceneUtil::UnrefWorkItem'
class UnrefWorkItem;
Players can no longer unilaterally use items on themselves in their inventory. When they try to use an item, they send a PlayerItemUse packet to the server with the item's details. A serverside script can then check the item and either send the packet back to make the item use go through or drop it.
Spell, potion, enchantment, creature, NPC, armor, book, clothing, miscellaneous and weapon record data can now be sent in a RecordDynamic packet. Additionally, the packets include data related to associated magical effects (for spells, potions and enchantments), data related to default inventory contents (for creatures and NPCs) and data related to body parts affected (for armor and clothing).
The server now has associated script functions for setting most of the details of the above, with the main exception being individual creature and NPC stats.
Records can either be created entirely from scratch or can use an existing record (set via the baseId variable) as a starting point for their values. In the latter case, only the values that are specifically set override the starting values. Creature and NPC records also have an inventoryBaseId that can be used on top of the baseId to base their inventories on another existing record.
The client's RecordHelper class has been heavily expanded to allow for the above mentioned functionality.
When players create spells, potions and enchantments as part of regular gameplay, they send RecordDynamic packets that provide the server with the complete details of the records that should be created. When they create enchantments, they also provide the server with armor, book, clothing and weapon records corresponding to the items they've enchanted.
This functionality added by this packet was originally supposed to be exclusive to the rewrite, but I've gone ahead and tried to provide it for the pre-rewrite in a way that can mostly be reused for the rewrite.
I originally added rotation animation sync as part of commit 068a45be87. Unfortunately, it meant the PlayerPosition packets were now twice as large as they had been before, which was less than ideal for such a frequently sent packet, which is why Koncord switched to a more optimized approach in commits 5f30dfd5db and d67db1a9bd.
Recently, there have since been some rotation animation problems in OpenMW, which have broken the way Koncord's approach looks. My original approach still looks somewhat okay, so I'm switching back to it until we can figure out how to reuse it under the current circumstances.
Although weather sync was added by Koncord to the rewrite in fd721143e2 in a way that used surprisingly few lines of code, it relied on the server requesting weather states every second from authority players and sending them to non-authority players, while also allowing only very sudden weather transitions across regions, i.e. if there was one player in the Ascadian Isles who had stormy weather, and another player with clear weather in the Bitter Coast Region walked across to the Ascadian Isles, that player was instantly made to have stormy weather with no kind of transition at all.
My approach solves both of those problems. It solves the packet spam by only sending weather updates to the server when weather changes happen or when there are new arrivals to a weather authority's region, and it allows for both sudden weather transitions when players teleport to a region and for soft, gradual transitions when players walk across to a region. It is inspired by my previous actor sync, and uses a WorldRegionAuthority packet to set players as region authorities in a similar way to how ActorAuthority sets players as cell AI authorities. Weather changes are created only by the region authority for a given region, and weather packets are also only sent by that authority.
However, it should be noted that gradual weather transitions are used by default in this implementation. To use sudden weather transitions, the serverside Lua scripts need to forward WorldWeather packets with the forceWeather boolean set to true. That is, however, already handled by our default Lua scripts in situations where it makes sense.
This should clarify the real meaning of the packet and its associated event.
The event itself has been renamed from OnPlayerKillCount to OnWorldKillCount.
This new crash catcher is preventing TES3MP from building on GNU/Linux
`components/crashcatcher/crashcatcher.cpp:23:28: fatal error: SDL_messagebox.h: No such file or directory`
Disable it until the developers isolate the issue and patch it
The server can now make actors activate players and objects, at least in theory. In practice, OpenMW''s AiActivate package needs to be worked so it allows specific objects as targets instead of just refIds.
Add serverside script functions for determining the killers of both players and actors.
Use unsigned ints for script functions returning an object or actor's refNumIndex or mpNum.
Remove updateDeadState() from LocalPlayer and make its code part of updateStatsDynamic() for simplicity.
ActorDeath packets are sent for dead actors before their StatsDynamic packets. They contain the actor's deathReason in a manner similar to that of PlayerDeath packets.
A future commit will replace the deathReason with a variable named killer which will be an mwmp::Target.
openmw/apps/openmw/mwworld/worldimp.cpp: In member function ‘virtual bool MWWorld::World::findExteriorPosition(const string&, ESM::Position&)’:
openmw/apps/openmw/mwworld/worldimp.cpp:2650:25: warning: catching polymorphic type ‘class std::invalid_argument’ by value [-Wcatch-value=]
catch (std::invalid_argument)
^~~~~~~~~~~~~~~~
openmw/apps/openmw/mwworld/worldimp.cpp:2654:25: warning: catching polymorphic type ‘class std::out_of_range’ by value [-Wcatch-value=]
catch (std::out_of_range)
^~~~~~~~~~~~
openmw/components/widgets/numericeditbox.cpp: In member function ‘void Gui::NumericEditBox::onEditTextChange(MyGUI::EditBox*)’:
openmw/components/widgets/numericeditbox.cpp:41:21: warning: catching polymorphic type ‘class std::invalid_argument’ by value [-Wcatch-value=]
catch (std::invalid_argument)
^~~~~~~~~~~~~~~~
openmw/components/widgets/numericeditbox.cpp:45:21: warning: catching polymorphic type ‘class std::out_of_range’ by value [-Wcatch-value=]
catch (std::out_of_range)
^~~~~~~~~~~~
Previously, two players entering the same cell only sent and received their latest changes for dynamic stats, attributes, skills and equipment when they started sharing that cell.
Previously, whenever a single attribute value changed for a player, that player then sent a PlayerAttribute packet with all values for all 8 attributes.
This did not cause anywhere as much packet spam as PlayerSkill used to, but there was no good reason not to fix it as well.
(cherry picked from commit b0965f094a)
Previously, whenever a single skill value changed for a player, that player then sent a PlayerSkill packet with all values for all 27 skills, plus the player's progress towards the next level and the bonuses to each attribute on the next level up as the result of sklll increases thus far.
This commit makes PlayerSkill contain only the values of specific skills, moves the player's progress towards the next level to PlayerLevel packets, and moves the bonuses to each attribute on the next level up to PlayerAttribute packets.
Players now also send a PlayerSkill packet whenever their progress towards a new point in a skill changes. This was previously avoided so as to not have massive packet spam.
(cherry picked from commit ef79a98544)
Previously, charGenStage.end was doing double duty as both the variable indicating the number of CharGen stages and – when set to 0 – the variable indicating that CharGen was over. The latter role is now filled by a new boolean.
(cherry picked from commit 926106cf8c)
cppcheck:
[apps/esmtool/record.cpp:697]: (performance) Prefer prefix ++/-- operators for non-primitive types.
[apps/esmtool/record.cpp:1126]: (performance) Prefer prefix ++/-- operators for non-primitive types.
[apps/esmtool/record.cpp:1138]: (performance) Prefer prefix ++/-- operators for non-primitive types.
[apps/niftest/niftest.cpp:36]: (performance) Function parameter 'filename' should be passed by reference.
[apps/niftest/niftest.cpp:41]: (performance) Function parameter 'filename' should be passed by reference.
[apps/opencs/model/prefs/boolsetting.cpp:25]: (warning) Possible leak in public function. The pointer 'mWidget' is not deallocated before it is allocated.
[apps/opencs/model/prefs/shortcuteventhandler.cpp:52]: (warning) Return value of std::remove() ignored. Elements remain in container.
[apps/openmw/mwstate/quicksavemanager.cpp:5]: (performance) Variable 'mSaveName' is assigned in constructor body. Consider performing initialization in initialization list.
PVS-Studio:
apps/opencs/model/filter/parser.cpp 582 warn V560 A part of conditional expression is always true: allowPredefined.
apps/opencs/view/world/referencecreator.cpp 67 warn V547 Expression '!errors.empty()' is always false.
apps/opencs/view/world/referencecreator.cpp 74 warn V547 Expression '!errors.empty()' is always false.
apps/opencs/view/doc/loader.cpp 170 warn V560 A part of conditional expression is always true: !completed.
apps/opencs/view/doc/loader.cpp 170 warn V560 A part of conditional expression is always true: !error.empty().
apps/opencs/model/tools/pathgridcheck.cpp 32 err V517 The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error presence. Check lines: 32, 34.
apps/opencs/model/world/refidadapterimp.cpp 1376 err V547 Expression 'subColIndex < 3' is always true.
apps/openmw/mwgui/widgets.hpp 318 warn V703 It is odd that the 'mEnableRepeat' field in derived class 'MWScrollBar' overwrites field in base class 'ScrollBar'. Check lines: widgets.hpp:318, MyGUI_ScrollBar.h:179.
apps/openmw/mwgui/widgets.hpp 319 warn V703 It is odd that the 'mRepeatTriggerTime' field in derived class 'MWScrollBar' overwrites field in base class 'ScrollBar'. Check lines: widgets.hpp:319, MyGUI_ScrollBar.h:180.
apps/openmw/mwgui/widgets.hpp 320 warn V703 It is odd that the 'mRepeatStepTime' field in derived class 'MWScrollBar' overwrites field in base class 'ScrollBar'. Check lines: widgets.hpp:320, MyGUI_ScrollBar.h:181
apps/openmw/mwmechanics/actors.cpp 1425 warn V547 Expression '!detected' is always true.
apps/openmw/mwmechanics/character.cpp 2155 err V547 Expression 'mode == 0' is always true.
apps/openmw/mwmechanics/character.cpp 1192 warn V592 The expression was enclosed by parentheses twice: ((expression)). One pair of parentheses is unnecessary or misprint is present.
apps/openmw/mwmechanics/character.cpp 521 warn V560 A part of conditional expression is always true: (idle == mIdleState).
apps/openmw/mwmechanics/pathfinding.cpp 317 err V547 Expression 'mPath.size() >= 2' is always true.
apps/openmw/mwscript/interpretercontext.cpp 409 warn V560 A part of conditional expression is always false: rank > 9.
apps/openmw/mwgui/windowbase.cpp 28 warn V560 A part of conditional expression is always true: !visible.
apps/openmw/mwgui/journalwindow.cpp 561 warn V547 Expression '!mAllQuests' is always false.
apps/openmw/mwgui/referenceinterface.cpp 18 warn V571 Recurring check. The '!mPtr.isEmpty()' condition was already verified in line 16.
apps/openmw/mwworld/scene.cpp 463 warn V547 Expression 'adjustPlayerPos' is always true.
apps/openmw/mwworld/worldimp.cpp 409 err V766 An item with the same key '"sCompanionShare"' has already been added.
apps/openmw/mwworld/cellstore.cpp 691 warn V519 The 'state.mWaterLevel' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 689, 691.
apps/openmw/mwworld/weather.cpp 1125 warn V519 The 'mResult.mParticleEffect' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 1123, 1125.
apps/openmw/mwworld/weather.cpp 1137 warn V519 The 'mResult.mParticleEffect' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 1135, 1137.
apps/wizard/unshield/unshieldworker.cpp 475 warn V728 An excessive check can be simplified. The '(A && B) || (!A && !B)' expression is equivalent to the 'bool(A) == bool(B)' expression.
apps/wizard/installationpage.cpp 163 warn V735 Possibly an incorrect HTML. The "</p" closing tag was encountered, while the "</span" tag was expected.
components/fontloader/fontloader.cpp 427 err V547 Expression 'i == 1' is always true.
components/nifosg/nifloader.cpp 282 warn V519 The 'created' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 278, 282.
components/esm/loadregn.cpp 119 err V586 The 'clear' function is called twice for deallocation of the same resource. Check lines: 112, 119.
components/esm/cellref.cpp 178 warn V581 The conditional expressions of the 'if' statements situated alongside each other are identical. Check lines: 175, 178.
components/esmterrain/storage.cpp 235 warn V560 A part of conditional expression is always true: colStart == 0.
components/esmterrain/storage.cpp 237 warn V560 A part of conditional expression is always true: rowStart == 0.
To fix warnings:
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/7.3.1/../../../../include/c++/7.3.1/bits/shared_ptr_base.h:588:8: warning: delete called on non-final 'NifOsg::ControllerFunction' that has virtual functions but non-virtual destructor
[-Wdelete-non-virtual-dtor]
delete __p;
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/7.3.1/../../../../include/c++/7.3.1/bits/shared_ptr_base.h:595:4: note: in instantiation of function template specialization
'std::__shared_count<__gnu_cxx::_S_atomic>::__shared_count<NifOsg::ControllerFunction *>' requested here
: __shared_count(__p)
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/7.3.1/../../../../include/c++/7.3.1/bits/shared_ptr_base.h:1079:17: note: in instantiation of function template specialization
'std::__shared_count<__gnu_cxx::_S_atomic>::__shared_count<NifOsg::ControllerFunction *>' requested here
: _M_ptr(__p), _M_refcount(__p, typename is_array<_Tp>::type())
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/7.3.1/../../../../include/c++/7.3.1/bits/shared_ptr.h:129:25: note: in instantiation of function template specialization 'std::__shared_ptr<NifOsg::ControllerFunction,
__gnu_cxx::_S_atomic>::__shared_ptr<NifOsg::ControllerFunction, void>' requested here
shared_ptr(_Yp* __p) : __shared_ptr<_Tp>(__p) { }
^
/home/elsid/dev/openmw/components/nifosg/nifloader.cpp:242:39: note: in instantiation of function template specialization 'std::shared_ptr<NifOsg::ControllerFunction>::shared_ptr<NifOsg::ControllerFunction, void>' requested here
callback->setFunction(std::shared_ptr<NifOsg::ControllerFunction>(new NifOsg::ControllerFunction(key)));
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/7.3.1/../../../../include/c++/7.3.1/bits/unique_ptr.h:78:2: warning: delete called on non-final 'MWGui::ResponseCallback' that has virtual functions but non-virtual destructor
[-Wdelete-non-virtual-dtor]
delete __ptr;
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/7.3.1/../../../../include/c++/7.3.1/bits/unique_ptr.h:268:4: note: in instantiation of member function 'std::default_delete<MWGui::ResponseCallback>::operator()' requested here
get_deleter()(__ptr);
^
/home/elsid/dev/openmw/apps/openmw/mwgui/dialogue.cpp:58:23: note: in instantiation of member function 'std::unique_ptr<MWGui::ResponseCallback, std::default_delete<MWGui::ResponseCallback> >::~unique_ptr' requested here
PersuasionDialog::PersuasionDialog(ResponseCallback* callback)
^
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.
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.
* http to https for supported urls
* http to https
* http to https
* http to https
* http to https
* http to https
* http to https
* http to https
* http tp https
* http to https
* http to https
* http to https
* http to https
* http to https
* http to https
* http to https
* http to https
* some url fixes
* http to https
This allows the OnPCDrop variable to get set correctly even when object placements have to go through the server first in order to gain a unique multiplayer index (mpNum).
Among other things, this makes it possible to roll marijuana joints in the popular mod "Tribunal Code Patch".
Double buffering, custom bounding box and the update in the cull visitor (instead of update) are now all handled internally rather than needing hacks and/or callbacks.
The double buffering is an implementation detail so it should be handled as such, rather than mandating the scene graph to be structured in a certain way.
Override accept(NodeVisitor&) instead of using callbacks.