From 7fb3146a77e9c6ce016872d5051ddefa2142be21 Mon Sep 17 00:00:00 2001 From: Uleat Date: Tue, 8 Oct 2019 15:23:19 -0400 Subject: [PATCH] Added server handler for OP_CharInventory .. Added rule-based option to split BulkSendInventory into parent item packets --- common/ruletypes.h | 1 + zone/client.cpp | 3 + zone/client.h | 8 ++ zone/client_packet.cpp | 91 +++++++++++++----- zone/client_packet.h | 1 + zone/client_process.cpp | 202 ++++++++++++++++++++++++++++++---------- 6 files changed, 231 insertions(+), 75 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index cb4b41405..a8acf5ea9 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -726,6 +726,7 @@ RULE_BOOL(Inventory, EnforceAugmentWear, true, "Forces augment wear slot validat RULE_BOOL(Inventory, DeleteTransformationMold, true, "False if you want mold to last forever") RULE_BOOL(Inventory, AllowAnyWeaponTransformation, false, "Weapons can use any weapon transformation") RULE_BOOL(Inventory, TransformSummonedBags, false, "Transforms summoned bags into disenchanted ones instead of deleting") +RULE_BOOL(Inventory, BulkSendEnMasse, false, "Sends player 'Enter World' inventory as one massive packet (true = normal behavior)") RULE_CATEGORY_END() RULE_CATEGORY(Client) diff --git a/zone/client.cpp b/zone/client.cpp index 3c16b8a9b..15bc1fca7 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -340,6 +340,9 @@ Client::Client(EQStreamInterface* ieqs) temp_pvp = false; is_client_moving = false; + m_op_charinventory_sent = 0; + m_op_charinventory_received = 0; + /** * GM */ diff --git a/zone/client.h b/zone/client.h index 3ade2e6fe..b271d718c 100644 --- a/zone/client.h +++ b/zone/client.h @@ -721,6 +721,11 @@ public: void OnDisconnect(bool hard_disconnect); + void IncrementOPCharInventorySent(size_t count = 1) { m_op_charinventory_sent += count; } + void IncrementOPCharInventoryReceived(size_t count = 1) { m_op_charinventory_received += count; } + size_t GetOpCharInventorySentCount() { return m_op_charinventory_sent; } + size_t GetOpCharInventoryReceivedCount() { return m_op_charinventory_received; } + uint16 GetSkillPoints() { return m_pp.points;} void SetSkillPoints(int inp) { m_pp.points = inp;} @@ -1626,6 +1631,9 @@ private: int client_max_level; + size_t m_op_charinventory_sent; + size_t m_op_charinventory_received; + #ifdef BOTS public: diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index c27a23065..a54aa6b87 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -89,6 +89,7 @@ void MapOpcodes() // connecting opcode handler assignments: ConnectingOpcodes[OP_ApproveZone] = &Client::Handle_Connect_OP_ApproveZone; ConnectingOpcodes[OP_BlockedBuffs] = &Client::Handle_OP_BlockedBuffs; + ConnectingOpcodes[OP_CharInventory] = &Client::Handle_Connect_OP_CharInventory; ConnectingOpcodes[OP_ClientError] = &Client::Handle_Connect_OP_ClientError; ConnectingOpcodes[OP_ClientReady] = &Client::Handle_Connect_OP_ClientReady; ConnectingOpcodes[OP_ClientUpdate] = &Client::Handle_Connect_OP_ClientUpdate; @@ -932,6 +933,41 @@ void Client::Handle_Connect_OP_ApproveZone(const EQApplicationPacket *app) return; } +void Client::Handle_Connect_OP_CharInventory(const EQApplicationPacket* app) +{ + if (ClientVersionBit() & EQEmu::versions::maskRoFAndLater) { + + IncrementOPCharInventoryReceived(); + if (GetOpCharInventorySentCount() == GetOpCharInventoryReceivedCount()) { + + /* + Weather Packet + This shouldent be moved, this seems to be what the client + uses to advance to the next state (sending ReqNewZone) + */ + auto outapp = new EQApplicationPacket(OP_Weather, 12); + Weather_Struct* ws = (Weather_Struct*)outapp->pBuffer; + ws->val1 = 0x000000FF; + if (zone->zone_weather == 1) { + ws->type = 0x31; // Rain + } + if (zone->zone_weather == 2) { + + outapp->pBuffer[8] = 0x01; + ws->type = 0x02; + } + outapp->priority = 6; + QueuePacket(outapp); + safe_delete(outapp); + + Handle_Connect_OP_ReqNewZone(nullptr); + SetAttackTimer(); + conn_state = ZoneInfoSent; + zoneinpacket_timer.Start(); + } + } +} + void Client::Handle_Connect_OP_ClientError(const EQApplicationPacket *app) { if (app->size != sizeof(ClientError_Struct)) { @@ -1655,15 +1691,20 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) this is not quite where live sends inventory, they do it after tribute */ if (loaditems) { /* Dont load if a length error occurs */ - if (admin >= minStatusToBeGM) + + if (admin >= minStatusToBeGM) { m_inv.SetGMInventory(true); // set to true to allow expansion-restricted packets through + } BulkSendInventoryItems(); + /* Send stuff on the cursor which isnt sent in bulk */ for (auto iter = m_inv.cursor_cbegin(); iter != m_inv.cursor_cend(); ++iter) { + /* First item cursor is sent in bulk inventory packet */ - if (iter == m_inv.cursor_cbegin()) + if (iter == m_inv.cursor_cbegin()) { continue; + } const EQEmu::ItemInstance *inst = *iter; SendItemPacket(EQEmu::invslot::slotCursor, inst, ItemPacketLimbo); } @@ -1692,31 +1733,31 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) FastQueuePacket(&outapp); } - /* - Weather Packet - This shouldent be moved, this seems to be what the client - uses to advance to the next state (sending ReqNewZone) - */ - outapp = new EQApplicationPacket(OP_Weather, 12); - Weather_Struct *ws = (Weather_Struct *)outapp->pBuffer; - ws->val1 = 0x000000FF; - if (zone->zone_weather == 1) { ws->type = 0x31; } // Rain - if (zone->zone_weather == 2) { - outapp->pBuffer[8] = 0x01; - ws->type = 0x02; - } - outapp->priority = 6; - QueuePacket(outapp); - safe_delete(outapp); + if (ClientVersionBit() & EQEmu::versions::maskUFAndEarlier) { // RoF+ moved to Client::Handle_Connect_OP_CharInventory(...) - if (ClientVersion() >= EQEmu::versions::ClientVersion::RoF) { - Handle_Connect_OP_ReqNewZone(nullptr); - } + /* + Weather Packet + This shouldent be moved, this seems to be what the client + uses to advance to the next state (sending ReqNewZone) + */ + outapp = new EQApplicationPacket(OP_Weather, 12); + Weather_Struct* ws = (Weather_Struct*)outapp->pBuffer; + ws->val1 = 0x000000FF; + if (zone->zone_weather == 1) { + ws->type = 0x31; // Rain + } + if (zone->zone_weather == 2) { + outapp->pBuffer[8] = 0x01; + ws->type = 0x02; + } + outapp->priority = 6; + QueuePacket(outapp); + safe_delete(outapp); - SetAttackTimer(); - conn_state = ZoneInfoSent; - zoneinpacket_timer.Start(); - return; + SetAttackTimer(); + conn_state = ZoneInfoSent; + zoneinpacket_timer.Start(); + } } // connected opcode handlers diff --git a/zone/client_packet.h b/zone/client_packet.h index 9605dc8cf..4c7eff206 100644 --- a/zone/client_packet.h +++ b/zone/client_packet.h @@ -1,6 +1,7 @@ /* Connecting OpCode Handlers */ void Handle_Connect_0x3e33(const EQApplicationPacket *app); void Handle_Connect_OP_ApproveZone(const EQApplicationPacket *app); + void Handle_Connect_OP_CharInventory(const EQApplicationPacket *app); void Handle_Connect_OP_ClientError(const EQApplicationPacket *app); void Handle_Connect_OP_ClientReady(const EQApplicationPacket *app); void Handle_Connect_OP_ClientUpdate(const EQApplicationPacket *app); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index a857c3c21..42e0b3ca8 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -731,83 +731,185 @@ void Client::OnDisconnect(bool hard_disconnect) { Disconnect(); } -// Sends the client complete inventory used in character login +// Sends the client complete inventory used in character login (enter world/zoning) void Client::BulkSendInventoryItems() { - // LINKDEAD TRADE ITEMS - // Move trade slot items back into normal inventory..need them there now for the proceeding validity checks - for (int16 slot_id = EQEmu::invslot::TRADE_BEGIN; slot_id <= EQEmu::invslot::TRADE_END; slot_id++) { + // Move any residual trade slot items back into normal inventory..need them there now for the proceeding validity checks + for (int16 slot_id = EQEmu::invslot::TRADE_BEGIN; slot_id <= EQEmu::invslot::TRADE_END; ++slot_id) { + EQEmu::ItemInstance* inst = m_inv.PopItem(slot_id); if(inst) { - bool is_arrow = (inst->GetItem()->ItemType == EQEmu::item::ItemTypeArrow) ? true : false; + + bool is_arrow = (inst->GetItem()->ItemType == EQEmu::item::ItemTypeArrow); int16 free_slot_id = m_inv.FindFreeSlot(inst->IsClassBag(), true, inst->GetItem()->Size, is_arrow); - LogInventory("Incomplete Trade Transaction: Moving [{}] from slot [{}] to [{}]", inst->GetItem()->Name, slot_id, free_slot_id); + LogInventory( + "Incomplete Trade Transaction: Moving [{}] from slot [{}] to [{}]", + inst->GetItem()->Name, + slot_id, + free_slot_id + ); PutItemInInventory(free_slot_id, *inst, false); database.SaveInventory(character_id, nullptr, slot_id); safe_delete(inst); } } - bool deletenorent = database.NoRentExpired(GetName()); - if (deletenorent) { //client was offline for more than 30 minutes, delete no rent items - if (RuleB(Inventory, TransformSummonedBags)) + // Client was offline for more than 30 minutes, delete no rent items + bool delete_no_rent = database.NoRentExpired(GetName()); + if (delete_no_rent) { + + if (RuleB(Inventory, TransformSummonedBags)) { DisenchantSummonedBags(false); + } RemoveNoRent(false); } RemoveDuplicateLore(false); MoveSlotNotAllowed(false); - EQEmu::OutBuffer ob; - EQEmu::OutBuffer::pos_type last_pos = ob.tellp(); + if (RuleB(Inventory, BulkSendEnMasse)) { // Default behavior - // Possessions items - for (int16 slot_id = EQEmu::invslot::POSSESSIONS_BEGIN; slot_id <= EQEmu::invslot::POSSESSIONS_END; slot_id++) { - const EQEmu::ItemInstance* inst = m_inv[slot_id]; - if (!inst) - continue; + EQEmu::OutBuffer ob; + EQEmu::OutBuffer::pos_type last_pos = ob.tellp(); - inst->Serialize(ob, slot_id); + for (int16 slot_id = EQEmu::invslot::POSSESSIONS_BEGIN; slot_id <= EQEmu::invslot::POSSESSIONS_END; ++slot_id) { - if (ob.tellp() == last_pos) - LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); - - last_pos = ob.tellp(); + const EQEmu::ItemInstance* inst = m_inv[slot_id]; + if (!inst) { + continue; + } + + inst->Serialize(ob, slot_id); + if (ob.tellp() == last_pos) { + LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); + } + last_pos = ob.tellp(); + } + + for (int16 slot_id = EQEmu::invslot::BANK_BEGIN; slot_id <= EQEmu::invslot::BANK_END; ++slot_id) { + + const EQEmu::ItemInstance* inst = m_inv[slot_id]; + if (!inst) { + continue; + } + + inst->Serialize(ob, slot_id); + if (ob.tellp() == last_pos) { + LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); + } + last_pos = ob.tellp(); + } + + for (int16 slot_id = EQEmu::invslot::SHARED_BANK_BEGIN; slot_id <= EQEmu::invslot::SHARED_BANK_END; ++slot_id) { + + const EQEmu::ItemInstance* inst = m_inv[slot_id]; + if (!inst) { + continue; + } + + inst->Serialize(ob, slot_id); + if (ob.tellp() == last_pos) { + LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); + } + last_pos = ob.tellp(); + } + + auto outapp = new EQApplicationPacket(OP_CharInventory); + outapp->size = ob.size(); + outapp->pBuffer = ob.detach(); + QueuePacket(outapp); + safe_delete(outapp); + + if (ClientVersionBit() & EQEmu::versions::maskRoFAndLater) { + IncrementOPCharInventorySent(); + } } + else { // Specialized behavior - // Bank items - for (int16 slot_id = EQEmu::invslot::BANK_BEGIN; slot_id <= EQEmu::invslot::BANK_END; slot_id++) { - const EQEmu::ItemInstance* inst = m_inv[slot_id]; - if (!inst) - continue; + for (int16 slot_id = EQEmu::invslot::POSSESSIONS_BEGIN; slot_id <= EQEmu::invslot::POSSESSIONS_END; ++slot_id) { - inst->Serialize(ob, slot_id); + const EQEmu::ItemInstance* inst = m_inv[slot_id]; + if (!inst) { + continue; + } - if (ob.tellp() == last_pos) - LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); + EQEmu::OutBuffer ob; + EQEmu::OutBuffer::pos_type last_pos = ob.tellp(); - last_pos = ob.tellp(); + inst->Serialize(ob, slot_id); + if (ob.tellp() == last_pos) { + + LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); + continue; + } + + auto outapp = new EQApplicationPacket(OP_CharInventory); + outapp->size = ob.size(); + outapp->pBuffer = ob.detach(); + QueuePacket(outapp); + safe_delete(outapp); + + if (ClientVersionBit() & EQEmu::versions::maskRoFAndLater) { + IncrementOPCharInventorySent(); + } + } + + for (int16 slot_id = EQEmu::invslot::BANK_BEGIN; slot_id <= EQEmu::invslot::BANK_END; ++slot_id) { + + const EQEmu::ItemInstance* inst = m_inv[slot_id]; + if (!inst) { + continue; + } + + EQEmu::OutBuffer ob; + EQEmu::OutBuffer::pos_type last_pos = ob.tellp(); + + inst->Serialize(ob, slot_id); + if (ob.tellp() == last_pos) { + + LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); + continue; + } + + auto outapp = new EQApplicationPacket(OP_CharInventory); + outapp->size = ob.size(); + outapp->pBuffer = ob.detach(); + QueuePacket(outapp); + safe_delete(outapp); + + if (ClientVersionBit() & EQEmu::versions::maskRoFAndLater) { + IncrementOPCharInventorySent(); + } + } + + for (int16 slot_id = EQEmu::invslot::SHARED_BANK_BEGIN; slot_id <= EQEmu::invslot::SHARED_BANK_END; ++slot_id) { + + const EQEmu::ItemInstance* inst = m_inv[slot_id]; + if (!inst) { + continue; + } + + EQEmu::OutBuffer ob; + EQEmu::OutBuffer::pos_type last_pos = ob.tellp(); + + inst->Serialize(ob, slot_id); + if (ob.tellp() == last_pos) { + + LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); + continue; + } + + auto outapp = new EQApplicationPacket(OP_CharInventory); + outapp->size = ob.size(); + outapp->pBuffer = ob.detach(); + QueuePacket(outapp); + safe_delete(outapp); + + if (ClientVersionBit() & EQEmu::versions::maskRoFAndLater) { + IncrementOPCharInventorySent(); + } + } } - - // SharedBank items - for (int16 slot_id = EQEmu::invslot::SHARED_BANK_BEGIN; slot_id <= EQEmu::invslot::SHARED_BANK_END; slot_id++) { - const EQEmu::ItemInstance* inst = m_inv[slot_id]; - if (!inst) - continue; - - inst->Serialize(ob, slot_id); - - if (ob.tellp() == last_pos) - LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); - - last_pos = ob.tellp(); - } - - auto outapp = new EQApplicationPacket(OP_CharInventory); - outapp->size = ob.size(); - outapp->pBuffer = ob.detach(); - QueuePacket(outapp); - safe_delete(outapp); } void Client::BulkSendMerchantInventory(int merchant_id, int npcid) {