Compare commits

...
Sign in to create a new pull request.

7 commits
master ... mmi

Author SHA1 Message Date
Michael Cook (mackal)
70e97f2a3c Merge branch 'master' into mmi 2018-02-04 18:37:41 -05:00
Michael Cook (mackal)
38bec1afe1 Merge branch 'master' into mmi 2018-02-02 21:07:47 -05:00
Michael Cook (mackal)
63517a9b4f Merge branch 'master' into mmi 2018-01-20 22:52:44 -05:00
Michael Cook (mackal)
9557581d05 Merge branch 'master' into mmi 2018-01-10 00:56:22 -05:00
Michael Cook (mackal)
4cba7a0bf3 Merge branch 'master' into mmi 2017-12-23 23:06:12 -05:00
Michael Cook (mackal)
a3eb4e62af Merge branch 'master' into mmi 2017-12-16 21:32:41 -05:00
Michael Cook (mackal)
b80dd8007e Work on OP_MoveMultipleItems
Unsure if this is ready. The one case the client does send this packet
seems to work, but we need to make sure it behaves correctly if someone
tries to forge a packet ...

Also client doing "illegal" moves that are really legal for client needs
to be resolved.
2017-12-11 17:05:20 -05:00
12 changed files with 397 additions and 3 deletions

View file

@ -1548,6 +1548,12 @@ struct MoveItem_Struct
/*0012*/
};
struct MultiMoveItem_Struct
{
/*0000*/ int32 count;
/*0004*/ MoveItem_Struct moves[0];
};
// both MoveItem_Struct/DeleteItem_Struct server structures will be changing to a structure-based slot format..this will
// be used for handling SoF/SoD/etc... time stamps sent using the MoveItem_Struct format. (nothing will be done with this
// info at the moment..but, it is forwarded on to the server for handling/future use)

View file

@ -1762,6 +1762,36 @@ namespace RoF
FINISH_ENCODE();
}
ENCODE(OP_MoveMultipleItems)
{
// can't use our macros :(
if ((*p)->size < sizeof(MultiMoveItem_Struct) + sizeof(MoveItem_Struct)) {
Log(Logs::Detail, Logs::Netcode,
"Wrong size on outbound %s (MultiMoveItem_Struct): Got %d, expected at least %d",
opcodes->EmuToName((*p)->GetOpcode()), (*p)->size,
sizeof(MultiMoveItem_Struct) + sizeof(MoveItem_Struct));
delete *p;
*p = nullptr;
return;
}
SETUP_VAR_ENCODE(MultiMoveItem_Struct);
int32 count = std::abs(emu->count);
ALLOC_VAR_ENCODE(structs::MultiMoveItem_Struct, sizeof(structs::MultiMoveItem_Struct) +
sizeof(structs::MultiMoveItemSub_Struct) * count);
OUT(count);
for (int i = 0; i < count; ++i) {
eq->moves[i].from_slot = ServerToRoFSlot(emu->moves[i].from_slot);
eq->moves[i].to_slot = ServerToRoFSlot(emu->moves[i].to_slot);
eq->moves[i].number_in_stack = emu->moves[i].number_in_stack;
}
FINISH_ENCODE();
}
ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); }
ENCODE(OP_NewZone)
@ -4873,6 +4903,39 @@ namespace RoF
FINISH_DIRECT_DECODE();
}
DECODE(OP_MoveMultipleItems)
{
// can't use our macros :(
if (__packet->size !=
sizeof(structs::MultiMoveItem_Struct) + sizeof(structs::MultiMoveItemSub_Struct)) {
Log(Logs::Detail, Logs::Netcode,
"Wrong size on incoming %s (structs::MultiMoveItem_Struct): Got %d, expected %d",
opcodes->EmuToName(__packet->GetOpcode()), __packet->size,
sizeof(structs::MultiMoveItem_Struct) + sizeof(structs::MultiMoveItemSub_Struct));
__packet->SetOpcode(OP_Unknown); /* invalidate the packet */
return;
}
// can be negative
int32 count = std::abs(*(int32 *)__packet->pBuffer);
unsigned char *__eq_buffer = __packet->pBuffer;
__packet->size = sizeof(MultiMoveItem_Struct) + count * sizeof(MoveItem_Struct);
__packet->pBuffer = new unsigned char[__packet->size];
MultiMoveItem_Struct *emu = (MultiMoveItem_Struct *) __packet->pBuffer;
structs::MultiMoveItem_Struct *eq = (structs::MultiMoveItem_Struct *) __eq_buffer;
IN(count);
for (int i = 0; i < count; ++i) {
emu->moves[i].from_slot = RoFToServerSlot(eq->moves[i].from_slot);
emu->moves[i].to_slot = RoFToServerSlot(eq->moves[i].to_slot);
emu->moves[i].number_in_stack = eq->moves[i].number_in_stack;
}
FINISH_DIRECT_DECODE();
}
DECODE(OP_PetCommands)
{
DECODE_LENGTH_EXACT(structs::PetCommand_Struct);

View file

@ -1831,6 +1831,36 @@ namespace RoF2
FINISH_ENCODE();
}
ENCODE(OP_MoveMultipleItems)
{
// can't use our macros :(
if ((*p)->size < sizeof(MultiMoveItem_Struct) + sizeof(MoveItem_Struct)) {
Log(Logs::Detail, Logs::Netcode,
"Wrong size on outbound %s (MultiMoveItem_Struct): Got %d, expected at least %d",
opcodes->EmuToName((*p)->GetOpcode()), (*p)->size,
sizeof(MultiMoveItem_Struct) + sizeof(MoveItem_Struct));
delete *p;
*p = nullptr;
return;
}
SETUP_VAR_ENCODE(MultiMoveItem_Struct);
int32 count = std::abs(emu->count);
ALLOC_VAR_ENCODE(structs::MultiMoveItem_Struct, sizeof(structs::MultiMoveItem_Struct) +
sizeof(structs::MultiMoveItemSub_Struct) * count);
OUT(count);
for (int i = 0; i < count; ++i) {
eq->moves[i].from_slot = ServerToRoF2Slot(emu->moves[i].from_slot);
eq->moves[i].to_slot = ServerToRoF2Slot(emu->moves[i].to_slot);
eq->moves[i].number_in_stack = emu->moves[i].number_in_stack;
}
FINISH_ENCODE();
}
ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); }
ENCODE(OP_NewZone)
@ -5112,6 +5142,39 @@ namespace RoF2
FINISH_DIRECT_DECODE();
}
DECODE(OP_MoveMultipleItems)
{
// can't use our macros :(
if (__packet->size !=
sizeof(structs::MultiMoveItem_Struct) + sizeof(structs::MultiMoveItemSub_Struct)) {
Log(Logs::Detail, Logs::Netcode,
"Wrong size on incoming %s (structs::MultiMoveItem_Struct): Got %d, expected %d",
opcodes->EmuToName(__packet->GetOpcode()), __packet->size,
sizeof(structs::MultiMoveItem_Struct) + sizeof(structs::MultiMoveItemSub_Struct));
__packet->SetOpcode(OP_Unknown); /* invalidate the packet */
return;
}
// can be negative
int32 count = std::abs(*(int32 *)__packet->pBuffer);
unsigned char *__eq_buffer = __packet->pBuffer;
__packet->size = sizeof(MultiMoveItem_Struct) + count * sizeof(MoveItem_Struct);
__packet->pBuffer = new unsigned char[__packet->size];
MultiMoveItem_Struct *emu = (MultiMoveItem_Struct *) __packet->pBuffer;
structs::MultiMoveItem_Struct *eq = (structs::MultiMoveItem_Struct *) __eq_buffer;
IN(count);
for (int i = 0; i < count; ++i) {
emu->moves[i].from_slot = RoF2ToServerSlot(eq->moves[i].from_slot);
emu->moves[i].to_slot = RoF2ToServerSlot(eq->moves[i].to_slot);
emu->moves[i].number_in_stack = eq->moves[i].number_in_stack;
}
FINISH_DIRECT_DECODE();
}
DECODE(OP_PetCommands)
{
DECODE_LENGTH_EXACT(structs::PetCommand_Struct);

View file

@ -95,6 +95,7 @@ E(OP_ManaChange)
E(OP_MercenaryDataResponse)
E(OP_MercenaryDataUpdate)
E(OP_MoveItem)
E(OP_MoveMultipleItems)
E(OP_NewSpawn)
E(OP_NewZone)
E(OP_OnLevelMessage)
@ -183,6 +184,7 @@ D(OP_ItemVerifyRequest)
D(OP_LoadSpellSet)
D(OP_LootItem)
D(OP_MoveItem)
D(OP_MoveMultipleItems)
D(OP_PetCommands)
D(OP_RaidInvite)
D(OP_ReadBook)

View file

@ -1837,7 +1837,7 @@ struct MultiMoveItemSub_Struct
struct MultiMoveItem_Struct
{
/*0000*/ uint32 count;
/*0000*/ int32 count;
/*0004*/ MultiMoveItemSub_Struct moves[0];
};

View file

@ -80,6 +80,7 @@ E(OP_ManaChange)
E(OP_MercenaryDataResponse)
E(OP_MercenaryDataUpdate)
E(OP_MoveItem)
E(OP_MoveMultipleItems)
E(OP_NewSpawn)
E(OP_NewZone)
E(OP_OnLevelMessage)
@ -167,6 +168,7 @@ D(OP_ItemVerifyRequest)
D(OP_LoadSpellSet)
D(OP_LootItem)
D(OP_MoveItem)
D(OP_MoveMultipleItems)
D(OP_PetCommands)
D(OP_RaidInvite)
D(OP_ReadBook)

View file

@ -1815,6 +1815,20 @@ struct MoveItem_Struct
/*0028*/
};
struct MultiMoveItemSub_Struct
{
/*0000*/ InventorySlot_Struct from_slot;
/*0012*/ InventorySlot_Struct to_slot;
/*0024*/ uint32 number_in_stack;
/*0028*/ uint8 unknown[8];
};
struct MultiMoveItem_Struct
{
/*0000*/ int32 count;
/*0004*/ MultiMoveItemSub_Struct moves[0];
};
//
// from_slot/to_slot
// -1 - destroy

View file

@ -47,10 +47,12 @@ namespace Titanium
// server to client inventory location converters
static inline int16 ServerToTitaniumSlot(uint32 serverSlot);
static inline int16 ServerToTitaniumCorpseSlot(uint32 serverCorpseSlot);
//static inline structs::InventorySlot_Struct ServerToNewStyleTitaniumSlot(uint32 serverSlot, ItemPacketType PacketType = ItemPacketInvalid);
// client to server inventory location converters
static inline uint32 TitaniumToServerSlot(int16 titaniumSlot);
static inline uint32 TitaniumToServerCorpseSlot(int16 titaniumCorpseSlot);
//static inline uint32 NewStyleTitaniumToServerSlot(structs::InventorySlot_Struct titaniumSlot, ItemPacketType PacketType = ItemPacketInvalid);
// server to client text link converter
static inline void ServerToTitaniumTextLink(std::string& titaniumTextLink, const std::string& serverTextLink);
@ -2474,6 +2476,20 @@ namespace Titanium
return titaniumCorpseSlot;
}
/*static inline uint32 NewStyleTitaniumToServerSlot(structs::InventorySlot_Struct titaniumSlot, ItemPacketType PacketType)
{
uint32 ServerSlot = INVALID_INDEX;
uint32 TempSlot = 0;
if (titaniumSlot.Type == invtype::InvTypePossessions) { // Worn/Personal Inventory and Cursor
TempSlot = titaniumSlot.Slot;
if (titaniumSlot.SubIndex >= EQEmu::inventory::containerBegin) // Bag Slots
TempSlot = (TempSlot + 3) * EQEmu::inventory::ContainerCount + titaniumSlot.SubIndex + 1;
ServerSlot = TempSlot;
}
}*/
static inline void ServerToTitaniumTextLink(std::string& titaniumTextLink, const std::string& serverTextLink)
{
if ((constants::SayLinkBodySize == EQEmu::legacy::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find('\x12') == std::string::npos)) {

View file

@ -48,7 +48,7 @@ namespace Titanium
InvTypeSharedBank,
InvTypeTrade,
InvTypeWorld,
InvTypeLimbo,
InvTypeLimbo, // this is where titanium actually ends
InvTypeTribute,
InvTypeGuildTribute,
InvTypeMerchant,

View file

@ -229,6 +229,13 @@ struct ClientReward
uint32 amount;
};
// used for OP_MoveMultipleItems
struct InternalMultiMoveItem {
MoveItem_Struct move;
EQEmu::ItemInstance *item;
bool move_finished;
};
class ClientFactory {
public:
Client *MakeClient(std::shared_ptr<EQStreamInterface> ieqs);
@ -869,6 +876,13 @@ public:
bool SwapItem(MoveItem_Struct* move_in);
void SwapItemResync(MoveItem_Struct* move_slots);
void QSSwapItemAuditor(MoveItem_Struct* move_in, bool postaction_call = false);
bool MoveMultipleItems(MultiMoveItem_Struct *move_in);
bool MoveMultipleItems(std::deque<InternalMultiMoveItem> &moves, bool send_packet = false);
bool MultiMovesGetItems(std::deque<InternalMultiMoveItem> &moves);
bool MultiMovesFinish(std::deque<InternalMultiMoveItem> &moves);
void MultiMovesFailReturnItems(std::deque<InternalMultiMoveItem> &moves);
void MultiMovesFailRevertMoves(std::deque<InternalMultiMoveItem> &moves);
void SendMoveMultipleItems(std::deque<InternalMultiMoveItem> &moves);
void PutLootInInventory(int16 slot_id, const EQEmu::ItemInstance &inst, ServerLootItem_Struct** bag_item_data = 0);
bool AutoPutLootInInventory(EQEmu::ItemInstance& inst, bool try_worn = false, bool try_cursor = true, ServerLootItem_Struct** bag_item_data = 0);
bool SummonItem(uint32 item_id, int16 charges = -1, uint32 aug1 = 0, uint32 aug2 = 0, uint32 aug3 = 0, uint32 aug4 = 0, uint32 aug5 = 0, uint32 aug6 = 0, bool attuned = false, uint16 to_slot = EQEmu::inventory::slotCursor, uint32 ornament_icon = 0, uint32 ornament_idfile = 0, uint32 ornament_hero_model = 0);

View file

@ -9858,7 +9858,9 @@ void Client::Handle_OP_MoveItem(const EQApplicationPacket *app)
void Client::Handle_OP_MoveMultipleItems(const EQApplicationPacket *app)
{
Kick(); // TODO: lets not desync though
// verify size? Translators already fail if too small
// any state checking?
MoveMultipleItems((MultiMoveItem_Struct *)app->pBuffer);
}
void Client::Handle_OP_OpenContainer(const EQApplicationPacket *app)

View file

@ -2088,6 +2088,218 @@ void Client::QSSwapItemAuditor(MoveItem_Struct* move_in, bool postaction_call) {
safe_delete(qspack);
}
/* The idea behind this is that we will move all the items out of their source slots
* into a temporary holding area. Then we will move them from the holding area to their
* destination slots. These steps can fail, and we will have to revert.
*
* The reason for this is because the client will say move item from slot x to slot y
* then move item from slot y to slot x. Which means we have to some sort of temporary
* area because otherwise we would just undo the move.
*/
bool Client::MoveMultipleItems(MultiMoveItem_Struct *move_in)
{
// We need somewhere to hold the moves and the items need some place to live temporarily
std::deque<InternalMultiMoveItem> moves;
for (int i = 0; i < move_in->count; ++i) {
InternalMultiMoveItem m;
m.move.from_slot = move_in->moves[i].from_slot;
m.move.to_slot = move_in->moves[i].to_slot;
m.move.number_in_stack = move_in->moves[i].number_in_stack;
m.item = nullptr;
m.move_finished = false;
if (!IsValidSlot(m.move.from_slot)) {
Log(Logs::Detail, Logs::Inventory, "Invalid slot move from slot %u to slot %u with %u charges!", m.move.from_slot, m.move.to_slot, m.move.number_in_stack);
return false;
}
if (!IsValidSlot(m.move.to_slot)) {
Log(Logs::Detail, Logs::Inventory, "Invalid slot move from slot %u to slot %u with %u charges!", m.move.from_slot, m.move.to_slot, m.move.number_in_stack);
return false;
}
moves.push_back(m);
}
return MoveMultipleItems(moves);
}
bool Client::MoveMultipleItems(std::deque<InternalMultiMoveItem> &moves, bool send_packet)
{
if (!MultiMovesGetItems(moves)) {
Log(Logs::Detail, Logs::Inventory, "MultiMovesGetItems failed!");
MultiMovesFailReturnItems(moves);
return false;
}
// The client also calls a function called UpdateToSlots here, I think this will make sure items can fit in destination
// and find where to put items that need a home (ex. when bandolier needs to remove an item)
if (!MultiMovesFinish(moves)) {
Log(Logs::Detail, Logs::Inventory, "MutliMovesFinish failed!");
MultiMovesFailRevertMoves(moves);
MultiMovesFailReturnItems(moves);
return false;
}
if (send_packet)
SendMoveMultipleItems(moves);
// Now we need to save, we couldn't do it in processing because of failure
// could be better ...
// This may save slots multiple times, can rethink later ...
for (auto &m : moves) {
database.SaveInventory(character_id, m_inv.GetItem(m.move.from_slot), m.move.from_slot);
database.SaveInventory(character_id, m_inv.GetItem(m.move.to_slot), m.move.to_slot);
}
return true;
}
bool Client::MultiMovesFinish(std::deque<InternalMultiMoveItem> &moves)
{
for (auto &m : moves) {
if (!m.item) { // this shouldn't happen
Log(Logs::Detail, Logs::Inventory, "MultiMovesFinish encountered a null item!");
return false;
}
// check lore, we could be moving from shared to non-shared bank
if (CheckLoreConflict(m.item->GetItem())) {
Log(Logs::Detail, Logs::Inventory, "MultiMovesFinish encountered a lore conflict!");
return false;
}
// check we are allowed to equip it
auto dst_inst = m_inv.GetItem(m.move.to_slot);
// so we need to verify each of these moves are good
auto parent_slot = m_inv.CalcSlotId(m.move.to_slot);
if (parent_slot != INVALID_INDEX) { // okay, we're moving into a bag, lets verify that
auto bag_inst = m_inv.GetItem(parent_slot);
if (!bag_inst) {
Log(Logs::Detail, Logs::Inventory, "MultiMovesFinish tried moving into a null bag!");
return false;
}
if (!bag_inst->IsClassBag()) {
Log(Logs::Detail, Logs::Inventory, "MultiMovesFinish tried moving into a non-bag!");
return false;
}
if (!m_inv.CanItemFitInContainer(m.item->GetItem(), bag_inst->GetItem())) {
Log(Logs::Detail, Logs::Inventory, "MultiMovesFinish tried moving into a bag it can't fit in!");
return false;
}
}
// so at this point, we know we're moving into a valid bag or not moving into a bag at all, time to check others
if (dst_inst) { // okay, there is still an item here, we must be stacking
if (m.item->GetCharges() + dst_inst->GetCharges() > m.item->GetItem()->StackSize) {
Log(Logs::Detail, Logs::Inventory, "MultiMoveFinish tried to over flow a stack!");
return false;
}
if (m.item->GetItem()->ID != dst_inst->GetItem()->ID) {
Log(Logs::Detail, Logs::Inventory, "MutliMoveFinish tried to stack different items!");
return false;
}
dst_inst->SetCharges(m.item->GetCharges() + dst_inst->GetCharges());
} else { // okay, now we're not stacking so we just need to move
m_inv.PutItem(m.move.to_slot, *m.item);
}
m.move_finished = true;
safe_delete(m.item);
}
return true;
}
/* This function will move item instances out of the inventory into a temporary holding area
* If this returns false, we need to move them back into their source slots
*/
bool Client::MultiMovesGetItems(std::deque<InternalMultiMoveItem> &moves)
{
for (auto &m : moves) {
auto inst = m_inv.GetItem(m.move.from_slot);
if (!inst) { // trying to move an item that doesn't exist ...
Log(Logs::Detail, Logs::Inventory, "MultiMovesGetItems tried to move from an empty slot!");
return false;
}
// if the item is stackable and we are not moving all the items out, we're splitting
if (m.move.number_in_stack > 0 && inst->IsStackable() && inst->GetCharges() != m.move.number_in_stack) {
if (inst->GetCharges() > m.move.number_in_stack) {
Log(Logs::Detail, Logs::Inventory, "MultiMovesGetItems tried to move more items in a stack than we have!");
return false;
}
// new instance
auto new_inst = database.CreateItem(inst->GetItem(), m.move.number_in_stack);
// modify old
inst->SetCharges(inst->GetCharges() - m.move.number_in_stack);
m.item = new_inst;
} else {
m.item = m_inv.PopItem(m.move.from_slot); // we need to steal the item!
}
}
return true;
}
/* This function will move the items back from the temporary holding area after a fail
*/
void Client::MultiMovesFailReturnItems(std::deque<InternalMultiMoveItem> &moves)
{
for (auto &m : moves) {
if (!m.item) // if the item is null, we haven't stolen it yet, so nothing to fix
continue;
// if we moved a partial stack, we need to put them all back
if (m.move.number_in_stack > 0 && m.item->IsStackable() && m.item->GetCharges() != m.move.number_in_stack) {
auto inst = m_inv.GetItem(m.move.from_slot);
if (!inst) { // well shit
safe_delete(m.item);
continue;
}
inst->SetCharges(inst->GetCharges() + m.item->GetCharges());
safe_delete(m.item);
} else {
m_inv.PutItem(m.move.from_slot, *m.item);
safe_delete(m.item);
}
}
}
/* This function will move items back into the temporary holding area after a fail
*/
void Client::MultiMovesFailRevertMoves(std::deque<InternalMultiMoveItem> &moves)
{
for (auto &m : moves) {
if (!m.move_finished) // if we haven't set this to true, we have not put the item in it's destination
continue;
m.item = m_inv.PopItem(m.move.to_slot);
}
}
void Client::SendMoveMultipleItems(std::deque<InternalMultiMoveItem> &moves)
{
auto app = new EQApplicationPacket(OP_MoveMultipleItems, sizeof(MultiMoveItem_Struct) + sizeof(MoveItem_Struct) * moves.size());
auto mmi = (MultiMoveItem_Struct *)app->pBuffer;
mmi->count = moves.size();
int i = 0;
for (auto &e : moves) {
mmi->moves[i].from_slot = e.move.from_slot;
mmi->moves[i].to_slot = e.move.to_slot;
mmi->moves[i].number_in_stack = e.move.number_in_stack;
++i;
}
FastQueuePacket(&app);
}
void Client::DyeArmor(EQEmu::TintProfile* dye){
int16 slot=0;
for (int i = EQEmu::textures::textureBegin; i <= EQEmu::textures::LastTintableTexture; i++) {