Added server handler for OP_CharInventory .. Added rule-based option to split BulkSendInventory into parent item packets

This commit is contained in:
Uleat 2019-10-08 15:23:19 -04:00
parent a26227f258
commit 7fb3146a77
6 changed files with 231 additions and 75 deletions

View File

@ -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, DeleteTransformationMold, true, "False if you want mold to last forever")
RULE_BOOL(Inventory, AllowAnyWeaponTransformation, false, "Weapons can use any weapon transformation") 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, 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_END()
RULE_CATEGORY(Client) RULE_CATEGORY(Client)

View File

@ -340,6 +340,9 @@ Client::Client(EQStreamInterface* ieqs)
temp_pvp = false; temp_pvp = false;
is_client_moving = false; is_client_moving = false;
m_op_charinventory_sent = 0;
m_op_charinventory_received = 0;
/** /**
* GM * GM
*/ */

View File

@ -721,6 +721,11 @@ public:
void OnDisconnect(bool hard_disconnect); 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;} uint16 GetSkillPoints() { return m_pp.points;}
void SetSkillPoints(int inp) { m_pp.points = inp;} void SetSkillPoints(int inp) { m_pp.points = inp;}
@ -1626,6 +1631,9 @@ private:
int client_max_level; int client_max_level;
size_t m_op_charinventory_sent;
size_t m_op_charinventory_received;
#ifdef BOTS #ifdef BOTS
public: public:

View File

@ -89,6 +89,7 @@ void MapOpcodes()
// connecting opcode handler assignments: // connecting opcode handler assignments:
ConnectingOpcodes[OP_ApproveZone] = &Client::Handle_Connect_OP_ApproveZone; ConnectingOpcodes[OP_ApproveZone] = &Client::Handle_Connect_OP_ApproveZone;
ConnectingOpcodes[OP_BlockedBuffs] = &Client::Handle_OP_BlockedBuffs; 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_ClientError] = &Client::Handle_Connect_OP_ClientError;
ConnectingOpcodes[OP_ClientReady] = &Client::Handle_Connect_OP_ClientReady; ConnectingOpcodes[OP_ClientReady] = &Client::Handle_Connect_OP_ClientReady;
ConnectingOpcodes[OP_ClientUpdate] = &Client::Handle_Connect_OP_ClientUpdate; ConnectingOpcodes[OP_ClientUpdate] = &Client::Handle_Connect_OP_ClientUpdate;
@ -932,6 +933,41 @@ void Client::Handle_Connect_OP_ApproveZone(const EQApplicationPacket *app)
return; 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) void Client::Handle_Connect_OP_ClientError(const EQApplicationPacket *app)
{ {
if (app->size != sizeof(ClientError_Struct)) { 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 this is not quite where live sends inventory, they do it after tribute
*/ */
if (loaditems) { /* Dont load if a length error occurs */ 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 m_inv.SetGMInventory(true); // set to true to allow expansion-restricted packets through
}
BulkSendInventoryItems(); BulkSendInventoryItems();
/* Send stuff on the cursor which isnt sent in bulk */ /* Send stuff on the cursor which isnt sent in bulk */
for (auto iter = m_inv.cursor_cbegin(); iter != m_inv.cursor_cend(); ++iter) { for (auto iter = m_inv.cursor_cbegin(); iter != m_inv.cursor_cend(); ++iter) {
/* First item cursor is sent in bulk inventory packet */ /* First item cursor is sent in bulk inventory packet */
if (iter == m_inv.cursor_cbegin()) if (iter == m_inv.cursor_cbegin()) {
continue; continue;
}
const EQEmu::ItemInstance *inst = *iter; const EQEmu::ItemInstance *inst = *iter;
SendItemPacket(EQEmu::invslot::slotCursor, inst, ItemPacketLimbo); SendItemPacket(EQEmu::invslot::slotCursor, inst, ItemPacketLimbo);
} }
@ -1692,15 +1733,19 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
FastQueuePacket(&outapp); FastQueuePacket(&outapp);
} }
if (ClientVersionBit() & EQEmu::versions::maskUFAndEarlier) { // RoF+ moved to Client::Handle_Connect_OP_CharInventory(...)
/* /*
Weather Packet Weather Packet
This shouldent be moved, this seems to be what the client This shouldent be moved, this seems to be what the client
uses to advance to the next state (sending ReqNewZone) uses to advance to the next state (sending ReqNewZone)
*/ */
outapp = new EQApplicationPacket(OP_Weather, 12); outapp = new EQApplicationPacket(OP_Weather, 12);
Weather_Struct *ws = (Weather_Struct *)outapp->pBuffer; Weather_Struct* ws = (Weather_Struct*)outapp->pBuffer;
ws->val1 = 0x000000FF; ws->val1 = 0x000000FF;
if (zone->zone_weather == 1) { ws->type = 0x31; } // Rain if (zone->zone_weather == 1) {
ws->type = 0x31; // Rain
}
if (zone->zone_weather == 2) { if (zone->zone_weather == 2) {
outapp->pBuffer[8] = 0x01; outapp->pBuffer[8] = 0x01;
ws->type = 0x02; ws->type = 0x02;
@ -1709,14 +1754,10 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
QueuePacket(outapp); QueuePacket(outapp);
safe_delete(outapp); safe_delete(outapp);
if (ClientVersion() >= EQEmu::versions::ClientVersion::RoF) {
Handle_Connect_OP_ReqNewZone(nullptr);
}
SetAttackTimer(); SetAttackTimer();
conn_state = ZoneInfoSent; conn_state = ZoneInfoSent;
zoneinpacket_timer.Start(); zoneinpacket_timer.Start();
return; }
} }
// connected opcode handlers // connected opcode handlers

View File

@ -1,6 +1,7 @@
/* Connecting OpCode Handlers */ /* Connecting OpCode Handlers */
void Handle_Connect_0x3e33(const EQApplicationPacket *app); void Handle_Connect_0x3e33(const EQApplicationPacket *app);
void Handle_Connect_OP_ApproveZone(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_ClientError(const EQApplicationPacket *app);
void Handle_Connect_OP_ClientReady(const EQApplicationPacket *app); void Handle_Connect_OP_ClientReady(const EQApplicationPacket *app);
void Handle_Connect_OP_ClientUpdate(const EQApplicationPacket *app); void Handle_Connect_OP_ClientUpdate(const EQApplicationPacket *app);

View File

@ -731,75 +731,86 @@ void Client::OnDisconnect(bool hard_disconnect) {
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() void Client::BulkSendInventoryItems()
{ {
// LINKDEAD TRADE ITEMS // Move any residual trade slot items back into normal inventory..need them there now for the proceeding validity checks
// 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) {
for (int16 slot_id = EQEmu::invslot::TRADE_BEGIN; slot_id <= EQEmu::invslot::TRADE_END; slot_id++) {
EQEmu::ItemInstance* inst = m_inv.PopItem(slot_id); EQEmu::ItemInstance* inst = m_inv.PopItem(slot_id);
if(inst) { 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); 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); PutItemInInventory(free_slot_id, *inst, false);
database.SaveInventory(character_id, nullptr, slot_id); database.SaveInventory(character_id, nullptr, slot_id);
safe_delete(inst); safe_delete(inst);
} }
} }
bool deletenorent = database.NoRentExpired(GetName()); // Client was offline for more than 30 minutes, delete no rent items
if (deletenorent) { //client was offline for more than 30 minutes, delete no rent items bool delete_no_rent = database.NoRentExpired(GetName());
if (RuleB(Inventory, TransformSummonedBags)) if (delete_no_rent) {
if (RuleB(Inventory, TransformSummonedBags)) {
DisenchantSummonedBags(false); DisenchantSummonedBags(false);
}
RemoveNoRent(false); RemoveNoRent(false);
} }
RemoveDuplicateLore(false); RemoveDuplicateLore(false);
MoveSlotNotAllowed(false); MoveSlotNotAllowed(false);
if (RuleB(Inventory, BulkSendEnMasse)) { // Default behavior
EQEmu::OutBuffer ob; EQEmu::OutBuffer ob;
EQEmu::OutBuffer::pos_type last_pos = ob.tellp(); EQEmu::OutBuffer::pos_type last_pos = ob.tellp();
// Possessions items for (int16 slot_id = EQEmu::invslot::POSSESSIONS_BEGIN; slot_id <= EQEmu::invslot::POSSESSIONS_END; ++slot_id) {
for (int16 slot_id = EQEmu::invslot::POSSESSIONS_BEGIN; slot_id <= EQEmu::invslot::POSSESSIONS_END; slot_id++) {
const EQEmu::ItemInstance* inst = m_inv[slot_id]; const EQEmu::ItemInstance* inst = m_inv[slot_id];
if (!inst) if (!inst) {
continue; continue;
}
inst->Serialize(ob, slot_id); inst->Serialize(ob, slot_id);
if (ob.tellp() == last_pos) {
if (ob.tellp() == last_pos)
LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id);
}
last_pos = ob.tellp(); last_pos = ob.tellp();
} }
// Bank items for (int16 slot_id = EQEmu::invslot::BANK_BEGIN; slot_id <= EQEmu::invslot::BANK_END; ++slot_id) {
for (int16 slot_id = EQEmu::invslot::BANK_BEGIN; slot_id <= EQEmu::invslot::BANK_END; slot_id++) {
const EQEmu::ItemInstance* inst = m_inv[slot_id]; const EQEmu::ItemInstance* inst = m_inv[slot_id];
if (!inst) if (!inst) {
continue; continue;
}
inst->Serialize(ob, slot_id); inst->Serialize(ob, slot_id);
if (ob.tellp() == last_pos) {
if (ob.tellp() == last_pos)
LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id);
}
last_pos = ob.tellp(); last_pos = ob.tellp();
} }
// SharedBank items for (int16 slot_id = EQEmu::invslot::SHARED_BANK_BEGIN; slot_id <= EQEmu::invslot::SHARED_BANK_END; ++slot_id) {
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]; const EQEmu::ItemInstance* inst = m_inv[slot_id];
if (!inst) if (!inst) {
continue; continue;
}
inst->Serialize(ob, slot_id); inst->Serialize(ob, slot_id);
if (ob.tellp() == last_pos) {
if (ob.tellp() == last_pos)
LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id);
}
last_pos = ob.tellp(); last_pos = ob.tellp();
} }
@ -808,6 +819,97 @@ void Client::BulkSendInventoryItems()
outapp->pBuffer = ob.detach(); outapp->pBuffer = ob.detach();
QueuePacket(outapp); QueuePacket(outapp);
safe_delete(outapp); safe_delete(outapp);
if (ClientVersionBit() & EQEmu::versions::maskRoFAndLater) {
IncrementOPCharInventorySent();
}
}
else { // Specialized behavior
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);
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();
}
}
}
} }
void Client::BulkSendMerchantInventory(int merchant_id, int npcid) { void Client::BulkSendMerchantInventory(int merchant_id, int npcid) {