Code:
Slot Criteria for ResyncInventory(): (Translated to EQEmulator Titanium-based Definitions - Current as of post revision)
(These values were tested with a SoF client on a Windows 32-bit 'ReleaseBots' build -U)
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|SLOT GROUP: |SLOT RANGE: |UPDATE METHOD: |DELETE METHOD: |(NOTES:) |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|PERSONAL |0 - 21 (& 9999) |REG |REG |(U:REG & D:REG work properly, including 9999 for SoF+ clients) |
|CURSOR |30 |REG |REG |(U:REG & D:REG work properly) |
|INVENTORY |22 - 29 |REG |REG |(U:REG & D:REG work properly) |
|INVENTORY BAG |251 - 330 |REG |ALT |(U:REG & D:REG work properly) |
|CURSOR BAG |331 - 340 |REG |ALT |(U:REG & D:REG work properly) |
|TRIBUTE |400 - 404 |UNK |UNK |(unverified if U:REG works properly, D:REG crashes client) |
|BANK |2000 - 2015 (2023) |REG |HCK |(U:REG works properly, D:REG crashes client, D:HCK allows deletion with spam, through 2023 for SoF+ clients)|
|BANK BAG |2031 - 2190 (2270) |REG |ALT |(U:REG works properly, D:REG crashes client, D:HCK allows deletion with spam, through 2270 for SoF+ clients)|
|SHARED BANK |2500 - 2501 |REG |UNK |(U:REG works properly, D:REG crashes client, D:HCK not effective) |
|SHARED BANK BAG |2531 - 2550 |REG |ALT |(U:REG works properly, D:REG crashes client, D:HCK not effective) |
|TRADER |3000 - 3007 |UNK |UNK |(need testing results) |
|TRADER BAG |3100 - 3179 |UNK |UNK |(need testing results) |
|WORLD CONTAINER |4000 - 4009 |UNK |UNK |(need testing results) |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
UPDATE METHOD DEFINITIONS:
Regular(REG): { const ItemInst* slot_inst = m_inv[slot_id];
SendItemPacket(slot_id, slot_inst, ItemPacketTrade); }
Alternate(ALT): No satisfactory method has been found.
Unknown(UNK): Is an untested process, results cannot be verified, or client does not update screen item
DELETE METHOD DEFINITIONS:
Regular(REG): { const Item_Struct* token_item = database.GetItem(22292); // any non-lore item 'should' work..this one is tested
ItemInst* token_inst = database.CreateItem(token_item, 1);
SendItemPacket(slot_id, token_inst, ItemPacketTrade); // to eliminate empty slot spam, trade something to it first
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); }
Alternate(ALT): Alternate method for addressing ALL non-broken bag slots appears to be best method for updating bags..When parent slot is
overwritten with non-container, client will automatically update contents. Then, a simple re-send of parent and occupied
slots will correct the de-sync.
Hack(HCK): (This is a hack method to trick the client into doing what we can't make it do normally.)
{ const Item_Struct* token_item = database.GetItem(1041); // any lore item 'should' work..this one is tested
ItemInst* token_inst = database.CreateItem(token_item, 1);
SendItemPacket(slot_id, token_inst, ItemPacketTrade); // must be ItemPacketTrade
(send to every bank and bank bag slot that is empty)
SendItemPacket(SLOT_CURSOR, token_inst, ItemPacketTrade); // with the last on cursor, client deletes and spams all others
(now use REG method above to delete the cursor) }
Unknown(UNK): Is an untested process, results cannot be verified, or client does not update screen item.
DESCRIPTION:
The purpose of this method is to correct as many client-server desyncronizations in-game as possible. It should eliminate the need for item
deletions due to desync's, and Kick()'s can be disabled when a player performs an action that is known to cause this condition, unless an
automatic banning process is in effect.
Whenever a MoveItem request is received, it is processed by Client::SwapItem(). When this action fails, the ResyncInventory() method is
called. It can also be called manually by the use of #resyncinv (a self-only command) if a player suspects an issue.
Currently, this method cannot correct every case of a desyncronization. Some ranges are also not addressable due to the way the client handles
certain slots. The Delete: HCK method above is a work-around to provide some solution, though it comes with undesired side-effects. This method
is coded to try to minimize these side-effects. (3rd-party software users may not receive the resync's full benefits.)
The workload is spread across multiple callbacks. This keeps the client active while the resync is updating and helps to eliminate errors due
to client overload. It also allows subsequent triggers to activate properly by interrupting the current process instead of just buffering the
request and performing an entire new cycle. The number of concurrent calls is limited to three at the moment. Once a fourth request is issued,
the process is stopped and the client is kicked to resolve the issue.
Due to reasons unknown, the bank slots cannot be directly deleted. The hack method employed does not correct shared bank slots due to the fact
that they allow duplicate lore items. When trying to use the normal delete method on these slots, the client responds by crashing..an undesired
effect. Changing pBuffer->number_in_stack to a 16-bit -1 (0xFFFF) alleviates the client crash, but serves no purpose as the action still does
not process correctly. Using OP_MoveItem also crashes the client on these slots regardless of value. It's possible that the client can just no
longer handle this request. (I've tested all 16-bit values of number_in_stack in these to_slot ranges: 0xFFF0 - 0x0004, 0x7FFC - 0x8003)
When updating the bank slots, a lore item is sent to each empty one, and then to the cursor. This triggers an action in the client to delete all
duplicate lore items. This will clear only the bank slots and any empty slots in the corresponding bags. The alternate method that I discovered
and implemented recently, is actually more efficient as it corrects ALL bag contents without the additional deletion process. Only the parent
bank or shared banks slots remain an issue. IF any of these slots are unoccupied, the client will still receive a 'deleting lore item from bank'
message around the 85% mark. This is far less than the previous way, which would send them for not only bank and shared bank slots, but also for
all empty bank and shared bank bag slots. These messages can be minimized/eliminated by ensuring that all bank/shared bank slots are occupied.
Until all of the bugs can be tracked down and fixed, zoning or re-logging are the only 100% ways to assure server-client syncronization. This
method only serves as an intermediate solution.
TODO:
1) Find and/or verify all client limits (SoF: 1 active, 36-depth list (37 total); server cursor depths: {0..36} )
2) Adjust 'delay_modifier' before 'ri_CallBackDelay' in incremental steps. The whole process probably needs tweaking for 'live' servers
(If you get 'CLIENT MOVE ITEM FAIL' errors, adjust the 'delay_modifier' up and retest. Continue to do this until client messages disappear)
3) Consider making 'delay_modifier' into rule, but only if it needs to be tweaked frequently