Browse Source

Synchronize player inventories (#5217)

* Synchronize item placement in player's backpack

* Synchronize item remove from player's backpack

* Synchronize item placement and removal from player's belt

* Set up loopback network provider for failing tests
pull/4447/head
Stephen C. Wills 4 years ago committed by GitHub
parent
commit
a06dd25d17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 89
      Source/inv.cpp
  2. 3
      Source/inv.h
  3. 105
      Source/msg.cpp
  4. 18
      Source/msg.h
  5. 15
      Source/player.cpp
  6. 3
      Source/storm/storm_net.cpp
  7. 9
      test/inv_test.cpp

89
Source/inv.cpp

@ -155,13 +155,17 @@ void AddItemToInvGrid(Player &player, int invGridIndex, int invListIndex, Size i
{
const int pitch = 10;
for (int y = 0; y < itemSize.height; y++) {
int rowGridIndex = invGridIndex + pitch * y;
for (int x = 0; x < itemSize.width; x++) {
if (x == 0 && y == itemSize.height - 1)
player.InvGrid[invGridIndex + x] = invListIndex;
player.InvGrid[rowGridIndex + x] = invListIndex;
else
player.InvGrid[invGridIndex + x] = -invListIndex;
player.InvGrid[rowGridIndex + x] = -invListIndex;
}
invGridIndex += pitch;
}
if (&player == MyPlayer) {
NetSendCmdChInvItem(false, invGridIndex);
}
}
@ -556,6 +560,9 @@ void CheckInvPaste(Player &player, Point cursorPosition)
if (player.HoldItem._itype == ItemType::Gold)
player._pGold = CalculateGold(player);
}
if (&player == MyPlayer) {
NetSendCmdChBeltItem(false, ii);
}
drawsbarflag = true;
} break;
case ILOC_NONE:
@ -799,8 +806,7 @@ void CheckInvCut(Player &player, Point cursorPosition, bool automaticMove, bool
}
if (!automaticMove || automaticallyMoved) {
beltItem.clear();
drawsbarflag = true;
player.RemoveSpdBarItem(r - SLOTXY_BELT_FIRST);
}
}
}
@ -1233,6 +1239,10 @@ bool AutoPlaceItemInBelt(Player &player, const Item &item, bool persistItem)
beltItem = item;
player.CalcScrolls();
drawsbarflag = true;
if (&player == MyPlayer) {
size_t beltIndex = std::distance<const Item *>(&player.SpdList[0], &beltItem);
NetSendCmdChBeltItem(false, beltIndex);
}
}
return true;
@ -1469,6 +1479,75 @@ void inv_update_rem_item(Player &player, inv_body_loc iv)
CalcPlrInv(player, player._pmode != PM_DEATH);
}
void CheckInvSwap(Player &player, int invGridIndex, int idx, uint16_t wCI, int seed, bool bId, uint32_t dwBuff)
{
Item item = {};
RecreateItem(item, idx, wCI, seed, 0, (dwBuff & CF_HELLFIRE) != 0);
if (bId) {
item._iIdentified = true;
}
auto itemSize = GetInventorySize(item);
const int pitch = 10;
int invListIndex = [&]() -> int {
for (int y = 0; y < itemSize.height; y++) {
int rowGridIndex = invGridIndex + pitch * y;
for (int x = 0; x < itemSize.width; x++) {
if (player.InvGrid[rowGridIndex + x] != 0)
return abs(player.InvGrid[rowGridIndex]);
}
}
player._pNumInv++;
return player._pNumInv;
}();
if (invListIndex < player._pNumInv) {
for (auto &itemIndex : player.InvGrid) {
if (itemIndex == invListIndex)
itemIndex = 0;
if (itemIndex == -invListIndex)
itemIndex = 0;
}
}
player.InvList[invListIndex - 1] = item;
for (int y = 0; y < itemSize.height; y++) {
int rowGridIndex = invGridIndex + pitch * y;
for (int x = 0; x < itemSize.width; x++) {
if (x == 0 && y == itemSize.height - 1)
player.InvGrid[rowGridIndex + x] = invListIndex;
else
player.InvGrid[rowGridIndex + x] = -invListIndex;
}
}
CalcPlrInv(player, true);
}
void CheckInvRemove(Player &player, int invGridIndex)
{
int invListIndex = abs(player.InvGrid[invGridIndex]) - 1;
if (invListIndex >= 0) {
player.RemoveInvItem(invListIndex);
}
}
void CheckBeltSwap(Player &player, int beltIndex, int idx, uint16_t wCI, int seed, bool bId, uint32_t dwBuff)
{
Item &item = player.SpdList[beltIndex];
item = {};
RecreateItem(item, idx, wCI, seed, 0, (dwBuff & CF_HELLFIRE) != 0);
if (bId) {
item._iIdentified = true;
}
}
void TransferItemToStash(Player &player, int location)
{
if (location == -1) {

3
Source/inv.h

@ -182,6 +182,9 @@ int AddGoldToInventory(Player &player, int value);
bool GoldAutoPlace(Player &player, Item &goldStack);
void CheckInvSwap(Player &player, inv_body_loc bLoc, int idx, uint16_t wCI, int seed, bool bId, uint32_t dwBuff);
void inv_update_rem_item(Player &player, inv_body_loc iv);
void CheckInvSwap(Player &player, int invGridIndex, int idx, uint16_t wCI, int seed, bool bId, uint32_t dwBuff);
void CheckInvRemove(Player &player, int invGridIndex);
void CheckBeltSwap(Player &player, int beltIndex, int idx, uint16_t wCI, int seed, bool bId, uint32_t dwBuff);
void TransferItemToStash(Player &player, int location);
void CheckInvItem(bool isShiftHeld = false, bool isCtrlHeld = false);

105
Source/msg.cpp

@ -1739,6 +1739,62 @@ size_t OnDeletePlayerItems(const TCmd *pCmd, int pnum)
return sizeof(message);
}
size_t OnChangeInventoryItems(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdChItem *>(pCmd);
Player &player = Players[pnum];
if (gbBufferMsgs == 1) {
SendPacket(pnum, &message, sizeof(message));
} else if (&player != MyPlayer && message.bLoc < InventoryGridCells && message.wIndx <= IDI_LAST) {
CheckInvSwap(player, message.bLoc, message.wIndx, message.wCI, message.dwSeed, message.bId != 0, message.dwBuff);
}
return sizeof(message);
}
size_t OnDeleteInventoryItems(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
Player &player = Players[pnum];
if (gbBufferMsgs == 1) {
SendPacket(pnum, &message, sizeof(message));
} else if (&player != MyPlayer && message.wParam1 < InventoryGridCells) {
CheckInvRemove(player, message.wParam1);
}
return sizeof(message);
}
size_t OnChangeBeltItems(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdChItem *>(pCmd);
Player &player = Players[pnum];
if (gbBufferMsgs == 1) {
SendPacket(pnum, &message, sizeof(message));
} else if (&player != MyPlayer && message.bLoc < MaxBeltItems && message.wIndx <= IDI_LAST) {
CheckBeltSwap(player, message.bLoc, message.wIndx, message.wCI, message.dwSeed, message.bId != 0, message.dwBuff);
}
return sizeof(message);
}
size_t OnDeleteBeltItems(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
Player &player = Players[pnum];
if (gbBufferMsgs == 1) {
SendPacket(pnum, &message, sizeof(message));
} else if (&player != MyPlayer && message.wParam1 < MaxBeltItems) {
player.RemoveSpdBarItem(message.wParam1);
}
return sizeof(message);
}
size_t OnPlayerLevel(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
@ -2893,6 +2949,47 @@ void NetSendCmdDelItem(bool bHiPri, uint8_t bLoc)
NetSendLoPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
}
void NetSendCmdChInvItem(bool bHiPri, int invGridIndex)
{
TCmdChItem cmd;
int8_t invListIndex = abs(MyPlayer->InvGrid[invGridIndex]) - 1;
const Item &item = MyPlayer->InvList[invListIndex];
cmd.bCmd = CMD_CHANGEINVITEMS;
cmd.bLoc = invGridIndex;
cmd.wIndx = item.IDidx;
cmd.wCI = item._iCreateInfo;
cmd.dwSeed = item._iSeed;
cmd.bId = item._iIdentified ? 1 : 0;
cmd.dwBuff = item.dwBuff;
if (bHiPri)
NetSendHiPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
else
NetSendLoPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
}
void NetSendCmdChBeltItem(bool bHiPri, int beltIndex)
{
TCmdChItem cmd;
const Item &item = MyPlayer->SpdList[beltIndex];
cmd.bCmd = CMD_CHANGEBELTITEMS;
cmd.bLoc = beltIndex;
cmd.wIndx = item.IDidx;
cmd.wCI = item._iCreateInfo;
cmd.dwSeed = item._iSeed;
cmd.bId = item._iIdentified ? 1 : 0;
cmd.dwBuff = item.dwBuff;
if (bHiPri)
NetSendHiPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
else
NetSendLoPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
}
void NetSendCmdDamage(bool bHiPri, uint8_t bPlr, uint32_t dwDam)
{
TCmdDamage cmd;
@ -3047,6 +3144,14 @@ size_t ParseCmd(int pnum, const TCmd *pCmd)
return OnChangePlayerItems(pCmd, pnum);
case CMD_DELPLRITEMS:
return OnDeletePlayerItems(pCmd, pnum);
case CMD_CHANGEINVITEMS:
return OnChangeInventoryItems(pCmd, pnum);
case CMD_DELINVITEMS:
return OnDeleteInventoryItems(pCmd, pnum);
case CMD_CHANGEBELTITEMS:
return OnChangeBeltItems(pCmd, pnum);
case CMD_DELBELTITEMS:
return OnDeleteBeltItems(pCmd, pnum);
case CMD_PLRLEVEL:
return OnPlayerLevel(pCmd, pnum);
case CMD_DROPITEM:

18
Source/msg.h

@ -281,6 +281,22 @@ enum _cmd_id : uint8_t {
//
// body (TCmdDelItem)
CMD_DELPLRITEMS,
// Put item into player's backpack.
//
// body (TCmdChItem)
CMD_CHANGEINVITEMS,
// Remove item from player's backpack.
//
// body (TCmdParam1)
CMD_DELINVITEMS,
// Put item into player's belt.
//
// body (TCmdChItem)
CMD_CHANGEBELTITEMS,
// Remove item from player's belt.
//
// body (TCmdParam1)
CMD_DELBELTITEMS,
// Damage target player.
//
// body (TCmdDamage)
@ -743,6 +759,8 @@ void NetSendCmdGItem(bool bHiPri, _cmd_id bCmd, uint8_t pnum, uint8_t ii);
void NetSendCmdPItem(bool bHiPri, _cmd_id bCmd, Point position, const Item &item);
void NetSendCmdChItem(bool bHiPri, uint8_t bLoc);
void NetSendCmdDelItem(bool bHiPri, uint8_t bLoc);
void NetSendCmdChInvItem(bool bHiPri, int invGridIndex);
void NetSendCmdChBeltItem(bool bHiPri, int invGridIndex);
void NetSendCmdDamage(bool bHiPri, uint8_t bPlr, uint32_t dwDam);
void NetSendCmdMonDmg(bool bHiPri, uint16_t wMon, uint32_t dwDam);
void NetSendCmdString(uint32_t pmask, const char *pszStr);

15
Source/player.cpp

@ -1775,6 +1775,17 @@ void Player::CalcScrolls()
void Player::RemoveInvItem(int iv, bool calcScrolls)
{
if (this == MyPlayer) {
// Locate the first grid index containing this item and notify remote clients
for (size_t i = 0; i < InventoryGridCells; i++) {
int8_t itemIndex = InvGrid[i];
if (abs(itemIndex) - 1 == iv) {
NetSendCmdParam1(false, CMD_DELINVITEMS, i);
break;
}
}
}
// Iterate through invGrid and remove every reference to item
for (int8_t &itemIndex : InvGrid) {
if (abs(itemIndex) - 1 == iv) {
@ -1807,6 +1818,10 @@ void Player::RemoveInvItem(int iv, bool calcScrolls)
void Player::RemoveSpdBarItem(int iv)
{
if (this == MyPlayer) {
NetSendCmdParam1(false, CMD_DELBELTITEMS, iv);
}
SpdList[iv].clear();
CalcScrolls();

3
Source/storm/storm_net.cpp

@ -9,6 +9,7 @@
#endif
#include "dvlnet/abstract_net.h"
#include "engine/demomode.h"
#include "menu.h"
#include "options.h"
#include "utils/stubs.h"
@ -142,7 +143,7 @@ bool SNetInitializeProvider(uint32_t provider, struct GameData *gameData)
std::lock_guard<SdlMutex> lg(storm_net_mutex);
#endif
dvlnet_inst = net::abstract_net::MakeNet(provider);
return mainmenu_select_hero_dialog(gameData);
return (HeadlessMode && !demo::IsRunning()) || mainmenu_select_hero_dialog(gameData);
}
/**

9
test/inv_test.cpp

@ -3,6 +3,7 @@
#include "cursor.h"
#include "inv.h"
#include "player.h"
#include "storm/storm_net.hpp"
using namespace devilution;
@ -136,6 +137,8 @@ TEST(Inv, GoldAutoPlace)
// Test removing an item from inventory with no other items.
TEST(Inv, RemoveInvItem)
{
SNetInitializeProvider(SELCONN_LOOPBACK, nullptr);
clear_inventory();
// Put a two-slot misc item into the inventory:
// | (item) | (item) | ... | ...
@ -153,6 +156,8 @@ TEST(Inv, RemoveInvItem)
// Test removing an item from inventory with other items in it.
TEST(Inv, RemoveInvItem_other_item)
{
SNetInitializeProvider(SELCONN_LOOPBACK, nullptr);
clear_inventory();
// Put a two-slot misc item and a ring into the inventory:
// | (item) | (item) | (ring) | ...
@ -175,6 +180,8 @@ TEST(Inv, RemoveInvItem_other_item)
// Test removing an item from the belt
TEST(Inv, RemoveSpdBarItem)
{
SNetInitializeProvider(SELCONN_LOOPBACK, nullptr);
// Clear the belt
for (int i = 0; i < MaxBeltItems; i++) {
MyPlayer->SpdList[i].clear();
@ -206,6 +213,8 @@ TEST(Inv, RemoveCurrentSpellScroll_inventory)
// Test removing a scroll from the belt
TEST(Inv, RemoveCurrentSpellScroll_belt)
{
SNetInitializeProvider(SELCONN_LOOPBACK, nullptr);
// Clear the belt
for (int i = 0; i < MaxBeltItems; i++) {
MyPlayer->SpdList[i].clear();

Loading…
Cancel
Save