question about selling items to npc

04/21/2021 16:26 bimbum*#1
since we blocking the confirmation(0xb034) and sends fake pick at buy case
simultaneously, at sell case i blocked the confirmation packet from reaching the client now iam missing the second part
04/21/2021 17:52 DaxterSoul#2
If you send a fake pick up when buying then send a fake drop when
selling
.

But imho you should use ADD_ITEM_BY_SERVER and DEL_ITEM_BY_SERVER instead. You can find them for example in Alchemy.
04/21/2021 18:07 bimbum*#3
Quote:
Originally Posted by DaxterSoul View Post
If you send a fake pick up when buying then send a fake drop when
selling
.

But imho you should use ADD_ITEM_BY_SERVER and DEL_ITEM_BY_SERVER instead. You can find them for example in Alchemy.
i forgot to mention that it's a transport pet
04/21/2021 18:28 DaxterSoul#4
The same applies for COS.
You can use PICK_ITEM_COS and DROP_ITEM_COS or my preferred operations
ADD_COSITEM_BY_SERVER and DEL_COSITEM_BY_SERVER which you can probably only find by reverse engineering.


Edit: Forget about what I said.
DROP_ITEM_COS and DEL_COSITEM_BY_SERVER are being parsed by the client but have no effect.

DEL_COSITEM_BY_SERVER doesn't even contain a GID of the COS it's targeting. Must be old code dating back to when there where no ability pets or you where able to only spawn a single COS.

Alright, after a some debugging I found the cause of DROP_ITEM_COS not working.

This is part of the code that reads the COSID from the message.
Code:
   case INV_OP_DROP_ITEM:                      // SP_DROP_ITEM
   case INV_OP_DROP_ITEM_COS:                  // SP_DROP_ITEM_COS
   case INV_OP_23:                             // SP_UNKNOWN_23
        if ( nOperation == INV_OP_DROP_ITEM_COS ) // SP_DROP_ITEM_COS
            MsgStreamBuffer::ReadDWORD(pMsg, &this->dwCOSID);// COS.UniqueID
Those operations types will be simplified into 2 bytes that specify the source and target storage.

When the sourceStorage is a COS and the targetStorage is the WORLD or SERVER which means either DROP or DELETE_BY_SERVER then this section will be executed.
Code:
switch(sourceStorage)
{
    case STORAGE_COS:
        switch(targetStorage)
        {
            case STORAGE_TARGET_WORLD:
            case STORAGE_TARGET_SERVER:
            dwCOSID = pOperation->dwGIDorParam0;
            pCOSManager = GetCOSManagerOrSomethingFromPlayer((int)g_pMyPlayerObj);
            pCOS = (char *)COSManager::GetByID((char *)pCOSManager, dwCOSID);
            v267 = pCOS;
            if ( !pCOS )
                goto label_exit;
                
            // Remove item visually...
            }
        }
}
You might have noticed that the dwCOSID is read from a different location than where it was stored into.

dwGIDorParam0 is normally used for NPCs and only used with COS when moving items between the player and the COS. So this looks like a real bug to me.

In order to fix this you'd need to patch the offset that is accessed. Although I haven't checked if it breaks anything yet.
Code:
0087DD96 mov     eax, [esi+0Ch] 
to
0087DD96 mov     eax, [esi+30h]
04/25/2021 19:04 Isoline*#5
Quote:
Originally Posted by bimbum* View Post
since we blocking the confirmation(0xb034) and sends fake pick at buy case
simultaneously, at sell case i blocked the confirmation packet from reaching the client now iam missing the second part
You can't "trick" the GS into expecting a different movement operation without any memory hooks.
I'm not sure what are you trying to achieve here. But i assume you are interested in Transport / NPC operations.

Code:
0xB034
Operation flags:
Code:
    public enum ItemMovement : byte
    {
        InventoryToInventory = byte.MinValue,
        StorageToStorage = 0x01,
        InventoryToStorage = 0x02,
        StorageToInventory = 0x03,
        InventoryToExchange = 0x04,
        ExchangeToInventory = 0x05,
        GroundToInventory = 0x06,
        InventoryToGround = 0x07,
        ShopToInventory = 0x08,
        InventoryToShop = 0x09,
        InventoryGoldToGround = 0x0A,
        StorageGoldToInventory = 0x0B,
        InventoryGoldToStorage = 0x0C,
        InventoryGoldToExchange = 0x0D,
        GameServerToInventory = 0x0E,
        InventoryToGameServer = 0x0F,
        PetToPet = 0x10,
        GroundToPet = 0x11,
        ShopToTransport = 0x13,
        TransportToShop = 0x14,
        ItemMallToInventory = 0x18,
        PetToInventory = 0x1A,
        InventoryToPet = 0x1B,
        GroundToPetToInventory = 0x1C,
        GuildToGuild = 0x1D,
        InventoryToGuild = 0x1E,
        GuildToInventory = 0x1F,
        InventoryGoldToGuild = 0x20,
        GuildGoldToInventory = 0x21,
        ShopBuyBack = 0x22,
        AvatarToInventory = 0x23,
        InventoryToAvatar = 0x24,
        OpenMagicCube = 0x2A,
        ShopToInventoryCoin = 0x2B,
        InventoryToCube = 0x27,
        MagicCubeConsumed = 0x29
    }
The grouped interpretation of the movement operations:
Code:
                        if (packet.ReadUInt8() == 0x01) // Success flag
                        {
                            ItemMovement itemMovement = (ItemMovement)packet.ReadUInt8();
                            switch (itemMovement)
                            {
                                #region Gold (source to different source and gold to ground)
                                case ItemMovement.InventoryGoldToStorage: // Tested, OK
                                    Parser.MoveGoldSrcToSrc(packet, ref storageGold, false);
                                    break;
                                case ItemMovement.StorageGoldToInventory: // Tested, OK
                                    Parser.MoveGoldSrcToSrc(packet, ref storageGold, true);
                                    break;
                                case ItemMovement.InventoryGoldToGuild: // Tested, OK
                                    Parser.MoveGoldSrcToSrc(packet, ref guildStorageGold, false);
                                    break;
                                case ItemMovement.GuildGoldToInventory: // Tested, OK
                                    Parser.MoveGoldSrcToSrc(packet, ref guildStorageGold, true);
                                    break;
                                case ItemMovement.InventoryGoldToGround: // Tested, OK
                                    Parser.MoveGoldToGround(packet);
                                    break;
                                #endregion
                                #region Items from source to the same source
                                case ItemMovement.InventoryToInventory: // Tested, OK
                                    Parser.MoveSrcToSameSrc(packet, Inventory);
                                    break;
                                case ItemMovement.GuildToGuild: // Tested, OK
                                    Parser.MoveSrcToSameSrc(packet, GuildStorage);
                                    break;
                                case ItemMovement.StorageToStorage: // Tested, OK
                                    Parser.MoveSrcToSameSrc(packet, Storage, false);
                                    break;
                                case ItemMovement.PetToPet: // Tested, OK
                                    uint InvPetUniqueID = packet.ReadUInt32();
                                    List<Item> petInventory = (InvPetUniqueID == GrabPet ? GrabpetInventory : TransportInventory);
                                    Parser.MoveSrcToSameSrc(packet, petInventory);
                                    break;
                                #endregion
                                #region Items from source to different source
                                case ItemMovement.InventoryToAvatar: // Tested, OK
                                    Parser.MoveSrcToDiffSrc(packet, Inventory, AvatarInventory);
                                    break;
                                case ItemMovement.AvatarToInventory: // Tested, OK
                                    Parser.MoveSrcToDiffSrc(packet, AvatarInventory, Inventory);
                                    break;
                                case ItemMovement.InventoryToStorage: // Tested, OK
                                    Parser.MoveSrcToDiffSrc(packet, Inventory, Storage);
                                    break;
                                case ItemMovement.StorageToInventory: // Tested, OK
                                    Parser.MoveSrcToDiffSrc(packet, Storage, Inventory);
                                    break;
                                case ItemMovement.InventoryToGuild: // Tested, OK
                                    Parser.MoveSrcToDiffSrc(packet, Inventory, GuildStorage);
                                    break;
                                case ItemMovement.GuildToInventory: // Tested, OK
                                    Parser.MoveSrcToDiffSrc(packet, GuildStorage, Inventory);
                                    break;
                                case ItemMovement.InventoryToPet: // Tested, OK
                                    packet.ReadUInt32(); // UniqueID
                                    Parser.MoveSrcToDiffSrc(packet, Inventory, GrabpetInventory);
                                    break;
                                case ItemMovement.PetToInventory: // Tested, OK
                                    packet.ReadUInt32(); // UniqueID
                                    Parser.MoveSrcToDiffSrc(packet, GrabpetInventory, Inventory);
                                    break;
                                #endregion
                                #region Exchange
                                case ItemMovement.InventoryToExchange: // Tested, OK
                                    Parser.MoveInventoryToExchange(packet, Inventory, ExchangeInput);
                                    break;
                                case ItemMovement.ExchangeToInventory: // Tested, OK
                                    Parser.MoveExchangeToInventory(packet, ExchangeInput);
                                    break;
                                case ItemMovement.InventoryGoldToExchange: // Tested, OK
                                    Parser.MoveGoldToExchange(packet, ExchangeInput);
                                    break;
                                #endregion
                                #region Shops, Item Mall and ShopBuyBack
                                case ItemMovement.InventoryToShop: // Tested, OK
                                    Parser.MoveInventoryToShopBuyBack(packet, Inventory, ShopBuyBack);
                                    break;
                                case ItemMovement.ShopToInventory: // Tested, OK
                                case ItemMovement.ShopToInventoryCoin: // Tested, OK
                                    Parser.MoveShopToSrc(packet, Inventory, SelectedEntity);
                                    break;
                                case ItemMovement.TransportToShop: // Tested, OK
                                    packet.ReadUInt32(); // Transport UniqueID
                                    Parser.MoveTransportToShop(packet, TransportInventory);
                                    break;
                                case ItemMovement.ShopToTransport: // Tested, OK
                                    packet.ReadUInt32(); // Transport UniqueID
                                    Parser.MoveShopToSrc(packet, TransportInventory, SelectedEntity);
                                    break;
                                case ItemMovement.ItemMallToInventory: // Tested, OK
                                    Parser.MoveItemMallToSrc(packet, Inventory);
                                    break;
                                case ItemMovement.ShopBuyBack: // Tested, OK
                                    Parser.MoveShopBuyBackToInventory(packet, ShopBuyBack, Inventory);
                                    packet.ReadUInt16(); // Amount Sold
                                    break;
                                #endregion
                                #region Pick or Drop
                                case ItemMovement.InventoryToGround:// Tested, OK
                                    Parser.MoveSrcToGround(packet, Inventory);
                                    break;
                                case ItemMovement.GroundToInventory: // Tested, OK
                                    Parser.MoveGroundToSrc(packet, Inventory);
                                    break;
                                case ItemMovement.GroundToPetToInventory: // Tested, OK
                                    packet.ReadUInt32(); // UniqueID
                                    Parser.MoveGroundToSrc(packet, Inventory);
                                    break;
                                case ItemMovement.GroundToPet: // Tested, OK
                                    uint gpetUniqueID = packet.ReadUInt32();
                                    List<Item> gpetInventory = (gpetUniqueID == GrabPet ? GrabpetInventory : TransportInventory);
                                    Parser.MoveGroundToSrc(packet, gpetInventory);
                                    break;
                                #endregion
                                #region GameServer (Quests, Mob traps, CTF, BA, etc)
                                case ItemMovement.GameServerToInventory: // Tested, OK
                                    Parser.MoveGameServerToInventory(packet, Inventory);
                                    break;
                                case ItemMovement.InventoryToGameServer: // Tested, OK
                                    Parser.MoveInventoryToGameServer(packet, Inventory);
                                    break;
                                #endregion
                                #region Magic Cubes
                                // Magic Cubes are not fully working in vSRO 1.88 
                                // So the implementation is a bit quirky but matches
                                // exactly what happens in game
                                case ItemMovement.OpenMagicCube: // Tested, OK
                                    Parser.OpenMagicCubeInventory(packet);
                                    break;
                                case ItemMovement.InventoryToCube: // Tested, OK
                                    Parser.MoveInventoryToCube(packet, Inventory);
                                    break;
                                case ItemMovement.MagicCubeConsumed: // Tested, OK
                                    Parser.MagicCubeConsumed(packet);
                                    break;
                                #endregion
                            }
                        }
Note that many of the operations incorporate the same logic hence they are grouped together.
"Src" is just the referenced container

For an accurate tracking of Shop items, you will have to interpolate the shops either from the client like bots or from the DB directly.

I should also mention that the Shops are identifiable by UniqueID, and you will have to perfectly parse any Entity spawn to correlate the data between the Shop UniqueID and commonID.

Despite of that, those UniqueIDs are somewhat "constant" for the NPCs even though UniqueID in its nature is not constant, that is because the GS loads and assigns the UniqueIDs for the NPCs when it initializes the WorldServer.

That's how the old bots have done it, back when there was no db available. But then any custom NPCs you added to your server are out of the question.
Anyway, not my forte.

Regards, Iso.
04/25/2021 20:23 DaxterSoul#6
Quote:
Originally Posted by Isoline* View Post
You can't "trick" the GS into expecting a different movement operation without any memory hooks.
I'm not sure what are you trying to achieve here. But i assume you are interested in Transport / NPC operations.
From what I understood the problem he's referring to is closely related to [Only registered and activated users can see links. Click Here To Register...].
But instead of the buy response crashing the client, the sell response is crashing the client. Thus it has to be translated into an inventory operation that is not shop depended in the client.
04/26/2021 02:07 bimbum*#7
Quote:
Originally Posted by DaxterSoul View Post
From what I understood the problem he's referring to is closely related to [Only registered and activated users can see links. Click Here To Register...].
But instead of the buy response crashing the client, the sell response is crashing the client. Thus it has to be translated into an inventory operation that is not shop depended in the client.
exactly