THIS IS A BETA PATCH. CLIENT TESTING IS STILL NEEDED
The previous post is referenced in the patch to eliminate loading up the code.
For now, this is the best I can do. Players, 'use #resyncinv help' for a quick description of what to expect.
It probably still needs minor timing tweaking, but here it is:
[ResyncInv.patch] (9 files, 627 lines)
Code:
Index: common/Item.cpp
===================================================================
--- common/Item.cpp (revision 2244)
+++ common/Item.cpp (working copy)
@@ -668,22 +668,20 @@
}
// Swap items in inventory
-void Inventory::SwapItem(sint16 slot_a, sint16 slot_b)
-{
- // Temp holding area for a
+bool Inventory::SwapItem(sint16 slot_a, sint16 slot_b)
+{ // Modified to fail all item swaps into illegal slots..'return false' will trigger resync -U
+
+ // Temp holding areas for a & b
ItemInst* inst_a = GetItem(slot_a);
+ ItemInst* inst_b = GetItem(slot_b);
- if(inst_a)
- {
- if(!inst_a->IsSlotAllowed(slot_b))
- return;
- }
+ if(inst_a) { if(!inst_a->IsSlotAllowed(slot_b)) { return false; } }
+ if(inst_b) { if(!inst_b->IsSlotAllowed(slot_a)) { return false; } }
- // Copy b->a
- _PutItem(slot_a, GetItem(slot_b));
-
- // Copy a->b
- _PutItem(slot_b, inst_a);
+ _PutItem(slot_a, inst_b); // Copy b->a
+ _PutItem(slot_b, inst_a); // Copy a->b
+
+ return true;
}
// Checks that user has at least 'quantity' number of items in a given inventory slot
Index: common/Item.h
===================================================================
--- common/Item.h (revision 2244)
+++ common/Item.h (working copy)
@@ -144,7 +144,8 @@
inline iter_queue cursor_begin() { return m_cursor.begin(); }
inline iter_queue cursor_end() { return m_cursor.end(); }
inline bool CursorEmpty() { return (m_cursor.size() == 0); }
-
+ inline int CursorSize() { return m_cursor.size(); }
+
// Retrieve a read-only item from inventory
inline const ItemInst* operator[](sint16 slot_id) const { return GetItem(slot_id); }
@@ -154,8 +155,8 @@
// Add item to cursor queue
sint16 PushCursor(const ItemInst& inst);
- // Swap items in inventory
- void SwapItem(sint16 slot_a, sint16 slot_b);
+ // Swap items in inventory (Changed to bool return function -U)
+ bool SwapItem(sint16 slot_a, sint16 slot_b);
// Remove item from inventory
bool DeleteItem(sint16 slot_id, uint8 quantity=0);
Index: zone/client.cpp
===================================================================
--- zone/client.cpp (revision 2244)
+++ zone/client.cpp (working copy)
@@ -324,6 +324,14 @@
}
MaxXTargets = 5;
XTargetAutoAddHaters = true;
+
+ // ResyncInventory() Initialization Stuff
+ ri_CallBackTimer = new Timer(0);
+ ri_Count = 0;
+ ri_Step = 0;
+ ri_CursorCount = 0;
+ ri_CallBackDelay = 0;
+
}
Client::~Client() {
@@ -406,6 +414,7 @@
safe_delete(taskstate);
safe_delete(KarmaUpdateTimer);
safe_delete(GlobalChatLimiterTimer);
+ safe_delete(ri_CallBackTimer); // ResyncInventory() Stuff
safe_delete(qGlobals);
safe_delete(adventure_request_timer);
safe_delete(adventure_create_timer);
Index: zone/client.h
===================================================================
--- zone/client.h (revision 2244)
+++ zone/client.h (working copy)
@@ -761,6 +761,7 @@
bool PushItemOnCursor(const ItemInst& inst, bool client_update = false);
void DeleteItemInInventory(sint16 slot_id, sint8 quantity = 0, bool client_update = false, bool update_db = true);
bool SwapItem(MoveItem_Struct* move_in);
+ void ResyncInventory(bool server_call = true, bool timer_callback = false);
void PutLootInInventory(sint16 slot_id, const ItemInst &inst, ServerLootItem_Struct** bag_item_data = 0);
bool AutoPutLootInInventory(ItemInst& inst, bool try_worn = false, bool try_cursor = true, ServerLootItem_Struct** bag_item_data = 0);
void SummonItem(uint32 item_id, sint16 charges = 0, uint32 aug1=0, uint32 aug2=0, uint32 aug3=0, uint32 aug4=0, uint32 aug5=0, bool attuned=false, uint16 to_slot=SLOT_CURSOR);
@@ -1397,6 +1398,25 @@
struct XTarget_Struct XTargets[XTARGET_HARDCAP];
+ // ResyncInventory() Private Stuff
+ Timer *ri_CallBackTimer;
+ void ResyncInvCallBack();
+ void ResyncInvProcSlots(sint16 slot_begin, sint16 slot_end, int8 sync_method = 128);
+ void ResyncInvClDelItem(sint16 slot_id, bool alt_delete = false);
+ int8 ri_Count;
+ int8 ri_Step;
+ int8 ri_CursorCount;
+ int32 ri_CallBackDelay;
+ // float ri_CallBackDelayModifier = ??
+#define RI_COUNT_LIMIT 3
+#define RI_STEP_COUNT 20
+#define RI_CURSOR_LIMIT 37
+#define RI_BIT_AU 1 // Alternate Update
+#define RI_BIT_UN 2 // Update Null
+#define RI_BIT_AD 4 // Alternate Delete
+#define RI_BIT_CP 8 // Check Parent
+#define RI_BIT_BROKEN 128
+
};
#include "parser.h"
Index: zone/client_packet.cpp
===================================================================
--- zone/client_packet.cpp (revision 2244)
+++ zone/client_packet.cpp (working copy)
@@ -3306,62 +3306,51 @@
casting_spell_id);
database.SetMQDetectionFlag(AccountName(), GetName(), detect, zone->GetShortName());
safe_delete_array(detect);
+ // This Kick() could be deleted unless an automatic ban is in use. (Unremark the code below) -U
Kick(); // Kick client to prevent client and server from getting out-of-sync inventory slots
+ //Message(13, "Warning: You have attempted an item move while casting a spell - this action is not allowed!");
+ //ResyncInventory();
return;
}
}
- // Added checks for illegal bagslot swaps..should help with certain cheats (currently checks personal and cursor bag slots.)
- // - If a player has used an illegal inventory cheat, they can become bugged at some point (especially concerning lore items.)
- //* Start
+ // Illegal bagslot useage checks. Currently, user only receives a message if this check is triggered. -U
bool mi_hack = false;
if (mi->from_slot >= 251 && mi->from_slot <= 340) {
- if (mi->from_slot > 330)
- mi_hack = true; // why are we moving from a cursor bagslot when you can't open it?
+ if (mi->from_slot > 330) { mi_hack = true; } // move << cursor bagslot
else {
- sint16 from_invslot = Inventory::CalcSlotId(mi->from_slot);
- const ItemInst *from_invslotitem = GetInv().GetItem(from_invslot);
+ sint16 from_parent = m_inv.CalcSlotId(mi->from_slot);
- if (!from_invslotitem) // trying to move from bag slots when parent inventory slot is empty
- mi_hack = true;
- else if (from_invslotitem->GetItem()->ItemClass == 1) { // checking the parent inventory slot for container
- if ((Inventory::CalcBagIdx(mi->from_slot) + 1) > from_invslotitem->GetItem()->BagSlots)
- mi_hack = 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
- mi_hack = true;
+ if (!m_inv[from_parent]) { mi_hack = true; } // move << empty parent
+ else if (m_inv[from_parent]->GetItem()->ItemClass != 1) { mi_hack = true; } // move << non-container parent
+ else if((m_inv.CalcBagIdx(mi->from_slot) + 1) > m_inv[from_parent]->GetItem()->BagSlots) { mi_hack = true; }// move << beyond container size
}
}
if (mi->to_slot >= 251 && mi->to_slot <= 340) {
- if (mi->to_slot > 330)
- mi_hack = true; // why are we moving to a cursor bagslot when you can't open it?
- else {
- sint16 to_invslot = Inventory::CalcSlotId(mi->to_slot);
- const ItemInst *to_invslotitem = GetInv().GetItem(to_invslot);
+ if (mi->to_slot > 330) { mi_hack = true; } // move >> cursor bagslot
+ else {
+ sint16 to_parent = m_inv.CalcSlotId(mi->to_slot);
- if (!to_invslotitem) // trying to move into bag slots when parent inventory slot is empty
- mi_hack = true;
- else if (to_invslotitem->GetItem()->ItemClass == 1) { // checking the parent inventory slot for container
- if ((Inventory::CalcBagIdx(mi->to_slot) + 1) > to_invslotitem->GetItem()->BagSlots)
- mi_hack = 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
- mi_hack = true;
+ if (!m_inv[to_parent]) { mi_hack = true; } // move >> empty parent
+ else if (m_inv[to_parent]->GetItem()->ItemClass != 1) { mi_hack = true; } // move >> non-container parent
+ else if((m_inv.CalcBagIdx(mi->to_slot) + 1) > m_inv[to_parent]->GetItem()->BagSlots) { mi_hack = true; }// move >> beyond container size
}
}
- if (mi_hack) { // a CSD can also cause this condition, but more likely the use of a cheat
- Message(13, "Hack detected: Illegal use of inventory bag slots!");
- // TODO: Decide whether to log player as hacker - currently has no teeth...
- // Kick();
- // return;
- } // End */
+ if (mi_hack) { Message(15, "Caution: Illegal use of inaccessable bag slots!"); }
- // if this swapitem call fails, then the server and client could be de-sync'd
- if (!SwapItem(mi) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot))
- Message(0, "Client SwapItem request failure - verify inventory integrity.");
+ if (!SwapItem(mi) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) {
+ Message(13, "Client OP_MoveItem request error!");
+ ResyncInventory();
+
+ if(mi_hack) {
+ Message(15, "Caution: If you use 3rd-party software to manipulate slots,");
+ Message(15, "this action may or may not correct problems in these areas.");
+ Message(15, "To ensure inventory syncronization, you must zone or relog.");
+ }
+ }
return;
}
Index: zone/client_process.cpp
===================================================================
--- zone/client_process.cpp (revision 2244)
+++ zone/client_process.cpp (working copy)
@@ -746,6 +746,9 @@
Message(0,"Your enemies have forgotten you!");
}
+ // ResyncInventory() callback timer check
+ if(ri_CallBackTimer && ri_CallBackTimer->Enabled() && ri_CallBackTimer->Check(false)) { ResyncInvCallBack(); }
+
return ret;
}
Index: zone/command.cpp
===================================================================
--- zone/command.cpp (revision 2244)
+++ zone/command.cpp (working copy)
@@ -459,7 +459,8 @@
command_add("xtargets", "Show your targets Extended Targets and optionally set how many xtargets they can have.", 250, command_xtargets) ||
command_add("printquestitems","Returns available quest items for multiquesting currently on the target npc.",200,command_printquestitems) ||
command_add("clearquestitems","Clears quest items for multiquesting currently on the target npc.",200,command_clearquestitems) ||
- command_add("zopp", "Troubleshooting command - Sends a fake item packet to you. No server reference is created.", 250, command_zopp)
+ command_add("zopp", "Troubleshooting command - Sends a fake item packet to you. No server reference is created.", 250, command_zopp) ||
+ command_add("resyncinv", "Client inventory resyncronation command. Use when you suspect an issue with your inventory.", 0, command_resyncinv)
)
{
command_deinit();
@@ -11701,7 +11702,24 @@
ItemInst* FakeItemInst = database.CreateItem(FakeItem, charges);
c->SendItemPacket(slotid, FakeItemInst, packettype);
- c->Message(0, "Sending zephyr op packet to client - [%s] %s (%u) with %i %s to slot %i.", packettype==ItemPacketTrade?"Trade":"Summon", FakeItem->Name, itemid, charges, abs(charges==1)?"charge":"charges", slotid);
+ c->Message(0, "Sending zephyr op packet to client - [%s] %s (%u), quantity: %i, to slot %i.", packettype==ItemPacketTrade?"Trade":"Summon", FakeItem->Name, itemid, charges, slotid);
safe_delete(FakeItemInst);
}
}
+
+void command_resyncinv(Client *c, const Seperator *sep) {
+ // This command can be set to GM status once all of the desync issues are found and corrected -U
+
+ if(!c) { return; }
+ else if(sep->argnum==1 && strcasecmp(sep->arg[1], "help") == 0) {
+ c->Message(0, "This command will attempt to resyncronize your inventory with the server.");
+ c->Message(0, "(Any empty bank slots will return 'lore deletion' messages for each slot)");
+ c->Message(0, "(A 'Worthless Coin' will be left in any empty shared bank slots until you");
+ c->Message(0, "zone or re-log. Clicking this coin will result in another resyncinv call)");
+ }
+ else if(sep->argnum > 0) { c->Message(15, "Usage: #resyncinv [help]"); }
+ else {
+ c->Message(15, "Player initiated inventory resyncronization:");
+ c->ResyncInventory(false);
+ }
+}
Index: zone/command.h
===================================================================
--- zone/command.h (revision 2244)
+++ zone/command.h (working copy)
@@ -323,6 +323,7 @@
void command_printquestitems(Client *c, const Seperator *sep);
void command_clearquestitems(Client *c, const Seperator *sep);
void command_zopp(Client *c, const Seperator *sep);
+void command_resyncinv(Client *c, const Seperator *sep);
#ifdef EMBPERL
void command_embperl_plugin(Client *c, const Seperator *sep);
Index: zone/inventory.cpp
===================================================================
--- zone/inventory.cpp (revision 2244)
+++ zone/inventory.cpp (working copy)
@@ -965,6 +965,7 @@
banker ? banker->GetName() : "UNKNOWN NPC", distance);
database.SetMQDetectionFlag(AccountName(), GetName(), hacked_string, zone->GetShortName());
safe_delete_array(hacked_string);
+ // This Kick() could be deleted unless an automatic ban is in use. A resyncinv is triggered on the 'return false;' -U
Kick(); // Kicking player to avoid item loss do to client and server inventories not being sync'd
return false;
}
@@ -1020,12 +1021,16 @@
move_in->from_slot = dst_slot_check;
move_in->to_slot = src_slot_check;
move_in->number_in_stack = dst_inst->GetCharges();
- if (!SwapItem(move_in)) // shouldn't fail because call wouldn't exist otherwise, but just in case...
- this->Message(13, "Error: Internal SwapItem call returned a failure!");
+ if (!SwapItem(move_in)) {
+#if (EQDEBUG>= 5)
+ LogFile->write(EQEMuLog::Debug, "Client::SwapItem() %s's recursive swapitem call resulted in an unknown failure.", GetName());
+#endif
+ }
}
- Message(13, "Error: Server found no item in slot %i (->%i), Deleting Item!", src_slot_id, dst_slot_id);
- LogFile->write(EQEMuLog::Debug, "Error: Server found no item in slot %i (->%i), Deleting Item!", src_slot_id, dst_slot_id);
- this->DeleteItemInInventory(dst_slot_id,0,true);
+ // This code is no longer needed :) -U
+ //Message(13, "Error: Server found no item in slot %i (->%i), Deleting Item!", src_slot_id, dst_slot_id);
+ //LogFile->write(EQEMuLog::Debug, "Error: Server found no item in slot %i (->%i), Deleting Item!", src_slot_id, dst_slot_id);
+ //this->DeleteItemInInventory(dst_slot_id,0,true);
return false;
}
//verify shared bank transactions in the database
@@ -1210,9 +1215,9 @@
else {
// Nothing in destination slot: split stack into two
if ((sint16)move_in->number_in_stack >= src_inst->GetCharges()) {
+ // Move entire stack
+ if(!m_inv.SwapItem(src_slot_id, dst_slot_id)) { return false; }
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);
}
else {
// Split into two
@@ -1232,8 +1237,8 @@
}
SetMaterial(dst_slot_id,src_inst->GetItem()->ID);
}
+ if(!m_inv.SwapItem(src_slot_id, dst_slot_id)) { return false; }
mlog(INVENTORY__SLOTS, "Moving entire item from slot %d to slot %d", src_slot_id, dst_slot_id);
- m_inv.SwapItem(src_slot_id, dst_slot_id);
}
int matslot = SlotConvert2(dst_slot_id);
@@ -1258,6 +1263,282 @@
return true;
}
+/*
+ This method is used to syncronize client and server inventories when a swapitem request fails.
+ I believe it works as well as it's going to, given the restrictions imposed upon us by the client -U
+
+ See this post for more information:
+ http://www.eqemulator.org/forums/showpost.php?p=213911&postcount=7
+*/
+void Client::ResyncInventory(bool server_call, bool timer_callback) {
+
+ float delay_modifier = 1.00; // adjust (1/4-point increments) to eliminate timing errors (consider making a rule)
+
+ if(!timer_callback) { /* DO INITIAL CALL WORK */
+ if(!ri_Count) {
+ Message(15, "Attempting to resyncronize %s's inventory - Please stand-by...", GetName());
+ Message(15, "(Do not use your inventory until resyncronization is complete.)");
+
+#if (EQDEBUG>=5)
+ if(server_call) { LogFile->write(EQEMuLog::Debug, "Client::ResyncInventory() server failure of swapitem called for resync of %s's inventory", GetName()); }
+#endif
+ }
+ // Close all containers - if possible
+
+ ri_CallBackTimer->Disable();
+ ri_Count += 1;
+
+ if(ri_Count > RI_COUNT_LIMIT) {
+ ri_Count = 0;
+ ri_Step = 0;
+ ri_CursorCount = 0;
+ ri_CallBackDelay = 0;
+
+ Kick();
+ return;
+ }
+ else if(ri_Count > 1) {
+ Message(13, "Warning: Do not perform any actions until resyncronization completes!");
+ ri_CursorCount = RI_CURSOR_LIMIT;
+ }
+ else {
+ if(m_inv.CursorSize() < RI_CURSOR_LIMIT) { ri_CursorCount = m_inv.CursorSize() + 1; }
+ else { ri_CursorCount = RI_CURSOR_LIMIT; }
+ }
+
+ ri_Step = RI_STEP_COUNT;
+ ri_CallBackDelay = static_cast<int32>(250 * delay_modifier); // Step 1 callback delay (timing critical for proper operation)
+
+ ri_CallBackTimer->Start(ri_CallBackDelay);
+
+ return;
+ }
+
+ switch(ri_Step) { /* PARSE WORK TO STEPS FOR EACH CALLBACK */
+ case RI_STEP_COUNT: { // Delete Client CURSOR (Step 1)
+ ResyncInvClDelItem(SLOT_CURSOR);
+
+ return;
+ }
+ case 19: { // Sync WORN & PERSONAL (Step 2)
+ ResyncInvProcSlots(0, 21, RI_BIT_UN);
+ if(GetClientVersion() >= EQClientSoF) { ResyncInvProcSlots(9999, 9999, RI_BIT_UN); }
+ ResyncInvProcSlots(22, 29, RI_BIT_UN);
+
+ ri_CallBackDelay = static_cast<int32>(500 * delay_modifier); // Step 3 callback delay
+ return;
+ }
+ case 18: { // Sync PERSONAL BAGS (Step 3) [First 40 slots]
+ ResyncInvProcSlots(251, 291, (RI_BIT_AU | RI_BIT_UN | RI_BIT_CP));
+
+ ri_CallBackDelay = static_cast<int32>(500 * delay_modifier); // Step 4 callback delay
+ return;
+ }
+ case 17: { // Sync PERSONAL BAGS (Step 4) [Second 40 slots]
+ ResyncInvProcSlots(291, 330, (RI_BIT_AU | RI_BIT_UN | RI_BIT_CP));
+
+ ri_CallBackDelay = static_cast<int32>(500 * delay_modifier); // Step 5 callback delay
+ return;
+ }
+ case 16: { // Sync TRIBUTE (Step 5) /* BROKEN */
+ ResyncInvProcSlots(400, 404, RI_BIT_BROKEN /* (RI_BIT_AU | RI_BIT_UN | RI_BIT_AD | RI_BIT_CP) */);
+
+ ri_CallBackDelay = static_cast<int32>(1 /* 1000 working */ * delay_modifier); // Step 6 callback delay
+ return;
+ }
+ case 15: { // Sync BANK (Step 6)
+ if(GetClientVersion() >= EQClientSoF) { ResyncInvProcSlots(2000, 2023, (RI_BIT_UN | RI_BIT_AD)); }
+ else { ResyncInvProcSlots(2000, 2015, (RI_BIT_UN | RI_BIT_AD)); }
+
+ ri_CallBackDelay = static_cast<int32>(1000 * delay_modifier); // Step 7 callback delay
+ return;
+ }
+ case 14: { // Sync BANK BAGS (Step 7) [SoF+ & Ti First 40 slots]
+ if(GetClientVersion() >= EQClientSoF) { ResyncInvProcSlots(2031, 2110, (RI_BIT_AU | RI_BIT_UN | RI_BIT_AD | RI_BIT_CP)); }
+ else { ResyncInvProcSlots(2031, 2070, (RI_BIT_AU | RI_BIT_UN | RI_BIT_AD | RI_BIT_CP)); }
+
+ ri_CallBackDelay = static_cast<int32>(500 * delay_modifier); // Step 8 callback delay
+ return;
+ }
+ case 13: { // Sync BANK BAGS (Step 8) [SoF+ & Ti Second 40 slots]
+ if(GetClientVersion() >= EQClientSoF) { ResyncInvProcSlots(2031, 2110, (RI_BIT_AU | RI_BIT_UN | RI_BIT_AD | RI_BIT_CP)); }
+ else { ResyncInvProcSlots(2071, 2110, (RI_BIT_AU | RI_BIT_UN | RI_BIT_AD | RI_BIT_CP)); }
+
+ ri_CallBackDelay = static_cast<int32>(500 * delay_modifier); // Step 9 callback delay
+ return;
+ }
+ case 12: { // Sync BANK BAGS (Step 9) [SoF+ & Ti Third 40 slots]
+ if(GetClientVersion() >= EQClientSoF) { ResyncInvProcSlots(2111, 2190, (RI_BIT_AU | RI_BIT_UN | RI_BIT_AD | RI_BIT_CP)); }
+ else { ResyncInvProcSlots(2111, 2150, (RI_BIT_AU | RI_BIT_UN | RI_BIT_AD | RI_BIT_CP)); }
+
+ ri_CallBackDelay = static_cast<int32>(500 * delay_modifier); // Step 10 callback delay
+ return;
+ }
+ case 11: { // Sync BANK BAGS (Step 10) [SoF+ & Ti Fourth 40 slots]
+ if(GetClientVersion() >= EQClientSoF) { ResyncInvProcSlots(2111, 2190, (RI_BIT_AU | RI_BIT_UN | RI_BIT_AD | RI_BIT_CP)); }
+ else { ResyncInvProcSlots(2151, 2190, (RI_BIT_AU | RI_BIT_UN | RI_BIT_AD | RI_BIT_CP)); }
+
+ ri_CallBackDelay = static_cast<int32>(500 * delay_modifier); // Step 11 callback delay
+ return;
+ }
+ case 10: { // Sync BANK BAGS (Step 11) [SoF+ Fifth 40 slots]
+ if(GetClientVersion() >= EQClientSoF) { ResyncInvProcSlots(2191, 2230, (RI_BIT_AU | RI_BIT_UN | RI_BIT_AD | RI_BIT_CP)); }
+
+ ri_CallBackDelay = static_cast<int32>(500 * delay_modifier); // Step 12 callback delay
+ return;
+ }
+ case 9: { // Sync BANK BAGS (Step 12) [SoF+ Sixth 40 slots]
+ if(GetClientVersion() >= EQClientSoF) { ResyncInvProcSlots(2231, 2270, (RI_BIT_AU | RI_BIT_UN | RI_BIT_AD | RI_BIT_CP)); }
+
+ ri_CallBackDelay = static_cast<int32>(500 * delay_modifier); // Step 13 callback delay
+ return;
+ }
+ case 8: { // Sync SHARED BANK & SHARED BANK BAGS (Step 13)
+ ResyncInvProcSlots(2500, 2501, (RI_BIT_UN | RI_BIT_AD));
+ ResyncInvProcSlots(2531, 2550, (RI_BIT_AU | RI_BIT_UN | RI_BIT_AD | RI_BIT_CP));
+
+ ri_CallBackDelay = static_cast<int32>(1000 * delay_modifier); // Step 14 callback delay
+ return;
+ }
+ case 7: { // Sync TRADER (Step 14) /* BROKEN */
+ ResyncInvProcSlots(3000, 3007, RI_BIT_BROKEN /* (RI_BIT_AU | RI_BIT_UN | RI_BIT_AD | RI_BIT_CP) */);
+
+ ri_CallBackDelay = static_cast<int32>(1 /* 1000 working */ * delay_modifier); // Step 15 callback delay
+ return;
+ }
+ case 6: { // Sync TRADER BAGS (Step 15) [First 40 Slots] /* BROKEN */
+ ResyncInvProcSlots(3100, 3139, RI_BIT_BROKEN /* (RI_BIT_AU | RI_BIT_UN | RI_BIT_AD | RI_BIT_CP) */);
+
+ ri_CallBackDelay = static_cast<int32>(1 /* 500 working */ * delay_modifier); // Step 16 callback delay
+ return;
+ }
+ case 5: { // Sync TRADER BAGS (Step 16) [Second 40 slots] /* BROKEN */
+ ResyncInvProcSlots(3140, 3179, RI_BIT_BROKEN /* (RI_BIT_AU | RI_BIT_UN | RI_BIT_AD | RI_BIT_CP) */);
+
+ ri_CallBackDelay = static_cast<int32>(1 /* 500 working */ * delay_modifier); // Step 17 callback delay
+ return;
+ }
+ case 4: { // Sync WORLD CONTAINER (Step 17) /* BROKEN */
+ ResyncInvProcSlots(4000, 4009, RI_BIT_BROKEN /* (RI_BIT_AU | RI_BIT_UN | RI_BIT_AD | RI_BIT_CP) */);
+
+ ri_CallBackDelay = static_cast<int32>(1 /* 1000 working */ * delay_modifier); // Step 18 callback delay
+ return;
+ }
+ case 3: { // Delete Client CURSOR (Step 18)
+ ResyncInvClDelItem(SLOT_CURSOR, true); // hack for Bank Slots
+
+ ri_CallBackDelay = static_cast<int32>(1500 * delay_modifier); // Step 19 callback delay
+ return;
+ }
+ case 2: { // Sync Client CURSOR (Step 19)
+ ResyncInvClDelItem(SLOT_CURSOR); // hack for Bank Slots
+
+ ri_CursorCount = 1;
+ for(std::list<ItemInst*>::const_iterator cursor_iter = m_inv.cursor_begin(); cursor_iter != m_inv.cursor_end() && ri_CursorCount <= RI_CURSOR_LIMIT;
+ cursor_iter++, ri_CursorCount++) { SendItemPacket(SLOT_CURSOR, *cursor_iter, ItemPacketSummonItem); }
+
+ if(ri_Count > 1) { ri_CallBackDelay = static_cast<int32>(4000 * delay_modifier); } // Step 20 callback delay (full) }
+ else { ri_CallBackDelay = static_cast<int32>(2000 * delay_modifier); } // Step 20 callback delay (normal)
+ return;
+ }
+ case 1: { // Sync Cursor Bags (Step 20)
+ ResyncInvProcSlots(331, 340, (RI_BIT_AU | RI_BIT_UN | RI_BIT_CP));
+
+ ri_CallBackDelay = static_cast<int32>(100 * delay_modifier); // Finalization callback delay
+ return;
+ }
+ case 0: { // Finalization
+ if (ri_CursorCount > RI_CURSOR_LIMIT) {
+ Message(15, "Caution: The server's cursor count exceeds client capacity.");
+ Message(15, "Remove all cursor items and resycinv again to correct this.");
+ }
+
+ ri_Count = 0;
+ ri_Step = 0;
+ ri_CursorCount = 0;
+ ri_CallBackDelay = 0;
+
+ Message(14, "%s's inventory resyncronization complete.", GetName());
+ Message(0, "(Close any container/trade windows that were open before using inventory) ");
+ return;
+ }
+ default: { // (somehow, 'ResyncInvStep' was set above the number of coded steps...)
+#if (EQDEBUG>=5)
+ LogFile->write(EQEMuLog::Debug, "Client::ResyncInventory() resyncinv for %s's inventory tried to process non-existent step %i", GetName(), ri_Step);
+#endif
+
+ ri_Count = 0;
+ ri_Step = 0;
+ ri_CursorCount = 0;
+ ri_CallBackDelay = 0;
+
+ Kick();
+ return;
+ }
+ }
+}
+
+void Client::ResyncInvCallBack() {
+
+ int8 step_percent = ((static_cast<float>(RI_STEP_COUNT - ri_Step) / RI_STEP_COUNT) * 100);
+ if(step_percent) { Message(1, "Resycronization %i percent complete...", step_percent); }
+
+ ri_CallBackTimer->Disable();
+ ResyncInventory(false, true);
+
+ if(ri_CursorCount && ri_Step == RI_STEP_COUNT) { ri_CursorCount -= 1; }
+ else if(ri_Step) { ri_Step -= 1; }
+
+ if(ri_CallBackDelay) { ri_CallBackTimer->Start(ri_CallBackDelay); }
+}
+
+void Client::ResyncInvProcSlots(sint16 slot_begin, sint16 slot_end, int8 sync_method) {
+
+ if(sync_method & RI_BIT_BROKEN) { return; }
+
+ for(sint16 slot_id = slot_begin; slot_id <= slot_end; slot_id++) {
+ const ItemInst* slot_inst = m_inv[slot_id];
+
+ if(sync_method & RI_BIT_CP) {
+ sint16 parent_slot = m_inv.CalcSlotId(slot_id);
+
+ if(parent_slot != SLOT_INVALID && m_inv[parent_slot] && m_inv[parent_slot]->GetItem()->ItemClass == 1) {
+ if(slot_inst) { SendItemPacket(slot_id, slot_inst, ItemPacketTrade); }
+ else { if(!(sync_method & RI_BIT_AU) && (sync_method & RI_BIT_UN)) { ResyncInvClDelItem(slot_id, (sync_method & RI_BIT_AD)); } }
+ }
+ }
+ else {
+ if(slot_inst) { SendItemPacket(slot_id, slot_inst, ItemPacketTrade); }
+ else { if(!(sync_method & RI_BIT_AU) && (sync_method & RI_BIT_UN)) { ResyncInvClDelItem(slot_id, (sync_method & RI_BIT_AD)); } }
+ }
+ }
+}
+
+void Client::ResyncInvClDelItem(sint16 slot_id, bool alt_delete) {
+
+ if(alt_delete) {
+ const Item_Struct* ri_TokenStruct = database.GetItem(1041); // 'Worthless Coin'
+ ItemInst* ri_TokenInst = database.CreateItem(ri_TokenStruct, 1);
+
+ SendItemPacket(slot_id, ri_TokenInst, ItemPacketTrade);
+ }
+ else {
+ const Item_Struct* ri_TokenStruct = database.GetItem(22292); // 'Copper Coin'
+ ItemInst* ri_TokenInst = database.CreateItem(ri_TokenStruct, 1);
+
+ SendItemPacket(slot_id, ri_TokenInst, ItemPacketTrade);
+
+ EQApplicationPacket* outapp = new EQApplicationPacket(OP_DeleteItem, sizeof(DeleteItem_Struct));
+ DeleteItem_Struct* delete_slot = (DeleteItem_Struct*)outapp->pBuffer;
+ delete_slot->from_slot = slot_id;
+ delete_slot->to_slot = 0xFFFFFFFF;
+ delete_slot->number_in_stack = 0xFFFFFFFF;
+
+ QueuePacket(outapp);
+ safe_delete(outapp);
+ }
+}
+
void Client::DyeArmor(DyeStruct* dye){
sint16 slot=0;
for(int i=0;i<7;i++){
__________________
Uleat of Bertoxxulous
Compilin' Dirty
|