2017-02-06 15:40:32 +00:00
# include "WorldEvent.hpp"
2017-01-27 18:57:47 +00:00
# include "Main.hpp"
2017-02-05 16:45:23 +00:00
# include "Networking.hpp"
2017-05-30 07:11:01 +00:00
# include "MechanicsHelper.hpp"
2017-02-05 16:45:23 +00:00
# include "LocalPlayer.hpp"
# include "DedicatedPlayer.hpp"
2017-05-30 07:11:01 +00:00
# include "PlayerList.hpp"
2017-06-09 01:58:56 +00:00
# include "CellController.hpp"
2017-01-27 18:57:47 +00:00
2017-01-28 10:34:45 +00:00
# include <components/openmw-mp/Log.hpp>
# include "../mwbase/world.hpp"
# include "../mwbase/environment.hpp"
# include "../mwbase/mechanicsmanager.hpp"
# include "../mwbase/soundmanager.hpp"
# include "../mwbase/windowmanager.hpp"
[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.
2018-03-26 16:27:36 +00:00
# include "../mwgui/container.hpp"
2017-05-30 07:11:01 +00:00
# include "../mwmechanics/aifollow.hpp"
2017-05-25 22:28:43 +00:00
# include "../mwmechanics/spellcasting.hpp"
2017-05-30 07:11:01 +00:00
# include "../mwmechanics/summoning.hpp"
# include "../mwrender/animation.hpp"
2017-05-25 22:28:43 +00:00
2017-01-28 10:34:45 +00:00
# include "../mwworld/class.hpp"
2017-02-05 16:45:23 +00:00
# include "../mwworld/containerstore.hpp"
2017-01-28 10:34:45 +00:00
# include "../mwworld/esmstore.hpp"
2017-06-09 01:58:56 +00:00
# include "../mwworld/inventorystore.hpp"
2017-01-28 10:34:45 +00:00
# include "../mwworld/manualref.hpp"
2017-01-27 18:57:47 +00:00
using namespace mwmp ;
using namespace std ;
2017-02-23 07:18:48 +00:00
WorldEvent : : WorldEvent ( )
2017-01-27 18:57:47 +00:00
{
2017-02-23 07:18:48 +00:00
2017-01-27 18:57:47 +00:00
}
2017-02-06 15:40:32 +00:00
WorldEvent : : ~ WorldEvent ( )
2017-01-27 18:57:47 +00:00
{
}
2017-02-06 15:40:32 +00:00
Networking * WorldEvent : : getNetworking ( )
2017-01-27 18:57:47 +00:00
{
return mwmp : : Main : : get ( ) . getNetworking ( ) ;
}
2017-04-05 06:04:41 +00:00
void WorldEvent : : reset ( )
{
cell . blank ( ) ;
2017-05-06 18:57:14 +00:00
worldObjects . clear ( ) ;
2017-04-05 06:04:41 +00:00
guid = mwmp : : Main : : get ( ) . getNetworking ( ) - > getLocalPlayer ( ) - > guid ;
[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.
2018-03-26 16:27:36 +00:00
action = - 1 ;
containerSubAction = 0 ;
2017-04-05 06:04:41 +00:00
}
2017-02-06 15:40:32 +00:00
void WorldEvent : : addObject ( WorldObject worldObject )
2017-01-28 10:34:45 +00:00
{
2017-05-06 18:57:14 +00:00
worldObjects . push_back ( worldObject ) ;
2017-01-28 10:34:45 +00:00
}
2017-02-14 17:31:56 +00:00
void WorldEvent : : editContainers ( MWWorld : : CellStore * cellStore )
2017-02-05 16:45:23 +00:00
{
[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.
2018-03-26 16:27:36 +00:00
bool isLocalEvent = guid = = Main : : get ( ) . getLocalPlayer ( ) - > guid ;
LOG_APPEND ( Log : : LOG_VERBOSE , " - isLocalEvent? %s " , isLocalEvent ? " true " : " false " ) ;
2017-02-05 16:45:23 +00:00
WorldObject worldObject ;
2017-05-06 18:57:14 +00:00
for ( unsigned int i = 0 ; i < worldObjectCount ; i + + )
2017-02-05 16:45:23 +00:00
{
2017-05-06 18:57:14 +00:00
worldObject = worldObjects . at ( i ) ;
2017-02-05 16:45:23 +00:00
[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.
2018-03-26 16:27:36 +00:00
//LOG_APPEND(Log::LOG_VERBOSE, "- container cellRef: %s %i-%i", worldObject.refId.c_str(), worldObject.refNumIndex, worldObject.mpNum);
2017-02-05 16:45:23 +00:00
2017-05-27 13:56:54 +00:00
MWWorld : : Ptr ptrFound = cellStore - > searchExact ( worldObject . refNumIndex , worldObject . mpNum ) ;
2017-02-05 16:45:23 +00:00
if ( ptrFound )
{
2017-05-14 08:08:53 +00:00
//LOG_APPEND(Log::LOG_VERBOSE, "-- Found %s, %i, %i", ptrFound.getCellRef().getRefId().c_str(),
// ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());
2017-02-05 16:45:23 +00:00
[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.
2018-03-26 16:27:36 +00:00
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 ;
}
}
2017-02-05 16:45:23 +00:00
MWWorld : : ContainerStore & containerStore = ptrFound . getClass ( ) . getContainerStore ( ptrFound ) ;
2017-02-05 18:04:50 +00:00
// If we are setting the entire contents, clear the current ones
2017-02-15 18:14:25 +00:00
if ( action = = BaseEvent : : SET )
2017-02-05 18:04:50 +00:00
containerStore . clear ( ) ;
2017-02-05 16:45:23 +00:00
[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.
2018-03-26 16:27:36 +00:00
bool isLocalDrop = isLocalEvent & & containerSubAction = = BaseEvent : : DROP ;
bool isLocalDrag = isLocalEvent & & containerSubAction = = BaseEvent : : DRAG ;
bool isLocalTakeAll = isLocalEvent & & containerSubAction = = BaseEvent : : TAKE_ALL ;
std : : string takeAllSound = " " ;
2017-06-27 07:49:07 +00:00
MWWorld : : Ptr ownerPtr = MWBase : : Environment : : get ( ) . getWorld ( ) - > getPlayerPtr ( ) ;
for ( const auto & containerItem : worldObject . containerItems )
2017-02-05 16:45:23 +00:00
{
[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.
2018-03-26 16:27:36 +00:00
//LOG_APPEND(Log::LOG_VERBOSE, "-- containerItem cellRef: %s, count: %i, actionCount: %i",
// containerItem.refId.c_str(), containerItem.count, containerItem.actionCount);
2018-01-17 09:01:31 +00:00
if ( containerItem . refId . find ( " $dynamic " ) ! = string : : npos )
continue ;
[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.
2018-03-26 16:27:36 +00:00
if ( action = = BaseEvent : : SET | | action = = BaseEvent : : ADD )
2017-02-05 17:33:11 +00:00
{
2017-02-06 19:28:03 +00:00
// Create a ManualRef to be able to set item charge
MWWorld : : ManualRef ref ( MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) , containerItem . refId , 1 ) ;
MWWorld : : Ptr newPtr = ref . getPtr ( ) ;
if ( containerItem . count > 1 )
newPtr . getRefData ( ) . setCount ( containerItem . count ) ;
if ( containerItem . charge > - 1 )
newPtr . getCellRef ( ) . setCharge ( containerItem . charge ) ;
2017-12-23 11:16:38 +00:00
if ( containerItem . enchantmentCharge > - 1 )
newPtr . getCellRef ( ) . setEnchantmentCharge ( containerItem . enchantmentCharge ) ;
2017-02-06 20:45:23 +00:00
containerStore . add ( newPtr , containerItem . count , ownerPtr , true ) ;
[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.
2018-03-26 16:27:36 +00:00
}
else if ( action = = BaseEvent : : REMOVE & & containerItem . actionCount > 0 )
2017-02-05 16:45:23 +00:00
{
2017-02-06 19:28:03 +00:00
// We have to find the right item ourselves because ContainerStore has no method
// accounting for charge
[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.
2018-03-26 16:27:36 +00:00
for ( const auto itemPtr : containerStore )
2017-02-06 19:28:03 +00:00
{
[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.
2018-03-26 16:27:36 +00:00
if ( Misc : : StringUtils : : ciEqual ( itemPtr . getCellRef ( ) . getRefId ( ) , containerItem . refId ) )
2017-02-06 19:28:03 +00:00
{
[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.
2018-03-26 16:27:36 +00:00
if ( itemPtr . getCellRef ( ) . getCharge ( ) = = containerItem . charge & &
itemPtr . getCellRef ( ) . getEnchantmentCharge ( ) = = containerItem . enchantmentCharge )
2017-02-06 19:28:03 +00:00
{
[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.
2018-03-26 16:27:36 +00:00
// Store the sound of the first item in a TAKE_ALL
if ( isLocalTakeAll & & takeAllSound . empty ( ) )
takeAllSound = itemPtr . getClass ( ) . getUpSoundId ( itemPtr ) ;
2017-06-09 01:58:56 +00:00
// Is this an actor's container? If so, unequip this item if it was equipped
2018-04-01 05:41:57 +00:00
if ( ptrFound . getClass ( ) . isActor ( ) & & ptrFound . getClass ( ) . hasInventoryStore ( ptrFound ) )
2017-06-09 01:58:56 +00:00
{
MWWorld : : InventoryStore & invStore = ptrFound . getClass ( ) . getInventoryStore ( ptrFound ) ;
[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.
2018-03-26 16:27:36 +00:00
if ( invStore . isEquipped ( itemPtr ) )
invStore . unequipItemQuantity ( itemPtr , ptrFound , containerItem . count ) ;
2017-06-09 01:58:56 +00:00
}
[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.
2018-03-26 16:27:36 +00:00
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 ) ;
}
}
2017-02-06 19:28:03 +00:00
}
}
}
2017-02-05 16:45:23 +00:00
}
}
2017-06-09 01:58:56 +00:00
// Was this a SET or ADD action on an actor's container, and are we the authority
// over the actor? If so, autoequip the actor
if ( ( action = = BaseEvent : : ADD | | action = = BaseEvent : : SET ) & & ptrFound . getClass ( ) . isActor ( ) & &
mwmp : : Main : : get ( ) . getCellController ( ) - > isLocalActor ( ptrFound ) )
{
MWWorld : : InventoryStore & invStore = ptrFound . getClass ( ) . getInventoryStore ( ptrFound ) ;
invStore . autoEquip ( ptrFound ) ;
}
[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.
2018-03-26 16:27:36 +00:00
// If this container was open for us, update its view
if ( isCurrentContainer )
2017-02-05 16:45:23 +00:00
{
[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.
2018-03-26 16:27:36 +00:00
if ( isLocalTakeAll )
2017-02-05 16:45:23 +00:00
{
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > removeGuiMode ( MWGui : : GM_Container ) ;
[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.
2018-03-26 16:27:36 +00:00
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > playSound ( takeAllSound ) ;
}
else
{
MWGui : : ContainerWindow * containerWindow = MWBase : : Environment : : get ( ) . getWindowManager ( ) - > getContainerWindow ( ) ;
containerWindow - > setPtr ( ptrFound ) ;
2017-02-05 16:45:23 +00:00
}
}
}
}
}
2017-02-06 15:40:32 +00:00
void WorldEvent : : placeObjects ( MWWorld : : CellStore * cellStore )
2017-01-28 10:34:45 +00:00
{
2018-01-28 15:00:14 +00:00
MWBase : : World * world = MWBase : : Environment : : get ( ) . getWorld ( ) ;
2017-06-27 07:49:07 +00:00
for ( const auto & worldObject : worldObjects )
2017-01-28 10:34:45 +00:00
{
2017-12-23 11:16:38 +00:00
LOG_APPEND ( Log : : LOG_VERBOSE , " - cellRef: %s, %i, %i, count: %i, charge: %i, enchantmentCharge: %i " , worldObject . refId . c_str ( ) ,
worldObject . refNumIndex , worldObject . mpNum , worldObject . count , worldObject . charge , worldObject . enchantmentCharge ) ;
2017-01-28 10:34:45 +00:00
2018-01-17 09:01:31 +00:00
// Ignore generic dynamic refIds because they could be anything on other clients
if ( worldObject . refId . find ( " $dynamic " ) ! = string : : npos )
continue ;
2017-05-27 13:56:54 +00:00
MWWorld : : Ptr ptrFound = cellStore - > searchExact ( 0 , worldObject . mpNum ) ;
2017-02-05 12:38:04 +00:00
2017-05-07 00:17:19 +00:00
// Only create this object if it doesn't already exist
if ( ! ptrFound )
{
2018-01-31 16:51:30 +00:00
try
{
MWWorld : : ManualRef ref ( world - > getStore ( ) , worldObject . refId , 1 ) ;
MWWorld : : Ptr newPtr = ref . getPtr ( ) ;
if ( worldObject . count > 1 )
newPtr . getRefData ( ) . setCount ( worldObject . count ) ;
2017-05-07 00:17:19 +00:00
2018-01-31 16:51:30 +00:00
if ( worldObject . charge > - 1 )
newPtr . getCellRef ( ) . setCharge ( worldObject . charge ) ;
2017-12-23 11:16:38 +00:00
2018-01-31 16:51:30 +00:00
if ( worldObject . enchantmentCharge > - 1 )
newPtr . getCellRef ( ) . setEnchantmentCharge ( worldObject . enchantmentCharge ) ;
2017-01-28 10:34:45 +00:00
2018-01-31 16:51:30 +00:00
newPtr . getCellRef ( ) . setGoldValue ( worldObject . goldValue ) ;
newPtr = world - > placeObject ( newPtr , cellStore , worldObject . position ) ;
2017-01-28 10:34:45 +00:00
2018-01-31 16:51:30 +00:00
// Because gold automatically gets replaced with a new object, make sure we set the mpNum at the end
newPtr . getCellRef ( ) . setMpNum ( worldObject . mpNum ) ;
2017-06-09 10:31:19 +00:00
2018-01-31 16:51:30 +00:00
if ( guid = = Main : : get ( ) . getLocalPlayer ( ) - > guid & & worldObject . droppedByPlayer )
world - > PCDropped ( newPtr ) ;
2018-01-28 15:00:14 +00:00
2018-01-31 16:51:30 +00:00
}
catch ( std : : exception & )
{
LOG_APPEND ( Log : : LOG_INFO , " -- Ignored placement of invalid object " ) ;
}
2017-05-07 00:17:19 +00:00
}
else
LOG_APPEND ( Log : : LOG_VERBOSE , " -- Object already existed! " ) ;
2017-01-28 10:34:45 +00:00
}
}
2017-05-29 03:59:05 +00:00
void WorldEvent : : spawnObjects ( MWWorld : : CellStore * cellStore )
{
2017-06-27 07:49:07 +00:00
for ( const auto & worldObject : worldObjects )
2017-05-29 03:59:05 +00:00
{
LOG_APPEND ( Log : : LOG_VERBOSE , " - cellRef: %s, %i, %i " , worldObject . refId . c_str ( ) ,
worldObject . refNumIndex , worldObject . mpNum ) ;
2018-01-17 09:01:31 +00:00
// Ignore generic dynamic refIds because they could be anything on other clients
if ( worldObject . refId . find ( " $dynamic " ) ! = string : : npos )
continue ;
2017-05-29 03:59:05 +00:00
MWWorld : : Ptr ptrFound = cellStore - > searchExact ( 0 , worldObject . mpNum ) ;
// Only create this object if it doesn't already exist
if ( ! ptrFound )
{
MWWorld : : ManualRef ref ( MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) , worldObject . refId , 1 ) ;
MWWorld : : Ptr newPtr = ref . getPtr ( ) ;
newPtr . getCellRef ( ) . setMpNum ( worldObject . mpNum ) ;
newPtr = MWBase : : Environment : : get ( ) . getWorld ( ) - > placeObject ( newPtr , cellStore , worldObject . position ) ;
2017-05-30 07:11:01 +00:00
if ( worldObject . hasMaster )
{
MWWorld : : Ptr masterPtr ;
if ( worldObject . master . refId . empty ( ) )
2017-06-27 08:34:32 +00:00
masterPtr = MechanicsHelper : : getPlayerPtr ( worldObject . master ) ;
2017-05-30 07:11:01 +00:00
else
masterPtr = cellStore - > searchExact ( worldObject . master . refNumIndex , worldObject . master . mpNum ) ;
if ( masterPtr )
{
LOG_APPEND ( Log : : LOG_VERBOSE , " -- Actor has master: %s " , masterPtr . getCellRef ( ) . getRefId ( ) . c_str ( ) ) ;
MWMechanics : : AiFollow package ( masterPtr . getCellRef ( ) . getRefId ( ) ) ;
newPtr . getClass ( ) . getCreatureStats ( newPtr ) . getAiSequence ( ) . stack ( package , newPtr ) ;
MWRender : : Animation * anim = MWBase : : Environment : : get ( ) . getWorld ( ) - > getAnimation ( newPtr ) ;
if ( anim )
{
const ESM : : Static * fx = MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) . get < ESM : : Static > ( )
. search ( " VFX_Summon_Start " ) ;
if ( fx )
anim - > addEffect ( " meshes \\ " + fx - > mModel , - 1 , false ) ;
}
int creatureActorId = newPtr . getClass ( ) . getCreatureStats ( newPtr ) . getActorId ( ) ;
MWMechanics : : CreatureStats & masterCreatureStats = masterPtr . getClass ( ) . getCreatureStats ( masterPtr ) ;
masterCreatureStats . setSummonedCreatureActorId ( worldObject . refId , creatureActorId ) ;
}
}
2017-05-29 03:59:05 +00:00
}
else
2017-05-30 07:11:01 +00:00
LOG_APPEND ( Log : : LOG_VERBOSE , " -- Actor already existed! " ) ;
2017-05-29 03:59:05 +00:00
}
}
2017-02-06 15:40:32 +00:00
void WorldEvent : : deleteObjects ( MWWorld : : CellStore * cellStore )
2017-01-28 10:34:45 +00:00
{
2017-06-27 07:49:07 +00:00
for ( const auto & worldObject : worldObjects )
2017-01-28 10:34:45 +00:00
{
2017-04-04 07:07:15 +00:00
LOG_APPEND ( Log : : LOG_VERBOSE , " - cellRef: %s, %i, %i " , worldObject . refId . c_str ( ) , worldObject . refNumIndex , worldObject . mpNum ) ;
2017-01-28 10:34:45 +00:00
2017-05-27 13:56:54 +00:00
MWWorld : : Ptr ptrFound = cellStore - > searchExact ( worldObject . refNumIndex , worldObject . mpNum ) ;
2017-01-28 10:34:45 +00:00
if ( ptrFound )
{
2017-04-05 04:10:22 +00:00
LOG_APPEND ( Log : : LOG_VERBOSE , " -- Found %s, %i, %i " , ptrFound . getCellRef ( ) . getRefId ( ) . c_str ( ) ,
ptrFound . getCellRef ( ) . getRefNum ( ) , ptrFound . getCellRef ( ) . getMpNum ( ) ) ;
2017-01-28 10:34:45 +00:00
2017-05-25 23:13:31 +00:00
// If we are in a container, and it happens to be this object, exit it
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 ( ) )
{
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > removeGuiMode ( MWGui : : GM_Container ) ;
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > setDragDrop ( false ) ;
}
}
2017-01-28 10:34:45 +00:00
MWBase : : Environment : : get ( ) . getWorld ( ) - > deleteObject ( ptrFound ) ;
}
}
}
2017-02-06 15:40:32 +00:00
void WorldEvent : : lockObjects ( MWWorld : : CellStore * cellStore )
2017-01-28 10:34:45 +00:00
{
2017-06-27 07:49:07 +00:00
for ( const auto & worldObject : worldObjects )
2017-01-28 10:34:45 +00:00
{
2017-04-04 07:07:15 +00:00
LOG_APPEND ( Log : : LOG_VERBOSE , " - cellRef: %s, %i, %i " , worldObject . refId . c_str ( ) , worldObject . refNumIndex , worldObject . mpNum ) ;
2017-01-28 10:34:45 +00:00
2017-05-27 13:56:54 +00:00
MWWorld : : Ptr ptrFound = cellStore - > searchExact ( worldObject . refNumIndex , worldObject . mpNum ) ;
2017-01-28 10:34:45 +00:00
if ( ptrFound )
{
2017-04-05 04:10:22 +00:00
LOG_APPEND ( Log : : LOG_VERBOSE , " -- Found %s, %i, %i " , ptrFound . getCellRef ( ) . getRefId ( ) . c_str ( ) ,
ptrFound . getCellRef ( ) . getRefNum ( ) , ptrFound . getCellRef ( ) . getMpNum ( ) ) ;
2017-01-28 10:34:45 +00:00
2017-05-24 10:28:34 +00:00
if ( worldObject . lockLevel > 0 )
ptrFound . getClass ( ) . lock ( ptrFound , worldObject . lockLevel ) ;
else
ptrFound . getClass ( ) . unlock ( ptrFound ) ;
2017-01-28 10:34:45 +00:00
}
}
}
2017-05-25 21:21:24 +00:00
void WorldEvent : : triggerTrapObjects ( MWWorld : : CellStore * cellStore )
{
2017-06-27 07:49:07 +00:00
for ( const auto & worldObject : worldObjects )
2017-05-25 21:21:24 +00:00
{
LOG_APPEND ( Log : : LOG_VERBOSE , " - cellRef: %s, %i, %i " , worldObject . refId . c_str ( ) , worldObject . refNumIndex , worldObject . mpNum ) ;
2017-05-27 13:56:54 +00:00
MWWorld : : Ptr ptrFound = cellStore - > searchExact ( worldObject . refNumIndex , worldObject . mpNum ) ;
2017-05-25 21:21:24 +00:00
if ( ptrFound )
{
LOG_APPEND ( Log : : LOG_VERBOSE , " -- Found %s, %i, %i " , ptrFound . getCellRef ( ) . getRefId ( ) . c_str ( ) ,
ptrFound . getCellRef ( ) . getRefNum ( ) , ptrFound . getCellRef ( ) . getMpNum ( ) ) ;
2017-05-25 22:28:43 +00:00
if ( ! worldObject . isDisarmed )
{
MWMechanics : : CastSpell cast ( ptrFound , ptrFound ) ;
cast . mHitPosition = worldObject . position . asVec3 ( ) ;
cast . cast ( ptrFound . getCellRef ( ) . getTrap ( ) ) ;
}
2017-05-25 21:21:24 +00:00
ptrFound . getCellRef ( ) . setTrap ( " " ) ;
}
}
}
2017-02-06 15:40:32 +00:00
void WorldEvent : : scaleObjects ( MWWorld : : CellStore * cellStore )
2017-01-28 10:34:45 +00:00
{
2017-06-27 07:49:07 +00:00
for ( const auto & worldObject : worldObjects )
2017-01-28 10:34:45 +00:00
{
2017-05-07 00:17:19 +00:00
LOG_APPEND ( Log : : LOG_VERBOSE , " - cellRef: %s, %i, %i, scale: %f " , worldObject . refId . c_str ( ) , worldObject . refNumIndex ,
2017-05-01 16:09:44 +00:00
worldObject . mpNum , worldObject . scale ) ;
2017-01-28 10:34:45 +00:00
2017-05-27 13:56:54 +00:00
MWWorld : : Ptr ptrFound = cellStore - > searchExact ( worldObject . refNumIndex , worldObject . mpNum ) ;
2017-01-28 10:34:45 +00:00
if ( ptrFound )
{
2017-04-05 04:10:22 +00:00
LOG_APPEND ( Log : : LOG_VERBOSE , " -- Found %s, %i, %i " , ptrFound . getCellRef ( ) . getRefId ( ) . c_str ( ) ,
ptrFound . getCellRef ( ) . getRefNum ( ) , ptrFound . getCellRef ( ) . getMpNum ( ) ) ;
2017-01-28 10:34:45 +00:00
MWBase : : Environment : : get ( ) . getWorld ( ) - > scaleObject ( ptrFound , worldObject . scale ) ;
}
}
}
2017-07-13 06:46:30 +00:00
void WorldEvent : : setObjectStates ( MWWorld : : CellStore * cellStore )
{
for ( const auto & worldObject : worldObjects )
{
LOG_APPEND ( Log : : LOG_VERBOSE , " - cellRef: %s, %i, %i, state: %s " , worldObject . refId . c_str ( ) , worldObject . refNumIndex ,
worldObject . mpNum , worldObject . objectState ? " true " : " false " ) ;
MWWorld : : Ptr ptrFound = cellStore - > searchExact ( worldObject . refNumIndex , worldObject . mpNum ) ;
if ( ptrFound )
{
LOG_APPEND ( Log : : LOG_VERBOSE , " -- Found %s, %i, %i " , ptrFound . getCellRef ( ) . getRefId ( ) . c_str ( ) ,
ptrFound . getCellRef ( ) . getRefNum ( ) , ptrFound . getCellRef ( ) . getMpNum ( ) ) ;
if ( worldObject . objectState )
MWBase : : Environment : : get ( ) . getWorld ( ) - > enable ( ptrFound ) ;
else
MWBase : : Environment : : get ( ) . getWorld ( ) - > disable ( ptrFound ) ;
}
}
}
2017-02-06 15:40:32 +00:00
void WorldEvent : : moveObjects ( MWWorld : : CellStore * cellStore )
2017-01-27 18:57:47 +00:00
{
2017-06-27 07:49:07 +00:00
for ( const auto & worldObject : worldObjects )
2017-01-28 10:34:45 +00:00
{
2017-04-04 07:07:15 +00:00
LOG_APPEND ( Log : : LOG_VERBOSE , " - cellRef: %s, %i, %i " , worldObject . refId . c_str ( ) , worldObject . refNumIndex , worldObject . mpNum ) ;
2017-01-28 10:34:45 +00:00
2017-05-27 13:56:54 +00:00
MWWorld : : Ptr ptrFound = cellStore - > searchExact ( worldObject . refNumIndex , worldObject . mpNum ) ;
2017-01-28 10:34:45 +00:00
if ( ptrFound )
{
2017-04-05 04:10:22 +00:00
LOG_APPEND ( Log : : LOG_VERBOSE , " -- Found %s, %i, %i " , ptrFound . getCellRef ( ) . getRefId ( ) . c_str ( ) ,
ptrFound . getCellRef ( ) . getRefNum ( ) , ptrFound . getCellRef ( ) . getMpNum ( ) ) ;
2017-01-28 10:34:45 +00:00
2017-04-10 06:25:20 +00:00
MWBase : : Environment : : get ( ) . getWorld ( ) - > moveObject ( ptrFound , worldObject . position . pos [ 0 ] , worldObject . position . pos [ 1 ] ,
worldObject . position . pos [ 2 ] ) ;
2017-01-28 10:34:45 +00:00
}
}
2017-01-27 18:57:47 +00:00
}
2017-02-06 15:40:32 +00:00
void WorldEvent : : rotateObjects ( MWWorld : : CellStore * cellStore )
2017-01-27 18:57:47 +00:00
{
2017-06-27 07:49:07 +00:00
for ( const auto & worldObject : worldObjects )
2017-01-28 10:34:45 +00:00
{
2017-04-04 07:07:15 +00:00
LOG_APPEND ( Log : : LOG_VERBOSE , " - cellRef: %s, %i, %i " , worldObject . refId . c_str ( ) , worldObject . refNumIndex , worldObject . mpNum ) ;
2017-01-28 10:34:45 +00:00
2017-05-27 13:56:54 +00:00
MWWorld : : Ptr ptrFound = cellStore - > searchExact ( worldObject . refNumIndex , worldObject . mpNum ) ;
2017-01-28 10:34:45 +00:00
if ( ptrFound )
{
2017-04-05 04:10:22 +00:00
LOG_APPEND ( Log : : LOG_VERBOSE , " -- Found %s, %i, %i " , ptrFound . getCellRef ( ) . getRefId ( ) . c_str ( ) ,
ptrFound . getCellRef ( ) . getRefNum ( ) , ptrFound . getCellRef ( ) . getMpNum ( ) ) ;
2017-01-28 10:34:45 +00:00
MWBase : : Environment : : get ( ) . getWorld ( ) - > rotateObject ( ptrFound ,
2017-04-10 06:25:20 +00:00
worldObject . position . rot [ 0 ] , worldObject . position . rot [ 1 ] , worldObject . position . rot [ 2 ] ) ;
2017-01-28 10:34:45 +00:00
}
}
}
2017-02-06 15:40:32 +00:00
void WorldEvent : : animateObjects ( MWWorld : : CellStore * cellStore )
2017-01-28 10:34:45 +00:00
{
2017-06-27 07:49:07 +00:00
for ( const auto & worldObject : worldObjects )
2017-01-28 10:34:45 +00:00
{
2017-04-05 06:12:02 +00:00
LOG_APPEND ( Log : : LOG_VERBOSE , " - cellRef: %s, %i, %i " , worldObject . refId . c_str ( ) , worldObject . refNumIndex , worldObject . mpNum ) ;
2017-05-27 13:56:54 +00:00
MWWorld : : Ptr ptrFound = cellStore - > searchExact ( worldObject . refNumIndex , worldObject . mpNum ) ;
2017-04-05 06:12:02 +00:00
if ( ptrFound )
{
LOG_APPEND ( Log : : LOG_VERBOSE , " -- Found %s, %i, %i " , ptrFound . getCellRef ( ) . getRefId ( ) . c_str ( ) ,
ptrFound . getCellRef ( ) . getRefNum ( ) , ptrFound . getCellRef ( ) . getMpNum ( ) ) ;
MWBase : : MechanicsManager * mechanicsManager = MWBase : : Environment : : get ( ) . getMechanicsManager ( ) ;
mechanicsManager - > playAnimationGroup ( ptrFound , worldObject . animGroup , worldObject . animMode ,
std : : numeric_limits < int > : : max ( ) , true ) ;
}
}
}
void WorldEvent : : activateDoors ( MWWorld : : CellStore * cellStore )
{
2017-06-27 07:49:07 +00:00
for ( const auto & worldObject : worldObjects )
2017-04-05 06:12:02 +00:00
{
LOG_APPEND ( Log : : LOG_VERBOSE , " - cellRef: %s, %i, %i " , worldObject . refId . c_str ( ) , worldObject . refNumIndex , worldObject . mpNum ) ;
2017-05-27 13:56:54 +00:00
MWWorld : : Ptr ptrFound = cellStore - > searchExact ( worldObject . refNumIndex , worldObject . mpNum ) ;
2017-04-05 06:12:02 +00:00
if ( ptrFound )
{
LOG_APPEND ( Log : : LOG_VERBOSE , " -- Found %s, %i, %i " , ptrFound . getCellRef ( ) . getRefId ( ) . c_str ( ) ,
ptrFound . getCellRef ( ) . getRefNum ( ) , ptrFound . getCellRef ( ) . getMpNum ( ) ) ;
ptrFound . getClass ( ) . setDoorState ( ptrFound , worldObject . doorState ) ;
MWBase : : Environment : : get ( ) . getWorld ( ) - > saveDoorState ( ptrFound , worldObject . doorState ) ;
}
}
}
2017-11-22 22:21:47 +00:00
void WorldEvent : : runConsoleCommands ( MWWorld : : CellStore * cellStore )
{
MWBase : : WindowManager * windowManager = MWBase : : Environment : : get ( ) . getWindowManager ( ) ;
LOG_APPEND ( Log : : LOG_VERBOSE , " - console command: %s " , consoleCommand . c_str ( ) ) ;
if ( worldObjects . empty ( ) )
{
2017-12-26 13:04:28 +00:00
windowManager - > clearConsolePtr ( ) ;
2017-11-22 22:21:47 +00:00
LOG_APPEND ( Log : : LOG_VERBOSE , " -- running with no object reference " ) ;
windowManager - > executeCommandInConsole ( consoleCommand ) ;
}
else
{
for ( const auto & worldObject : worldObjects )
{
2017-12-26 13:04:28 +00:00
windowManager - > clearConsolePtr ( ) ;
2017-11-22 22:21:47 +00:00
if ( worldObject . isPlayer )
{
2017-12-26 13:04:28 +00:00
if ( worldObject . guid = = Main : : get ( ) . getLocalPlayer ( ) - > guid )
{
LOG_APPEND ( Log : : LOG_VERBOSE , " -- running on local player " ) ;
2018-01-31 21:23:52 +00:00
windowManager - > setConsolePtr ( Main : : get ( ) . getLocalPlayer ( ) - > getPlayerPtr ( ) ) ;
2017-12-26 13:04:28 +00:00
windowManager - > executeCommandInConsole ( consoleCommand ) ;
}
2018-01-04 23:24:15 +00:00
else
2017-12-26 13:04:28 +00:00
{
2018-01-31 21:23:52 +00:00
DedicatedPlayer * player = PlayerList : : getPlayer ( worldObject . guid ) ;
2017-11-22 22:21:47 +00:00
2018-01-04 23:24:15 +00:00
if ( player ! = 0 )
{
LOG_APPEND ( Log : : LOG_VERBOSE , " -- running on player %s " , player - > npc . mName . c_str ( ) ) ;
2018-01-31 21:23:52 +00:00
windowManager - > setConsolePtr ( player - > getPtr ( ) ) ;
2018-01-04 23:24:15 +00:00
windowManager - > executeCommandInConsole ( consoleCommand ) ;
}
2017-12-26 13:04:28 +00:00
}
2017-11-22 22:21:47 +00:00
}
else
{
LOG_APPEND ( Log : : LOG_VERBOSE , " -- running on cellRef: %s, %i, %i " , worldObject . refId . c_str ( ) , worldObject . refNumIndex , worldObject . mpNum ) ;
MWWorld : : Ptr ptrFound = cellStore - > searchExact ( worldObject . refNumIndex , worldObject . mpNum ) ;
if ( ptrFound )
{
LOG_APPEND ( Log : : LOG_VERBOSE , " -- Found %s, %i, %i " , ptrFound . getCellRef ( ) . getRefId ( ) . c_str ( ) ,
ptrFound . getCellRef ( ) . getRefNum ( ) , ptrFound . getCellRef ( ) . getMpNum ( ) ) ;
2017-12-26 13:04:28 +00:00
windowManager - > setConsolePtr ( ptrFound ) ;
2017-11-22 22:21:47 +00:00
windowManager - > executeCommandInConsole ( consoleCommand ) ;
}
}
}
2017-12-26 13:04:28 +00:00
windowManager - > clearConsolePtr ( ) ;
2018-01-31 21:23:52 +00:00
}
2017-11-22 22:21:47 +00:00
}
2017-04-05 06:12:02 +00:00
void WorldEvent : : setLocalShorts ( MWWorld : : CellStore * cellStore )
{
2017-06-27 07:49:07 +00:00
for ( const auto & worldObject : worldObjects )
2017-04-05 06:12:02 +00:00
{
2017-05-07 00:17:19 +00:00
LOG_APPEND ( Log : : LOG_VERBOSE , " - cellRef: %s, %i, %i, index: %i, shortVal: %i " , worldObject . refId . c_str ( ) ,
2017-04-05 06:12:02 +00:00
worldObject . refNumIndex , worldObject . mpNum , worldObject . index , worldObject . shortVal ) ;
2017-05-27 13:56:54 +00:00
MWWorld : : Ptr ptrFound = cellStore - > searchExact ( worldObject . refNumIndex , worldObject . mpNum ) ;
2017-04-05 06:12:02 +00:00
if ( ptrFound )
{
LOG_APPEND ( Log : : LOG_VERBOSE , " -- Found %s, %i, %i " , ptrFound . getCellRef ( ) . getRefId ( ) . c_str ( ) ,
ptrFound . getCellRef ( ) . getRefNum ( ) , ptrFound . getCellRef ( ) . getMpNum ( ) ) ;
ptrFound . getRefData ( ) . getLocals ( ) . mShorts . at ( worldObject . index ) = worldObject . shortVal ;
}
}
}
void WorldEvent : : setLocalFloats ( MWWorld : : CellStore * cellStore )
{
2017-06-27 07:49:07 +00:00
for ( const auto & worldObject : worldObjects )
2017-04-05 06:12:02 +00:00
{
2017-05-07 00:17:19 +00:00
LOG_APPEND ( Log : : LOG_VERBOSE , " - cellRef: %s, %i, %i, index: %i, floatVal: %f " , worldObject . refId . c_str ( ) ,
2017-04-05 06:12:02 +00:00
worldObject . refNumIndex , worldObject . mpNum , worldObject . index , worldObject . floatVal ) ;
2017-05-27 13:56:54 +00:00
MWWorld : : Ptr ptrFound = cellStore - > searchExact ( worldObject . refNumIndex , worldObject . mpNum ) ;
2017-04-05 06:12:02 +00:00
if ( ptrFound )
{
LOG_APPEND ( Log : : LOG_VERBOSE , " -- Found %s, %i, %i " , ptrFound . getCellRef ( ) . getRefId ( ) . c_str ( ) ,
ptrFound . getCellRef ( ) . getRefNum ( ) , ptrFound . getCellRef ( ) . getMpNum ( ) ) ;
ptrFound . getRefData ( ) . getLocals ( ) . mFloats . at ( worldObject . index ) = worldObject . floatVal ;
}
}
}
void WorldEvent : : setMemberShorts ( )
{
2017-06-27 07:49:07 +00:00
for ( const auto & worldObject : worldObjects )
2017-04-05 06:12:02 +00:00
{
2017-05-07 00:17:19 +00:00
LOG_APPEND ( Log : : LOG_VERBOSE , " - cellRef: %s, index: %i, shortVal: %i " , worldObject . refId . c_str ( ) ,
2017-04-05 06:12:02 +00:00
worldObject . index , worldObject . shortVal ) ;
// Mimic the way a Ptr is fetched in InterpreterContext for similar situations
MWWorld : : Ptr ptrFound = MWBase : : Environment : : get ( ) . getWorld ( ) - > searchPtr ( worldObject . refId , false ) ;
if ( ! ptrFound . isEmpty ( ) )
{
LOG_APPEND ( Log : : LOG_VERBOSE , " -- Found %s, %i, %i " , ptrFound . getCellRef ( ) . getRefId ( ) . c_str ( ) ,
ptrFound . getCellRef ( ) . getRefNum ( ) , ptrFound . getCellRef ( ) . getMpNum ( ) ) ;
std : : string scriptId = ptrFound . getClass ( ) . getScript ( ptrFound ) ;
ptrFound . getRefData ( ) . setLocals (
* MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) . get < ESM : : Script > ( ) . find ( scriptId ) ) ;
ptrFound . getRefData ( ) . getLocals ( ) . mShorts . at ( worldObject . index ) = worldObject . shortVal ; ;
}
}
}
void WorldEvent : : setGlobalShorts ( )
{
2017-06-27 07:49:07 +00:00
for ( const auto & worldObject : worldObjects )
2017-04-05 06:12:02 +00:00
{
2017-05-07 00:17:19 +00:00
LOG_APPEND ( Log : : LOG_VERBOSE , " - varName: %s, shortVal: %i " , worldObject . varName . c_str ( ) , worldObject . shortVal ) ;
2017-04-05 06:12:02 +00:00
MWBase : : Environment : : get ( ) . getWorld ( ) - > setGlobalInt ( worldObject . varName , worldObject . shortVal ) ;
}
}
void WorldEvent : : playMusic ( )
{
2017-06-27 07:49:07 +00:00
for ( const auto & worldObject : worldObjects )
2017-04-05 06:12:02 +00:00
{
LOG_APPEND ( Log : : LOG_VERBOSE , " - filename: %s " , worldObject . filename . c_str ( ) ) ;
MWBase : : Environment : : get ( ) . getSoundManager ( ) - > streamMusic ( worldObject . filename ) ;
}
}
void WorldEvent : : playVideo ( )
{
2017-06-27 07:49:07 +00:00
for ( const auto & worldObject : worldObjects )
2017-04-05 06:12:02 +00:00
{
2017-05-07 00:17:19 +00:00
LOG_APPEND ( Log : : LOG_VERBOSE , " - filename: %s, allowSkipping: %s " , worldObject . filename . c_str ( ) ,
2017-04-05 06:12:02 +00:00
worldObject . allowSkipping ? " true " : " false " ) ;
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > playVideo ( worldObject . filename , worldObject . allowSkipping ) ;
}
}
[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.
2018-03-26 16:27:36 +00:00
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 ) ;
}
2018-01-28 15:00:14 +00:00
void WorldEvent : : addObjectPlace ( const MWWorld : : Ptr & ptr , bool droppedByPlayer )
2017-04-05 06:12:02 +00:00
{
2018-01-17 09:01:31 +00:00
if ( ptr . getCellRef ( ) . getRefId ( ) . find ( " $dynamic " ) ! = string : : npos )
{
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > messageBox ( " You're trying to place a custom item, but those are not synchronized in multiplayer yet. " ) ;
return ;
}
2017-04-05 06:12:02 +00:00
cell = * ptr . getCell ( ) - > getCell ( ) ;
mwmp : : WorldObject worldObject ;
worldObject . refId = ptr . getCellRef ( ) . getRefId ( ) ;
worldObject . refNumIndex = ptr . getCellRef ( ) . getRefNum ( ) . mIndex ;
worldObject . mpNum = 0 ;
worldObject . charge = ptr . getCellRef ( ) . getCharge ( ) ;
2017-12-23 11:16:38 +00:00
worldObject . enchantmentCharge = ptr . getCellRef ( ) . getEnchantmentCharge ( ) ;
2018-01-28 15:00:14 +00:00
worldObject . droppedByPlayer = droppedByPlayer ;
2017-04-05 06:12:02 +00:00
// Make sure we send the RefData position instead of the CellRef one, because that's what
// we actually see on this client
2017-04-10 06:25:20 +00:00
worldObject . position = ptr . getRefData ( ) . getPosition ( ) ;
2017-04-05 06:12:02 +00:00
// We have to get the count from the dropped object because it gets changed
// automatically for stacks of gold
worldObject . count = ptr . getRefData ( ) . getCount ( ) ;
// Get the real count of gold in a stack
worldObject . goldValue = ptr . getCellRef ( ) . getGoldValue ( ) ;
2017-01-28 10:34:45 +00:00
2017-05-29 03:59:05 +00:00
addObject ( worldObject ) ;
}
void WorldEvent : : addObjectSpawn ( const MWWorld : : Ptr & ptr )
{
2018-01-17 09:01:31 +00:00
if ( ptr . getCellRef ( ) . getRefId ( ) . find ( " $dynamic " ) ! = string : : npos )
{
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > messageBox ( " You're trying to spawn a custom object, but those are not synchronized in multiplayer yet. " ) ;
return ;
}
2017-05-29 03:59:05 +00:00
cell = * ptr . getCell ( ) - > getCell ( ) ;
mwmp : : WorldObject worldObject ;
worldObject . refId = ptr . getCellRef ( ) . getRefId ( ) ;
worldObject . refNumIndex = ptr . getCellRef ( ) . getRefNum ( ) . mIndex ;
worldObject . mpNum = 0 ;
2017-05-30 07:11:01 +00:00
worldObject . hasMaster = false ;
// Make sure we send the RefData position instead of the CellRef one, because that's what
// we actually see on this client
worldObject . position = ptr . getRefData ( ) . getPosition ( ) ;
addObject ( worldObject ) ;
}
void WorldEvent : : addObjectSpawn ( const MWWorld : : Ptr & ptr , const MWWorld : : Ptr & master )
{
cell = * ptr . getCell ( ) - > getCell ( ) ;
mwmp : : WorldObject worldObject ;
worldObject . refId = ptr . getCellRef ( ) . getRefId ( ) ;
worldObject . refNumIndex = ptr . getCellRef ( ) . getRefNum ( ) . mIndex ;
worldObject . mpNum = 0 ;
worldObject . hasMaster = true ;
if ( master = = MWBase : : Environment : : get ( ) . getWorld ( ) - > getPlayerPtr ( ) )
{
worldObject . master . guid = mwmp : : Main : : get ( ) . getLocalPlayer ( ) - > guid ;
worldObject . master . refId . clear ( ) ;
}
else if ( mwmp : : PlayerList : : isDedicatedPlayer ( master ) )
{
worldObject . master . guid = mwmp : : PlayerList : : getPlayer ( master ) - > guid ;
worldObject . master . refId . clear ( ) ;
}
else
{
MWWorld : : CellRef * masterRef = & master . getCellRef ( ) ;
worldObject . master . refId = masterRef - > getRefId ( ) ;
worldObject . master . refNumIndex = masterRef - > getRefNum ( ) . mIndex ;
worldObject . master . mpNum = masterRef - > getMpNum ( ) ;
}
2017-05-29 03:59:05 +00:00
// Make sure we send the RefData position instead of the CellRef one, because that's what
// we actually see on this client
worldObject . position = ptr . getRefData ( ) . getPosition ( ) ;
2017-05-07 00:07:09 +00:00
2017-04-05 06:12:02 +00:00
addObject ( worldObject ) ;
2017-01-28 10:34:45 +00:00
}
2017-05-06 17:44:14 +00:00
void WorldEvent : : addObjectDelete ( const MWWorld : : Ptr & ptr )
2017-01-28 10:34:45 +00:00
{
2017-04-05 06:12:02 +00:00
cell = * ptr . getCell ( ) - > getCell ( ) ;
2017-01-28 10:34:45 +00:00
2017-04-05 06:12:02 +00:00
mwmp : : WorldObject worldObject ;
worldObject . refId = ptr . getCellRef ( ) . getRefId ( ) ;
worldObject . refNumIndex = ptr . getCellRef ( ) . getRefNum ( ) . mIndex ;
worldObject . mpNum = ptr . getCellRef ( ) . getMpNum ( ) ;
addObject ( worldObject ) ;
}
2017-01-28 10:34:45 +00:00
2017-05-06 17:44:14 +00:00
void WorldEvent : : addObjectLock ( const MWWorld : : Ptr & ptr , int lockLevel )
2017-04-05 06:12:02 +00:00
{
cell = * ptr . getCell ( ) - > getCell ( ) ;
2017-01-28 10:34:45 +00:00
2017-04-05 06:12:02 +00:00
mwmp : : WorldObject worldObject ;
worldObject . refId = ptr . getCellRef ( ) . getRefId ( ) ;
worldObject . refNumIndex = ptr . getCellRef ( ) . getRefNum ( ) . mIndex ;
worldObject . mpNum = ptr . getCellRef ( ) . getMpNum ( ) ;
worldObject . lockLevel = lockLevel ;
addObject ( worldObject ) ;
2017-01-28 10:34:45 +00:00
}
2017-05-25 22:28:43 +00:00
void WorldEvent : : addObjectTrap ( const MWWorld : : Ptr & ptr , const ESM : : Position & pos , bool isDisarmed )
2017-05-25 21:21:24 +00:00
{
cell = * ptr . getCell ( ) - > getCell ( ) ;
mwmp : : WorldObject worldObject ;
worldObject . refId = ptr . getCellRef ( ) . getRefId ( ) ;
worldObject . refNumIndex = ptr . getCellRef ( ) . getRefNum ( ) . mIndex ;
worldObject . mpNum = ptr . getCellRef ( ) . getMpNum ( ) ;
2017-05-25 22:28:43 +00:00
worldObject . isDisarmed = isDisarmed ;
worldObject . position = pos ;
2017-05-25 21:21:24 +00:00
addObject ( worldObject ) ;
}
2017-05-06 17:44:14 +00:00
void WorldEvent : : addObjectScale ( const MWWorld : : Ptr & ptr , float scale )
2017-01-28 10:34:45 +00:00
{
2017-04-05 06:12:02 +00:00
cell = * ptr . getCell ( ) - > getCell ( ) ;
2017-01-28 10:34:45 +00:00
2017-04-05 06:12:02 +00:00
mwmp : : WorldObject worldObject ;
worldObject . refId = ptr . getCellRef ( ) . getRefId ( ) ;
worldObject . refNumIndex = ptr . getCellRef ( ) . getRefNum ( ) . mIndex ;
worldObject . mpNum = ptr . getCellRef ( ) . getMpNum ( ) ;
worldObject . scale = scale ;
addObject ( worldObject ) ;
}
2017-01-28 10:34:45 +00:00
2017-07-13 06:46:30 +00:00
void WorldEvent : : addObjectState ( const MWWorld : : Ptr & ptr , bool objectState )
{
cell = * ptr . getCell ( ) - > getCell ( ) ;
mwmp : : WorldObject worldObject ;
worldObject . refId = ptr . getCellRef ( ) . getRefId ( ) ;
worldObject . refNumIndex = ptr . getCellRef ( ) . getRefNum ( ) . mIndex ;
worldObject . mpNum = ptr . getCellRef ( ) . getMpNum ( ) ;
worldObject . objectState = objectState ;
addObject ( worldObject ) ;
}
2017-05-06 17:44:14 +00:00
void WorldEvent : : addObjectAnimPlay ( const MWWorld : : Ptr & ptr , std : : string group , int mode )
2017-04-05 06:12:02 +00:00
{
cell = * ptr . getCell ( ) - > getCell ( ) ;
mwmp : : WorldObject worldObject ;
worldObject . refId = ptr . getCellRef ( ) . getRefId ( ) ;
worldObject . refNumIndex = ptr . getCellRef ( ) . getRefNum ( ) . mIndex ;
worldObject . mpNum = ptr . getCellRef ( ) . getMpNum ( ) ;
worldObject . animGroup = group ;
worldObject . animMode = mode ;
addObject ( worldObject ) ;
2017-01-28 10:34:45 +00:00
}
2017-05-06 17:44:14 +00:00
void WorldEvent : : addDoorState ( const MWWorld : : Ptr & ptr , int state )
2017-01-28 10:34:45 +00:00
{
2017-04-05 06:12:02 +00:00
cell = * ptr . getCell ( ) - > getCell ( ) ;
2017-01-28 10:34:45 +00:00
2017-04-05 06:12:02 +00:00
mwmp : : WorldObject worldObject ;
worldObject . refId = ptr . getCellRef ( ) . getRefId ( ) ;
worldObject . refNumIndex = ptr . getCellRef ( ) . getRefNum ( ) . mIndex ;
worldObject . mpNum = ptr . getCellRef ( ) . getMpNum ( ) ;
worldObject . doorState = state ;
addObject ( worldObject ) ;
}
2017-01-28 10:34:45 +00:00
2017-05-06 17:44:14 +00:00
void WorldEvent : : addMusicPlay ( std : : string filename )
2017-04-05 06:12:02 +00:00
{
mwmp : : WorldObject worldObject ;
worldObject . filename = filename ;
addObject ( worldObject ) ;
2017-01-28 10:34:45 +00:00
}
2017-05-06 17:44:14 +00:00
void WorldEvent : : addVideoPlay ( std : : string filename , bool allowSkipping )
2017-01-28 10:34:45 +00:00
{
2017-04-05 06:12:02 +00:00
mwmp : : WorldObject worldObject ;
worldObject . filename = filename ;
worldObject . allowSkipping = allowSkipping ;
addObject ( worldObject ) ;
}
2017-01-28 10:34:45 +00:00
2017-05-06 17:44:14 +00:00
void WorldEvent : : addScriptLocalShort ( const MWWorld : : Ptr & ptr , int index , int shortVal )
2017-04-05 06:12:02 +00:00
{
cell = * ptr . getCell ( ) - > getCell ( ) ;
2017-01-28 10:34:45 +00:00
2017-04-05 06:12:02 +00:00
mwmp : : WorldObject worldObject ;
worldObject . refId = ptr . getCellRef ( ) . getRefId ( ) ;
worldObject . refNumIndex = ptr . getCellRef ( ) . getRefNum ( ) . mIndex ;
worldObject . mpNum = ptr . getCellRef ( ) . getMpNum ( ) ;
worldObject . index = index ;
worldObject . shortVal = shortVal ;
addObject ( worldObject ) ;
2017-01-28 10:34:45 +00:00
}
2017-05-06 17:44:14 +00:00
void WorldEvent : : addScriptLocalFloat ( const MWWorld : : Ptr & ptr , int index , float floatVal )
2017-01-28 10:34:45 +00:00
{
2017-04-05 06:12:02 +00:00
cell = * ptr . getCell ( ) - > getCell ( ) ;
2017-01-28 10:34:45 +00:00
2017-04-05 06:12:02 +00:00
mwmp : : WorldObject worldObject ;
worldObject . refId = ptr . getCellRef ( ) . getRefId ( ) ;
worldObject . refNumIndex = ptr . getCellRef ( ) . getRefNum ( ) . mIndex ;
worldObject . mpNum = ptr . getCellRef ( ) . getMpNum ( ) ;
worldObject . index = index ;
worldObject . floatVal = floatVal ;
addObject ( worldObject ) ;
}
2017-01-28 10:34:45 +00:00
2017-05-06 17:44:14 +00:00
void WorldEvent : : addScriptMemberShort ( std : : string refId , int index , int shortVal )
2017-04-05 06:12:02 +00:00
{
mwmp : : WorldObject worldObject ;
worldObject . refId = refId ;
worldObject . index = index ;
worldObject . shortVal = shortVal ;
addObject ( worldObject ) ;
2017-01-28 10:34:45 +00:00
}
2017-05-06 17:44:14 +00:00
void WorldEvent : : addScriptGlobalShort ( std : : string varName , int shortVal )
2017-01-28 10:34:45 +00:00
{
2017-04-05 06:12:02 +00:00
mwmp : : WorldObject worldObject ;
worldObject . varName = varName ;
worldObject . shortVal = shortVal ;
addObject ( worldObject ) ;
2017-05-06 17:44:14 +00:00
}
void WorldEvent : : sendObjectPlace ( )
{
2018-01-17 09:01:31 +00:00
if ( worldObjects . size ( ) = = 0 )
return ;
2017-05-06 17:44:14 +00:00
LOG_MESSAGE_SIMPLE ( Log : : LOG_VERBOSE , " Sending ID_OBJECT_PLACE about %s " , cell . getDescription ( ) . c_str ( ) ) ;
2017-06-27 07:49:07 +00:00
for ( const auto & worldObject : worldObjects )
LOG_APPEND ( Log : : LOG_VERBOSE , " - cellRef: %s, count: %i " , worldObject . refId . c_str ( ) , worldObject . count ) ;
2017-05-06 17:44:14 +00:00
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_OBJECT_PLACE ) - > setEvent ( this ) ;
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_OBJECT_PLACE ) - > Send ( ) ;
}
2017-05-29 03:59:05 +00:00
void WorldEvent : : sendObjectSpawn ( )
{
2018-01-17 09:01:31 +00:00
if ( worldObjects . size ( ) = = 0 )
return ;
2017-05-29 03:59:05 +00:00
LOG_MESSAGE_SIMPLE ( Log : : LOG_VERBOSE , " Sending ID_OBJECT_SPAWN about %s " , cell . getDescription ( ) . c_str ( ) ) ;
2017-06-27 07:49:07 +00:00
for ( const auto & worldObject : worldObjects )
LOG_APPEND ( Log : : LOG_VERBOSE , " - cellRef: %s-%i " , worldObject . refId . c_str ( ) , worldObject . refNumIndex ) ;
2017-05-29 03:59:05 +00:00
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_OBJECT_SPAWN ) - > setEvent ( this ) ;
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_OBJECT_SPAWN ) - > Send ( ) ;
}
2017-05-06 17:44:14 +00:00
void WorldEvent : : sendObjectDelete ( )
{
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_OBJECT_DELETE ) - > setEvent ( this ) ;
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_OBJECT_DELETE ) - > Send ( ) ;
}
void WorldEvent : : sendObjectLock ( )
{
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_OBJECT_LOCK ) - > setEvent ( this ) ;
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_OBJECT_LOCK ) - > Send ( ) ;
}
2017-05-25 21:21:24 +00:00
void WorldEvent : : sendObjectTrap ( )
{
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_OBJECT_TRAP ) - > setEvent ( this ) ;
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_OBJECT_TRAP ) - > Send ( ) ;
}
2017-05-06 17:44:14 +00:00
void WorldEvent : : sendObjectScale ( )
{
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_OBJECT_SCALE ) - > setEvent ( this ) ;
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_OBJECT_SCALE ) - > Send ( ) ;
}
2017-07-13 06:46:30 +00:00
void WorldEvent : : sendObjectState ( )
{
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_OBJECT_STATE ) - > setEvent ( this ) ;
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_OBJECT_STATE ) - > Send ( ) ;
}
2017-05-06 17:44:14 +00:00
void WorldEvent : : sendObjectAnimPlay ( )
{
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_OBJECT_ANIM_PLAY ) - > setEvent ( this ) ;
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_OBJECT_ANIM_PLAY ) - > Send ( ) ;
}
void WorldEvent : : sendDoorState ( )
{
LOG_MESSAGE_SIMPLE ( Log : : LOG_VERBOSE , " Sending ID_DOOR_STATE about %s " , cell . getDescription ( ) . c_str ( ) ) ;
2017-06-27 07:49:07 +00:00
for ( const auto & worldObject : worldObjects )
LOG_APPEND ( Log : : LOG_VERBOSE , " - cellRef: %s-%i, state: %s " , worldObject . refId . c_str ( ) , worldObject . refNumIndex ,
worldObject . doorState ? " true " : " false " ) ;
2017-05-06 17:44:14 +00:00
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_DOOR_STATE ) - > setEvent ( this ) ;
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_DOOR_STATE ) - > Send ( ) ;
}
void WorldEvent : : sendMusicPlay ( )
{
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_MUSIC_PLAY ) - > setEvent ( this ) ;
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_MUSIC_PLAY ) - > Send ( ) ;
}
void WorldEvent : : sendVideoPlay ( )
{
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_VIDEO_PLAY ) - > setEvent ( this ) ;
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_VIDEO_PLAY ) - > Send ( ) ;
}
void WorldEvent : : sendScriptLocalShort ( )
{
LOG_MESSAGE_SIMPLE ( Log : : LOG_VERBOSE , " Sending ID_SCRIPT_LOCAL_SHORT about %s " , cell . getDescription ( ) . c_str ( ) ) ;
2017-06-27 07:49:07 +00:00
for ( const auto & worldObject : worldObjects )
LOG_APPEND ( Log : : LOG_VERBOSE , " - cellRef: %s-%i, index: %i, shortVal: %i " , worldObject . refId . c_str ( ) ,
worldObject . refNumIndex , worldObject . index , worldObject . shortVal ) ;
2017-05-06 17:44:14 +00:00
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_SCRIPT_LOCAL_SHORT ) - > setEvent ( this ) ;
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_SCRIPT_LOCAL_SHORT ) - > Send ( ) ;
}
void WorldEvent : : sendScriptLocalFloat ( )
{
LOG_MESSAGE_SIMPLE ( Log : : LOG_VERBOSE , " Sending ID_SCRIPT_LOCAL_FLOAT about %s " , cell . getDescription ( ) . c_str ( ) ) ;
2017-06-27 07:49:07 +00:00
for ( const auto & worldObject : worldObjects )
LOG_APPEND ( Log : : LOG_VERBOSE , " - cellRef: %s-%i, index: %i, floatVal: %f " , worldObject . refId . c_str ( ) ,
worldObject . refNumIndex , worldObject . index , worldObject . floatVal ) ;
2017-05-06 17:44:14 +00:00
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_SCRIPT_LOCAL_FLOAT ) - > setEvent ( this ) ;
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_SCRIPT_LOCAL_FLOAT ) - > Send ( ) ;
}
void WorldEvent : : sendScriptMemberShort ( )
{
LOG_MESSAGE_SIMPLE ( Log : : LOG_VERBOSE , " Sending ID_SCRIPT_MEMBER_SHORT " ) ;
2017-06-27 07:49:07 +00:00
for ( const auto & worldObject : worldObjects )
LOG_APPEND ( Log : : LOG_VERBOSE , " - cellRef: %s, index: %i, shortVal: %i " , worldObject . refId . c_str ( ) ,
worldObject . index , worldObject . shortVal ) ;
2017-05-06 17:44:14 +00:00
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_SCRIPT_MEMBER_SHORT ) - > setEvent ( this ) ;
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_SCRIPT_MEMBER_SHORT ) - > Send ( ) ;
}
void WorldEvent : : sendScriptGlobalShort ( )
{
LOG_MESSAGE_SIMPLE ( Log : : LOG_VERBOSE , " Sending ID_SCRIPT_GLOBAL_SHORT " ) ;
2017-06-27 07:49:07 +00:00
for ( const auto & worldObject : worldObjects )
LOG_APPEND ( Log : : LOG_VERBOSE , " - varName: %s, shortVal: %i " , worldObject . varName . c_str ( ) , worldObject . shortVal ) ;
2017-01-28 10:34:45 +00:00
2017-04-05 06:12:02 +00:00
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_SCRIPT_GLOBAL_SHORT ) - > setEvent ( this ) ;
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_SCRIPT_GLOBAL_SHORT ) - > Send ( ) ;
2017-05-06 17:44:14 +00:00
}
[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.
2018-03-26 16:27:36 +00:00
void WorldEvent : : sendCellContainers ( MWWorld : : CellStore * cellStore )
2017-05-06 17:44:14 +00:00
{
reset ( ) ;
cell = * cellStore - > getCell ( ) ;
action = BaseEvent : : SET ;
MWWorld : : CellRefList < ESM : : Container > * containerList = cellStore - > getContainers ( ) ;
2017-06-27 07:49:07 +00:00
for ( auto & container : containerList - > mList )
2017-05-06 17:44:14 +00:00
{
mwmp : : WorldObject worldObject ;
2017-06-27 07:49:07 +00:00
worldObject . refId = container . mRef . getRefId ( ) ;
worldObject . refNumIndex = container . mRef . getRefNum ( ) . mIndex ;
worldObject . mpNum = container . mRef . getMpNum ( ) ;
2017-05-06 17:44:14 +00:00
2017-06-27 07:49:07 +00:00
MWWorld : : ContainerStore & containerStore = container . mClass - > getContainerStore ( MWWorld : : Ptr ( & container , 0 ) ) ;
2017-05-06 17:44:14 +00:00
2017-06-27 07:49:07 +00:00
for ( const auto itemPtr : containerStore )
2017-05-06 17:44:14 +00:00
{
[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.
2018-03-26 16:27:36 +00:00
addContainerItem ( worldObject , itemPtr , 0 ) ;
2017-05-06 17:44:14 +00:00
}
2017-01-28 10:34:45 +00:00
2017-05-06 17:44:14 +00:00
addObject ( worldObject ) ;
}
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_CONTAINER ) - > setEvent ( this ) ;
mwmp : : Main : : get ( ) . getNetworking ( ) - > getWorldPacket ( ID_CONTAINER ) - > Send ( ) ;
2017-01-27 18:57:47 +00:00
}