patch for bugged corpses. It may still need a little work, but most issues should be ironed out:
Code:
Index: inventory.cpp
===================================================================
--- inventory.cpp (revision 2175)
+++ inventory.cpp (working copy)
@@ -397,9 +405,11 @@
LogFile->write(EQEMuLog::Debug, "DeleteItemInInventory(%i, %i, %s)", slot_id, quantity, (client_update) ? "true":"false");
#endif
- if(!m_inv[slot_id]) {
+ // Added 'IsSlotValid(slot_id)' check to both segments of client packet processing.
+ // Cursor queue slots were slipping through and crashing client
+ if(!m_inv[slot_id]) {
// Make sure the client deletes anything in this slot to match the server.
- if(client_update) {
+ if(client_update && IsValidSlot(slot_id)) {
EQApplicationPacket* outapp;
outapp = new EQApplicationPacket(OP_DeleteItem, sizeof(DeleteItem_Struct));
DeleteItem_Struct* delitem = (DeleteItem_Struct*)outapp->pBuffer;
@@ -427,7 +437,7 @@
database.SaveInventory(character_id, inst, slot_id);
}
- if(client_update) {
+ if(client_update && IsValidSlot(slot_id)) {
EQApplicationPacket* outapp;
if(inst) {
if(!inst->IsStackable() && !isDeleted)
@@ -437,13 +447,13 @@
// Stackable, arrows, etc ? Delete one from the stack
outapp = new EQApplicationPacket(OP_DeleteItem, sizeof(MoveItem_Struct));
- DeleteItem_Struct* delitem = (DeleteItem_Struct*)outapp->pBuffer;
- delitem->from_slot = slot_id;
- delitem->to_slot = 0xFFFFFFFF;
- delitem->number_in_stack = 0xFFFFFFFF;
- for(int loop=0;loop<quantity;loop++)
- QueuePacket(outapp);
- safe_delete(outapp);
+ DeleteItem_Struct* delitem = (DeleteItem_Struct*)outapp->pBuffer;
+ delitem->from_slot = slot_id;
+ delitem->to_slot = 0xFFFFFFFF;
+ delitem->number_in_stack = 0xFFFFFFFF;
+ for(int loop=0;loop<quantity;loop++)
+ QueuePacket(outapp);
+ safe_delete(outapp);
}
else {
outapp = new EQApplicationPacket(OP_MoveItem, sizeof(MoveItem_Struct));
Index: PlayerCorpse.cpp
===================================================================
--- PlayerCorpse.cpp (revision 2175)
+++ PlayerCorpse.cpp (working copy)
@@ -352,13 +352,20 @@
pp->silver = 0;
pp->gold = 0;
pp->platinum = 0;
+
+ // need to tell client that cash has changed..could be RespawnFromHover=true..need to check
+ // issue looks like a money duplication error, but server value is actually correct
// get their tints
memcpy(item_tint, &client->GetPP().item_tint, sizeof(item_tint));
// solar: TODO soulbound items need not be added to corpse, but they need
// to go into the regular slots on the player, out of bags
-
+
+ // personal and cursor bag slots (251-340) are moved to corpse..should be deleting db entries too.
+ // reworked code to return and merge a list for the query builder instead of adding MoveItemToCorpse
+ // code to each loop.
+
// worn + inventory + cursor
std::list<uint32> removed_list;
bool cursor = false;
@@ -367,23 +374,27 @@
item = client->GetInv().GetItem(i);
if((item && (!client->IsBecomeNPC())) || (item && client->IsBecomeNPC() && !item->GetItem()->NoRent))
{
- MoveItemToCorpse(client, item, i);
- removed_list.push_back(i);
+ removed_list.merge(MoveItemToCorpse(client, item, i));
}
}
// cursor queue
+ // bumped starting assignment to 8001 because any in-memory 'slot 8000' item was moved above as 'slot 30'
+ // this was mainly for client profile state reflection..should match db player inventory entries now.
iter_queue it;
- for(it=client->GetInv().cursor_begin(),i=8000; it!=client->GetInv().cursor_end(); it++,i++) {
+ for(it=client->GetInv().cursor_begin(),i=8001; it!=client->GetInv().cursor_end(); it++,i++) {
item = *it;
if((item && (!client->IsBecomeNPC())) || (item && client->IsBecomeNPC() && !item->GetItem()->NoRent))
{
- MoveItemToCorpse(client, item, i);
+ removed_list.merge(MoveItemToCorpse(client, item, i));
cursor = true;
}
}
- if(removed_list.size() != 0) {
+ // client->DeleteItemInInventory is memory only..update_db is passed as false..not sure why unless it's a
+ // sync issue... client->Save() alone doesn't appear to handle inventory deletion with &inv[slot_id] being
+ // null. player inventory db entries did not reflect client.inv states after 'client->Save()' alone.
+ if(removed_list.size() != 0) {
std::stringstream ss("");
ss << "DELETE FROM inventory WHERE charid=" << client->CharacterID();
ss << " AND (";
@@ -401,14 +412,40 @@
ss << ")";
database.RunQuery(ss.str().c_str(), ss.str().length());
}
-
+
if(cursor) {
- std::list<ItemInst*>::const_iterator start = client->GetInv().cursor_begin();
- std::list<ItemInst*>::const_iterator finish = client->GetInv().cursor_end();
- database.SaveCursor(client->CharacterID(),
- start, finish);
+ // cycle cursor to clear memory queue..will delete server queue, but not client queue with hover enabled...
+ // if can figure out client issue, change argument 3 (client_update) to false if handled elsewhere.
+ // might could solve by forcing zone entry event to reset client inventory or sending client clear and
+ // resending bulkinventory. don't want to send client delete cursor on revive..might cause item loss...
+
+ while(!client->GetInv().CursorEmpty()) {
+ if (RuleB(Character, RespawnFromHover)) {
+ client->Message(0, "Attempting to delete %s from cursor.", client->GetInv().GetItem(SLOT_CURSOR)->GetItem()->Name);
+ client->DeleteItemInInventory(SLOT_CURSOR, 0, true, false);
+ }
+ else {
+ client->DeleteItemInInventory(SLOT_CURSOR, 0, false, false); // no need to update client without hover
+ }
+ }
+
+ if (RuleB(Character, RespawnFromHover)) {
+ client->Message(13, "Warning: Your cursor may contain duplicate items not found on the server!");
+ client->Message(0, "Place these items into an empty inventory slot to resolve this problem.");
+ }
+
+ // this code didn't appear to accomplish anything db-wise. it looks like it just re-saved the cursor
+ // queue data. with this code in-place, the cursor queue was re-saved and prevented lost items..which
+ // were the items on the corpses causing the 'bugged' issue in the first place. this code can be
+ // deleted after testing and verification of code submission.
+
+ //std::list<ItemInst*>::const_iterator start = client->GetInv().cursor_begin();
+ //std::list<ItemInst*>::const_iterator finish = client->GetInv().cursor_end();
+ //database.SaveCursor(client->CharacterID(),
+ // start, finish);
}
+ client->CalcBonuses(); // shouldn't matter to a corpse, but hey..at least Magelo will update correctly =)
client->Save();
} //end "not leaving naked corpses"
@@ -417,28 +454,35 @@
}
// solar: helper function for client corpse constructor
-void Corpse::MoveItemToCorpse(Client *client, ItemInst *item, sint16 equipslot)
+std::list<uint32> Corpse::MoveItemToCorpse(Client *client, ItemInst *item, sint16 equipslot)
{
int bagindex;
sint16 interior_slot;
ItemInst *interior_item;
+ std::list<uint32> returnlist;
AddItem(item->GetItem()->ID, item->GetCharges(), equipslot, item->GetAugmentItemID(0), item->GetAugmentItemID(1), item->GetAugmentItemID(2), item->GetAugmentItemID(3), item->GetAugmentItemID(4));
- if(item->IsType(ItemClassContainer))
+ returnlist.push_back(equipslot);
+
+ // Qualified bag slot iterations. processing bag slots that don't exist is not a good idea.
+ if(item->IsType(ItemClassContainer) && ((equipslot >= 22 && equipslot <=30))) // Limit the bag check to inventory and cursor slots.
{
for(bagindex = 0; bagindex <= 10; bagindex++)
{
+ // For empty bags in cursor queue, slot was being resolved as SLOT_INVALID (-1)
interior_slot = Inventory::CalcSlotId(equipslot, bagindex);
interior_item = client->GetInv().GetItem(interior_slot);
if(interior_item)
{
AddItem(interior_item->GetItem()->ID, interior_item->GetCharges(), interior_slot, interior_item->GetAugmentItemID(0), interior_item->GetAugmentItemID(1), interior_item->GetAugmentItemID(2), interior_item->GetAugmentItemID(3), interior_item->GetAugmentItemID(4));
+ returnlist.push_back(Inventory::CalcSlotId(equipslot, bagindex));
client->DeleteItemInInventory(interior_slot, 0, true, false);
}
}
- }
+ } // */
client->DeleteItemInInventory(equipslot, 0, true, false);
+ return returnlist;
}
// To be called from LoadFromDBData
@@ -981,19 +1025,43 @@
ItemList::iterator cur,end;
cur = itemlist.begin();
end = itemlist.end();
+
+ // Observed some odd behavior concerning the last corpse slot as coded (SoF client in this case.)
+ // Clicking index 29 (zero-based) with an item in index 28 results in retrieving index 28 item.
+ // Clicking index 29 with no item in index 28 results in the closing of the corpse loot window.
+
+ int corpselootlimit = 30; // 30 is the original value
+ /* need actual corpse limit values per client (or client range)..if always 30, then these con checks are unneeded
+ // enumeration shouldn't be needed unless someone finds a use for this info elsewhere
+ if (client->GetClientVersion()>=EQClientVoA)
+ corpselootlimit=30;
+ else if (client->GetClientVersion()>=EQClientHoT)
+ corpselootlimit=30;
+ else if (client->GetClientVersion()>=EQClientUnderfoot)
+ corpselootlimit=30;
+ else if (client->GetClientVersion()>=EQClientSoD)
+ corpselootlimit=30;
+ else if (client->GetClientVersion()>=EQClientSoF) // SoF has 32 visible slots..change untested
+ corpselootlimit=30;
+ else if (client->GetClientVersion()>=EQClientTitanium)
+ corpselootlimit=30;
+ else if (client->GetClientVersion()>=EQClient62)
+ corpselootlimit=30;
+ else
+ corpselootlimit=30; // */
+
for(; cur != end; cur++) {
ServerLootItem_Struct* item_data = *cur;
item_data->lootslot = 0xFFFF;
+
+ // Dont display the item if it's in a bag
- // Dont display the item if it's in a bag
- if(!IsPlayerCorpse() || item_data->equipSlot <= 30 || tCanLoot>=3)
+ // added cursor queue slots to corpse item visibility list. Nothing else should be making it to corpse.
+ if(!IsPlayerCorpse() || item_data->equipSlot <= 30 || tCanLoot>=3 ||
+ (item_data->equipSlot >= 8000 && item_data->equipSlot <= 8999))
{
- if (i >= 30)
+ if (i < corpselootlimit) // < 30 (0 - 29)
{
- Message(13, "Warning: Too many items to display. Loot some then re-loot the corpse to see the rest");
- }
- else
- {
item = database.GetItem(item_data->item_id);
if (client && item)
{
@@ -1006,9 +1074,24 @@
item_data->lootslot = i;
}
}
+ else if (i == corpselootlimit) // = 30
+ {
+ client->Message(13, "Warning: This corpse contains more items than can be displayed!");
+ client->Message(0, "Remove items and re-loot corpse to access remaining inventory.");
+ }
i++;
}
}
+ if (i > corpselootlimit) // > 30 (remember 'i' is increased again after the last iteration, so no '=')
+ client->Message(0, "(%s contains %i additional %s.)", GetName(), (i-corpselootlimit), (i-corpselootlimit)==1?"item":"items");
+
+ if (IsPlayerCorpse() && i == 0 && itemlist.size() > 0) { // somehow, corpse contains items, but client doesn't see them...
+ client->Message(13, "Warning: This corpse contains items that you do not permission to access!");
+ client->Message(13, "Contact a GM for assistance to determine if item replacement is necessary.");
+ client->Message(0, "BUGGED CORPSE [DBID: %i, Name: %s, Item Count: %i]", GetDBID(), GetName(), itemlist.size());
+ // if needed/wanted - create log dump->iterate corpse list..need pointer to log file
+ // could add code to check for owning client and give list of bugged items on corpse
+ }
}
// Disgrace: Client seems to require that we send the packet back...
Index: PlayerCorpse.h
===================================================================
--- PlayerCorpse.h (revision 2175)
+++ PlayerCorpse.h (working copy)
@@ -111,7 +111,7 @@
inline int GetRezzExp() { return rezzexp; }
protected:
- void MoveItemToCorpse(Client *client, ItemInst *item, sint16 equipslot);
+ std::list<uint32> MoveItemToCorpse(Client *client, ItemInst *item, sint16 equipslot);
private:
bool p_PlayerCorpse;
Remember, this patch is NOT commit ready.