Updating item stats whilst bypassing the update queue.

This forum is for discussions on how to edit what can not be edited through the txt files, needless to say this isn't about battle net hacking.

Moderators: Nefarius, Havvoric

Post Reply
User avatar
Nefarius
Retired Admin
Cherub
Posts: 11607
Joined: Sat Jun 15, 2002 8:13 pm
Location: Where the blood forever rains
Contact:

Hand-picked

Updating item stats whilst bypassing the update queue.

Post by Nefarius » Thu Jun 16, 2016 1:05 am

Due to the nature in which item updates are dispatched the game's normal queue can't be used to send stat updates reliably (any other ITEMCMD will override the reload command). It is more reliable to send them separate from their queue. I decided to share this because over the years there've been a plethora of posts about item stat update dilemmas. It should be noted that past approaches to do this that involve expanding the durability/quantity updater to permit adding stats to extra statlists should be avoided, that is known to cause cryptic sync problems in fringe cases because it was never meant to be used for those tasks.

This isn't an easy fix and you'll have to piece together the individual snippets on your own, but if you're messing with realtime stat updates already and aren't in over your head this shouldn't be too difficult to do.

Code: Select all

BOOL __stdcall SITEM_Refresh(D2Game* pGame, D2Unit* pUnit, D2Unit* pItem)
{
    // WRITTEN 2014.APR.26 (by Nefarius)

    ASSERT(pGame);

    if (!UNIT_IS_PLAYER_OR_MONSTER(pUnit))
        return FALSE;

    if (!UNIT_IS_ITEM(pItem))
        return FALSE;

    SendUpdateUnitsItemMessageParams tParams;
    tParams.eMessage    = ITEMUPDATEMESSAGE_REFRESH;
    tParams.dwMask      = ITEMFLAG_RELOAD;
    tParams.pUnit       = pUnit;
    tParams.pItem       = pItem;
    SUNIT_WalkPlayerList(pGame, SITEM_CB_SendUpdateUnitsItemMessage, &tParams);

    SITEM_UpdateEquipment(pGame, pUnit, FALSE);
    return TRUE;
}

Code: Select all

struct SendUpdateUnitsItemMessageParams
{
    int eMessage;
    DWORD dwMask;
    D2Unit* pUnit;
    D2Unit* pItem;
};

void __fastcall SITEM_CB_SendUpdateUnitsItemMessage(D2Game* pGame, D2Unit* pPlayer, void* hParams)
{
    // WRITTEN 2014.MAR.10 (by Nefarius)
    // CHANGED 2014.APR.27 (by Nefarius) - It's possible to send all item update messages with this now.

    SendUpdateUnitsItemMessageParams* pParams;
    pParams = (SendUpdateUnitsItemMessageParams*)hParams;

    D2Client* hClient;
    ASSERT(hClient = SPLAYER_INL_GetClient(pPlayer));
    SITEM_SendUpdateUnitsItemMessage(hClient, pParams->pUnit, pParams->pItem, pParams->dwMask, pParams->eMessage, FALSE);
}

Code: Select all

void __stdcall SITEM_SendUpdateUnitsItemMessage(D2Client* hClient, D2Unit* pUnit, D2Unit* pItem, DWORD dwUpdateMask, BYTE eUpdateMessage, BOOL bItemIsInsideGambleInventory)
{
    // REVISED 2014.FEB.08 (by Nefarius)

    ASSERT(hClient);

    if (pUnit == NULL)
        return;

    if (!UNIT_IS_ITEM(pItem))
        return;

    D2ItemData* pItemData;
    ASSERT(pItemData = pItem->pItemData);

    SMessageUpdateUnitsItem tMessage;
    MEMSET(&tMessage, 0, sizeof(tMessage));

    tMessage.bID            = SMESSAGE_UPDATEUNITSITEM;
    tMessage.bType          = eUpdateMessage;
    tMessage.bComponent     = INV_GetComponent(pUnit, pItem);
    tMessage.dwItemID       = pItem->dwID;
    tMessage.eOwnerType     = pUnit->eType;
    tMessage.dwOwnerID      = pUnit->dwID;

    DWORD dwPreviousMask = pItemData->dwItemFlags;
    pItemData->dwItemFlags |= dwUpdateMask;
    DWORD dwMessageSize = D2I_Serialize(pItem, tMessage.bStream, sizeof(tMessage.bStream), FALSE, FALSE, bItemIsInsideGambleInventory);
    dwMessageSize += sizeof(SMessageUpdateUnitsItemHeader);
    ASSERT(dwMessageSize <= MAXIMUM_BYTES_PER_ITEM_MESSAGE);
    tMessage.bSize = (BYTE)dwMessageSize;
    pItemData->dwItemFlags = dwPreviousMask;

    SNET_SendMessage(hClient, &tMessage, dwMessageSize);
    SITEM_PRV_SendSocketContents(hClient, pItem, dwUpdateMask);
}

static void SITEM_PRV_SendSocketContents(D2Client* hClient, D2Unit* pItem, DWORD dwUpdateMask)
{
    // REVISED 2014.FEB.08 (by Nefarius)
    
    if (dwUpdateMask & ITEMFLAG_REMOVED)
        return;
    
    if (!ITEMS_HasInventory(pItem))
        return;
    
    D2Inventory* hInventory = pItem->hInventory;
    if (!IS_ACCESSIBLE_INVENTORY(hInventory))
        return;

    dwUpdateMask |= ITEMFLAG_IGNORE;

    D2ItemData* pItemData;
    D2Unit* pNode = hInventory->pItem;
    while (pItemData = ITEMS_INL_GetIterator(pNode))
    {
        SITEM_SendUpdateUnitsItemMessage(hClient, pItem, pNode, dwUpdateMask, ITEMUPDATEMESSAGE_SOCKET_SET, FALSE);
        pNode = pItemData->pNextItem;
    }
}
''(...) The game can basically be considered unhackable. '' - Blizzard Entertainment (30th May 2000)
Black Omen Productions | MetalStorm: Progress Report | Screenshots

Post Reply

Return to “Code Editing”