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, 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)

View File

@ -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
*/

View File

@ -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:

View File

@ -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

View File

@ -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);

View File

@ -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) {