PDA

View Full Version : Client-Server Item Desyncronization Bugs


Uleat
07-10-2012, 07:14 AM
Hello All!


I am creating this thread so that a thorough bug description repository and feedback forum will be available for me
and others to use to troubleshoot and trace the source of client-server item desyncronizations.

I have already researched posted issues both here and at www.peqtgc.com and have accumulated a small library. Most of
what I have seen are the results of desyncronization and not the actual bugs themselves.


I have been able to find, and reproduce with 100% accuracy, at least six desyncronization bugs. Some minor fixes have
led to even more bugs revealing themselves. None of the bugs, that I have discovered, are exploits. However, certain
conditions can lead to exploits or permanent loss of items.

I will not give detailed information on what bugs lead to exploits and ask that anyone posting information not to give
explainations of how to do so as well. We know that certain bugs can be exploited..we just need information about what
led to the occurrence of the bug. If you choose to find a way to exploit these bugs, you will most likely be treated
as a hacker by the GM's and Admin's of your host server and suffer any consequences thereof.


As this is the EQEmulator forum, we cannot promise any bug submission rewards for posted information. You will need to
apply on the forums of your host server, should they offer them. They will determine feasability based on their set of
rules and criteria.


So far, all of the bugs that I have discovered are initiated by some function that deals with sorting stackable items.
I have added immense debug code to inventory.cpp so that I may see in real-time what is occurring. This includes standard
item swapping, bandolier swapping and the possibility of corpse looting. I have not tested the corpse looting myself, but
looking over the code responsible for handling stackable items, I see a major difference in the way that it's handled
versus the way the client and other server methods do.

Without this debug code added to the zone server, you will not be able to see the actual bug conditions as they happen.
Only the results of a desyncronization will be apparent.

ANY desyncronization will lead to problems at some point and it is not recommended to replicate these bugs on anything
but test or private servers.


Here are some of the conditions that I have found that will lead to desync:

- Holding a weapon (especially one used in a bandolier swap) in-hand while performing a swap that contains arrows.
- Clicking a stack of arrows that doesn't respond, then clicking on another item or empty slot.
- Holding a partial stack of ammo that is used during a bandolier swap.
- Certain swap conditions will lead to an arrow with zero charges.
- Certain swap conditions will lead to having a backpack (or other non-ammo item) placed in the ammo slot.

You should zone or camp as soon as possible if you discover that you have a stackable item that is not clickable!
(If you manage to get a backpack in your ammo slot, zone and then click on the ammo slot with the backpack that appears.)


The following patch is not guaranteed to be bug free nor provide 100% reliable results. The code is intended to aid in
resolving current issues and may or may not be upgraded or refined in the future. Some portions are untested and could
cause a server crash with item loss. Use it at your own risk. (It was reverted and patched with no apparent issues.)

<inventory.cpp.patch>
Index: inventory.cpp
================================================== =================
--- inventory.cpp (revision 2158)
+++ inventory.cpp (working copy)
@@ -35,13 +35,21 @@

// @merth: this needs to be touched up
uint32 Client::NukeItem(uint32 itemnum, uint8 where_to_check) {
- if (itemnum == 0)
+ Message(4, "NukeIt->Entry(ItemID[%u])", itemnum);
+
+ if (itemnum == 0) {
+ Message(4, "NukeIt->Exit(Item[Null])");
+
return 0;
+ }
+
uint32 x = 0;
ItemInst *cur = NULL;

int i;
if(where_to_check & invWhereWorn) {
+ Message(4, "NukeIt->Process(Worn[%s])", database.GetItem(itemnum)?database.GetItem(itemnum )->Name:"InvalidID");
+
for (i=0; i<=21; i++) { // Equipped
if (GetItemIDAt(i) == itemnum || (itemnum == 0xFFFE && GetItemIDAt(i) != INVALID_ID)) {
cur = m_inv.GetItem(i);
@@ -72,6 +80,8 @@
}

if(where_to_check & invWhereCursor) {
+ Message(4, "NukeIt->Process(Cursor[%s])", database.GetItem(itemnum)?database.GetItem(itemnum )->Name:"InvalidID");
+
if (GetItemIDAt(30) == itemnum || (itemnum == 0xFFFE && GetItemIDAt(30) != INVALID_ID)) {
cur = m_inv.GetItem(30);
if(cur && cur->GetItem()->Stackable) {
@@ -98,6 +108,8 @@
}

if(where_to_check & invWherePersonal) {
+ Message(4, "NukeIt->Process(Inventory[%s])", database.GetItem(itemnum)?database.GetItem(itemnum )->Name:"InvalidID");
+
for (i=22; i<=29; i++) { // Equipped
if (GetItemIDAt(i) == itemnum || (itemnum == 0xFFFE && GetItemIDAt(i) != INVALID_ID)) {
cur = m_inv.GetItem(i);
@@ -126,6 +138,8 @@
}

if(where_to_check & invWhereBank) {
+ Message(4, "NukeIt->Process(Bank[%s])", database.GetItem(itemnum)?database.GetItem(itemnum )->Name:"InvalidID");
+
for (i=2000; i<=2023; i++) { // Bank slots
if (GetItemIDAt(i) == itemnum || (itemnum == 0xFFFE && GetItemIDAt(i) != INVALID_ID)) {
cur = m_inv.GetItem(i);
@@ -154,6 +168,8 @@
}

if(where_to_check & invWhereSharedBank) {
+ Message(4, "NukeIt->Process(SharedBank[%s])", database.GetItem(itemnum)?database.GetItem(itemnum )->Name:"InvalidID");
+
for (i=2500; i<=2501; i++) { // Shared bank
if (GetItemIDAt(i) == itemnum || (itemnum == 0xFFFE && GetItemIDAt(i) != INVALID_ID)) {
cur = m_inv.GetItem(i);
@@ -180,29 +196,48 @@
}
}
}
+ Message(4, "NukeIt->Exit(Item[%s])->NukeTotal[%u]", database.GetItem(itemnum)?database.GetItem(itemnum )->Name:"InvalidID", x);

return x;
}


bool Client::CheckLoreConflict(const Item_Struct* item) {
- if (!item)
+ Message(4, "ChkLoreCon->Entry(ItemID[%u])", item?item->ID:0);
+
+ if (!item) {
+ Message(4, "ChkLoreCon->Exit(Item[Null])");
+
return false;
- if (!(item->LoreFlag))
+ }
+
+ if (!(item->LoreFlag)) {
+ Message(4, "ChkLoreCon->Exit(NoLoreFlag[%s])", item->Name);
+
return false;
+ }

- if (item->LoreGroup == -1) // Standard lore items; look everywhere except the shared bank, return the result
- return (m_inv.HasItem(item->ID, 0, ~invWhereSharedBank) != SLOT_INVALID);
+ if (item->LoreGroup == -1) { // Standard lore items; look everywhere except the shared bank, return the result
+ Message(4, "ChkLoreCon->Exit(LoreCheck[%s]->Conflict[%s])", item->Name, (m_inv.HasItem(item->ID, 0, ~invWhereSharedBank) != SLOT_INVALID)?"True":"False");
+
+ return (m_inv.HasItem(item->ID, 0, ~invWhereSharedBank) != SLOT_INVALID);
+ }
+ //If the item has a lore group, we check for other items with the same group and return the result
+ Message(4, "ChkLoreCon->Exit(LoreGroupCheck[%s]->Conflict[%s])", item->Name, (m_inv.HasItemByLoreGroup(item->LoreGroup, ~invWhereSharedBank) != SLOT_INVALID)?"True":"False");

- //If the item has a lore group, we check for other items with the same group and return the result
return (m_inv.HasItemByLoreGroup(item->LoreGroup, ~invWhereSharedBank) != SLOT_INVALID);
}

void Client::SummonItem(uint32 item_id, sint16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5, bool attuned, uint16 to_slot) {
+ Message(4, "SumIt->Entry(ItemID[%u])", item_id);
+
const Item_Struct* item = database.GetItem(item_id);

if (item == NULL) {
Message(0, "No such item: %i", item_id);
+
+ Message(4, "SumIt->Exit(Item[Null]->SummonCancelled->InvalidItemID[%u])", item_id);
+
return;
}
// Checking to see if the Item is lore or not.
@@ -217,73 +252,131 @@
if (!foundlore && !foundgm) { // Okay, It isn't LORE, or if it is, it is not in player's inventory.
ItemInst* inst = database.CreateItem(item, charges);
if (inst) {
+ Message(4, "SumIt->Process(Create[%s]->SetCharges[%i])", inst->GetItem()->Name, inst->GetCharges());
+
// Custom logic for SummonItem
- if ((inst->GetCharges()==0))// && inst->IsStackable())
+ if ((inst->GetCharges()==0)) { // && inst->IsStackable())
+ Message(4, "SumIt->Process(SetCharges[%s]->Charges[1])", inst->GetItem()->Name);
+
inst->SetCharges(1);
- if ((inst->GetCharges()>0))
+ }
+
+ if ((inst->GetCharges()>0)) {
+ Message(4, "SumIt->Process(SetCharges[%s]->GetCharges[%i])", inst->GetItem()->Name, inst->GetCharges());
+
inst->SetCharges(inst->GetCharges());
+ }
+
if (aug1) {
+ Message(4, "SumIt->Process(Item[%s]->AugmentationSlot[1]->AddRequest)", inst->GetItem()->Name);
+
const Item_Struct* augitem1 = database.GetItem(aug1);
if (augitem1) {
+ Message(4, "SumIt->Process(Item[%s]->AugmentationSlot[1]->AugmentationFound[%s])", inst->GetItem()->Name, augitem1->Name);
+
if (!CheckLoreConflict(augitem1)) {
+ Message(4, "SumIt->Process(Item[%s]->AugmentationSlot[1]->AddingAugmentation[%s])", inst->GetItem()->Name, augitem1->Name);
+
inst->PutAugment(&database, 0, aug1);
}
else {
Message(0, "You already have a %s (%i) in your inventory!", item->Name, item_id);
+
+ Message(4, "SumIt->Process(Item[%s]->AugmentationSlot[1]->DuplicateLoreItem->Augmentation[%s])", inst->GetItem()->Name, augitem1->Name);
}
}
}
if (aug2) {
+ Message(4, "SumIt->Process(Item[%s]->AugmentationSlot[2]->AddRequest)", inst->GetItem()->Name);
+
const Item_Struct* augitem2 = database.GetItem(aug2);
if (augitem2) {
+ Message(4, "SumIt->Process(Item[%s]->AugmentationSlot[2]->AugmentationFound[%s])", inst->GetItem()->Name, augitem2->Name);
+
if (!CheckLoreConflict(augitem2)) {
+ Message(4, "SumIt->Process(Item[%s]->AugmentationSlot[2]->AddingAugmentation[%s])", inst->GetItem()->Name, augitem2->Name);
+
inst->PutAugment(&database, 1, aug2);
}
else {
Message(0, "You already have a %s (%i) in your inventory!", item->Name, item_id);
+
+ Message(4, "SumIt->Process(Item[%s]->AugmentationSlot[2]->DuplicateLoreItem->Augmentation[%s])", inst->GetItem()->Name, augitem2->Name);
}
}
}
if (aug3) {
+ Message(4, "SumIt->Process(Item[%s]->AugmentationSlot[3]->AddRequest)", inst->GetItem()->Name);
+
const Item_Struct* augitem3 = database.GetItem(aug3);
if (augitem3) {
+ Message(4, "SumIt->Process(Item[%s]->AugmentationSlot[3]->AugmentationFound[%s])", inst->GetItem()->Name, augitem3->Name);
+
if (!CheckLoreConflict(augitem3)) {
+ Message(4, "SumIt->Process(Item[%s]->AugmentationSlot[3]->AddingAugmentation[%s])", inst->GetItem()->Name, augitem3->Name);
+
inst->PutAugment(&database, 2, aug3);
}
else {
Message(0, "You already have a %s (%i) in your inventory!", item->Name, item_id);
+
+ Message(4, "SumIt->Process(Item[%s]->AugmentationSlot[3]->DuplicateLoreItem->Augmentation[%s])", inst->GetItem()->Name, augitem3->Name);
}
}
}
if (aug4) {
+ Message(4, "SumIt->Process(Item[%s]->AugmentationSlot[4]->AddRequest)", inst->GetItem()->Name);
+
const Item_Struct* augitem4 = database.GetItem(aug4);
if (augitem4) {
+ Message(4, "SumIt->Process(Item[%s]->AugmentationSlot[4]->AugmentationFound[%s])", inst->GetItem()->Name, augitem4->Name);
+
if (!CheckLoreConflict(augitem4)) {
+ Message(4, "SumIt->Process(Item[%s]->AugmentationSlot[4]->AddingAugmentation[%s])", inst->GetItem()->Name, augitem4->Name);
+
inst->PutAugment(&database, 3, aug4);
}
else {
Message(0, "You already have a %s (%i) in your inventory!", item->Name, item_id);
+
+ Message(4, "SumIt->Process(Item[%s]->AugmentationSlot[4]->DuplicateLoreItem->Augmentation[%s])", inst->GetItem()->Name, augitem4->Name);
}
}
}
if (aug5) {
+ Message(4, "SumIt->Process(Item[%s]->AugmentationSlot[5]->AddRequest)", inst->GetItem()->Name);
+
const Item_Struct* augitem5 = database.GetItem(aug5);
if (augitem5) {
+ Message(4, "SumIt->Process(Item[%s]->AugmentationSlot[5]->AugmentationFound[%s])", inst->GetItem()->Name, augitem5->Name);
+
if (!CheckLoreConflict(augitem5)) {
+ Message(4, "SumIt->Process(Item[%s]->AugmentationSlot[5]->AddingAugmentation[%s])", inst->GetItem()->Name, augitem5->Name);
+
inst->PutAugment(&database, 4, aug5);
}
else {
Message(0, "You already have a %s (%i) in your inventory!", item->Name, item_id);
+
+ Message(4, "SumIt->Process(Item[%s]->AugmentationSlot[5]->DuplicateLoreItem->Augmentation[%s])", inst->GetItem()->Name, augitem5->Name);
}
}
}
+
if (attuned) {
+ Message(4, "SumIt->Process(Attuned[True])");
+
if (inst->GetItem()->Attuneable) {
+ Message(4, "SumIt->Process(Attuned->SetNoDrop[True])");
+
inst->SetInstNoDrop(true);
}
}
+
if (to_slot == SLOT_CURSOR)
{
+ Message(4, "SumIt->Process(PushToCursor[%s]->Charges[%i])", inst->GetItem()->Name, inst->GetCharges());
+
//inst->SetCharges(
PushItemOnCursor(*inst);
// Send item packet to user
@@ -291,33 +384,50 @@
}
else
{
+ Message(4, "SumIt->Process(PutItemInInventory[%s]->ToSlot[%u]->Charges[%i])", inst->GetItem()->Name, to_slot, inst->GetCharges());
+
PutItemInInventory(to_slot, *inst, true);
}
safe_delete(inst);

if ((RuleB(Character, EnableDiscoveredItems)))
{
- if(!GetGM() && !IsDiscovered(item_id))
- DiscoverItem(item_id);
+ Message(4, "SumIt->Process(ItemDiscovery[Enabled])");
+
+ if(!GetGM() && !IsDiscovered(item_id)) {
+ Message(4, "SumIt->Process(Player[%s]->DiscoverItem[%s])", GetName(), database.GetItem(item_id)->Name);
+
+ DiscoverItem(item_id);
+ }
}
}
}
else { // Item was already in inventory & is a LORE item or was a GM only item. Give them a message about it.
if (foundlore){
Message_StringID(0,PICK_LORE);
+
+ Message(4, "SumIt->Process(DuplicateLoreItem[%s]->SummonCancelled)", database.GetItem(item_id)->Name);
//Message(0, "You already have a %s (%i) in your inventory!", item->Name, item_id);
}
- else if (foundgm)
+ else if (foundgm) {
Message(0, "You are not a GM to summon this item");
+
+ Message(4, "SumIt->Process(GMOnlyItem[%s]->SummonCancelled)", database.GetItem(item_id)->Name);
+ }
}
+ Message(4, "SumIt->Exit");
}

// Drop item from inventory to ground (generally only dropped from SLOT_CURSOR)
void Client::DropItem(sint16 slot_id)
{
+ Message(4, "DropIt->Entry");

if (GetInv().CheckNoDrop(slot_id) && RuleI(World, FVNoDropFlag) == 0 || RuleI(Character, MinStatusForNoDropExemptions) < Admin() && RuleI(World, FVNoDropFlag) == 2) {
//Message(0, "No Drop Exploit: Items Destroyed.");
+
+ Message(4, "DropIt->Exit(NoDropExploit[%s]->Slot[%i]->Delete)", GetInv().GetItem(slot_id)->GetItem()->Name, slot_id);
+
database.SetHackerFlag(this->AccountName(), this->GetCleanName(), "Tried to drop an item on the ground that was nodrop!");
GetInv().DeleteItem(slot_id);
return;
@@ -329,15 +439,24 @@
if (!inst) {
// Item doesn't exist in inventory!
Message(13, "Error: Item not found in slot %i", slot_id);
+
+ Message(4, "DropIt->Exit(Item[Null]->Slot[%i])", slot_id);
+
return;
}

// Save client inventory change to database
if (slot_id==SLOT_CURSOR) {
+ Message(4, "DropIt->Process(SaveCursor[Null])");
+
list<ItemInst*>::const_iterator s=m_inv.cursor_begin(),e=m_inv.cursor_end();
database.SaveCursor(CharacterID(), s, e);
- } else
+ }
+ else {
+ Message(4, "DropIt->Process(SaveInventory[Null]->Slot[%i])", slot_id);
+
database.SaveInventory(CharacterID(), NULL, slot_id);
+ }

// Package as zone object
Object* object = new Object(this, inst);
@@ -345,14 +464,21 @@
object->StartDecay();

safe_delete(inst);
+
+ Message(4, "DropIt->Exit");
}

// Drop inst
void Client::DropInst(const ItemInst* inst)
{
+ Message(4, "DropInst->Entry(ItemID[%u])", inst?inst->GetItem()->ID:0);
+
if (!inst) {
// Item doesn't exist in inventory!
Message(13, "Error: Item not found");
+
+ Message(4, "DropInst->Exit(Item[Null])");
+
return;
}

@@ -360,6 +486,9 @@
if (inst->GetItem()->NoDrop == 0)
{
Message(13, "This item is NODROP. Deleting.");
+
+ Message(4, "DropInst->Exit(NoDropItem[%s]->Delete)", inst->GetItem()->Name);
+
return;
}

@@ -367,37 +496,62 @@
Object* object = new Object(this, inst);
entity_list.AddObject(object, true);
object->StartDecay();
+
+ Message(4, "DropInst->Exit");
}

// Returns a slot's item ID (returns INVALID_ID if not found)
uint32 Client::GetItemIDAt(sint16 slot_id) {
+ // Something is calling this function every 8 seconds in-game. Debug is remarked out to avoid spamming
+ //Message(4, "GetItIDAt->Entry");
+
const ItemInst* inst = m_inv[slot_id];
- if (inst)
+ if (inst) {
+ //Message(4, "GetItIDAt->Exit(Item[%s]->ItemID[%u]->Slot[%i])", inst->GetItem()->Name, inst->GetItem()->ID, slot_id);
+
return inst->GetItem()->ID;
-
+ }
// None found
+ //Message(4, "GetItIDAt->Exit(Item[Null]->Slot[%i])", slot_id);
+
return INVALID_ID;
}

// Returns an augment's ID that's in an item (returns INVALID_ID if not found)
// Pass in the slot ID of the item and which augslot you want to check (0-4)
uint32 Client::GetAugmentIDAt(sint16 slot_id, uint8 augslot) {
+ Message(4, "GetAugIDAt->Entry");
+
const ItemInst* inst = m_inv[slot_id];
- if (inst)
- if (inst->GetAugmentItemID(augslot))
+ if (inst) {
+ if (inst->GetAugmentItemID(augslot)) {
+ Message(4, "GetAugIDAt->Exit(Augmentation[%s]->AugID[%u]->Slot[%i]->AugSlot[%i])", inst->GetAugment(augslot)->GetItem()->Name, inst->GetAugmentItemID(augslot), slot_id, augslot);
+
return inst->GetAugmentItemID(augslot);
+ }
+ else {
+ Message(4, "GetAugIDAt->Exit(Augmentation[Null]->Slot[%i]->AugSlot[%i])", slot_id, augslot);

+ return INVALID_ID;
+ }
+ }
// None found
+ Message(4, "GetAugIDAt->Exit(Item[Null]->Slot[%i])", slot_id);
+
return INVALID_ID;
}

// Remove item from inventory
void Client::DeleteItemInInventory(sint16 slot_id, sint8 quantity, bool client_update, bool update_db) {
+ Message(4, "DelItInInv->Entry");
+
#if (EQDEBUG >= 5)
LogFile->write(EQEMuLog::Debug, "DeleteItemInInventory(%i, %i, %s)", slot_id, quantity, (client_update) ? "true":"false");
#endif

if(!m_inv[slot_id]) {
+ Message(4, "DelItInInv->Process(EmptySlot->ClientDelete->Slot[%i])", slot_id);
+
// Make sure the client deletes anything in this slot to match the server.
if(client_update) {
EQApplicationPacket* outapp;
@@ -416,26 +570,42 @@

const ItemInst* inst=NULL;
if (slot_id==SLOT_CURSOR) {
+ Message(4, "DelItInInv->Process(CursorDelete)");
+
list<ItemInst*>::const_iterator s=m_inv.cursor_begin(),e=m_inv.cursor_end();
- if(update_db)
+ if(update_db) {
+ Message(4, "DelItInInv->Process(CursorDelete->UpdateDatabase)");
+
database.SaveCursor(character_id, s, e);
+ }
}
else {
+ Message(4, "DelItInInv->Process(InventoryDelete->Slot[%i])", slot_id);
+
// Save change to database
inst = m_inv[slot_id];
- if(update_db)
+ if(update_db) {
+ Message(4, "DelItInInv->Process(InventoryDelete->Slot[%i]->UpdateDatabase)", slot_id);
+
database.SaveInventory(character_id, inst, slot_id);
+ }
}

if(client_update) {
EQApplicationPacket* outapp;
if(inst) {
- if(!inst->IsStackable() && !isDeleted)
+ if(!inst->IsStackable() && !isDeleted) {
+ Message(4, "DelItInInv->Process(Client->ChargeItem->DeleteCharge[%s])", inst->GetItem()->Name);
+
// Non stackable item with charges = Item with clicky spell effect ? Delete a charge.
outapp = new EQApplicationPacket(OP_DeleteCharge, sizeof(MoveItem_Struct));
- else
+ }
+ else {
+ Message(4, "DelItInInv->Process(Client->StackableItem->DeleteItem[%s])", inst->GetItem()->Name);
+
// 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;
@@ -446,6 +616,8 @@
safe_delete(outapp);
}
else {
+ Message(4, "DelItInInv->Process(Client->DeleteItem->Slot[%i])", slot_id);
+
outapp = new EQApplicationPacket(OP_MoveItem, sizeof(MoveItem_Struct));
MoveItem_Struct* delitem = (MoveItem_Struct*)outapp->pBuffer;
delitem->from_slot = slot_id;
@@ -455,7 +627,7 @@
safe_delete(outapp);
}
}
-
+ Message(4, "DelItInInv->Exit");
}

// Puts an item into the person's inventory
@@ -464,55 +636,91 @@
// client_update: Sends packet to client
bool Client::PushItemOnCursor(const ItemInst& inst, bool client_update)
{
+ Message(4, "PushItOnCur->Entry(ItemID[%u])", inst?inst.GetItem()->ID:0);
+ Message(4, "PushItOnCur->Process(PushToCursor[%s])", inst?inst.GetItem()->Name:0);
+
mlog(INVENTORY__SLOTS, "Putting item %s (%d) on the cursor", inst.GetItem()->Name, inst.GetItem()->ID);
m_inv.PushCursor(inst);

if (client_update) {
+ Message(4, "PushItOnCur->Process(ClientUpdate->PushToCursor[%s])", inst?inst.GetItem()->Name:"Null");
+
SendItemPacket(SLOT_CURSOR, &inst, ItemPacketSummonItem);
}

list<ItemInst*>::const_iterator s=m_inv.cursor_begin(),e=m_inv.cursor_end();
+
+ Message(4, "PushItOnCur->Exit(SaveCursor[%s])", inst?inst.GetItem()->Name:"Null");
+
return database.SaveCursor(CharacterID(), s, e);
}

bool Client::PutItemInInventory(sint16 slot_id, const ItemInst& inst, bool client_update)
{
+ // No code to handle inst=null
+ Message(4, "PutItInInv->Entry(ItemID[%u])", inst?inst.GetItem()->ID:0);
+
mlog(INVENTORY__SLOTS, "Putting item %s (%d) into slot %d", inst.GetItem()->Name, inst.GetItem()->ID, slot_id);
if (slot_id==SLOT_CURSOR)
{
+ Message(4, "PutItInInv->Exit(PushToCursor[%s])", inst?inst.GetItem()->Name:"Null");
+
return PushItemOnCursor(inst,client_update);
}
- else
+ else {
+ Message(4, "PutItInInv->Process(PutItemInInventory[%s]->Slot[%i])", inst?inst.GetItem()->Name:"Null", slot_id);
+
m_inv.PutItem(slot_id, inst);
+ }

if (client_update) {
+ Message(4, "PutItInInv->Process(ClientUpdate->PutItemInInventory[%s]->Slot[%i])", inst?inst.GetItem()->Name:"Null", slot_id);
+
SendItemPacket(slot_id, &inst, (slot_id==SLOT_CURSOR)?ItemPacketSummonItem:ItemPa cketTrade);
}

- if (slot_id==SLOT_CURSOR) {
+ if (slot_id==SLOT_CURSOR) { // This code is NOT needed and never processed here..duplicate of PushItemOnCursor->called in first con check
+ Message(4, "PutItInInv->Exit(SaveCursor[%s])", inst?inst.GetItem()->Name:"Null");
+
list<ItemInst*>::const_iterator s=m_inv.cursor_begin(),e=m_inv.cursor_end();
return database.SaveCursor(this->CharacterID(), s, e);
- } else
+ }
+ else { // This is the code that is always executed if (slot_id != SLOT_CURSOR)
+ Message(4, "PutItInInv->Exit(SaveInventory[%s]->Slot[%i])", inst?inst.GetItem()->Name:"Null", slot_id);
+
return database.SaveInventory(this->CharacterID(), &inst, slot_id);
-
+ }
+ // How do we ever calc these bonuses with the above 'return's!?
CalcBonuses();
+
+ Message(4, "PutItInInv->Exit(LET ME KNOW IF YOU EVER SEE THIS MESSAGE! - ULEAT");
}

void Client::PutLootInInventory(sint16 slot_id, const ItemInst &inst, ServerLootItem_Struct** bag_item_data)
{
+ Message(4, "PutLootInInv->Entry(ItemID[%u])", inst?inst.GetItem()->ID:0);
+
mlog(INVENTORY__SLOTS, "Putting loot item %s (%d) into slot %d", inst.GetItem()->Name, inst.GetItem()->ID, slot_id);
m_inv.PutItem(slot_id, inst);

SendLootItemInPacket(&inst, slot_id);

if (slot_id==SLOT_CURSOR) {
+ Message(4, "PutLootInInv->Process(SaveCursor[%s])", inst?inst.GetItem()->Name:"Null");
+
list<ItemInst*>::const_iterator s=m_inv.cursor_begin(),e=m_inv.cursor_end();
database.SaveCursor(this->CharacterID(), s, e);
- } else
+ }
+ else {
+ Message(4, "PutLootInInv->Process(SaveInventory[%s]->Slot[%i])", inst?inst.GetItem()->Name:"Null", slot_id);
+
database.SaveInventory(this->CharacterID(), &inst, slot_id);
+ }

if(bag_item_data) // bag contents
{
+ Message(4, "PutLootInInv->Process(BagLoad)");
+
sint16 interior_slot;
// solar: our bag went into slot_id, now let's pack the contents in
for(int i = 0; i < 10; i++)
@@ -522,22 +730,39 @@
const ItemInst *bagitem = database.CreateItem(bag_item_data[i]->item_id, bag_item_data[i]->charges, bag_item_data[i]->aug1, bag_item_data[i]->aug2, bag_item_data[i]->aug3, bag_item_data[i]->aug4, bag_item_data[i]->aug5);
interior_slot = Inventory::CalcSlotId(slot_id, i);
mlog(INVENTORY__SLOTS, "Putting bag loot item %s (%d) into slot %d (bag slot %d)", inst.GetItem()->Name, inst.GetItem()->ID, interior_slot, i);
+
+ Message(4, "PutLootInInv->Process(PutLootItem[%s]->InteriorSlot[%i]->InSlot[%i])", inst?inst.GetItem()->Name:"Null", interior_slot, i);
+
PutLootInInventory(interior_slot, *bagitem);
safe_delete(bagitem);
}
}

CalcBonuses();
+
+ Message(4, "PutLootInInv->Exit");
}
bool Client::TryStacking(ItemInst* item, int8 type, bool try_worn, bool try_cursor){
- if(!item || !item->IsStackable() || item->GetCharges()>=item->GetItem()->StackSize)
+ Message(4, "TryStac->Entry(ItemID[%u])", item?item->GetItem()->ID:0);
+
+ if(!item || !item->IsStackable() || item->GetCharges()>=item->GetItem()->StackSize) {
+ if(!item){
+ Message(4, "TryStac->Exit(Item[Null])");
+ } else {
+ Message(4, "TryStac->Exit(Item[%s]->IsStackable[%s]->StackSize[%i]->Charges[%i])", item->GetItem()->Name, item->IsStackable()?"True":"False", item->GetItem()->StackSize, item->GetCharges());
+ }
+
return false;
+ }
+
sint16 i;
int32 item_id = item->GetItem()->ID;
for (i = 22; i <= 29; i++)
{
ItemInst* tmp_inst = m_inv.GetItem(i);
if(tmp_inst && tmp_inst->GetItem()->ID == item_id && tmp_inst->GetCharges() < tmp_inst->GetItem()->StackSize){
+ Message(4, "TryStac->Process(InvSlot[%i]->MoveCharges->ChargeCount[%i])", i, tmp_inst->GetCharges());
+
MoveItemCharges(*item, i, type);
CalcBonuses();
if(item->GetCharges()) // we didn't get them all
@@ -553,6 +778,8 @@
ItemInst* tmp_inst = m_inv.GetItem(slotid);

if(tmp_inst && tmp_inst->GetItem()->ID == item_id && tmp_inst->GetCharges() < tmp_inst->GetItem()->StackSize){
+ Message(4, "TryStac->Process(InvSlot[%i]->BagSlot[%u]->MoveCharges->ChargeCount[%i])", i, j, tmp_inst->GetCharges());
+
MoveItemCharges(*item, slotid, type);
CalcBonuses();
if(item->GetCharges()) // we didn't get them all
@@ -561,6 +788,8 @@
}
}
}
+ Message(4, "TryStac->Exit");
+
return false;
}
// Locate an available space in inventory to place an item
@@ -568,6 +797,8 @@
// The change will be saved to the database
bool Client::AutoPutLootInInventory(ItemInst& inst, bool try_worn, bool try_cursor, ServerLootItem_Struct** bag_item_data)
{
+ Message(4, "AutoPutLootInInv->Entry(ItemID[%u])", inst?inst.GetItem()->ID:0);
+
// #1: Try to auto equip
if (try_worn && inst.IsEquipable(GetBaseRace(), GetClass()) && inst.GetItem()->ReqLevel<=level && !inst.GetItem()->Attuneable && inst.GetItem()->ItemType != ItemTypeAugment)
{
@@ -610,6 +841,8 @@
{
SendWearChange(worn_slot_material);
}
+ Message(4, "AutoPutLootInInv->Exit(AutoLoot[%s]->EquipSlot[%i])", inst.GetItem()->Name, i);
+
return true;
}
}
@@ -619,8 +852,11 @@
// #2: Stackable item?
if (inst.IsStackable())
{
- if(TryStacking(&inst, ItemPacketTrade, try_worn, try_cursor))
+ if(TryStacking(&inst, ItemPacketTrade, try_worn, try_cursor)) {
+ Message(4, "AutoPutLootInInv->Exit(AutoLoot[%s]->AddedToExistingStack)", inst.GetItem()->Name);
+
return true;
+ }
}

// #3: put it in inventory
@@ -628,20 +864,27 @@
sint16 slot_id = m_inv.FindFreeSlot(inst.IsType(ItemClassContainer) , try_cursor, inst.GetItem()->Size, is_arrow);
if (slot_id != SLOT_INVALID)
{
+ Message(4, "AutoPutLootInInv->Exit(AutoLoot[%s]->InvSlot[%i])", inst.GetItem()->Name, slot_id);
+
PutLootInInventory(slot_id, inst, bag_item_data);
return true;
}
-
+ Message(4, "AutoPutLootInInv->Exit(AutoLoot[%s]->Failed)", inst.GetItem()->Name);
+
return false;
}

// solar: helper function for AutoPutLootInInventory
void Client::MoveItemCharges(ItemInst &from, sint16 to_slot, int8 type)
{
+ Message(4, "MoveItChrgs->Entry(ItemID[%u])", from?from.GetItem()->ID:0);
+
ItemInst *tmp_inst = m_inv.GetItem(to_slot);

if(tmp_inst && tmp_inst->GetCharges() < tmp_inst->GetItem()->StackSize)
{
+ Message(4, "MoveItChrgs->Process(Item[%s]->PartialStack[%i]->StackSize[%i])", tmp_inst?tmp_inst->GetItem()->Name:"Null", tmp_inst?tmp_inst->GetCharges():0, tmp_inst?tmp_inst->GetItem()->StackSize:0);
+
// this is how much room is left on the item we're stacking onto
int charge_slots_left = tmp_inst->GetItem()->StackSize - tmp_inst->GetCharges();
// this is how many charges we can move from the looted item to
@@ -655,14 +898,23 @@
from.SetCharges(from.GetCharges() - charges_to_move);
SendLootItemInPacket(tmp_inst, to_slot);
if (to_slot==SLOT_CURSOR){
+ Message(4, "MoveItChrgs->Process(CursorStack->AddAmount[%i])", charges_to_move);
+
list<ItemInst*>::const_iterator s=m_inv.cursor_begin(),e=m_inv.cursor_end();
database.SaveCursor(this->CharacterID(), s, e);
- } else
+ }
+ else {
+ Message(4, "MoveItChrgs->Process(InventoryStack->Slot[%i]->AddAmount[%i])", to_slot, charges_to_move);
+
database.SaveInventory(this->CharacterID(), tmp_inst, to_slot);
+ }
}
+ Message(4, "MoveItChrgs->Exit");
}

bool Client::MakeItemLink(char* &ret_link, const ItemInst *inst) {
+ Message(4, "MakeItLink->Entry(ItemID[%u])", inst?inst->GetItem()->ID:0);
+
//we're sending back the entire "link", minus the null characters & item name
//that way, we can use it for regular links & Task links
//note: initiator needs to pass us ret_link
@@ -675,8 +927,11 @@
http://eqitems.13th-floor.org/phpBB2/viewtopic.php?p=510#510
*/

- if (!inst) //have to have an item to make the link
+ if (!inst) { //have to have an item to make the link
+ Message(4, "MakeItLink->Exit(Item[Null])");
+
return false;
+ }

const Item_Struct* item = inst->GetItem();
//format:
@@ -692,6 +947,8 @@
//int hash = GetItemLinkHash(inst); //eventually this will work (currently crashes zone), but for now we'll skip the extra overhead
if (GetClientVersion() >= EQClientSoF)
{
+ Message(4, "MakeItLink->Process(MakeLink[%s]->Client->SoF)", inst->GetItem()->Name);
+
MakeAnyLenString(&ret_link, "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X",
0,
item->ID,
@@ -709,6 +966,8 @@
}
else
{
+ Message(4, "MakeItLink->Process(MakeLink[%s]->Client->Titanium)", inst->GetItem()->Name);
+
MakeAnyLenString(&ret_link, "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%08X",
0,
item->ID,
@@ -723,15 +982,21 @@
hash
);
}
+ Message(4, "MakeItLink->Exit");

return true;
}

int Client::GetItemLinkHash(const ItemInst* inst) {
+ Message(4, "GetItLinkHash->Entry(ItemID[%u])", inst?inst->GetItem()->Name:"Null");
+
//pre-Titanium: http://eqitems.13th-floor.org/phpBB2/viewtopic.php?t=70&postdays=0&postorder=asc
//Titanium: http://eqitems.13th-floor.org/phpBB2/viewtopic.php?t=145
- if (!inst) //have to have an item to make the hash
+ if (!inst) { //have to have an item to make the hash
+ Message(4, "GetItLinkHash->Exit(NullItem)");
+
return 0;
+ }

const Item_Struct* item = inst->GetItem();
char* hash_str = 0;
@@ -739,6 +1004,8 @@

//now the fun part, since different types of items use different hashes...
if (item->ItemClass == 0 && item->CharmFileID) { //charm
+ Message(4, "GetItLinkHash->Process(GetLink->Charm[%s])", item->Name);
+
MakeAnyLenString(&hash_str, "%d%s-1-1-1-1-1%d %d %d %d %d %d %d %d %d",
item->ID,
item->Name,
@@ -752,6 +1019,8 @@
item->Favor,
item->GuildFavor);
} else if (item->ItemClass == 2) { //book
+ Message(4, "GetItLinkHash->Process(GetLink->Book[%s])", item->Name);
+
MakeAnyLenString(&hash_str, "%d%s%d%d%09X",
item->ID,
item->Name,
@@ -759,6 +1028,8 @@
item->BookType,
item->Price);
} else if (item->ItemClass == 1) { //bag
+ Message(4, "GetItLinkHash->Process(GetLink->Bag[%s])", item->Name);
+
MakeAnyLenString(&hash_str, "%d%s%x%d%09X%d",
item->ID,
item->Name,
@@ -767,6 +1038,8 @@
item->Price,
item->Weight);
} else { //everything else
+ Message(4, "GetItLinkHash->Process(GetLink->Item[%s])", item->Name);
+
MakeAnyLenString(&hash_str, "%d%s-1-1-1-1-1%d %d %d %d %d %d %d %d %d %d %d %d %d",
item->ID,
item->Name,
@@ -811,20 +1084,27 @@
}
*/

+ Message(4, "GetItLinkHash->Exit");
+
safe_delete_array(hash_str);
return hash;
}

void Client::SendItemLink(const ItemInst* inst, bool send_to_all)
{
+ Message(4, "SentItLink->Entry(ItemID[%u])", inst?inst->GetItem()->ID:0);
+
/*

this stuff is old, live dosent do this anymore. they send a much smaller
packet with the item number in it, but I cant seem to find it right now

*/
- if (!inst)
+ if (!inst) {
+ Message(4, "SentItLink->Exit(NullItem)");
+
return;
+ }

const Item_Struct* item = inst->GetItem();
const char* name2 = &item->Name[0];
@@ -844,8 +1124,16 @@
memcpy(outapp->pBuffer,buffer2,outapp->size);
QueuePacket(outapp);
safe_delete(outapp);
- if (send_to_all==false)
+
+ if (send_to_all==false) {
+ Message(4, "SentItLink->Exit(SendToAll[False])");
+
return;
+ }
+ else {
+ Message(4, "SentItLink->Process(SendToAll[True])");
+ }
+
const char* charname = this->GetName();
outapp = new EQApplicationPacket(OP_ItemLinkText,strlen(itemlin k)+14+strlen(charname));
char buffer3[150] = {0};
@@ -853,15 +1141,23 @@
memcpy(outapp->pBuffer,buffer3,outapp->size);
entity_list.QueueCloseClients(this->CastToMob(),outapp,true,200,0,false);
safe_delete(outapp);
+
+ Message(4, "SentItLink->Exit");
}

void Client::SendLootItemInPacket(const ItemInst* inst, sint16 slot_id)
{
+ Message(4, "SendLootItInPack->Entry(ItemID[%u])", inst?inst->GetItem()->ID:0);
+
SendItemPacket(slot_id,inst, ItemPacketTrade);
+
+ Message(4, "SendLootItInPack->Exit(ClientUpdate->SendItem[%s])", inst?inst->GetItem()->Name:"Null");
}

bool Client::IsValidSlot(uint32 slot)
{
+ Message(4, "IsValSlot->Entry");
+
if((slot == (uint32)SLOT_INVALID) || // Destroying/Dropping item
(slot >= 0 && slot <= 30) || // Worn inventory, normal inventory, and cursor
(slot >= 251 && slot <= 340) || // Normal inventory bags and cursor bag
@@ -874,22 +1170,31 @@
(slot >= 4000 && slot <= 4009) || // Tradeskill container
(slot == 9999)) // Power Source
{
+ Message(4, "IsValSlot->Exit(ValidSlot[%u])", slot);
+
return true;
}
else {
+ Message(4, "IsValSlot->Exit(InvalidSlot[%u])", slot);
+
return false;
}
}

bool Client::IsBankSlot(uint32 slot)
{
+ Message(4, "IsBankSlot->Entry");
+
if((slot >= 2000 && slot <= 2023) || // Bank
(slot >= 2031 && slot <= 2270) || // Bank bags
(slot >= 2500 && slot <= 2501) || // Shared bank
(slot >= 2531 && slot <= 2550)) // Shared bank bags
{
+ Message(4, "IsBankSlot->Exit(ValidBankSlot[%u])", slot);
+
return true;
}
+ Message(4, "IsBankSlot->Exit(InvalidBankSlot[%u])", slot);

return false;
}
@@ -897,6 +1202,7 @@
// Moves items around both internally and in the database
// In the future, this can be optimized by pushing all changes through one database REPLACE call
bool Client::SwapItem(MoveItem_Struct* move_in) {
+ Message(4, "SwapIt->Entry");

uint32 src_slot_check = move_in->from_slot;
uint32 dst_slot_check = move_in->to_slot;
@@ -907,6 +1213,9 @@
if(src_slot_check < 2147483647)
Message(13, "Warning: Invalid slot move from slot %u to slot %u with %u charges!", src_slot_check, dst_slot_check, stack_count_check);
mlog(INVENTORY__SLOTS, "Invalid slot move from slot %u to slot %u with %u charges!", src_slot_check, dst_slot_check, stack_count_check);
+
+ Message(4, "SwapIt->Exit(SRCSlotCheck->SRCSlotCheck[%u]->DSTSlotCheck[%u]->StackCountCheck[%u])", src_slot_check, dst_slot_check, stack_count_check);
+
return false;
}

@@ -915,28 +1224,48 @@
if(src_slot_check < 2147483647)
Message(13, "Warning: Invalid slot move from slot %u to slot %u with %u charges!", src_slot_check, dst_slot_check, stack_count_check);
mlog(INVENTORY__SLOTS, "Invalid slot move from slot %u to slot %u with %u charges!", src_slot_check, dst_slot_check, stack_count_check);
+
+ Message(4, "SwapIt->Exit(DSTSlotCheck->SRCSlotCheck[%u]->DSTSlotCheck[%u]->StackCountCheck[%u])", src_slot_check, dst_slot_check, stack_count_check);
+
return false;
}

- if (move_in->from_slot == move_in->to_slot)
+ if (move_in->from_slot == move_in->to_slot) {
+ Message(4, "SwapIt->Exit(SummonItem->Slot[%u]->StackCount[%u])", move_in->from_slot, move_in->number_in_stack);
+
return true; // Item summon, no further proccessing needed
+ }

if (move_in->to_slot == (uint32)SLOT_INVALID) {
if(move_in->from_slot == (uint32)SLOT_CURSOR) {
mlog(INVENTORY__SLOTS, "Client destroyed item from cursor slot %d", move_in->from_slot);
+
+ Message(4, "SwapIt->Exit(ClientAction->DeleteItem->Cursor[%u])", move_in->from_slot);
+
DeleteItemInInventory(move_in->from_slot);
return true; // Item destroyed by client
}
else {
mlog(INVENTORY__SLOTS, "Deleted item from slot %d as a result of an inventory container tradeskill combine.", move_in->from_slot);
+
+ Message(4, "SwapIt->Exit(TradeskillAction->DeleteItem->Slot[%u])", move_in->from_slot);
+
DeleteItemInInventory(move_in->from_slot);
return true; // Item deletetion
}
}
- if(auto_attack && (move_in->from_slot == SLOT_PRIMARY || move_in->from_slot == SLOT_SECONDARY || move_in->from_slot == SLOT_RANGE))
+
+ if(auto_attack && (move_in->from_slot == SLOT_PRIMARY || move_in->from_slot == SLOT_SECONDARY || move_in->from_slot == SLOT_RANGE)) {
+ Message(4, "SwapIt->Process(SwapWeapon->FromSlot[%u]->StartTimer)", move_in->from_slot);
+
SetAttackTimer();
- else if(auto_attack && (move_in->to_slot == SLOT_PRIMARY || move_in->to_slot == SLOT_SECONDARY || move_in->to_slot == SLOT_RANGE))
+ }
+ else if(auto_attack && (move_in->to_slot == SLOT_PRIMARY || move_in->to_slot == SLOT_SECONDARY || move_in->to_slot == SLOT_RANGE)) {
+ Message(4, "SwapIt->Process(SwapWeapon->ToSlot[%u]->StartTimer)", move_in->to_slot);
+
SetAttackTimer();
+ }
+
// Step 1: Variables
sint16 src_slot_id = (sint16)move_in->from_slot;
sint16 dst_slot_id = (sint16)move_in->to_slot;
@@ -946,6 +1275,8 @@
IsBankSlot(src_slot_check) ||
IsBankSlot(dst_slot_check))
{
+ Message(4, "SwapIt->Process(BankerCheck)");
+
uint32 distance = 0;
NPC *banker = entity_list.GetClosestBanker(this, distance);

@@ -969,18 +1300,29 @@
if (src_inst){
mlog(INVENTORY__SLOTS, "Src slot %d has item %s (%d) with %d charges in it.", src_slot_id, src_inst->GetItem()->Name, src_inst->GetItem()->ID, src_inst->GetCharges());
srcitemid = src_inst->GetItem()->ID;
+
+ Message(4, "SwapIt->Process(SRCItem[%s]->SRCItemIDAssignment[%u])", src_inst?src_inst->GetItem()->Name:"Null", srcitemid);
+
//SetTint(dst_slot_id,src_inst->GetColor());
if (src_inst->GetCharges() > 0 && (src_inst->GetCharges() < (sint16)move_in->number_in_stack || move_in->number_in_stack > src_inst->GetItem()->StackSize))
{
Message(13,"Error: Insufficent number in stack.");
+
+ Message(4, "SwapIt->Exit(SRCItem[%s]->SRCCharges[%i]->MIStackCount[%u]->SRCStackSize[%i]->ERROR)", src_inst?src_inst->GetItem()->Name:"Null", src_inst?src_inst->GetCharges():-1, move_in?move_in->number_in_stack:-1, src_inst?src_inst->GetItem()->StackSize:-1);
+
return false;
}
}
if (dst_inst) {
mlog(INVENTORY__SLOTS, "Dest slot %d has item %s (%d) with %d charges in it.", dst_slot_id, dst_inst->GetItem()->Name, dst_inst->GetItem()->ID, dst_inst->GetCharges());
dstitemid = dst_inst->GetItem()->ID;
+
+ Message(4, "SwapIt->Process(DSTItem[%s]->DSTItemIDAssignment[%u])", dst_inst?dst_inst->GetItem()->Name:"Null", dstitemid);
}
+
if (Trader && srcitemid>0){
+ Message(4, "SwapIt->Process(TraderSatchelCheck)");
+
ItemInst* srcbag;
ItemInst* dstbag;
uint32 srcbagid =0;
@@ -1005,12 +1347,16 @@
// After this, we can assume src_inst is a valid ptr
if (!src_inst && (src_slot_id<4000 || src_slot_id>4009)) {
if (dst_inst) {
+ Message(4, "SwapIt->Process(InvalidSourceItem[Null]->DSTItem[%s]->Delete", dst_inst?dst_inst->GetItem()->Name:"Null");
// If there is no source item, but there is a destination item,
// move the slots around before deleting the invalid source slot item,
// which is now in the destination slot.
move_in->from_slot = dst_slot_check;
move_in->to_slot = src_slot_check;
move_in->number_in_stack = dst_inst->GetCharges();
+
+ Message(4, "SwapIt->Process(RecursiveCallToDeleteSRCItem)");
+
SwapItem(move_in);
}
Message(13, "Error: Server found no item in slot %i (->%i), Deleting Item!", src_slot_id, dst_slot_id);
@@ -1020,6 +1366,8 @@
}
//verify shared bank transactions in the database
if(src_inst && src_slot_id >= 2500 && src_slot_id <= 2550) {
+ Message(4, "SwapIt->Process(SharedBankCheck->SRC)");
+
if(!database.VerifyInventory(account_id, src_slot_id, src_inst)) {
LogFile->write(EQEMuLog::Error, "Player %s on account %s was found exploiting the shared bank.\n", account_name, GetName());
DeleteItemInInventory(dst_slot_id,0,true);
@@ -1035,6 +1383,8 @@
}
}
if(dst_inst && dst_slot_id >= 2500 && dst_slot_id <= 2550) {
+ Message(4, "SwapIt->Process(SharedBankCheck->DST)");
+
if(!database.VerifyInventory(account_id, dst_slot_id, dst_inst)) {
LogFile->write(EQEMuLog::Error, "Player %s on account %s was found exploting the shared bank.\n", account_name, GetName());
DeleteItemInInventory(src_slot_id,0,true);
@@ -1059,11 +1409,16 @@
&& RuleI(World, FVNoDropFlag) == 0 || RuleI(Character, MinStatusForNoDropExemptions) < Admin() && RuleI(World, FVNoDropFlag) == 2) {
DeleteItemInInventory(src_slot_id);
WorldKick();
+
+ // No message applied here since the player is being kicked
+
return false;
}

// Step 3: Check for interaction with World Container (tradeskills)
if(m_tradeskill_object != NULL) {
+ Message(4, "SwapIt->Process(WorldContainerCheck)");
+
if (src_slot_id>=4000 && src_slot_id<=4009) {
// Picking up item from world container
ItemInst* inst = m_tradeskill_object->PopItem(Inventory::CalcBagIdx(src_slot_id));
@@ -1136,6 +1491,8 @@

// Step 4: Check for entity trade
if (dst_slot_id>=3000 && dst_slot_id<=3007) {
+ Message(4, "SwapIt->Process(EntityTradeCheck)");
+
if (src_slot_id != SLOT_CURSOR) {
Kick();
return false;
@@ -1161,23 +1518,38 @@

// Step 5: Swap (or stack) items
if (move_in->number_in_stack > 0) {
+ Message(4, "SwapIt->Process(StackableItem[%s])", src_inst?src_inst->GetItem()->Name:"Null");
+
// Determine if charged items can stack
if (dst_inst) {
if(src_inst->GetID() != dst_inst->GetID()) {
mlog(INVENTORY__ERROR, "Move from %d to %d with stack size %d. Incompatible item types: %d != %d", src_slot_id, dst_slot_id, move_in->number_in_stack, src_inst->GetID(), dst_inst->GetID());
- return(false);
+
+ Message(4, "SwapIt->Process(StackableIDMisMatch->SRCItem[%s]->SRCID[%u]->DSTItem[%s]->DSTID[%u])", src_inst?src_inst->GetItem()->Name:"Null", src_inst?src_inst->GetItem()->ID:0, dst_inst?dst_inst->GetItem()->Name:"Null", dst_inst?dst_inst->GetItem()->ID:0);
+
+ return false;
}
if(dst_inst->GetCharges() < dst_inst->GetItem()->StackSize) {
//we have a chance of stacking.
mlog(INVENTORY__SLOTS, "Move from %d to %d with stack size %d. dest has %d/%d charges", src_slot_id, dst_slot_id, move_in->number_in_stack, dst_inst->GetCharges(), dst_inst->GetItem()->StackSize);
// Charges can be emptied into dst
uint16 usedcharges = dst_inst->GetItem()->StackSize - dst_inst->GetCharges();
- if (usedcharges > move_in->number_in_stack)
+
+ Message(4, "SwapIt->Process(StackSize[%i]->DSTCharges[%i]->UsedCharges[%u])", dst_inst->GetItem()->StackSize, dst_inst->GetCharges(), usedcharges);
+
+ if (usedcharges > move_in->number_in_stack) {
+ Message(4, "SwapIt->Process(UsedChargeAdjustment[%u]->NumberInStack[%u])", usedcharges, move_in->number_in_stack);
+
usedcharges = move_in->number_in_stack;
+ }
+ dst_inst->SetCharges(dst_inst->GetCharges() + usedcharges);

- dst_inst->SetCharges(dst_inst->GetCharges() + usedcharges);
+ Message(4, "SwapIt->Process(SetDSTCharges->CurrentCharges[%i])", dst_inst?dst_inst->GetCharges():-1);
+
src_inst->SetCharges(src_inst->GetCharges() - usedcharges);

+ Message(4, "SwapIt->Process(SetSRCCharges->CurrentCharges[%i])", src_inst?src_inst->GetCharges():-1);
+
// Depleted all charges?
if (src_inst->GetCharges() < 1)
{
@@ -1187,7 +1559,11 @@
} else {
mlog(INVENTORY__SLOTS, "Dest (%d) now has %d charges, source (%d) has %d (%d moved)", dst_slot_id, dst_inst->GetCharges(), src_slot_id, src_inst->GetCharges(), usedcharges);
}
- } else {
+ Message(4, "SwapIt->Process(SRCCharges->Remaining[%i])", src_inst?src_inst->GetCharges():-1);
+ }
+ else {
+ Message(4, "SwapIt->Process(Scooby->Ruh Roh Rhaggy!)");
+
//stack is full, so
}
}
@@ -1197,6 +1573,8 @@
mlog(INVENTORY__SLOTS, "Move entire stack from %d to %d with stack size %d. Dest empty.", src_slot_id, dst_slot_id, move_in->number_in_stack);
// Move entire stack
m_inv.SwapItem(src_slot_id, dst_slot_id);
+
+ Message(4, "SwapIt->Process(MoveStack->SRCSlot[%i]->DSTSlot[]%i->StackCount[%u])", src_slot_id, dst_slot_id, move_in->number_in_stack);
}
else {
// Split into two
@@ -1205,6 +1583,8 @@
ItemInst* inst = database.CreateItem(src_inst->GetItem(), move_in->number_in_stack);
m_inv.PutItem(dst_slot_id, *inst);
safe_delete(inst);
+
+ Message(4, "SwapIt->Process(SplitStack->SRCSlot[%i]->DSTSlot[%i]->StackCount[%u])", src_slot_id, dst_slot_id, src_inst?src_inst->GetCharges():-1);
}
}
}
@@ -1217,6 +1597,9 @@
SetMaterial(dst_slot_id,src_inst->GetItem()->ID);
}
mlog(INVENTORY__SLOTS, "Moving entire item from slot %d to slot %d", src_slot_id, dst_slot_id);
+
+ Message(4, "SwapIt->Process(SwapItem[%s]->SRCSlot[%i]->DSTSlot[%i])", src_inst?src_inst->GetItem()->Name:"Null", src_slot_id, dst_slot_id);
+
m_inv.SwapItem(src_slot_id, dst_slot_id);
}

@@ -1226,23 +1609,45 @@
}

// Step 7: Save change to the database
- if (src_slot_id==SLOT_CURSOR){
+ if (src_slot_id==SLOT_CURSOR) {
+ //Message(4, "SwapIt->Process(SaveCursor[%s]->SRC)", src_inst?src_inst->GetItem()->Name:"Null");
+ Message(4, "SwapIt->Process(SaveCursor[%s]->SRC)", "{src_inst->pointerissues-willcrashzone}");
+
list<ItemInst*>::const_iterator s=m_inv.cursor_begin(),e=m_inv.cursor_end();
database.SaveCursor(character_id, s, e);
- } else
+ }
+ else {
+ //Message(4, "SwapIt->Process(SaveInventory[%s]->SRC[%i])", src_inst?src_inst->GetItem()->Name:"Null", src_slot_id);
+ Message(4, "SwapIt->Process(SaveInventory[%s]->SRC[%i])", "{src_inst->pointerissues-willcrashzone}", src_slot_id);
+
database.SaveInventory(character_id, m_inv.GetItem(src_slot_id), src_slot_id);
+ }
+
if (dst_slot_id==SLOT_CURSOR) {
+ //Message(4, "SwapIt->Process(SaveCursor[%s]->DST)", dst_inst?dst_inst->GetItem()->Name:"Null");
+ Message(4, "SwapIt->Process(SaveCursor[%s]->DST)", "{dst_inst->pointerissues-willcrashzone}");
+
list<ItemInst*>::const_iterator s=m_inv.cursor_begin(),e=m_inv.cursor_end();
database.SaveCursor(character_id, s, e);
- } else
+ }
+ else {
+ //Message(4, "SwapIt->Process(SaveInventory[%s]->DST[%i])", dst_inst?dst_inst->GetItem()->Name:"Null", dst_slot_id);
+ Message(4, "SwapIt->Process(SaveInventory[%s]->DST[%i])", "{dst_inst->pointerissues-willcrashzone}", dst_slot_id);
+
database.SaveInventory(character_id, m_inv.GetItem(dst_slot_id), dst_slot_id);
+ }

// Step 8: Re-calc stats
CalcBonuses();
+
+ Message(4, "SwapIt->Exit");
+
return true;
}

void Client::DyeArmor(DyeStruct* dye){
+ Message(4, "DyeArm->Entry");
+
sint16 slot=0;
for(int i=0;i<7;i++){
if(m_pp.item_tint[i].rgb.blue!=dye->dye[i].rgb.blue ||
@@ -1276,6 +1681,8 @@
QueuePacket(outapp);
safe_delete(outapp);
Save();
+
+ Message(4, "DyeArm->Exit");
}

/*bool Client::DecreaseByItemType(int32 type, int8 amt) {
@@ -1330,6 +1737,8 @@
}*/

bool Client::DecreaseByID(int32 type, int8 amt) {
+ Message(4, "DecByID->Entry");
+
const Item_Struct* TempItem = 0;
ItemInst* ins;
int x;
@@ -1349,8 +1758,13 @@
break;
}
}
- if (num < amt)
+
+ if (num < amt) {
+ Message(4, "DecByID->Exit(NumberLessThanAmount)");
+
return false;
+ }
+
for(x=0; x < 331; x++)
{
if (x == 31)
@@ -1375,11 +1789,15 @@
break;
}
}
+ Message(4, "DecByID->Exit");
+
return true;
}


void Client::RemoveNoRent() {
+ Message(4, "RemNoRent->Entry");
+
const Item_Struct* TempItem = 0;
ItemInst* ins;
int x;
@@ -1471,6 +1889,7 @@
}
}
}
+ Message(4, "RemNoRent->Exit");
}

// these functions operate with a material slot, which is from 0 to 8
@@ -1536,6 +1955,8 @@
}

bool Client::LootToStack(int32 itemid) { //Loots stackable items to existing stacks - Wiz
+ Message(4, "LootToStac->Entry");
+
// @merth: Need to do loot code with new inventory struct
/*
const Item_Struct* item;
@@ -1576,14 +1997,22 @@
}
}
*/
+
+ Message(4, "LootToStac->Exit");
+
return false;
}

// Send an item packet (including all subitems of the item)
void Client::SendItemPacket(sint16 slot_id, const ItemInst* inst, ItemPacketType packet_type)
{
- if (!inst)
+ Message(4, "SendItPack->Entry(ItemID[%u])", inst?inst->GetItem()->Name:0);
+
+ if (!inst) {
+ Message(4, "SendItPack->Exit(Item[Null])");
+
return;
+ }

// Serialize item into |-delimited string
string packet = inst->Serialize(slot_id);
@@ -1604,12 +2033,19 @@
#endif
//DumpPacket(outapp);
FastQueuePacket(&outapp);
+
+ Message(4, "SendItPack->Exit(ClientUpdate->SendItem[%s])", inst->GetItem()->Name);
}

EQApplicationPacket* Client::ReturnItemPacket(sint16 slot_id, const ItemInst* inst, ItemPacketType packet_type)
{
- if (!inst)
+ Message(4, "RetItemPack->Entry(ItemID[%u])", inst?inst->GetItem()->ID:0);
+
+ if (!inst) {
+ Message(4, "RetItemPack->Exit(NullItem)");
+
return 0;
+ }

// Serialize item into |-delimited string
string packet = inst->Serialize(slot_id);
@@ -1628,6 +2064,8 @@
DumpPacket(outapp);
#endif

+ Message(4, "RetItemPack->Exit(ClientUpdate->ReturnItem[%s])", inst->GetItem()->Name);
+
return outapp;
}

@@ -1646,6 +2084,7 @@
}

void Client::CreateBandolier(const EQApplicationPacket *app) {
+ Message(4, "CreBand->Entry");

// Store bandolier set with the number and name passed by the client, along with the items that are currently
// in the players weapon slots.
@@ -1669,17 +2108,24 @@
_log(INVENTORY__BANDOLIER, "Char: %s adding item %s to slot %i", GetName(),BaseItem->Name, WeaponSlot);
m_pp.bandoliers[bs->number].items[BandolierSlot].item_id = BaseItem->ID;
m_pp.bandoliers[bs->number].items[BandolierSlot].icon = BaseItem->Icon;
+
+ Message(4, "CreBand->Process(Bandolier->AddItem[%s]->BandolierSlot[%i]->WeaponSlot[%i])", InvItem->GetItem()->Name, BandolierSlot, WeaponSlot);
}
else {
_log(INVENTORY__BANDOLIER, "Char: %s no item in slot %i", GetName(), WeaponSlot);
m_pp.bandoliers[bs->number].items[BandolierSlot].item_id = 0;
m_pp.bandoliers[bs->number].items[BandolierSlot].icon = 0;
+
+ Message(4, "CreBand->Process(Bandolier->AddItem[Null]->BandolierSlot[%i]->WeaponSlot[%i])", BandolierSlot, WeaponSlot);
}
}
Save();
+
+ Message(4, "CreBand->Exit");
}

void Client::RemoveBandolier(const EQApplicationPacket *app) {
+ Message(4, "RemBand->Entry");

// Delete bandolier with the specified number

@@ -1689,11 +2135,16 @@
for(int i = bandolierMainHand; i <= bandolierAmmo; i++) {
m_pp.bandoliers[bds->number].items[i].item_id = 0;
m_pp.bandoliers[bds->number].items[i].icon = 0;
+
+ Message(4, "RemBand->Process(Bandolier->RemoveItem->BandolierSlot[%i])", i);
}
Save();
+
+ Message(4, "RemBand->Exit");
}

void Client::SetBandolier(const EQApplicationPacket *app) {
+ Message(4, "SetBand->Entry");

// Swap the weapons in the given bandolier set into the character's weapon slots and return
// any items currently in the weapon slots to inventory.
@@ -1707,16 +2158,22 @@
// First we pull the items for this bandolier set out of their inventory, this makes space to put the
// currently equipped items back.
for(int BandolierSlot = bandolierMainHand; BandolierSlot <= bandolierAmmo; BandolierSlot++) {
+ Message(4, "SetBand->Process(BandolierSlot[%i]->Start)", BandolierSlot);
+
// If this bandolier set has an item in this position
if(m_pp.bandoliers[bss->number].items[BandolierSlot].item_id) {
WeaponSlot = BandolierSlotToWeaponSlot(BandolierSlot);

+ Message(4, "SetBand->Process(BandolierSlot->WeaponAssigned)");
+
// Check if the player has the item specified in the bandolier set on them.
//
slot = m_inv.HasItem(m_pp.bandoliers[bss->number].items[BandolierSlot].item_id, 1,
invWhereWorn|invWherePersonal|invWhereCursor);
// if the player has this item in their inventory,
if(slot != SLOT_INVALID) {
+ Message(4, "SetBand->Process(InventorySearch->WeaponFound[%s]->InSlot[%i])", GetInv().GetItem(slot)?GetInv().GetItem(slot)->GetItem()->Name:"Null", slot);
+
// Pull the item out of the inventory
BandolierItems[BandolierSlot] = m_inv.PopItem(slot);
// If ammo with charges, only take one charge out to put in the range slot, that is what
@@ -1725,34 +2182,55 @@
if(((BandolierSlot == bandolierAmmo) || (BandolierSlot == bandolierRange)) &&
BandolierItems[BandolierSlot] && BandolierItems[BandolierSlot]->IsStackable()){
int Charges = BandolierItems[BandolierSlot]->GetCharges();
+
+ Message(4, "SetBand->Process(StackableAmmoCheck)");
+
// If there is more than one charge
if(Charges > 1) {
BandolierItems[BandolierSlot]->SetCharges(Charges-1);
+
+ Message(4, "SetBand->Process(AmmoStack[%s]->Remove[1])", BandolierItems[BandolierSlot]?BandolierItems[BandolierSlot]->GetItem()->Name:"Null");
+
// Take one charge out and put the rest back
m_inv.PutItem(slot, *BandolierItems[BandolierSlot]);
database.SaveInventory(character_id, BandolierItems[BandolierSlot], slot);
BandolierItems[BandolierSlot]->SetCharges(1);
}
- else // Remove the item from the inventory
+ else { // Remove the item from the inventory
+ Message(4, "SetBand->Process(AmmoItem[%s]->TakeFromInventory)", BandolierItems[BandolierSlot]?BandolierItems[BandolierSlot]->GetItem()->Name:"Null");
+
database.SaveInventory(character_id, 0, slot);
+ }
}
- else // Remove the item from the inventory
+ else { // Remove the item from the inventory
+ Message(4, "SetBand->Process(Item[%s])->RemoveFromInventory", GetInv().GetItem(slot)?GetInv().GetItem(slot)->GetItem()->Name:"Null");
+
database.SaveInventory(character_id, 0, slot);
+ }
}
else { // The player doesn't have the required weapon with them.
+ Message(4, "SetBand->Process(InventorySearch->WeaponNotFound)");
+
BandolierItems[BandolierSlot] = 0;
if(slot == SLOT_INVALID) {
+ Message(4, "SetBand->Process(InvalidSlotAssignment)");
+
_log(INVENTORY__BANDOLIER, "Character does not have required bandolier item for slot %i", WeaponSlot);
ItemInst *InvItem = m_inv.PopItem(WeaponSlot);
if(InvItem) {
// If there was an item in that weapon slot, put it in the inventory
_log(INVENTORY__BANDOLIER, "returning item %s in weapon slot %i to inventory",
InvItem->GetItem()->Name, WeaponSlot);
- if(MoveItemToInventory(InvItem))
+ if(MoveItemToInventory(InvItem)) {
+ Message(4, "SetBand->Process(Item[%s]->ReturnToInventory->ToSlot[%i])", InvItem?InvItem->GetItem()->Name:"Null", WeaponSlot);
+
database.SaveInventory(character_id, 0, WeaponSlot);
- else
- _log(INVENTORY__BANDOLIER, "Char: %s, ERROR returning %s to inventory", GetName(),
- InvItem->GetItem()->Name);
+ }
+ else {
+ Message(4, "SetBand->Process(Item[%s]->ReturnToInventory->ERROR)", InvItem?InvItem->GetItem()->Name:"Null");
+
+ _log(INVENTORY__BANDOLIER, "Char: %s, ERROR returning %s to inventory", GetName(), InvItem->GetItem()->Name);
+ }
safe_delete(InvItem);
}

@@ -1770,8 +2248,12 @@

WeaponSlot = BandolierSlotToWeaponSlot(BandolierSlot);

+ Message(4, "SetBand->Process(SwapItem->Begin)");
+
// if there is an item in this Bandolier slot ?
if(m_pp.bandoliers[bss->number].items[BandolierSlot].item_id) {
+ Message(4, "SetBand->Process(BandolierWeaponAssignment[%s])", (m_pp.bandoliers[bss->number].items[BandolierSlot].item_id)?database.GetItem(m_pp.bandoliers[bss->number].items[BandolierSlot].item_id)->Name:"Null");
+
// if the player has this item in their inventory, and it is not already where it needs to be
if(BandolierItems[BandolierSlot]) {
// Pull the item that we are going to replace
@@ -1779,15 +2261,22 @@
// Put the item specified in the bandolier where it needs to be
m_inv.PutItem(WeaponSlot, *BandolierItems[BandolierSlot]);

+ Message(4, "SetBand->Process(SwapAction->FromWeapon[%s]->ToWeapon[%s])", InvItem?InvItem->GetItem()->Name:"Null", BandolierItems[BandolierSlot]?BandolierItems[BandolierSlot]->GetItem()->Name:"Null");
+
safe_delete(BandolierItems[BandolierSlot]);
// Update the database, save the item now in the weapon slot
database.SaveInventory(character_id, m_inv.GetItem(WeaponSlot), WeaponSlot);

if(InvItem) {
// If there was already an item in that weapon slot that we replaced, find a place to put it
- if(!MoveItemToInventory(InvItem))
- _log(INVENTORY__BANDOLIER, "Char: %s, ERROR returning %s to inventory", GetName(),
- InvItem->GetItem()->Name);
+ if(!MoveItemToInventory(InvItem)) {
+ Message(4, "SetBand->Process(AssignedBandolierSlot->ReturningWeaponToInventory[%s]->ERROR)", InvItem?InvItem->GetItem()->Name:"Null");
+
+ _log(INVENTORY__BANDOLIER, "Char: %s, ERROR returning %s to inventory", GetName(), InvItem->GetItem()->Name);
+ }
+ else {
+ Message(4, "SetBand->Process(AssignedBandolierSlot->ReturningWeaponToInventory[%s])", InvItem?InvItem->GetItem()->Name:"Null");
+ }
safe_delete(InvItem);
}
}
@@ -1796,25 +2285,37 @@
// This bandolier set has no item for this slot, so take whatever is in the weapon slot and
// put it in the player's inventory.
ItemInst *InvItem = m_inv.PopItem(WeaponSlot);
+
+ Message(4, "SetBand->Process(BandolierWeaponAssignment[Null])");
+
if(InvItem) {
- _log(INVENTORY__BANDOLIER, "Bandolier has no item for slot %i, returning item %s to inventory",
- WeaponSlot, InvItem->GetItem()->Name);
+ Message(4, "SetBand->Process(ReturnCurrentWeaponToInventory[%s])", InvItem?InvItem->GetItem()->Name:"Null");
+
+ _log(INVENTORY__BANDOLIER, "Bandolier has no item for slot %i, returning item %s to inventory", WeaponSlot, InvItem->GetItem()->Name);
// If there was an item in that weapon slot, put it in the inventory
- if(MoveItemToInventory(InvItem))
+ if(MoveItemToInventory(InvItem)) {
+ Message(4, "SetBand->Process(EmptyBandolierSlot->ReturningWeaponToInventory[%s])", InvItem?InvItem->GetItem()->Name:"Null");
+
database.SaveInventory(character_id, 0, WeaponSlot);
- else
- _log(INVENTORY__BANDOLIER, "Char: %s, ERROR returning %s to inventory", GetName(),
- InvItem->GetItem()->Name);
+ }
+ else {
+ Message(4, "SetBand->Process(EmptyBandolierSlot->ReturningWeaponToInventory[%s]->ERROR)", InvItem?InvItem->GetItem()->Name:"Null");
+
+ _log(INVENTORY__BANDOLIER, "Char: %s, ERROR returning %s to inventory", GetName(), InvItem->GetItem()->Name);
+ }
safe_delete(InvItem);
}
}
}
// finally, recalculate any stat bonuses from the item change
CalcBonuses();
+
+ Message(4, "SetBand->Exit");
}

bool Client::MoveItemToInventory(ItemInst *ItemToReturn, bool UpdateClient) {
-
+ Message(4, "MoveItToInv->Entry(ItemID[%u])", ItemToReturn?ItemToReturn->GetItem()->ID:0);
+
// This is a support function for Client::SetBandolier, however it can be used anywhere it's functionality is required.
//
// When the client moves items around as Bandolier sets are activated, it does not send details to the
@@ -1832,12 +2333,18 @@
// depends on current behaviour, this routine operates the same as the client when moving items back to inventory when
// swapping bandolier sets.

- if(!ItemToReturn) return false;
+ if(!ItemToReturn) {
+ Message(4, "MoveItToInv->Exit(Item[Null])");

+ return false;
+ }
+
_log(INVENTORY__SLOTS,"Char: %s Returning %s to inventory", GetName(), ItemToReturn->GetItem()->Name);

int32 ItemID = ItemToReturn->GetItem()->ID;

+ Message(4, "MoveItToInv->Process(Item[%s])", ItemToReturn->GetItem()->Name);
+
// If the item is stackable (ammo in range slot), try stacking it with other items of the same type
//
if(ItemToReturn->IsStackable()) {
@@ -1862,8 +2369,11 @@

ItemToReturn->SetCharges(ItemToReturn->GetCharges() - ChargesToMove);

- if(!ItemToReturn->GetCharges())
+ if(!ItemToReturn->GetCharges()) {
+ Message(4, "MoveItToInv->Exit(MoveCharges->ToSlot[%i])", i);
+
return true;
+ }
}
// If there is a bag in this slot, look inside it.
//
@@ -1895,8 +2405,11 @@

ItemToReturn->SetCharges(ItemToReturn->GetCharges() - ChargesToMove);

- if(!ItemToReturn->GetCharges())
+ if(!ItemToReturn->GetCharges()) {
+ Message(4, "MoveItToInv->Exit(MoveCharges->ToSlot[%i]->ToBagSlot[%i])", i, BagSlot);
+
return true;
+ }
}
}
}
@@ -1920,6 +2433,8 @@

_log(INVENTORY__SLOTS, "Char: %s Storing in main inventory slot %i", GetName(), i);

+ Message(4, "MoveItToInv->Exit(Move->ToSlot[%i])", i);
+
return true;
}
if(InvItem->IsType(ItemClassContainer) && Inventory::CanItemFitInContainer(ItemToReturn->GetItem(), InvItem->GetItem())) {
@@ -1943,6 +2458,8 @@

_log(INVENTORY__SLOTS, "Char: %s Storing in bag slot %i", GetName(), BaseSlotID + BagSlot);

+ Message(4, "MoveItToInv->Exit(Move->ToSlot[%i]->ToBagSlot[%i])", i, BagSlot);
+
return true;
}
}
@@ -1955,6 +2472,8 @@

PushItemOnCursor(*ItemToReturn, UpdateClient);

+ Message(4, "MoveItToInv->Exit(PushToCursor[%s]->WithCharges[%i])", ItemToReturn->GetItem()->Name, ItemToReturn->GetCharges());
+
return true;
}




Uleat of Bertoxxulous

joligario
07-10-2012, 08:51 AM
You can probably also check the item swap / item cursor with cast or click as well. Especially with the speed of the MQ2 macros.

Uleat
07-10-2012, 01:03 PM
Do you happen to know if the problem occurs all the time or just when a summoned item is created per chance?

I saw a few postings about that before I found one of the major bugs and did not include it in my search criteria.


The most noticeable cursor issue is has to do with how the server is coded to handle a swap action of partial stacks. If you are holding a partial stack on
the cursor, the server will jump to placing any additional partial stacks in either an empty personal slot or bag slot, while the client will check for a
partial stack on the cursor first. This creates the client-server de-sync and the player becomes bugged to future trades/swaps resulting in obvious item
deletions - some permanent.

(Yes, the fix for that is really easy, but DO NOT TRY TO COMMIT A FIX before speaking with me privately. There are MAJOR issues that arise when you
make that change.)


I have a small list of these bugs that I will post soon, but am welcome to more input in the mean time. I need to sort my notes between de-sync issues
and actual bug triggering.

Again, item loss is not the bug, but the result of one. Like a damaged car is a result of an accident, we want the events leading up to and including the
accident, not an in-depth description of the damaged car :)

Uleat
07-10-2012, 04:13 PM
Here are some known bugs and behaviors that I have observed.

Some of the behaviors I have seen are omitted since they are duplicate scenarios of the same issue. There are also some other
non-revelent issues here since I'm looking at the entire inventory.cpp module.


[Cursoring a partial arrow stack involved in bandolier swap, with no other partials in inventory, results in de-sync (Bug)]
- This is caused by MoveItemToInventory(). It does not check the cursor for a partial stack before trying to find an empty
slot. Client behavior checks the cursor before moving on to empty slots. {TESTED AND VERIFIED - Partial fix appears to work}


[Cursoring one of the weapons involved in bandolier swap that has arrows results in de-sync (Bug)]
- Known issue, but still researching the exact cause. Could be related to the above bug. {TESTED}


[Clicking an item stack and it is non-responsive (Behavior)]
- At this point, you are bugged. You need to camp or zone to fix this problem asap before item loss becomes permanent. Any
items that you click, trade, buy or receive after invoking this behavior become subject to item loss. I guess this could be
applied to any item, but if not, that would narrow down the cause list. {TESTED}


[Placing an arrow stack in the ammo slot results in an 'Insufficient Quantity' error message and item deletion (Behavior)]
- The part of the code that triggers this message is working correctly. Again, you have become bugged by this point. Other
stackables are probably affected the same way. {ARROWS - TESTED AND VERFIED}


[Moving an item results in an 'Item Not Found On Server' error message and item deletion (Behavior)]
- Bugged..you know what to do. {TESTED AND VERFIED}


[Zero-charge stackable item (Bug or Behavior - still in-work)]
- It's possible to have an arrow (or possibly any stackable item) with zero charges. This can turn into an exploit, so I will
not elaborate on it's cause. (I have triggered this, but do not have an exact procedure as yet. I was already suspecting this
as a problem, but I have found a way to provide evidence of it.) {TESTED}


[Backpack - the new ammo! (Behavior)]
- Overloading the inventory and then calling a swap will result in some REALLY odd behavior (And major item loss.) I managed
to get a backpack into my ammo slot..I did not try to use my bow, however... (I wonder if a bag would actually fly across the
screen..hmmm...) {TESTED}


[Probable issue with Client::PutItemInInventory 'CalcBonuses' call]
- If you look at this particular function, you will notice there are two (slot_id == SLOT_CURSOR) checks. The first one is
always processed on 'true,' but the second one is never processed on 'true,' however... The call to PushItemOnCursor in the
first check duplicates the entire segment of code that is missed. This is not the problem...

- Look at the second one again. The check ALWAYS handles a return and CalcBonuses() is never called. {NOT TESTED}


[Possible issue with Client::TryStacking]
- With stackable items, this method checks the eight personal slots first before going back through bags and misses the cursor
altogether. This does not match the client and other server scheme for parsing stackable items. Corpse looting will likely
become bugged due to the same de-sync issues described above. {NOT TESTED}


[Possible issue with Inventory::SwapItem (item.cpp)]
- One-way check... slot 'a' item is checked against slot 'b,' but not vica-versa. First attempt at a fix crashed zone.exe...
This needs to be researched more thoroughly. {CONSEQUENCE OF LACK OF CHECK - NOT TESTED}


[Not pertinent - possible message issue with summoning items with augmentations]
- This is not tested, but I have seen where a possible lore conflict message will kick back when no other lore item is owned
and also on non-lore flagged items. The lore checks are correct, but the reference to the lore item in the message is set to
the base item and not the augmentation itself - meaning that you are trying to create an additional lore augmentation, but not
lore item itself, and it is reporting the wrong lore conflict. Let me know if you have experienced this. {NOT TESTED}


[Not pertinent - #peekinv is not showing slot #30 in the listing when its value is null, or supposed to be]
- I haven't looked at the code to see if the 'GetItem()' command is just not reporting or is in a failed state. {OBSERVED}


After working these issues, and anything new that crops up with fixes, I'll take a look at consumption issues. I just need to
verify that they are not a part of these exisiting de-sync problems.


If you are posting symptoms, please include server revision, system and client information as well. I don't believe these
problems are unique to, or exclusive of, any particular setup, but we won't be able to determine that without the proper data.

These issues are long-lived and I'm sure that everyone would love to see them fixed. Please support this request if you have
pertinent information.

And to remind anyone wanting to fix these issues one at a time - Please do not do so with the item swapping issues. You will
introduce major exploits unless all of the issues are coordinated and fixed together.


Thanks!

sorvani
07-10-2012, 07:12 PM
Cursor stacking can also become messed up without actually becoming desync'd in such a manner that anything becomes unusable.
I do not have a way to make it happen off hand, but it most often happens when doing tradeskills.
I was speeding through (manually not with MQ2) making something, drinks i think, and then went to give some to another player and they received instead one of the components I had been using. My inventory was seemingly normal, but upon camping and coming back some of the drinks i had turned into a component.

Along these lines I've also ended up with a bag in a bag

Uleat
07-10-2012, 09:13 PM
That almost sounds like the zero-charge bug..I'll see if I can do some ts'ing and replicate it.

I've also noticed something along the lines of the server finding an item in the cursor slot (with multiple items on it) but getting the first item
regardless of if it's the correct one..almost like it doesn't iterate to find what it found...

Uleat
07-11-2012, 04:25 AM
I also managed to #summonitem a stack of 100 water flasks... Should probably add code to check max stack size to help avoid problems.

I was trying to induce the arrow error and camp immediately to check the database for possible zero stacks. What I found was a stack of 100 water flasks...

Uleat
07-11-2012, 06:21 AM
I'm trying to sort out some issues as I make changes. One of the things I need to know is this:

Do all clients operate in the same manner in regards to inventory placement order? I can account for any discrepancies in
client versions, but I just need to know about them.


I also know about the top-to-bottom fill order of the personal inventory slots, and the left-to-right of bag slots.


Here is the actual placement order that I have observed with my SoF client:

[Stackable Item Charge Movements Only]
> Slot 22
>> Slot 22 : Bag Slots (0 -> 9)
> Slot 23
>> Slot 23 : Bag Slots (0 -> 9)

..etc... etc...

> Slot 29
>> Slot 29 : Bag Slots (0 -> 9)
> Slot 30
>> Slot 30 : Bag Slots (0 -> 9)

[Stackable Movement Reiteration/Regular Item Movement]
> Slot 22
>> Slot 22 : Bag Slots (0 -> 9)
> Slot 23
>> Slot 23 : Bag Slots (0 -> 9)

..etc... etc...

> Slot 29
>> Slot 29 : Bag Slots (0 -> 9)
> Slot 30
>> Slot 30 : Bag Slots (0 -> 9)
>>> Slot 30 Array Push


The functions/methods that I'm dealing with at the moment all start at 22 and end at 29. I am changing the max to 30
and will check in-game to see if worn slots are included when considering certain actions. (I have not tried 'looting' an
arrow to see where it goes yet.)

If the worn slots are auto-equipped by the client, but are pushed into the personal inventory by the server, this would
account for the CSD (Client-Server Desyncronization..I got tired of spelling that everytime...) and massive item loss
associated with it.

[Theory:]
(Player auto-loots a corpse with a partial stack in ammo -> auto-equips all weapons and armor -> finds stackable ammo for the
range slot..client puts it in the ammo slot, but server puts it in slot 22 -> transfers the the bags..client puts all of the
bags in the proper place, but server places last bag on the cursor -> player zones and finds a bag on his/her cursor...
Speculative..but would like to know if anyone has experienced that exactly. I may have to test this.)

I'm still learning the processes involved and may answer my own question about the auto-equip issue.


Thanks to everyone that's provided feedback so far!

Uleat
07-12-2012, 04:34 AM
Ok, disregard the [Theory] in the previous post. I tried to trigger a bug with that, but it always seemed to put the arrows in the correct places.


Is there an auto-loot button? I keep reading about how it is bugged, but can't seem to find a button or command...

Burningsoul
07-12-2012, 04:52 AM
With SOD+ I believe (been awhile since I've used SoD, on HoT client now) there does exist an auto-loot button - right click corpse, check right by the loot button. Loot All is there, and I've been able to replicate a few of your mentioned bugs with it. They're actually pretty annoying on a single player LAN server, but I can very easilly see how they could be used for abuse. Kudos for bringing this to light.

Uleat
07-12-2012, 06:55 AM
Ahh, I'm using a SoF client..only button I have is 'Link All' :mad:

I believe I see the problem with it, so I may include that in an untested patch after I do this one. I think I've already described that above, but basically
it's just not iterating to match the client and items are being placed in different slots on the client versus server in some cases. I'm still waiting for
additional feedback on this, however..especially since I can't create a test for it.


I found one source of the problems, but I'm still getting this error series:

Error: Insufficent number in stack.
SwapIt->Exit(SRCItem[Shadowspike Arrow]->SRCCharges[48]->MIStackCount[49]->SRCStackSize[100]->ERROR)

The missing 3 charges were 2 on the cursor and 1 in the ammo slot.

I just need to find this other source and test it out. My head is stuck in a recursive loop at the moment... :confused:


Anyways, I'm hoping these fixes will be easy and won't require any major recoding.

Uleat
07-12-2012, 06:39 PM
I'm starting to get some private feedback on these bugs as well and it seems there is proven evidence of their existence.


Give me some time to sort out this mess I call 'notes' and I'll see what I can do with them.

I 'think' that currently bugged players will be fixable as well, but someone else will probably submit that..my sql is REALLY horrible!

lerxst2112
07-12-2012, 08:17 PM
Testing with SoF is probably not the right thing to do. From what I've heard it is the least stable client and probably the least used.

cavedude
07-12-2012, 10:22 PM
Testing with SoF is probably not the right thing to do. From what I've heard it is the least stable client and probably the least used.

Based on what I've seen on PEQ, I'd agree with both points.

Uleat
07-13-2012, 01:36 AM
Unfortunately, my monthly income is about -$250 a month, so I have to use what I have atm http://www.eqemulator.net/forums/images/icons/icon9.gif

I missed the Steam download by one day..I found the thread here after I had starting researching the different perl versions.


Anyways, I'll do what I can with this and post it as an alpha patch. There's no way that I can thoroughly test it the same as someone with internet
access and multiple players though.

The feedback is appreciated too! I spend most of my time inside VS trying to follow logic paths, so when I get verification of things that I see, it makes it
soooo much easier to find a solution. (No eta on a patch atm..I want to understand 100% what I'm seeing before I make any actual submissions.)

Edit: p.s. If anyone has any ideas about there being different client loot orders, I still need an answer to resolve that. Thanks!

Uleat
07-13-2012, 02:24 AM
[Sidenote: Trevius]

This is speculation, especially since it is so deep in the kernel, but that issue with Inventory::SwapItem may also be playing a part in this.


Inv:SI is coded to return a true or false in the case of a success or failure.

However, the few instances that I've seen in <inventory.cpp> all call it blind...

Even if the memory pointer swap fails, the calling method doesn't have code to process a failed swap, so it just assumes it was successful.
(Added to list...)


I could be WAY off on this, but it's another possible area to consider.

Uleat
07-13-2012, 03:29 AM
(<Bump> on the item swap/casting issue... http://www.eqemulator.org/forums/showpost.php?p=210859&postcount=2)


Do you know if this happens with any spell? Or just with spells with stackable components, per chance?

Uleat
07-13-2012, 06:46 AM
Here's a change to my posting about the Inventory::SwapItem function above...

It currently is a void function, but I was thinking that it was a bool instead.

The same idea applies and maybe it needs to be changed to 'bool Inventory::SwapItem' to keep the calling method from processing a success
when the swap actually failed.


Sorry about that... Like I said, my head is a bit off atm.

Uleat
07-13-2012, 09:32 AM
One last question (well, maybe two) for you guru's out there before I head off to bed...


I see that items stored on the cursor beyond slot #30 are in slots beginning at #8001 in the database. This appears to successfully reload since I had
both the slot #30 and #8001 items when I logged back in.

First, I'm not seeing where this extra range is defined anywhere in Item.h, Item.cpp, inventory.h or inventory.cpp. In addition, no checks are made to
this range. These are probably not an issue since we don't want to be trading in this area anyways.


Question 1: What is the maximum range for the additional cursor slots?

Question 2: Where are the bag slots defined for these cursor extra slots??? (see where I'm going?)


If a full bag is loaded to one of the #8001+ slots due to overloading your inventory, when it gets bumped to slot #30, where does the information come
from for the items that used to be in there?

If there are bag slot id ranges for the additional cursor range, I'm still trying to find them somewhere. Otherwise, this is where the loss of items is coming
from on the corpse looting when someone grabs extra stuff for a corpse run. If there are no extra slots, the only thing I can think to do would be to auto-loot
everything but the bags, then calculate how many slots are left to hold bags and cancel the transaction if there aren't enough spaces to safely loot them.


I would like to know the ranges for the extra cursor slots and their bag slots, if they exist.


Thanks!

Uleat
07-14-2012, 03:10 AM
This is what I have so far with the extended cursor slots:

- The server will allow creation of up to at least 250 additional items with the range being from #8001 to #8250

- These values can be verified by viewing the player inventory in the database.

- The client (SoF in this case) only recongnizes 36 additional slots and any more than this the client seems to ignore

- Unless you log or zone at this point, you will be CSD bugged if you had any additional items beyond 36.

- Since the server 'bumps' the item positions down, the client is 'reloaded' in the case of logging or zoning.

- I have not found any extended cursor bag slots as of yet..will continue testing.


I'm really not sure if the client is ignoring the extra slots or not. I want to either crash the client or server in an attempt to find the ceiling or to find
those extended bag slots. I don't believe they exist, personally...

Uleat
07-14-2012, 05:02 AM
From what I can tell, the server is coded to allow 101 items the be stored on the extended cursor at any given time.

It has a total range of 8001 to 8999 and will reiterate any items found here down to the active range of 8001 to 8101.

I'm having issues verifying whether or not there are extended bag slots on the client due to the server renumbering my entries to the 8001 ~ 8101 range
and not sending my original slot numbers to the client.


I've noticed some system packet commands. Is there one in particular that will allow me to send 'item->to_slot' type commands? Or should I look at
writing a custom script that will do this?


Thanks!

Uleat
07-14-2012, 09:52 AM
Can anyone give me pointers on this? I want to send a 'Fake Item' to the client at a specific slot number, but the best I've suceeded in doing is
crashing zone...

(I've already updated command.h and the command_init portions.)

void command_zitemtest(Client *c, const Seperator *sep)
{
if (!sep->argnum==2)
c->Message(0, "Usage: #zitemtest [slot id] [item id]");
else if (!sep->IsNumber(1) && !sep->IsNumber(2))
c->Message(0, "Usage: #zitemtest [slot id] [item id]");
else {
sint16 slotid = atoi(sep->arg[1]);
int32 itemid = atoi(sep->arg[2]);

ItemInst* FakeItem;

FakeItem->SetItem(database.GetItem(itemid));

if (!FakeItem) {
c->Message(0, "Error: Item [%u] is not a valid item number.", itemid);
return;
}

if (database.GetItemStatus(itemid) > c->Admin()) {
c->Message(13, "Error: Insufficient status to summon this item.");
return;
}

FakeItem->SetCharges(1);
c->SendItemPacket(slotid, FakeItem, ItemPacketTrade);
}
}

I know it's my lack of understanding of item and packet structures, but I can't seem to find a similar method to follow.

I just basically want to send the client an item packet for the specified slot, without the server being updated, so I can watch client behavior.


Thanks!

lerxst2112
07-14-2012, 03:25 PM
Look at how Client::DoTributeUpdate works and see if that helps. It creates an item and sends it to to client.

You might also look at the #peekinv command which might help you narrow down when the server and client disagree on something.

Uleat
07-14-2012, 07:48 PM
Thanks Lerxst! I'll play around with that later this evening and see what becomes of it.

I was trying to use existing functions, but that's probably part of the problem.


Since I can't 'force' my client to pickup a loaded bag and put it on the extended cursor and watch behavior from that, I'm having issues understanding
where the item info for those bag slots is coming from.

I think this may be a whole other issue and, if I can put my finger on it, I'll start another thread specifically for that.


If you have a private/test server and want to see the client cursor limitation, try this:

> create a macro to summon an arrow (#summonitem 8005 1)

> use this macro, say, like 51 times. This will put 1 arrow on your cursor and 50 on the extended.

> Now, find an empty slot and start placing your arrows. In my case, I end up with a stack of 37 arrows (slot #30 plus the 36 extended ones.)

> If your cursor still has items on it, you may be using a different client..will need input from people if they do this with different results.

> Now, zone or relog and voila! You should have 1 arrow on your cursor and 13 on the extended.

> Check this by finding another empty slot and start stacking..the results should be 14 arrows.

Uleat
07-15-2012, 01:50 AM
I now have a working #ztestitem command and will do some testing with it. If I find a range that holds extended cursor bag items, I'll start another thread
to elaborate on that.

(I found out that I was not initializing the item instance correctly..thanks again Lerxst for pointing me in a good direction!)


p.s. This test command is not meant for committed server use. It will cause CSDs and is meant for testing only. If anyone would like a copy of it, please
post here and I will diff it. Only one request is needed..no need to fill up the thread with requests.

Uleat
07-15-2012, 07:11 AM
Ok, I think that I've finally resolved my understanding of what's going here... (correct me if I'm wrong.)


[Client-Side:]

- Slot 30 is assigned as the cursor and has a built-in buffer array of x (37 total in the case of my SoF client [0..36].)

- Slots 331 to 340 are assigned to slot 30 [0], but there is no buffer array on these.

- Cursor buffer slots were never meant to hold actual occupied bags..only single items that may have been received in quantities greater than one.

- There are no slot assignments in the range of 8001 to 8999 registered in the client.


[Server-Side:]

- Cursor slot 30 is assigned normally, as well as slots 331 to 340 for bag contents.

- Cursor slot is buffered, and is assigned to the range of 8000 + x [1..101]. Reiteration of the cursor re-enumerates exisiting buffer items.

- Cursor array will reiterate any items between 8001 to 8999 to keep the buffer filled.

- Cursor bags slots are not arrayed, as indicated by the log posted below.

- Any occupied bags forced into the cursor array will override the contents of the cursor bag stored in the server player profile..but not update the client.


That last one is nasty if you loot a corpse with a full inventory. You will lose all items up to the last bag. But since this also created a CSD, you stand a
chance of losing those as well as any other items you interact with afterwards.


Fix? Possible... Depends on what the desired behavior is. We could just not allow occupied bags to be placed in the cursor buffer... Or, a cursor bag array
could be created, but some sort of keeping the array sync'd to the actual bag it belongs in would need to be implemented..as well as a client update
for the cursor bag slots...


This is a postulate, but much comes from the observed behavior of my client interactions.

Here's the log..notice the item in slot 30, depth 37 will not show up on my client until I relog or zone:

[Sun Jul 15 06:21:44 2012] Logging to 'eqlog.txt' is now *ON*.
[Sun Jul 15 06:22:00 2012] You are out of food.
[Sun Jul 15 06:22:04 2012] You say, '#peekinv all'
[Sun Jul 15 06:22:04 2012] Displaying inventory for Meogre...
[Sun Jul 15 06:22:04 2012] WornSlot: 0, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] WornSlot: 1, Item: 10007 (Golden Earring)
[Sun Jul 15 06:22:04 2012] WornSlot: 2, Item: 3025 (Large Chain Coif)
[Sun Jul 15 06:22:04 2012] WornSlot: 3, Item: 3026 (Large Iron Mask)
[Sun Jul 15 06:22:04 2012] WornSlot: 4, Item: 10007 (Golden Earring)
[Sun Jul 15 06:22:04 2012] WornSlot: 5, Item: 3027 (Large Chainmail Neckguard)
[Sun Jul 15 06:22:04 2012] WornSlot: 6, Item: 3029 (Large Chainmail Mantle)
[Sun Jul 15 06:22:04 2012] WornSlot: 7, Item: 3032 (Large Iron Armplates)
[Sun Jul 15 06:22:04 2012] WornSlot: 8, Item: 3030 (Large Chainmail Cape)
[Sun Jul 15 06:22:04 2012] WornSlot: 9, Item: 3033 (Large Chainmail Bracelet)
[Sun Jul 15 06:22:04 2012] WornSlot: 10, Item: 3033 (Large Chainmail Bracelet)
[Sun Jul 15 06:22:04 2012] WornSlot: 11, Item: 27180 (Bow)
[Sun Jul 15 06:22:04 2012] WornSlot: 12, Item: 3034 (Large Chainmail Gloves)
[Sun Jul 15 06:22:04 2012] WornSlot: 13, Item: 5006 (Halberd)
[Sun Jul 15 06:22:04 2012] WornSlot: 14, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] WornSlot: 15, Item: 10008 (Gold Ring)
[Sun Jul 15 06:22:04 2012] WornSlot: 16, Item: 10008 (Gold Ring)
[Sun Jul 15 06:22:04 2012] WornSlot: 17, Item: 3028 (Large Chainmail Coat)
[Sun Jul 15 06:22:04 2012] WornSlot: 18, Item: 3035 (Large Iron Legplates)
[Sun Jul 15 06:22:04 2012] WornSlot: 19, Item: 3036 (Large Iron Boots)
[Sun Jul 15 06:22:04 2012] WornSlot: 20, Item: 3031 (Large Chainmail Belt)
[Sun Jul 15 06:22:04 2012] WornSlot: 21, Item: 8005 (Arrow)
[Sun Jul 15 06:22:04 2012] InvSlot: 22, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvSlot: 23, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 261 (Slot #23, Bag #0), Item: 13006 (Water Flask)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 262 (Slot #23, Bag #1), Item: 7003 (Spear)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 263 (Slot #23, Bag #2), Item: 13006 (Water Flask)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 264 (Slot #23, Bag #3), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 265 (Slot #23, Bag #4), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 266 (Slot #23, Bag #5), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 267 (Slot #23, Bag #6), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 268 (Slot #23, Bag #7), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 269 (Slot #23, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 270 (Slot #23, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvSlot: 24, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 271 (Slot #24, Bag #0), Item: 26903 (Luclinite)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 272 (Slot #24, Bag #1), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 273 (Slot #24, Bag #2), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 274 (Slot #24, Bag #3), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 275 (Slot #24, Bag #4), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 276 (Slot #24, Bag #5), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 277 (Slot #24, Bag #6), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 278 (Slot #24, Bag #7), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 279 (Slot #24, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 280 (Slot #24, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvSlot: 25, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 281 (Slot #25, Bag #0), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 282 (Slot #25, Bag #1), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 283 (Slot #25, Bag #2), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 284 (Slot #25, Bag #3), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 285 (Slot #25, Bag #4), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 286 (Slot #25, Bag #5), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 287 (Slot #25, Bag #6), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 288 (Slot #25, Bag #7), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 289 (Slot #25, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 290 (Slot #25, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvSlot: 26, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 291 (Slot #26, Bag #0), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 292 (Slot #26, Bag #1), Item: 13006 (Water Flask)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 293 (Slot #26, Bag #2), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 294 (Slot #26, Bag #3), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 295 (Slot #26, Bag #4), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 296 (Slot #26, Bag #5), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 297 (Slot #26, Bag #6), Item: 26903 (Luclinite)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 298 (Slot #26, Bag #7), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 299 (Slot #26, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 300 (Slot #26, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvSlot: 27, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 301 (Slot #27, Bag #0), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 302 (Slot #27, Bag #1), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 303 (Slot #27, Bag #2), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 304 (Slot #27, Bag #3), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 305 (Slot #27, Bag #4), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 306 (Slot #27, Bag #5), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 307 (Slot #27, Bag #6), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 308 (Slot #27, Bag #7), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 309 (Slot #27, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 310 (Slot #27, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvSlot: 28, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 311 (Slot #28, Bag #0), Item: 36592 (Shadowspike Arrow)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 312 (Slot #28, Bag #1), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 313 (Slot #28, Bag #2), Item: 36592 (Shadowspike Arrow)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 314 (Slot #28, Bag #3), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 315 (Slot #28, Bag #4), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 316 (Slot #28, Bag #5), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 317 (Slot #28, Bag #6), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 318 (Slot #28, Bag #7), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 319 (Slot #28, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvBagSlot: 320 (Slot #28, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] InvSlot: 29, Item: 8005 (Arrow)
[Sun Jul 15 06:22:04 2012] InvSlot: 9999, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 0, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 1, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 2, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 3, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 4, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 5, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 6, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 7, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 8, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 9, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 10, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 11, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 12, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 13, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 14, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 15, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 16, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 17, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 18, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 19, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 20, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 21, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 22, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 23, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 24, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 25, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 26, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 27, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 28, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 29, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 30, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 31, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 32, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 33, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 34, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 35, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 36, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorSlot: 30, Depth: 37, Item: 17005 (Backpack)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 331 (Slot #30, Bag #0), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 332 (Slot #30, Bag #1), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 333 (Slot #30, Bag #2), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 334 (Slot #30, Bag #3), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 335 (Slot #30, Bag #4), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 336 (Slot #30, Bag #5), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 337 (Slot #30, Bag #6), Item: 9991 (Bread Cakes*)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 338 (Slot #30, Bag #7), Item: 9990 (Skin of Milk)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 339 (Slot #30, Bag #8), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] CursorBagSlot: 340 (Slot #30, Bag #9), Item: 0 (null)
[Sun Jul 15 06:22:04 2012] TributeSlot: 400, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] TributeSlot: 401, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] TributeSlot: 402, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] TributeSlot: 403, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] TributeSlot: 404, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] BankSlot: 2000, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] BankSlot: 2001, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] BankSlot: 2002, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] BankSlot: 2003, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] BankSlot: 2004, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] BankSlot: 2005, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] BankSlot: 2006, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] BankSlot: 2007, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] BankSlot: 2008, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] BankSlot: 2009, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] BankSlot: 2010, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] BankSlot: 2011, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] BankSlot: 2012, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] BankSlot: 2013, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] BankSlot: 2014, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] BankSlot: 2015, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] BankSlot: 2016, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] BankSlot: 2017, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] BankSlot: 2018, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] BankSlot: 2019, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] BankSlot: 2020, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] BankSlot: 2021, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] BankSlot: 2022, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] BankSlot: 2023, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] ShBankSlot: 2500, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] ShBankSlot: 2501, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] TradeSlot: 3000, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] TradeSlot: 3001, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] TradeSlot: 3002, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] TradeSlot: 3003, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] TradeSlot: 3004, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] TradeSlot: 3005, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] TradeSlot: 3006, Item: 0 (null)
[Sun Jul 15 06:22:04 2012] TradeSlot: 3007, Item: 0 (null)


Back to working the original CSD issues..again, let me know if I am off-base with this. I need to understand how this works before I can decide what
method needs to be used in a fix for these other issues.

Uleat
07-16-2012, 04:46 AM
I think I found a bug with '#equipitem,' but not sure atm if it's an actual issue..might be exploitable, so no description will be provided.
(Added to list of things to check.)


(Does MQ use #equipitem? If this is how that exploit is being performed, those users aren't gonna be happy...)

lerxst2112
07-16-2012, 07:44 AM
As with all # commands, it is only usable by an account with a status of whatever is set in the commands table. I know of very few servers where it is available to all players. If you had the status to use the command you could certainly use it in a MQ2 macro, but otherwise, no.

Uleat
07-16-2012, 09:18 AM
kk, thanks! I did add some code to it (and another function) to help alleviate anything from this particular possibility.

I'm nowhere near done and will have to test all of this before I post an alpha patch..but it is coming along.


EDIT: I need a favor from the community... If someone positively knows this, please post it, otherwise:

I need to know what the cursor limits are for each client. For my SoF client, holds 1 visible cursor item and an
additional 36 behind it. Anything after that is ignored. Use the instructions here to determine the limit:

http://www.eqemulator.org/forums/showpost.php?p=210988&postcount=24

Also, does anyone know if any clients actually hold bag data for those hidden cursor slots? SoF doesn't appear
to because I can't force items beyond the visible bag.

Thanks!

Uleat
07-17-2012, 11:23 PM
Regardless of the problems and issues that I've already described, the more that I dig into this, the more I am finding wrong.

This is not going to be a quick fix and will probably require a rework of many functions.

I've made some progress with what I have so far, like the server catching and deleting 'phantom' client items before they
bug 'real' server items.

Because of the differences in the way that some calling methods use certain functions, the 'swap' actions are being processed
in the wrong portion of code... (i..e., weapons are being treated as stackable items and having their 'charge' removed to become
'zero-charge' items when they are moved.)

I'd be willing to bet that any bugs dealing with the transfer of items can be traced back to what I've found (and am still finding.)


I'm anything but an expert, but I am making some progress..so, any input from you guys will help and is greatly appreciated!

Uleat
07-19-2012, 01:22 AM
I'm having an idiot moment...

Can someone point me to where the client packet stream is processed and a 'trade' or a 'swap' call is directed to Client::SwapItem?

VC 'find' is not cooperating... (('swapitem' == ':swapitem') != '.swapitem' != '>swapitem')

lerxst2112
07-19-2012, 01:32 AM
Client::Handle_OP_MoveItem

I just searched for "SwapItem" and looked at all 5 places where a function of that name is called. I have no idea what the search you posted is supposed to do.

Uleat
07-19-2012, 02:33 AM
Lol! Sorry about that! I'm on my latop..very old laptop..that's boasting a 2.4 GHz, non-HT cpu, and a whopping 1-Gig of ram...

For some reason my 'searches' are not finding things they way it should when I search for specific criteria.

That thing at the end is what my search results say is equal and not equal..not my actual criteria line.

searching for 'swapitem' will find ':swapitem', but does not return '>swapitem' or '.swapitem' hits for some reason.

Even with the above four criteria, 'Client::Handle_OP_MoveItem' never came up..even when searching 'Entire Solution...'

Thanks for the info!

EDIT: Yes, I searched up and down, but something just isn't right about my searching or my laptop..or both...

Uleat
07-20-2012, 06:36 AM
I finally managed to bug an 'empty' corpse the other night so that it would not go away.

The strings of the corpses in the backup appeared different than the one that was still active. I think this CSD issue
is creeping into this area too.

Could someone with knowledge of corspe inventory strings and access to an active database check to see if there are
corpses that players can't loot anything else off of, but that the corspe will not decay after looting?

I'm looking for it having items stored on it that have zero charges, resulting in the client not seeing them, but the server
saying they are there..hence the reason a corpse will not decay properly when bugged...


Thanks!

sorvani
07-20-2012, 03:14 PM
You can duplicate this by having an item on the cursor when you die.

Make a rogue.
fill up the inventory.
use pickpocket to get an item on the cursor.
use pickpocket again and you should get the message that it won't work with an item on your cursor.
die.
get rez.
loot corpse.
corpse will not rot.

this of course also is easy to do with foragers, but it should be repeatable with any character and a full inventory and an item on the cursor. I am semi confident it does not happen if the inventory is not 100% full.

as for look at corpse items.. those are in a blob, so need to parse it out of there. I vaguely recall seeing a script to check blobs once but do not recall if it was on corpses or players. i'll poke at it later if no one gets back to you sooner.

Uleat
07-27-2012, 06:43 AM
(Sorvani, that almost sounds like when a player dies with an item in the cursor queue, past [0], the server knows
that it's there, but the loot routine is only grabbing [0] and sending it to the client and not sending queue slots...
I'll add it to the list... It might be fixable by doing a cursor iteration when sending the loot window packets, if that's
what is happening.)


Anyways, I've been distracted by r/l and am starting to get back into this.

I'm posting an ALPHA patch here for some minor issues. I didn't want to start a new thread in another forum with a portion
of this not tested. (#summonitem was modified to report lore conflict failures with augments properly (imo) and I haven't
looked up any augment item id's just yet to test it out.) The rest of the code is tested and should work as intended.


[CSD Patch 1]
Index: command.cpp
================================================== =================
--- command.cpp (revision 2171)
+++ command.cpp (working copy)
@@ -456,7 +456,8 @@
command_add("sensetrap", "Analog for ldon sense trap for the newer clients since we still don't have it working.", 0, command_sensetrap) ||
command_add("picklock", "Analog for ldon pick lock for the newer clients since we still don't have it working.", 0, command_picklock) ||
command_add("mysql", "Mysql CLI, see 'help' for options.", 250, command_mysql) ||
- command_add("xtargets", "Show your targets Extended Targets and optionally set how many xtargets they can have.", 250, command_xtargets)
+ command_add("xtargets", "Show your targets Extended Targets and optionally set how many xtargets they can have.", 250, command_xtargets) ||
+ command_add("zopp", "Troubleshooting command - Sends a fake item packet to you. No server reference is created.", 250, command_zopp)
)
{
command_deinit();
@@ -3013,6 +3014,7 @@
c->Message(0, "Usage: (targted) #nukeitem itemnum - removes the item from the player's inventory");
}

+// 'CSD 1' - Made modifications to show charge count of items as well - other changes are noted where they occur
void command_peekinv(Client *c, const Seperator *sep)
{
// Displays what the server thinks the user has in inventory
@@ -3035,15 +3037,17 @@
item = (inst) ? inst->GetItem() : NULL;
if (c->GetClientVersion() >= EQClientSoF)
{
- c->Message((item==0), "WornSlot: %i, Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c)", i,
+ c->Message((item==0), "WornSlot: %i, Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c), Charges: %i", i,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
else
{
- c->Message((item==0), "WornSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c )", i,
+ c->Message((item==0), "WornSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c ), Charges: %i", i,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
}
}
@@ -3055,15 +3059,17 @@
item = (inst) ? inst->GetItem() : NULL;
if (c->GetClientVersion() >= EQClientSoF)
{
- c->Message((item==0), "InvSlot: %i, Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c)", i,
+ c->Message((item==0), "InvSlot: %i, Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c), Charges: %i", i,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
else
{
- c->Message((item==0), "InvSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c )", i,
+ c->Message((item==0), "InvSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c ), Charges: %i", i,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}

if (inst && inst->IsType(ItemClassContainer)) {
@@ -3072,17 +3078,19 @@
item = (instbag) ? instbag->GetItem() : NULL;
if (c->GetClientVersion() >= EQClientSoF)
{
- c->Message((item==0), " InvBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c)",
+ c->Message((item==0), " InvBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c), Charges: %i",
Inventory::CalcSlotId(i, j),
i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:instbag->GetCharges()));
}
else
{
- c->Message((item==0), " InvBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c )",
+ c->Message((item==0), " InvBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c ), Charges: %i",
Inventory::CalcSlotId(i, j),
i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:instbag->GetCharges()));
}
}
}
@@ -3091,50 +3099,73 @@
{
const ItemInst* inst = client->GetInv().GetItem(9999);
item = (inst) ? inst->GetItem() : NULL;
- c->Message((item==0), "InvSlot: %i, Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c)", 9999,
+ c->Message((item==0), "InvSlot: %i, Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c), Charges: %i", 9999,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
}
+
+ // 'CSD 1' - Changed to show 'empty' cursors and not to show bag slots on 'queued' cursor slots (cursor bag slots 331 to 340 are not queued...)
+ // - was pointless to show bags on anything after slot 30[0], because they don't exist and only repeat the 30[0] bag items.
if (bAll || (strcasecmp(sep->arg[1], "cursor")==0)) {
// Personal inventory items
bFound = true;
iter_queue it;
int i=0;
- for(it=client->GetInv().cursor_begin();it!=client->GetInv().cursor_end();it++,i++) {
- const ItemInst* inst = *it;
- item = (inst) ? inst->GetItem() : NULL;
+
+ if(client->GetInv().CursorEmpty()) { // 'CSD 1' - Display 'front' cursor slot even if 'empty' (item(30[0]) == null)
if (c->GetClientVersion() >= EQClientSoF)
{
- c->Message((item==0), "CursorSlot: %i, Depth: %i, Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c)", SLOT_CURSOR,i,
- ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ c->Message((item==0), "CursorSlot: %i, Depth: %i, Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c), Charges: %i", SLOT_CURSOR,i,
+ 0, 0x12, 0, "null", 0x12, 0);
}
else
{
- c->Message((item==0), "CursorSlot: %i, Depth: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c )", SLOT_CURSOR,i,
- ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ c->Message((item==0), "CursorSlot: %i, Depth: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c ), Charges: %i", SLOT_CURSOR,i,
+ 0, 0x12, 0, "null", 0x12, 0);
}
+ }
+ else {
+ for(it=client->GetInv().cursor_begin();it!=client->GetInv().cursor_end();it++,i++) {
+ const ItemInst* inst = *it;
+ item = (inst) ? inst->GetItem() : NULL; // 'CSD 1' - (item == null) will not include an 'empty' cursor (30[0])
+ if (c->GetClientVersion() >= EQClientSoF)
+ {
+ c->Message((item==0), "CursorSlot: %i, Depth: %i, Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c), Charges: %i", SLOT_CURSOR,i,
+ ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
+ }
+ else
+ {
+ c->Message((item==0), "CursorSlot: %i, Depth: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c ), Charges: %i", SLOT_CURSOR,i,
+ ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
+ }

- if (inst && inst->IsType(ItemClassContainer)) {
- for (uint8 j=0; j<10; j++) {
- const ItemInst* instbag = client->GetInv().GetItem(SLOT_CURSOR, j);
- item = (instbag) ? instbag->GetItem() : NULL;
- if (c->GetClientVersion() >= EQClientSoF)
- {
- c->Message((item==0), " CursorBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c)",
- Inventory::CalcSlotId(SLOT_CURSOR, j),
- SLOT_CURSOR, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ if (inst && inst->IsType(ItemClassContainer) && i==0) { // 'CSD 1' - only display contents of slot 30[0] container..higher ones don't exist
+ for (uint8 j=0; j<10; j++) {
+ const ItemInst* instbag = client->GetInv().GetItem(SLOT_CURSOR, j);
+ item = (instbag) ? instbag->GetItem() : NULL;
+ if (c->GetClientVersion() >= EQClientSoF)
+ {
+ c->Message((item==0), " CursorBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c), Charges: %i",
+ Inventory::CalcSlotId(SLOT_CURSOR, j),
+ SLOT_CURSOR, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:instbag->GetCharges()));
+ }
+ else
+ {
+ c->Message((item==0), " CursorBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c ), Charges: %i",
+ Inventory::CalcSlotId(SLOT_CURSOR, j),
+ SLOT_CURSOR, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:instbag->GetCharges()));
+ }
}
- else
- {
- c->Message((item==0), " CursorBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c )",
- Inventory::CalcSlotId(SLOT_CURSOR, j),
- SLOT_CURSOR, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
- }
}
}
}
@@ -3148,15 +3179,17 @@
item = (inst) ? inst->GetItem() : NULL;
if (c->GetClientVersion() >= EQClientSoF)
{
- c->Message((item==0), "TributeSlot: %i, Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c)", i,
+ c->Message((item==0), "TributeSlot: %i, Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c), Charges: %i", i,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
else
{
- c->Message((item==0), "TributeSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c )", i,
+ c->Message((item==0), "TributeSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c ), Charges: %i", i,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
}
}
@@ -3170,15 +3203,17 @@
item = (inst) ? inst->GetItem() : NULL;
if (c->GetClientVersion() >= EQClientSoF)
{
- c->Message((item==0), "BankSlot: %i, Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c)", i,
+ c->Message((item==0), "BankSlot: %i, Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c), Charges: %i", i,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
else
{
- c->Message((item==0), "BankSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c )", i,
+ c->Message((item==0), "BankSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c ), Charges: %i", i,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}

if (inst && inst->IsType(ItemClassContainer)) {
@@ -3187,17 +3222,19 @@
item = (instbag) ? instbag->GetItem() : NULL;
if (c->GetClientVersion() >= EQClientSoF)
{
- c->Message((item==0), " BankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c)",
+ c->Message((item==0), " BankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c), Charges: %i",
Inventory::CalcSlotId(i, j),
i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
else
{
- c->Message((item==0), " BankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c )",
+ c->Message((item==0), " BankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c ), Charges: %i",
Inventory::CalcSlotId(i, j),
i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
}
}
@@ -3207,15 +3244,17 @@
item = (inst) ? inst->GetItem() : NULL;
if (c->GetClientVersion() >= EQClientSoF)
{
- c->Message((item==0), "ShBankSlot: %i, Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c)", i,
+ c->Message((item==0), "ShBankSlot: %i, Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c), Charges: %i", i,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
else
{
- c->Message((item==0), "ShBankSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c )", i,
+ c->Message((item==0), "ShBankSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c ), Charges: %i", i,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}

if (inst && inst->IsType(ItemClassContainer)) {
@@ -3224,17 +3263,19 @@
item = (instbag) ? instbag->GetItem() : NULL;
if (c->GetClientVersion() >= EQClientSoF)
{
- c->Message((item==0), " ShBankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c)",
+ c->Message((item==0), " ShBankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c), Charges: %i",
Inventory::CalcSlotId(i, j),
i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
else
{
- c->Message((item==0), " ShBankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c )",
+ c->Message((item==0), " ShBankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c ), Charges: %i",
Inventory::CalcSlotId(i, j),
i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
}
}
@@ -3248,15 +3289,17 @@
item = (inst) ? inst->GetItem() : NULL;
if (c->GetClientVersion() >= EQClientSoF)
{
- c->Message((item==0), "TradeSlot: %i, Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c)", i,
+ c->Message((item==0), "TradeSlot: %i, Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c), Charges: %i", i,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
else
{
- c->Message((item==0), "TradeSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c )", i,
+ c->Message((item==0), "TradeSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c ), Charges: %i", i,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}

if (inst && inst->IsType(ItemClassContainer)) {
@@ -3265,17 +3308,19 @@
item = (instbag) ? instbag->GetItem() : NULL;
if (c->GetClientVersion() >= EQClientSoF)
{
- c->Message((item==0), " TradeBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c)",
+ c->Message((item==0), " TradeBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X0000000000000000000000000000000000000000000 0%s%c), Charges: %i",
Inventory::CalcSlotId(i, j),
i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
else
{
- c->Message((item==0), " TradeBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c )",
+ c->Message((item==0), " TradeBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c ), Charges: %i",
Inventory::CalcSlotId(i, j),
i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}

}
@@ -3286,7 +3331,7 @@
if (!bFound)
{
c->Message(0, "Usage: #peekinv [worn|cursor|inv|bank|trade|trib|all]");
- c->Message(0, " Displays a portion of the targetted user's inventory");
+ c->Message(0, " Displays a portion of the targeted user's inventory");
c->Message(0, " Caution: 'all' is a lot of information!");
}
}
@@ -11529,3 +11581,40 @@
else
t->ShowXTargets(c);
}
+
+// 'CSD 3' - Troubleshooting command used to send a fake item to the client..server item not created.
+// - Owner only command..non-targetable to eliminate malicious or mischievious activities.
+void command_zopp(Client *c, const Seperator *sep)
+{
+ if (sep->argnum < 2 || sep->argnum > 3)
+ c->Message(0, "Usage: #zopp [slot id] [item id] [*charges]");
+ else if (!sep->IsNumber(1) || !sep->IsNumber(2) || (sep->argnum == 3 && !sep->IsNumber(3)))
+ c->Message(0, "Usage: #zopp [slot id] [item id] [*charges]");
+ else {
+ sint16 slotid = atoi(sep->arg[1]);
+ int32 itemid = atoi(sep->arg[2]);
+ sint16 charges = sep->argnum == 3 ? atoi(sep->arg[3]) : 1; // defaults to 1 charge if not specified
+
+ const Item_Struct* FakeItem = database.GetItem(itemid);
+
+ if (!FakeItem) {
+ c->Message(13, "Error: Item [%u] is not a valid item id.", itemid);
+ return;
+ }
+
+ if (database.GetItemStatus(itemid) > c->Admin()) {
+ c->Message(13, "Error: Insufficient status to use this command.");
+ return;
+ }
+
+ if (charges < 0 || charges > FakeItem->StackSize) {
+ c->Message(13, "Warning: The specified charge count does not meet expected criteria!");
+ c->Message(0, "Processing request..results may be unpredictable or cause instability.");
+ }
+
+ ItemInst* FakeItemInst = database.CreateItem(FakeItem, charges);
+ c->SendItemPacket(slotid, FakeItemInst, ItemPacketTrade);
+ c->Message(0, "Sending zephyr op packet to client - %s (%u) with %i %s to slot %i.", FakeItem->Name, itemid, charges, abs(charges==1)?"charge":"charges", slotid);
+ safe_delete(FakeItemInst);
+ }
+}
\ No newline at end of file
Index: command.h
================================================== =================
--- command.h (revision 2171)
+++ command.h (working copy)
@@ -320,6 +320,7 @@
void command_qtest(Client *c, const Seperator *sep);
void command_mysql(Client *c, const Seperator *sep);
void command_xtargets(Client *c, const Seperator *sep);
+void command_zopp(Client *c, const Seperator *sep);

#ifdef EMBPERL
void command_embperl_plugin(Client *c, const Seperator *sep);
Index: inventory.cpp
================================================== =================
--- inventory.cpp (revision 2171)
+++ inventory.cpp (working copy)
@@ -222,6 +222,14 @@
inst->SetCharges(1);
if ((inst->GetCharges()>0))
inst->SetCharges(inst->GetCharges());
+
+ // 'CSD 7' - This reduces overcharged items to maximum stacksize
+ if ((inst->GetCharges()>inst->GetItem()->StackSize)) {
+ inst->SetCharges(inst->GetItem()->StackSize);
+ Message(0, "Your summoned item is charged beyond maximum allowable - adjusting to %i charges!", inst->GetCharges());
+ }
+
+ // 'CSD 7' - Corrected the augment references to reflect augment name/id instead of base item name/id
if (aug1) {
const Item_Struct* augitem1 = database.GetItem(aug1);
if (augitem1) {
@@ -229,7 +237,7 @@
inst->PutAugment(&database, 0, aug1);
}
else {
- Message(0, "You already have a %s (%i) in your inventory!", item->Name, item_id);
+ Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem1->Name, aug1);
}
}
}
@@ -240,7 +248,7 @@
inst->PutAugment(&database, 1, aug2);
}
else {
- Message(0, "You already have a %s (%i) in your inventory!", item->Name, item_id);
+ Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem2->Name, aug2);
}
}
}
@@ -251,7 +259,7 @@
inst->PutAugment(&database, 2, aug3);
}
else {
- Message(0, "You already have a %s (%i) in your inventory!", item->Name, item_id);
+ Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem3->Name, aug3);
}
}
}
@@ -262,7 +270,7 @@
inst->PutAugment(&database, 3, aug4);
}
else {
- Message(0, "You already have a %s (%i) in your inventory!", item->Name, item_id);
+ Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem4->Name, aug4);
}
}
}
@@ -273,7 +281,7 @@
inst->PutAugment(&database, 4, aug5);
}
else {
- Message(0, "You already have a %s (%i) in your inventory!", item->Name, item_id);
+ Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem5->Name, aug5);
}
}
}


If this isn't committed, that's fine. I'll include it in a later patch.


I think I have a pretty good idea of what's happening now, but if anyone still has any suggestions... (constructive... I know how to do THAT already!)

Uleat
07-27-2012, 12:36 PM
Here's another 'in-work' mod...

(Remember, I'm trying to find ALL occurences of CSDs/bugged inventories.)


Index: client_packet.cpp
================================================== =================
--- client_packet.cpp (revision 2173)
+++ client_packet.cpp (working copy)
@@ -3278,6 +3278,8 @@
return;
}

+// 'CSD 11' - Added checks for illegal bagslot swaps..should help with certain cheats
+// - If a player has used a cheat that allows illegal item placement, they could beomce bugged at some point (especially lore items.)
void Client::Handle_OP_MoveItem(const EQApplicationPacket *app)
{
if(!CharacterID())
@@ -3291,6 +3293,7 @@
}

MoveItem_Struct* mi = (MoveItem_Struct*)app->pBuffer;
+
if(spellend_timer.Enabled() && casting_spell_id && !IsBardSong(casting_spell_id))
{
if(mi->from_slot != mi->to_slot && (mi->from_slot < 30 || mi->from_slot > 39) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot))
@@ -3310,6 +3313,53 @@
return;
}
}
+
+ // REMOVE OR REMARK OUT BEGIN FOR ALLOWING ILLEGAL BAGSLOT USE
+ bool hackflag = false;
+
+ if (mi->from_slot >=251 && mi->from_slot <=340) {
+ if (mi->from_slot > 330)
+ hackflag = true; // why are we moving from a cursor bagslot when you can't open it?
+ else {
+ sint16 invslot = Inventory::CalcSlotId(mi->from_slot);
+ const ItemInst *invslotitem = GetInv().GetItem(invslot);
+
+ if (invslotitem->GetItem()->ItemClass==1) { // checking the parent inventory slot for container
+ if (Inventory::CalcBagIdx(mi->from_slot) > invslotitem->GetItem()->BagSlots)
+ hackflag = true; // trying to move from slots beyond container size
+ }
+ else { // trying to move from bag slots when inventory item is not a container
+ hackflag = true;
+ }
+ }
+ }
+
+ if (mi->to_slot >= 251 && mi->to_slot <=340) {
+ bool hackflag = false;
+
+ if (mi->to_slot > 330)
+ hackflag = true; // why are we moving to a cursor bagslot when you can't open it?
+ else {
+ sint16 invslot = Inventory::CalcSlotId(mi->to_slot);
+ const ItemInst *invslotitem = GetInv().GetItem(invslot);
+
+ if (invslotitem->GetItem()->ItemClass==1) { // checking the parent inventory slot for container
+ if (Inventory::CalcBagIdx(mi->from_slot) > invslotitem->GetItem()->BagSlots)
+ hackflag = true; // trying to move into slots beyond container size
+ }
+ else { // trying to move into bag slots when inventory item is not a container
+ hackflag = true;
+ }
+ }
+ }
+
+ if (hackflag) {
+ Message(13, "Hack attempt detected - Illegal use of inventory bagslots!");
+ // TODO: Decide whether to log player as hacker
+ // Kick();
+ }
+ // REMOVE OR REMARK OUT END FOR ALLOWING ILLEGAL BAGSLOT USE */
+
SwapItem(mi);
return;
}


Tested with legal trades, but untested for illegal...

This is still in a crude state, but any input is appreciated!

Uleat
07-28-2012, 12:34 AM
I missed removing that second 'hackflag' definition..they were originally internal.

Uleat
07-28-2012, 05:20 AM
I always forget to check for 'null' values on new stuff..usually that is already done...

I also changed the pointer reference when I was trying to figure this out..it currently works, but it could have the other way as well... (didn't check it.)


This is an ALPHA patch as well since it more proof of concept than a finished product.

[CSD Patch 2]
Index: client_packet.cpp
================================================== =================
--- client_packet.cpp (revision 2173)
+++ client_packet.cpp (working copy)
@@ -3278,6 +3278,8 @@
return;
}

+// 'CSD 11' - Added checks for illegal bagslot swaps..should help with certain cheats
+// - If a player has used a cheat that allows illegal item placement, they could beomce bugged at some point (especially lore items.)
void Client::Handle_OP_MoveItem(const EQApplicationPacket *app)
{
if(!CharacterID())
@@ -3291,6 +3293,7 @@
}

MoveItem_Struct* mi = (MoveItem_Struct*)app->pBuffer;
+
if(spellend_timer.Enabled() && casting_spell_id && !IsBardSong(casting_spell_id))
{
if(mi->from_slot != mi->to_slot && (mi->from_slot < 30 || mi->from_slot > 39) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot))
@@ -3310,6 +3313,58 @@
return;
}
}
+
+ //* REMOVE OR REMARK OUT BEGIN FOR ALLOWING ILLEGAL BAGSLOT USE
+ bool hackflag = false;
+
+ if (mi->from_slot >=251 && mi->from_slot <=340) {
+ if (mi->from_slot > 330)
+ hackflag = true; // why are we moving from a cursor bagslot when you can't open it?
+ else {
+ sint16 from_invslot = this->m_inv.CalcSlotId(mi->from_slot);
+ const ItemInst *from_invslotitem = GetInv().GetItem(from_invslot);
+
+ if (!from_invslotitem) { // trying to move from bag slots when parent inventory slot is empty
+ hackflag = true;
+ }
+ else if (from_invslotitem->GetItem()->ItemClass==1) { // checking the parent inventory slot for container
+ if (this->m_inv.CalcBagIdx(mi->from_slot) > (from_invslotitem->GetItem()->BagSlots - 1))
+ hackflag = true; // trying to move from slots beyond parent container size
+ }
+ else { // trying to move from bag slots when inventory slot item is not a container
+ hackflag = true;
+ }
+ }
+ }
+
+ if (mi->to_slot >= 251 && mi->to_slot <=340) {
+ if (mi->to_slot > 330)
+ hackflag = true; // why are we moving to a cursor bagslot when you can't open it?
+ else {
+ sint16 to_invslot = this->m_inv.CalcSlotId(mi->to_slot);
+ const ItemInst *to_invslotitem = GetInv().GetItem(to_invslot);
+
+ if (!to_invslotitem) { // trying to move into bag slots when parent inventory slot is empty
+ hackflag = true;
+ }
+ else if (to_invslotitem->GetItem()->ItemClass==1) { // checking the parent inventory slot for container
+ if (this->m_inv.CalcBagIdx(mi->from_slot) > (to_invslotitem->GetItem()->BagSlots - 1))
+ hackflag = true; // trying to move into slots beyond parent container size
+ }
+ else { // trying to move into bag slots when inventory slot item is not a container
+ hackflag = true;
+ }
+ }
+ }
+
+ if (hackflag) {
+ Message(13, "Hack attempt detected: Illegal use of inventory bagslots!");
+ // TODO: Decide whether to log player as hacker
+ // Kick();
+ // return;
+ }
+ // REMOVE OR REMARK OUT END FOR ALLOWING ILLEGAL BAGSLOT USE */
+
SwapItem(mi);
return;
}


Still looking for feedback :D

Uleat
07-28-2012, 08:45 AM
(I know you guys get tired of reading my rantings..but I have to keep it out here in case something happens to me or my notes...)

Back in this post - http://www.eqemulator.org/forums/showpost.php?p=210952&postcount=19 - I mentioned how the cursor is slot 30 and
the queue begins at 8001, per database observation. Slot 8000 is apparently not used for the active cursor.

It is, however, referenced several times in the code..here's one instance:


[PlayerCorpse.cpp]
Corpse::Corpse(Client* client, sint32 in_rezexp)
...
for(i = 0; i <= 30; i++)
{
item = client->GetInv().GetItem(i);
if((item && (!client->IsBecomeNPC())) || (item && client->IsBecomeNPC() && !item->GetItem()->NoRent))
{
MoveItemToCorpse(client, item, i);
removed_list.push_back(i);
}
}

// cursor queue
iter_queue it;
for(it=client->GetInv().cursor_begin(),i=8000; it!=client->GetInv().cursor_end(); it++,i++) {
item = *it;
if((item && (!client->IsBecomeNPC())) || (item && client->IsBecomeNPC() && !item->GetItem()->NoRent))
{
MoveItemToCorpse(client, item, i);
cursor = true;
}
}
...


Obviously, the first loop catches the cursor slot 30. How does slot 8000 not being occupied actually affect the operation
of the second loop?

And, what would happen if both slot 30 and slot 8000 were occupied? We should lose one of them, right?


Muse it over and let me know if you think this is an issue. It's on my list, so I'll eventually find a way to test what is happening.

lerxst2112
07-28-2012, 04:53 PM
Obviously, the first loop catches the cursor slot 30. How does slot 8000 not being occupied actually affect the operation
of the second loop?


They appear to be two separate unrelated loops. One moves items in slots 0-30, and the second moves items from the cursor queue to 8000-(8000 + N). I didn't look at how MoveItemToCorpse works, but I would guess since the slot numbers don't overlap it isn't an issue.


And, what would happen if both slot 30 and slot 8000 were occupied? We should lose one of them, right?


Sounds like a good thing to test for.

Uleat
07-30-2012, 01:39 AM
They are seperate, but they're not... I had to include both for reference.

The first one handles the 'visible' cursor slot 30. The second handles slot 8000, which supposedly is also the visible cursor slot.
(There are several references in the code that indicate this..mostly in remark statements.)


Here's a little test, with validation checks, to induce a bugged corpse:

- Ensure no items are on your cursor

- #summonitem 13201 (Blackbox A Series)
- #summonitem 13202 (Blackbox B Series)
- #summonitem 13203 (Blackbox C Series)
- #summonitem 13204 (Blackbox D Series)

- Open your database tool software and look at this character's inventory..it should read:
-- Slot 30 - 13201
-- Slot 8001 - 13202
-- Slot 8002 - 13203
-- Slot 8003 - 13204

- Now, #kill yourself..it should crash your client.

- Refresh you database table and reinspect the values:
-- Slot 30 - 13202
-- Slot 8001 - 13203
-- Slot 8002 - 13204

- At this point, we can assume that item 13201 made it to the corpse, or that it was deleted for some reason.

- Now, revive and you should have cursor items. (Best to do all of this next to your bind point...)
-- Three items total, Blackboxes B, C & D.

- Loot your corpse..in addition to your equipment, you will find Blackbox A at the end.

- You didn't actually lose anything this time, but now your corpse is bugged.


Something is crashing the client..probably while trying to process slot 8000.

I will have to work up an in-depth test to figure out what's going on exactly..or learn how to actually use the
debug feature...

I suspect the blob data of the corpse will contain either a bugged item 13201 or slot 8000 of some type.

IF tradeskilling is using slot 8000 for combines or returns, I suspect this same issue is at the root of that problem too.
(Not death, but the use of slot 8000...)


Since the problem could reach back into the kernel, I don't wan't to simply 'fix' it here. Some more research
is needed..but it is on the list for sure now...

Uleat
07-30-2012, 04:00 AM
Ok, I managed to get EQ Editor 1.30 to work enough to be able to inspect a corpse's inventory.

What I found was that my bugged corpse had two items on it...

Item 1 - CHARM slot (0) -> ITEM ID 0
Item 2 - CHARM slot (0) -> ITEM ID 0


Obviously, something is not right with the way the server currently handles loot transfers to a corpse. Whether it is in the
cursor queue or elsewhere, the corpse DOES contain entries in the blob that keeps the server from properly decaying it.

(EQ Editor looks pretty thorough..Sorry George, I still haven't tried your tools yet... I was looking specifically for some code
to incorporate into a '#command' that can be used in-game and ran across this. AndMetal seems to have something on
the wiki that could be adapted to a 'live' command for checking blobs..I'd like to add that as well. I need to find out who
wrote EQ Editor and see if I can use thier code too since it would only have to be converted from php to c++ or perl.)


http://code.google.com/p/ultraeqeditor/ will take you to this program.

To get this working, in the interim, you will need to make at least three changes:

1 - edit login.php and remove the 'md5()' wrapper from the password check at the beginning
2 - edit functions.php and remove the 'md5()' wrapper from the password check in 'CheckAuth()'
3 - edit corpse.php and change 'parent_corpse_id' to 'id'

I had to remove the wrappers because I can't log into the world server when my http password is security hashed...
And account passwords seem to be encrypted with SHA and not MD5 anyways...


These changes will let you at least see the items on the bugged corpse that was created in the last post.

Of course, you will need the same setup installed and running that's required for AllaClone. I just moved my
AllaClone files out and copied these in. You will need to edit the configuration.php file as well.

Uleat
07-30-2012, 05:20 AM
Apparently, this needs some more work..I can't seem to find newer corpses...

This corpse I was looking at was several weeks old, so I'll see what I can do with the search criteria.

Uleat
07-30-2012, 06:30 AM
I was able to finally get to my recent corpses and see that they actually had three items of slot (0) -> itemid (0).

This matches the amount of queued items that I had on the cursor when I died (Blackboxes B, C & D.)


Ouside of the 'slot 8000' possibility, I need to follow the calls back and see if any of the queued items are actually
being allowed since these methods are processing them.

Uleat
07-30-2012, 09:34 AM
Ok, sadly it appears that EQ Editor needs alot more work... I finally got to a corpse that had ALL of my equip and
it reported all slots and itemids as '0' as well...

Anyways... Back to trying to find out what is crashing the client.

Uleat
07-30-2012, 01:26 PM
I gave up short term on the blob data..was taking too much time.

Instead, I just added a command to view the server data of a particular corpse.


What I found was (I left my cursor item prior to crash in place):

- item: 13201 -> equipslot: 30 -> lootslot: 27
- item: 13202 -> equipslot: 8000 -> lootslot: 65535
- item: 13203 -> equipslot: 8001 -> lootslot: 65535
- item: 13204 -> equipslot: 8002 -> lootslot: 65535


With my testing with my 'zopp' command, I saw that the client does not recognize the 8000-series of slots.
When a packet is sent in that range, the client just ignores it..meaning moving/deleting the cursor item
will not 'bump' the next item up if you assume that 8001 is the next client slot.

With that being obvious, 'MoveItemToCorpse' is calling 'DeleteItemInInventory.' This function, among other
things, sends a client delete item packet. I'm not sure if the equipslot or lootslot ID is causing the crash at this
point, but I'm still working on it.


If anyone has any idea on why the lootitem slots are 65535 and/or the possibility of the slot 8000 range
causing client crashes, you could save me a whole bunch of time. Every time I find something new, I have to
stop to find and learn why something is causing that behavior...

Uleat
07-31-2012, 07:04 AM
I believe the bugged corpse issue can be resolved soon.

I found what needed to be changed to allow ALL items to be seen by the client and now all of my corpses are
COMPLETELY lootable.


However... There is a duplication problem that this fix brings to light.

Currently, when a player logs back in after the crash (dying with queued cursor items,) those items are back on the cursor.

Now, when a player loots their corpse, those items are there again as well. This is what was going on behind the scenes
with the current code, but was being blocked as well.


Now I need to pin-down the client crash (I have, but need to ensure no other calls are affected) and the duplication cause.

In addition, I need to resolve any issues with a queued cursor bag that has contents..I'll probably just push them to the
cursor so they are not lost (rememeber, queued cursor slots do not have queued bag slots.)

Caryatis
07-31-2012, 10:30 AM
I'm sure eventually you will actually produce something useful but seriously man, 8 posts by you in a row like an hour apart. If you want to start a twitter feed to track your rambling around this problem, feel free but nobody needs to know that you havent used Georges tools and just got an out of date program to kinda work but then turns out it wasnt needed at all because that was a circuitous route around the actual issue.

You might take offense to this but you don't seem experienced enough to be tackling this all at once. I don't see why you don't break out each bit into its own diff and finalize those. You got some test code here, other code there and your scope keeps growing every post, at this rate you will never finish.

The other thing to take into account is that this thread doesn't really inspire confidence in your final product, so if you dump a 10k line diff at the end, that is going to have to be combed over like crazy before a commit since this is a very exploit prone area and nobody likes to go through a huge ass diff like that, shit it can take like a month to get a multi line diff added around here.

Uleat
07-31-2012, 01:05 PM
I appreciate your candor Caryatis, and you are probably right on all counts. I am mature enough not to take criticism
personally, though.


I have devised a fairly decent system of keeping track of this mess. Any function that I modify is considered a 'Change.'
When changes must be deployed together, it becomes a 'Patch.'


As far as patches in this thread, it is not my intent that they be implemented. I will start a new one over in server code
submission specifically for a finalized product. Anything here is mainly for review and suggestions.

I will post a 'support' patch soon. The other issues, like bandolier swaps, arrow stacking issues, etc... will come later since
they are the most intrusive into the kernel.


I do tend to get a little 'manic' at times, so I'll try to do a better job of monitoring my 'tweet' rate!
</points to my signature block>

Uleat
08-01-2012, 02:05 PM
Ok, I think I have got a working fix for the bugged corpses.

I fixed the 'invisible item' (bugged corpses) and client crash issues with queued cursor items.

I have even somewhat fixed the new item duplication bug that arose from that.


I am now looking for a way to clear the client-side cursor queue when your dead. I can remove the immediate cursor item,
but when a player is dead, the queue doesn't update and I end up with 'phantom' items when a player is revived.
Logging out and back in resets the client, so there is no problem there.

Any ideas on how to do this? I don't want to 'Kick()' them everytime they die with items on their cursor...

Uleat
08-06-2012, 11:53 PM
Got a couple of questions, if anyone happens to know an answer or two...


With what I can gleen from from the code, it appears the server player profile is based on the Titanium model with translations
(Encode/Decode) and adaptations applied per client.

Are there any instances where this is not true?


I've also been playing around with the bugged corpse issue and have had some luck. There are two specific things I can't
deduce and wonder if different clients are handling things differently.


I've added code to basically allow any item that is not assigned to a bag slot to show up in the corpse list..but not necessarily
add them when the corpse is created.

Should the trader slots be a part of corpse creation? My testing shows that trade items are put back into the player's
inventory after the corpse is processed. (The server code does not currently process trader slots to a corpse.)


I'm also having trouble clearing the cursor of a dead player client-side. I can clear the server and database with no issues,
but the client won't release them until revived.

Do all clients have this behavior? SoF does, but the server IS currently coded to move cursor queue items to the corpse. (source of bugged corpses)
I can do a work-around, but I need to know what the desired behavior should be. (Leave queue items on cursor, or force
zone change event on rez/revive..like Evac does.)


Thanks!

sorvani
08-07-2012, 01:33 AM
you are forgetting that the death hover window now exists. it is generally turned off because it was causing issues itself. but one would think that since all the clients are designed for the hover, that may be one reason some things do not clean up (client side) like you are thinking.

test trades. make a second toon, open a trade window then #kill it.

Uleat
08-08-2012, 02:12 AM
Yeah, cancelled trades are definitely processed after the corpse is created. I see the item pop back into my inventory about
a half-a-second after I use #kill.

Setting 'Hover' mode to false definitely eliminates the issue since forcing a zone change event is what I was wanting to
use to correct the situation. The only problem is trying to figure out how to handle the CSD associated with it when
'Hover' is set to true. It's more cosmetic than anything since trying to place the 'phantom' items in your inventory results
in the server deleting them anyways.


I'll still leave that question out there in case someone has a decent idea of how to handle that.

I want to add a bit more code to let the player know if the corpse contains 'bugged' items and then tidy it up a
little bit, then I'll post an alpha patch here to let you guys see where this is going.

Uleat
08-08-2012, 07:06 AM
Ok, here is an ALPHA patch for bugged corpses. It may still need a little work, but most issues should be ironed out:

[CSD Patch 4]
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(equipsl ot, 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.

Uleat
08-13-2012, 07:25 AM
I ran across a couple of client bugs that can be mentioned, but don't really need a new thread.


First, there's a odd issue with my client (SoF) where with a full corpse (30 items), clicking index 29 (zero-based) will
loot index 28. Clicking index 29 again, with 28 empty, will cause the loot window to close.

Second, with the submitted bugged corpse patch, and with the RespawnFromHover = false, dying outside of your
binding zone with certain items loaded on your cursor, will cause a glitch when you reload. The icon from one of the
cursor items will appear when you load into zone. If you die in your binding zone after this, but before leaving, this
issue doesn't appear.


[Random Items - no issue]
17005 (first item on cursor)
8005
13201
17003
1619

[Sequential Items, all have the same icon - issue arises]
13201 (first item on cursor)
13202
13203
13204
13205


I think it is a graphic glitch as putting this 'phantom' item in your inventory results in no server message. If you
use the #zopp command (slot 30, item id) and place it in your inventory, you get a server item deletion message.

Uleat
08-15-2012, 04:47 AM
Now that I've confused, flustered and maddened everyone with my code submissions so far, let's move on to something more productive.


Here's what I'm seeing at first glance with the 'Bandolier' issue.


inventory.cpp::Client::SetBandolier(const EQApplicationPacket *app)
slot = m_inv.HasItem(m_pp.bandoliers[bss->number].items[BandolierSlot].item_id, 1, invWhereWorn|invWherePersonal|invWhereCursor);


Item.cpp::Inventory::HasItem(uint32 item_id, uint8 quantity, uint8 where)
if(where & invWhereCursor) {
// Check cursor queue
slot_id = _HasItem(m_cursor, item_id, quantity);
if (slot_id != SLOT_INVALID)
return slot_id;
}


The following internal function is overloaded allowing two argument sets. The checks in Inventory::HasItem all use the first definition with
the exception of the last check that uses the second one, which is m_cursor.


Item.cpp::Inventory::_HasItem(ItemInstQueue& iqueue, uint32 item_id, uint8 quantity)
for (it=iqueue.begin(); it!=iqueue.end(); it++) {
ItemInst* inst = *it;
if (inst)
{
if (inst->GetID() == item_id) {
quantity_found += (inst->GetCharges()<=0) ? 1 : inst->GetCharges();
if (quantity_found >= quantity)
return SLOT_CURSOR;
}


Notice how 'it' is iterated. If an item is found, it returns SLOT_CURSOR. MAJOR PROBLEM there unless it's found at slot 8000...

SetBandolier assigns the weapon slot based on 'HasItem.' Say, for instance, the item being sought is in the fourth queue position. The actual
item is in slot 8003, but the Bandolier function thinks it is in slot 30 (queue slot 8000) because of 'return SLOT_CURSOR;' This is creating a
CSD.

We can't change that to slot_id because the client doesn't allow cursor queue manipulation. We should consider only checking the queue
for items, but not allowing their removal unless all items up to that item are also removed, and possibly pushed back to the cursor.


I think this is one of the causes of the bandolier issue, and possibly the tradeskill cursor issue as well.


If you think my logic is flawed, your input is welcome. I need to understand this if I'm to stand any chance of fixing it.

sorvani
08-16-2012, 02:04 AM
having done no research into this code at all, but reading your post and knowing some history of the issue, I wonder if things get broke because it is using the cursor slots?

What if someone hits forage then hits a bandolier swap right as the forage is placed on the cursor? or some other event that returns an item to the cursor?

Uleat
08-16-2012, 03:10 AM
The testing I've done appears to be ok with only using slot 30. Anytime a bandolier item is found further back in the queue,
the first item is taken..explains why a 'Box of Abu-Kar' wound up as my primary weapon...

I imagine that any items in it would also go poof...


Also, the support function doesn't check the cursor for stacking before trying to move stackable items.

When moving an arrow out of worn inventory, the client adds it to the cursor stack, but the server puts it in an empty bag slot.
Further manual swaps show the CSD condition by making items disappear.


I'm going to try dropping the 'invWhereCursor' and add a single cursor check in addition to bumping up the stackable range
check to 30 from 29 and see where that takes us.


In regards to foraging, or any other timer-based action, we could just throw in a timer check in the beginning and exit out if
any of them are active..maybe throw a "You must finish your current action before using the bandolier" message to the client.


I also scanned through the tradeskill combine functions. They appear to only use the 'm_inv' list, but I'm trying to find where
the relationship is defined between list and slot to see if the cursor is also being checked. I found the index declarations.

Uleat
08-16-2012, 08:04 AM
Both of the bandolier issue fixes appear to work so far. I'm at 100% success on items going where they're suppose to. (I'm not
checking the database on this yet since that part of the code should work. It was only where the items were going that was
the issue. I will check though before this gets posted down the road.)

This does not mean that issues won't still arise once the client becomes de-sync'd due to another reason. It just means that
the bandolier shouldn't be the cause of the de-sync.


As far as the timer possibility, I don't know how feasible that really is. There are no client packets sent in the SetBandolier
function, so all switching is handled client-side. Cancelling the process would create a major de-sync. We'll have to watch this.

Uleat
08-17-2012, 10:22 PM
I looked through the ts and foraging (fish/forage) code and didn't see anything out of the ordinary. I'm still sorting through the 'inventory' calls.
There's several areas that I don't trust based on what I see, but I still need to find all of the ways to test it in-game.

(The two foraging actions call different methods, PushItemOnCursor vs. PutItemInInventory(SLOT_CURSOR), but the end result is the same..just a
little more overhead with one of them.)


As far as cursor items being out of order, unless I find something specific somewhere, I'm thinking the client may be receiving UDP packets out of order.

There's no unique identifier in a series of cursor items. If the server sends three packets (a, b & c) to the client with the summonitem argument, the
client won't know that b->c->a is the wrong order unless it has the ability to identify and re-order based on timestamps. (I just don't know...)

My server is setup with drop_packets->false and is on a 100Mbs LAN..single player as well. I probably won't be able to reproduce issues that arise with
public servers.

Uleat
08-21-2012, 10:40 PM
I'm not sure what's going on with this code snippet, but I can't seem to get it to work properly when I add it to another function.

This code is copied directly from inventory.cpp::Client::DeleteItemInInventory. It handles client updates when deleting partial quantity from current stack charges.

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

I've adapted it to my needs, but what is happening is the client deletes the entire stack from the cursor, then each iteration
after the first creates the 'Move Item Failed In Client Application' error message.

Changing 'number_in_stack' to 'quantity' and disabling the iteration produces no results. I've tried changing from delete_struct
to moveitem_struct, but can't seem to get the client to take charge change commands unless the client is the originator.


I think this is another area where CSDs could be creeping in..server tells client to delete 2 charges in a 5 charge stack,
but all are deleted..and the client generates a 'Move Item Failed' error.

I don't see where normal 'actions' are causing this, but it could creep up in scripted/quest calls.

Harakiri23
08-29-2012, 09:58 AM
The client has 2 different opcodes for this - one is MSG_MoveCharges to move charges from a stackable item anywhere (or delete with 0xFFFF..) and one is MSG_MoveItem - which moves non-stackable items (or delete them), can also be used to "delete" a stack of items.

The opcode names for you may differ, but that is your issue to figure out the naming in eqemu.

image
08-29-2012, 10:45 AM
have you tried OP_DeleteCharge instead?

Uleat
08-29-2012, 01:21 PM
It's been a while since I messed with this portion of the code.

I can't remember what I did and did not try. But, when I get back to a point where I can, I will try both of those.

Thanks!

Uleat
09-01-2012, 04:36 PM
I'm going to try and work on the CSD issues a little more. I think that I can manage keeping my home source code updated by manual file transfer.
Posting-to-reply times will be delayed by at least a day, however...

I've not heard any negative feedback on this CSD patch series, as yet. Just remember..if you are using the bandolier and become de-sync'd from another
unfixed bug, you can still lose items just as any 'normal' item swap would cause. So, keep an eye out for those de-sync messages and take corrective action
immediately. (I can't guarantee that you will receive a message on a de-sync that originates in code outside of this patch, so be careful out there with
bandolier use and any other action that creates/moves/deletes items without doing a zone change/re-log first.)


My next area to work on is code using inventory.cpp::Client::TryStacking(). I've noticed that the stack search order does not match
Client::MoveItemToInventory() or the client behavior that I noticed in the work with the Bandolier function. I need to find all of the ways that it is used in-game,
test them out, and verify client behavior while using them.

The two functions that call this procedure are Client::AutoPutItemInInventory() and Client::Handle_OP_ShopPlayerBuy(). ShopPlayerBuy() is a client event
driven call. The other function is called by Client::AdventureMerchantPurchase(), Client::AltCurrencyPurchase(), Corpse::LootItem() and Client::TryStacking()
(and any possible script calls.)

I will be looking through the forums in search of postings about these in-game actions. But, if you know definitively about de-sync's and item loss while using
these actions, please re-post them here. This can include mysterious item movements (especially involving the cursor) since players are likely to change
zones/log and restore syncronization before switching equipment around.


Thanks!

Uleat
09-03-2012, 03:48 PM
Just a little note on item loss with overloaded inventory...


I do have a working theory on this one, but I HAVE to ensure that I can test every call to the affected code. (I'm assuming that I won't get any script calls
unless there is something defined to handle it. Otherwise, another function calls it.)

The problem likely lies in multiple areas, but the code in item.cpp::Inventory::FindFreeSlot() is most culprit. (Kinda the reverse issue that was in the Bandolier code.)

In the case of a corpse [Loot All]... When you have a full inventory (with an empty cursor), but still have occupied bags on your corpse:

- The next bag is pushed to SLOT_CURSOR and the contents are pushed to 331 - 340
- ALL subsequent bags are also pushed to SLOT_CURSOR, via the queue, and each bag's contents overwrite any previous bag's data in 331 - 340
- The bags themselves will end up in the cursor queue
- The contents of the first cursored bag will be:
-- The contents of the last corpse bag if it was full -or-
-- A mix of contents of both previous and last corpse bag items if it was not full
-- (I'll need to double-check, but I believe _putitem is used instead of _swapitem..which means existing items go bye-bye..changing it would create more problems)
- Any queued cursored bags will be empty

I'm not sure exactly what behavior would occur if a non-container item was first on the cursor since bag slots aren't moved in that case. A bag 'popping' into the
cursor slot 'may' avail any lost and un-deleted items in the 331 to 340 range. These items would now be assigned to that bag and any remaining bags would
still be empty.


This problem will probably arise with overloaded trades as well. I've seen posts regarding both loot all and trading with this issue.


Anyways... I can't seem to find an OP_Code that handles the 'Loot All' action. Does the client actually send a loot request op for each item in the corpse
window?

If it does, and the client DOES NOT move the items, we could cancel the transaction and leave the bags on the corpse. If the client DOES move the items
regardless, all we can do is minimize the damage..maybe by 'unpacking' the bags to the cursor queue, but we might have to bump up the active queue array
size. Dropping them to the ground would most likely lead to them being lost if the player can't clear their inventory to make room. (I guess we could unpack
the bags when the corpse is created as well... Wouldn't that be a mess...)

(What I've seen with the cursor queue size on the server and a SoF client: {client: 37; profile: 102; database: 1000})


(This conversation sounds familiar..sorry if I duped it from somewhere else...)

Uleat
09-08-2012, 08:00 PM
Currently, I am working on a fix for item deletion during a de-sync condition.

It may, or may not, work depending on my success with manipulating the client...


Does anyone know of way to send a 'NULL' item instance to the client? I'm trying to avoid using 'OP_DeleteItem' because of the issues surrounding its use and
already empty slots. (I'd like to avoid ANY messages to the client with the exception of the one informing them of the 'Inventory Resync' itself.)

client->SendItemPacket(slot_id, item_inst, ItemPacketTrade);

If I can use this to 'empty' a slot, it would save a ton of overhead.

Uleat
10-12-2012, 11:15 PM
I've bugged at least one of the dev's enough with pm's about this resync thing, so I'm taking it public.

I'm just getting back into this after a hiatus and wanted to get some feedback, if anyone has some good suggestions.


What I'm looking at doing is creating two structs for one opcode. First for incoming, and the second for outgoing.

MoveItem requires a 32-bit incoming because of the timestamp sent by the client. However, the client seems to crash when you send a 32-bit data value
(size-wise) to certain slots. Inventory appears to be ok, but bank and shared bank slots appear to be the cause of the crash.

BulkSendInventory uses a sint16 data value when sending initial equipment and I want to convert the outgoing MoveItem_Struct to sint16 as well... Obviously,
there is a lot that will require mod'ing, so I wanted to get some input from you guys before I devote that much time and effort into something that may not work.

Thanks!

lerxst2112
10-13-2012, 12:46 AM
You can't just assume that because one opcode uses a particular sized variable that another would do the same.

You would need packet captures of all the cases you are trying to figure out and then you would need to reverse engineer the packets to determine the difference when moving to/from the problem slots.

Uleat
10-13-2012, 07:01 PM
Yeah, I agree that would be the proper way to do it. I just don't have the means to actually do that and I don't want to involve anyone into my chaotic thought
processes beyond the Q & A at this point...

It could be that the client is not coded to handle server-originating move/delete OP's in those slots and is crashing instead of giving the normal 'failure' message.

Whether it's that or the struct size (or something else entirely,) I'd still like to pin-point the cause. The resync function works with the exception of those slots. If I
can figure this out, that would eliminate the de-sync'd item deletion completely.

The main problem with this concept is that I don't believe live servers or clients are coded to do what I'm trying to do..which is why I'm running into issues...
I believe that I was told there is a known issue with certain op code actions and slots, but they are rare in their occurrences.

I don't mind fruitless ventures as long as I can use it as a training exercise, so I'll see what becomes of it. If this does pan out, I'll see if I can get someone to
verify my results with a live packet collection.

Uleat
10-26-2012, 11:51 PM
I'm not quite ready to play with adding a special out-going struct for move/delete item, so..two quick questions: (don't hit me Lerxst! :) )


1) The tooltip info for int32 says it's a typedef for an unsigned int32. However, MSDN .net (and/or silverlight) defines it as a signed int32...

Question: Does MSVC++ not define it the same way, is it re-defined somewhere in the code, or is this an issue between compilers (and languages?)


2) I had been playing around and discovered that a 16-bit value for 'to_slot' and/or 'number_in_stack' didn't crash the client (though it may not work...)

Question: Would a bad conversion in the client where an (unsigned) 32-bit (~0) is being forced to an (unsigned) 16-bit register cause the crash? (I'm thinking
that might be causing an overload, or at least data loss, if it's not that the client isn't coded to handle the request.)


Thanks!

lerxst2112
10-27-2012, 01:02 AM
There is no such thing as an int32 in the C++ standard. In this project, as you've seen, it is a typedef, or alias, for unsigned int. It just happens to be 32 bits on the platforms the code is intended to compile on. A compiler could generate code using 64 bit integers and still be 100% standard compliant.

The packets sent between server and client are the result of packet capturing and reverse engineering. It is possible that some of the types used are not exactly the same as what the client expects, but the sizes of the packets are almost certainly correct which means changing values to a smaller type would result in an incorrectly sized packet and also offset anything that came after that value unless you add the appropriate amount of padding to match the size. If you believe a packet is being sent incorrectly the right thing to do is capture traces of those specific packets and check them against the existing code. Perhaps the packet you're looking at does expect 0xffff and not 0xffffffff in that position, but the only way to know for sure is to verify it against packets from live.

Uleat
10-28-2012, 12:21 AM
Ok..duh on the int32. I didn't have my code def window up when I was looking or I would have found <types.h>

I am really leaning away from even testing a modified out-going structure at this point. My question about conversions was more about the possibility of
corrupted data when narrowing. I seem to get what I expect when using integers, so I'm guessing data loss is more prevelent with floats.


(Sorry for the re-iterations..I just want to clarify the actual issue.)

As far as capturing from live... I don't believe what I'm trying to do is ever done on live.


For instance:

When a player deletes a bank item, they first move it to their cursor. Then a client-originating moveitem deletes it (from->30: to->-1: num_in_stk->0)

When an item is on the cursor, we can issue a server-originating moveitem to delete, regardless of client action. (from->30: to->-1: num_in_stk->-1 seems to
work and is what is currently used in existing code.)

But there is no existing action (that I'm aware of) that will generate a server-originating direct bank slot deletion on live. All quest updates, tradeskill combines,
etc... come from the client's cursor or personal inventory (and bags), which I have no trouble deleting from.


That's why I don't think there's a way to actually capture this particular value for a direct bank slot delete action.

(I can overwrite the existing item with no problem. But, then we're left with a client that could have 'phantom' items in empty slots and we're back at starting
point for this whole resync business... I do have a method that will clear empty bank slots on the client, but the client will spam itself with 'deletion' messages
for each empty slot, which I'm trying to avoid.)

lerxst2112
10-28-2012, 12:57 AM
If there's no way to legally generate a deletion from a bank slot then why are you worried about it? If it can't happen on live then it shouldn't happen on the emu. If it does, then that's the bug.

Uleat
10-28-2012, 05:29 PM
This whole 'ResyncInv' thing is just a band-aid to keep issues at bay, and eliminate item deletions, until all of the code can be found and corrected that causes
desync's.

I'm pretty sure my work on the Bandolier patch alleviated it as being a source of desync's, but it can still faulter and delete items if the client was already in a
desync'd condition when the player uses it. Of course, any moveitem call would cause that under the same conditions.

Even less supportive: If a player were to switch items around in their bank while desync'd, they may travel half-way around the world and be in the middle of a
raid before they discover that the stack of poison-resist potions they thought they had is actually a stack of rabbit meat.

I'm just trying to ensure that this method actually does what it says, is all.

lerxst2112
10-28-2012, 11:41 PM
Well, if the client does not handle it properly, then you're on a fool's errand.

Uleat
10-30-2012, 03:16 PM
Lol! You have no idea Lerxst!

My past life incarnations include: village idiot, court jester, and dancing fool...

Uleat
11-02-2012, 10:04 PM
The resyncinv method does appear to do what I want it to, so hopefully all that is left is tweaking it.


I'd like to move on to the remaining issues, but I still need some help with the 'Loot All' feature in clients > SoF.

Specifically:

- What OP_Code is used for this action?

- Does the client send a request for each item in the loot window, or just one?

- Does a failure on the server cancel the client action, or does it continue?

- When an character's inventory is 'over-loaded,' does the corpse still have items on it, and then a re-loot shows it empty?


Any help would be appreciated. With some basic behavior knowledge, I might be able to figure out what exactly is wrong with this process.

Thanks!


(sidenote: I know SOE doesn't condone/support eqemu..but I wonder if they could be convinced to allow Steam to sell the UF client again. It wouldn't hurt
their current online offering and it would bring in a little more revenue from an obselete piece of software...)

Uleat
11-05-2012, 12:21 PM
Two quick questions...

First:
Is there something wrong with this? (trying to track down inventory issues)
uint8 ItemInst::FirstOpenSlot() const {

int8 slots=m_item->BagSlots,i;

for(i=0;i<slots;i++) {
if (!GetItem(i))
break;
}

return (i<slots) ? i : 0xff;
}

I though that I had read that assignments were not allowed in multi-declaration statements.

Is that true? Or, was it true in the past?


Second:
Is there a problem with this delegate?
static std::list<type> Class::Function(type arg);

It compiles with no errors, but something I did is crashing zone after I select the character. I can trace the cause by process of elimination, but re-compiling takes
time. If there's a simple answer...


Thanks!

Uleat
11-05-2012, 06:32 PM
Duh! disregard the second question... I forgot to return the function result...

First question still stands though.

Uleat
01-01-2013, 12:11 AM
Ok, I think I have a pretty good handle on where the causes of CSD's occur and don't see a need to keep this thread active any longer.

After I finish up the current projects that I'm working on, and as soon as some consensus is reached on any rework of the inventory system, I will
start pushing the changes to correct these issues.

Please post any desync issues as new threads.

(If an admin wants to lock this thread, they can..it will keep me from rambling on :) )

sorvani
01-01-2013, 12:44 AM
(If an admin wants to lock this thread, they can..it will keep me from rambling on :) )

This is not possible.

Uleat
01-01-2013, 12:49 AM
Hey! Even a monkey gets lucky once in awhile if he hits the keyboard enough times!