Welcome!

Join our community of MMO enthusiasts and game developers! By registering, you'll gain access to discussions on the latest developments in MMO server files and collaborate with like-minded individuals. Join us today and unlock the potential of MMO server development!

Join Today!

AddInventoryInfo v.62 (CashItem)

Newbie Spellweaver
Joined
Mar 7, 2011
Messages
62
Reaction score
0
I'm trying to improve my cashshop, and am looking for some ways to upgrade some packages, like this one here. I'm not very good at IDA, so I need you to get me a question, what part of the game is responsible for making me move my items back to CashShopInventory? I know that it is necessary to have the function for this, but because it does not get "blue" when it goes to my inventory in the v.83 type, my intention is that it should be something in AddInventoryInfo, but I can not say that it is there , Can it be in iteminfo?

addInventoryInfo

Code:
private static void addInventoryInfo(MaplePacketLittleEndianWriter mplew, MapleCharacter chr) {
        mplew.writeInt(chr.getMeso());
        for (byte i = 1; i <= 5; i++) {
            mplew.write(chr.getInventory(MapleInventoryType.getByType(i)).getSlotLimit());
        }
        MapleInventory iv = chr.getInventory(MapleInventoryType.EQUIPPED);
        Collection<IItem> equippedC = iv.list();
        List<Item> equipped = new ArrayList<Item>(equippedC.size());
        List<Item> equippedCash = new ArrayList<Item>(equippedC.size());
        for (IItem item : equippedC) {
            if (item.getPosition() <= -100) {
                equippedCash.add((Item) item);
            } else {
                equipped.add((Item) item);
            }
        }
        Collections.sort(equipped);
        for (Item item : equipped) {
            addItemInfo(mplew, item);
        }
        mplew.writeShort(0); // start of equip cash
        for (Item item : equippedCash) {
            addItemInfo(mplew, item);
        }
        mplew.writeShort(0); // start of equip inventory
        for (IItem item : chr.getInventory(MapleInventoryType.EQUIP).list()) {
            addItemInfo(mplew, item);
        }
        mplew.writeInt(0);
        for (IItem item : chr.getInventory(MapleInventoryType.USE).list()) {
            addItemInfo(mplew, item);
        }
        mplew.write(0);
        for (IItem item : chr.getInventory(MapleInventoryType.SETUP).list()) {
            addItemInfo(mplew, item);
        }
        mplew.write(0);
        for (IItem item : chr.getInventory(MapleInventoryType.ETC).list()) {
            addItemInfo(mplew, item);
        }
        mplew.write(0);
        for (IItem item : chr.getInventory(MapleInventoryType.CASH).list()) {
            addItemInfo(mplew, item);
        }
    }


It would be something like this, but when I clicked the ring it would turn blue:

EUe8sP - AddInventoryInfo v.62 (CashItem) - RaGEZONE Forums
 

Attachments

You must be registered for see attachments list
Newbie Spellweaver
Joined
Mar 7, 2011
Messages
62
Reaction score
0
You would need to tweak your addItemInfo.
Will all sources have their additeminfo broken? Because everyone I looked at does not exist a note, any tips?

Code:
/**
     * Adds item info to existing MaplePacketLittleEndianWriter.
     *
     * [USER=2000183830]para[/USER]m mplew The MaplePacketLittleEndianWriter to write to.
     * [USER=2000183830]para[/USER]m item The item to add info about.
     * [USER=2000183830]para[/USER]m zeroPosition Is the position zero?
     * [USER=2000183830]para[/USER]m leaveOut Leave out the item if position is zero?
     */
    private static void addItemInfo(MaplePacketLittleEndianWriter mplew, IItem item, boolean zeroPosition, boolean leaveOut) {
        MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
        boolean ring = false;
        IEquip equip = null;
        if (item.getType() == IItem.EQUIP) {
            equip = (IEquip) item;
            if (equip.getRingId() > -1) {
                ring = true;
            }
        }
        byte pos = item.getPosition();
        boolean masking = false;
        boolean equipped = false;
        if (zeroPosition) {
            if (!leaveOut) {
                mplew.write(0);
            }
        } else if (pos <= (byte) -1) {
            pos *= -1;
            if (pos > 100 || ring) {
                masking = true;
                mplew.write(0);
                mplew.write(pos - 100);
            } else {
                mplew.write(pos);
            }
            equipped = true;
        } else {
            mplew.write(item.getPosition());
        }
        if (item.getPetId() > -1) {
            mplew.write(3);
        } else {
            mplew.write(item.getType());
        }
        mplew.writeInt(item.getItemId());
       if (ring) {
            mplew.write(1);
            mplew.writeInt(equip.getRingId());
            mplew.writeInt(0);
        }
        if (item.getPetId() > -1) {
            MaplePet pet = MaplePet.loadFromDb(item.getItemId(), item.getPosition(), item.getPetId());
            String petname = pet.getName();
            mplew.write(1);
            mplew.writeInt(item.getPetId());
            mplew.writeInt(0);
            mplew.write(0);
            mplew.write(ITEM_MAGIC);
            mplew.write(HexTool.getByteArrayFromHexString("BB 46 E6 17 02"));
            if (petname.length() > 13) {
                petname = petname.substring(0, 13);
            }
            mplew.writeAsciiString(petname);
            for (int i = petname.length(); i < 13; i++) {
                mplew.write(0);
            }
            mplew.write(pet.getLevel());
            mplew.writeShort(pet.getCloseness());
            mplew.write(pet.getFullness());

            mplew.writeLong(getKoreanTimestamp((long) (System.currentTimeMillis() * 1.2)));
            mplew.writeInt(0);

            return;
        }
         if (masking && !ring) {
            // 07.03.2008 06:49... o.o
            mplew.write(HexTool.getByteArrayFromHexString("01 41 B4 38 00 00 00 00 00 80 20 6F"));
            addExpirationTime(mplew, item.getExpiration(), false);
        } else if (ring) {
            mplew.writeLong(getKoreanTimestamp((long) (System.currentTimeMillis() * 1.2)));
        } else {
            mplew.writeShort(0);
            mplew.write(ITEM_MAGIC);
            addExpirationTime(mplew, item.getExpiration(), false);
        }
        if (item.getType() == IItem.EQUIP) {
            mplew.write(equip.getUpgradeSlots());
            mplew.write(equip.getLevel());
            mplew.writeShort(equip.getStr()); // str

            mplew.writeShort(equip.getDex()); // dex

            mplew.writeShort(equip.getInt()); // int

            mplew.writeShort(equip.getLuk()); // luk

            mplew.writeShort(equip.getHp()); // hp

            mplew.writeShort(equip.getMp()); // mp

            mplew.writeShort(equip.getWatk()); // watk

            mplew.writeShort(equip.getMatk()); // matk

            mplew.writeShort(equip.getWdef()); // wdef

            mplew.writeShort(equip.getMdef()); // mdef

            mplew.writeShort(equip.getAcc()); // accuracy

            mplew.writeShort(equip.getAvoid()); // avoid

            mplew.writeShort(equip.getHands()); // hands

            mplew.writeShort(equip.getSpeed()); // speed

            mplew.writeShort(equip.getJump()); // jump

            mplew.writeMapleAsciiString(equip.getOwner());

            // 0 normal; 1 locked
            mplew.write(equip.getLocked());

           if (ring && !equipped) {
                mplew.write(0);
            }
            if (!masking && !ring) {
                mplew.write(0);
                mplew.writeLong(0);
            }
        } else {
            mplew.writeShort(item.getQuantity());
            mplew.writeMapleAsciiString(item.getOwner());
            mplew.writeShort(0); // this seems to end the item entry
            // but only if its not a THROWING STAR :))9 O.O!

            if (ii.isThrowingStar(item.getItemId()) || ii.isBullet(item.getItemId())) {
                mplew.write(HexTool.getByteArrayFromHexString("02 00 00 00 54 00 00 34"));
            }
        }
    }
 
Upvote 0
Custom Title Activated
Loyal Member
Joined
Jan 18, 2010
Messages
3,109
Reaction score
1,139
If you're able to see your inventory loaded in the Cash Shop, then your inventory info is perfectly fine. The real reason you aren't able to move items is because you need the handling and packet for that action. I don't think a standard Odin source has this done, it is typically just placed into your inventory upon purchase.. you may want to look at Moople for that handling part. Assuming you have the handling of it implemented though, it is just a Cash Shop packet that visually shows you moving/transferring items from your Storage to your Locker (NX Inventory), StoL.

You said you're not very good at IDA but if you do understand it, look for CCashShop::OnCashItemResMoveStoLDone. That's what you're looking for and need in order for it to work. It'll be like this:

PHP:
public static OutPacket OnCashItemResMoveStoLDone(GW_CashItemInfo aciNew) {
	OutPacket oPacket = new OutPacket(LoopbackPacket.CashShopCashItemResult, false);
	oPacket.Encode1(ShopResCode.MoveStoL_Done.getRes());
	aciNew.Encode(oPacket);
	return oPacket;
}

The item being added/moved is an encoded GW_CashItemInfo. You can find it's structure in GW_CashItemInfo::Decode in IDA. Mine may have more than what's directly in 62, but here's mine if you need any reference:

PHP:
public void Encode(OutPacket oPacket) {
	oPacket.EncodeBuffer(liSN, 8);
	oPacket.Encode4(dwAccountID);
	oPacket.Encode4(dwCharacterID);
	oPacket.Encode4(nItemID);
	oPacket.Encode4(nCommodityID);
	oPacket.Encode2(nNumber);
	oPacket.EncodeBuffer(sBuyCharacterID, 13);
	oPacket.EncodeBuffer(dateExpire, 8);
	oPacket.Encode4(nPaybackRate);
	oPacket.Encode4(nDiscountRate);
}

Now that you have the bytes of the packet you should be able to implement/find it fairly easily (as I said before, feel free to check out Moople, they do have that working properly and you can reference it). My Cash Result code for this is different because I'm in v90, but it's MoveStoL_Done(0x78),. I also have v83, and that is MoveStoL_Done(0x68),. I don't have v62's on me sadly, so I'm not really sure which version it changed :/
 
Upvote 0
Joined
Jul 2, 2008
Messages
469
Reaction score
42
If you're able to see your inventory loaded in the Cash Shop, then your inventory info is perfectly fine. The real reason you aren't able to move items is because you need the handling and packet for that action. I don't think a standard Odin source has this done, it is typically just placed into your inventory upon purchase.. you may want to look at Moople for that handling part. Assuming you have the handling of it implemented though, it is just a Cash Shop packet that visually shows you moving/transferring items from your Storage to your Locker (NX Inventory), StoL.

You said you're not very good at IDA but if you do understand it, look for CCashShop::OnCashItemResMoveStoLDone. That's what you're looking for and need in order for it to work. It'll be like this:

PHP:
public static OutPacket OnCashItemResMoveStoLDone(GW_CashItemInfo aciNew) {
	OutPacket oPacket = new OutPacket(LoopbackPacket.CashShopCashItemResult, false);
	oPacket.Encode1(ShopResCode.MoveStoL_Done.getRes());
	aciNew.Encode(oPacket);
	return oPacket;
}

The item being added/moved is an encoded GW_CashItemInfo. You can find it's structure in GW_CashItemInfo::Decode in IDA. Mine may have more than what's directly in 62, but here's mine if you need any reference:

PHP:
public void Encode(OutPacket oPacket) {
	oPacket.EncodeBuffer(liSN, 8);
	oPacket.Encode4(dwAccountID);
	oPacket.Encode4(dwCharacterID);
	oPacket.Encode4(nItemID);
	oPacket.Encode4(nCommodityID);
	oPacket.Encode2(nNumber);
	oPacket.EncodeBuffer(sBuyCharacterID, 13);
	oPacket.EncodeBuffer(dateExpire, 8);
	oPacket.Encode4(nPaybackRate);
	oPacket.Encode4(nDiscountRate);
}

Now that you have the bytes of the packet you should be able to implement/find it fairly easily (as I said before, feel free to check out Moople, they do have that working properly and you can reference it). My Cash Result code for this is different because I'm in v90, but it's MoveStoL_Done(0x78),. I also have v83, and that is MoveStoL_Done(0x68),. I don't have v62's on me sadly, so I'm not really sure which version it changed :/

But right now I think he can't even click on the cash item??
 
Upvote 0
Newbie Spellweaver
Joined
Mar 7, 2011
Messages
62
Reaction score
0
If you're able to see your inventory loaded in the Cash Shop, then your inventory info is perfectly fine. The real reason you aren't able to move items is because you need the handling and packet for that action. I don't think a standard Odin source has this done, it is typically just placed into your inventory upon purchase.. you may want to look at Moople for that handling part. Assuming you have the handling of it implemented though, it is just a Cash Shop packet that visually shows you moving/transferring items from your Storage to your Locker (NX Inventory), StoL.

You said you're not very good at IDA but if you do understand it, look for CCashShop::OnCashItemResMoveStoLDone. That's what you're looking for and need in order for it to work. It'll be like this:

PHP:
public static OutPacket OnCashItemResMoveStoLDone(GW_CashItemInfo aciNew) {
	OutPacket oPacket = new OutPacket(LoopbackPacket.CashShopCashItemResult, false);
	oPacket.Encode1(ShopResCode.MoveStoL_Done.getRes());
	aciNew.Encode(oPacket);
	return oPacket;
}

The item being added/moved is an encoded GW_CashItemInfo. You can find it's structure in GW_CashItemInfo::Decode in IDA. Mine may have more than what's directly in 62, but here's mine if you need any reference:

PHP:
public void Encode(OutPacket oPacket) {
	oPacket.EncodeBuffer(liSN, 8);
	oPacket.Encode4(dwAccountID);
	oPacket.Encode4(dwCharacterID);
	oPacket.Encode4(nItemID);
	oPacket.Encode4(nCommodityID);
	oPacket.Encode2(nNumber);
	oPacket.EncodeBuffer(sBuyCharacterID, 13);
	oPacket.EncodeBuffer(dateExpire, 8);
	oPacket.Encode4(nPaybackRate);
	oPacket.Encode4(nDiscountRate);
}

Now that you have the bytes of the packet you should be able to implement/find it fairly easily (as I said before, feel free to check out Moople, they do have that working properly and you can reference it). My Cash Result code for this is different because I'm in v90, but it's MoveStoL_Done(0x78),. I also have v83, and that is MoveStoL_Done(0x68),. I don't have v62's on me sadly, so I'm not really sure which version it changed :/

Thanks Eric / jadeling for your answers.

But, I looked for what you told me and I did not find this handler in moopledev or any v.90 source. The fonts may only use the position of the item to move it without using this class. I can already transfer it to my inventory, it works very well by the way, the only problem is that it is not available so I can put it back into my cashshop inventory. The funny thing is that I did some tests, and I even saw him enabled, but he gave me the items I was equipped and not the ones I had in the inventory, and there always was an error because the item is nullo. I think it's my own addinventoryinfo, I miss studying calmly. If you're interested in seeing my code now, just talk.
 
Upvote 0
Custom Title Activated
Loyal Member
Joined
Jan 18, 2010
Messages
3,109
Reaction score
1,139

So let me get this straight.. because maybe I'm lost and jadeling made it clear to me. You're saying that your issue is that if you move an item from your Cash Storage to your Character Inventory that it works fine, but moving it from your Character Inventory right back to your Cash Inventory afterwards won't work? If this is so, what happens if you were to exit the CS and re-enter it, only to add the item back to your Cash Inventory from your Character Inventory? Does it work properly, or you can not add back items period once it's been transferred to your Character Inventory from the Cash Inventory?

If it can't be moved back to your Cash Inventory only after it is moved to your Character Inventory, but works if you re-enter, then it is an issue with your packet moving it from LtoS (LockerToStorage). However, if you can't add items back period, then you're right -- it's your addInventoryInfo. BUT, it's not the addInventoryInfo that's messing up, it's the addItemInfo portion inside of it like jadeling had pointed out earlier. Looking at your addItemInfo it's a bit weird, I guess that's just Odin's layout of it though.. I don't really see how the masking and ring bools work out because those bytes are written out as ints and shorts and bytes everywhere when it's really just a FileTime long, geez.. Well, let me try and guide you through it in comparison to Nexon. This will probably be long, so if you don't want to understand why it doesn't work and how to reference IDA then just skip to the tldr at the bottom I guess lol.

Compare your addItemInfo to what Nexon does (IDA) and ignore the position part, since the position part is correct:
PHP:
void __thiscall GW_ItemSlotBase::RawEncode(GW_ItemSlotBase *this, COutPacket *oPacket)
{
  signed int v2; // [sp+0h] [bp-10h]@2
  GW_ItemSlotBase *thisa; // [sp+8h] [bp-8h]@1

  thisa = this;
  COutPacket::Encode4(oPacket, this->nItemID);
  if ( *(_QWORD *)&thisa->liCashItemSN._s0.LowPart )
    v2 = 1;
  else
    v2 = 0;
  COutPacket::Encode1(oPacket, v2);
  if ( v2 )
    COutPacket::EncodeBuffer(oPacket, &thisa->liCashItemSN, 8u);
  COutPacket::EncodeBuffer(oPacket, &thisa->dateExpire, 8u);
}

So as you can see you start out encoding the base of the item. int ItemID as you have, then a boolean for if the liCashItemSN > 0 or not. What you SHOULD do at least is add a check for ii.isCash(item.getItemId()) and write a long for it, otherwise just do nothing. Then you have another long which is written for all items, this is the expiration date long. You don't use expirations since I can see hard-coded longs there, but you could always just write a hard-coded long for infinite time so it looks better imo.

Moving on to the actual equip data, we then have this sub:
PHP:
void __thiscall GW_ItemSlotEquip::RawEncode(GW_ItemSlotEquip *this, COutPacket *oPacket)
{
  char *v2; // ecx@1
  ZXString<char> v3; // [sp-4h] [bp-10h]@1
  int v4; // [sp+0h] [bp-Ch]@1
  GW_ItemSlotEquip *thisa; // [sp+4h] [bp-8h]@1
  ZXString<char> *v6; // [sp+8h] [bp-4h]@1

  thisa = this;
  GW_ItemSlotBase::RawEncode(&this->baseclass_0, oPacket);
  COutPacket::Encode1(oPacket, thisa->nRUC);
  COutPacket::Encode1(oPacket, thisa->nCUC);
  COutPacket::Encode2(oPacket, thisa->niSTR);
  COutPacket::Encode2(oPacket, thisa->niDEX);
  COutPacket::Encode2(oPacket, thisa->niINT);
  COutPacket::Encode2(oPacket, thisa->niLUK);
  COutPacket::Encode2(oPacket, thisa->niMaxHP);
  COutPacket::Encode2(oPacket, thisa->niMaxMP);
  COutPacket::Encode2(oPacket, thisa->niPAD);
  COutPacket::Encode2(oPacket, thisa->niMAD);
  COutPacket::Encode2(oPacket, thisa->niPDD);
  COutPacket::Encode2(oPacket, thisa->niMDD);
  COutPacket::Encode2(oPacket, thisa->niACC);
  COutPacket::Encode2(oPacket, thisa->niEVA);
  COutPacket::Encode2(oPacket, thisa->niCraft);
  COutPacket::Encode2(oPacket, thisa->niSpeed);
  COutPacket::Encode2(oPacket, thisa->niJump);
  v3._m_pStr = v2;
  v6 = &v3;
  v4 = ZXString<char>::ZXString<char>(thisa->sTitle, -1);
  COutPacket::EncodeStr(oPacket, v3);
  COutPacket::Encode2(oPacket, thisa->nAttribute);
  if ( !GW_ItemSlotBase::IsCashItem(&thisa->baseclass_0) )
    COutPacket::EncodeBuffer(oPacket, &thisa->liSN, 8u);
}

Which following this does not match your packet because of the ring and "masked" booleans. So why won't "masked" always work the way it should? Well, simply put, you're assuming that the item is a "Cash Item" (NX) only if the slot position > 100. While this is true, it is ONLY true when you EQUIP that NX item. If it is otherwise being moved in your inventory or within the Cash Shop etc, masked will result to false because it is being moved to the slot of the inventory which would be 1~96. The issue with this is that cash items hold their own structure, different to a non-NX item. They should be handled regardless of position pretty much.

Moving on.. so the whole reason it isn't working is because of this part here:
PHP:
// 0 normal; 1 locked
mplew.write(equip.getLocked());

if (ring && !equipped) {
	mplew.write(0); //This byte is irrelevant. 
}
if (!masking && !ring) {
	mplew.write(0); //This byte is irrelevant.
	mplew.writeLong(0);
}

For starters, reference BMS's RawEncode. Notice a difference? The byte for locked items (even though it isn't actually for locked items lol it's the item flag/attribute mask for multiple things) is a single byte here, but in reality it is actually a short. Instead of having two random conditionals adding the same byte, just change that into a short and remove the irrelevant byte. Now we are back to the masking issue. What I suggest is that where you declare masking as false, you change it to what I suggested earlier:

Code:
boolean masking = ii.isCash(item.getItemId());

If you can transition into determining real cash items, then you can utilize the checks properly and have no issue. Now let's go back to BMS's RawEncode. Notice how that long is written ONLY if it is NON-NX? Well, because "masking" returns false here even if it is NX, it will always write that long. In that sense, your packet is actually writing NX and telling the client it is non-NX. In the actual game that is fine because SN's are generally useless there, it's mainly for tracking/logging purposes for Nexon. However, if you're making an item Non-NX when slot position isn't 100, and you're placing this "Non-NX" (which is actually NX) into the Cash Shop, then it won't do anything because you can't move NX into the Cash Storage. The only reason it isn't grayed out and un-clickable like the regular Non-NX is because the client knows in the WZ files itself that the item is "cash".

TL;DR, your "masking" boolean needs to determine real NX through something like ii.isCash rather than the position, and you need to remove the two irrelevant bytes and make the getLock() byte into a short (aka nAttribute). If you do want to truly update that packet then yeah a lot more could be done, but now you hopefully know why that addItemInfo won't work in its current state and that referencing BMS is fairly easy once you get the hang of it. If your cash item handling is otherwise working and the changes to addItemInfo are done correctly, then moving them should work perfectly fine.
 
Upvote 0
Newbie Spellweaver
Joined
Mar 7, 2011
Messages
62
Reaction score
0
So let me get this straight.. because maybe I'm lost and jadeling made it clear to me. You're saying that your issue is that if you move an item from your Cash Storage to your Character Inventory that it works fine, but moving it from your Character Inventory right back to your Cash Inventory afterwards won't work? If this is so, what happens if you were to exit the CS and re-enter it, only to add the item back to your Cash Inventory from your Character Inventory? Does it work properly, or you can not add back items period once it's been transferred to your Character Inventory from the Cash Inventory?

If it can't be moved back to your Cash Inventory only after it is moved to your Character Inventory, but works if you re-enter, then it is an issue with your packet moving it from LtoS (LockerToStorage). However, if you can't add items back period, then you're right -- it's your addInventoryInfo. BUT, it's not the addInventoryInfo that's messing up, it's the addItemInfo portion inside of it like jadeling had pointed out earlier. Looking at your addItemInfo it's a bit weird, I guess that's just Odin's layout of it though.. I don't really see how the masking and ring bools work out because those bytes are written out as ints and shorts and bytes everywhere when it's really just a FileTime long, geez.. Well, let me try and guide you through it in comparison to Nexon. This will probably be long, so if you don't want to understand why it doesn't work and how to reference IDA then just skip to the tldr at the bottom I guess lol.

Compare your addItemInfo to what Nexon does (IDA) and ignore the position part, since the position part is correct:
PHP:
void __thiscall GW_ItemSlotBase::RawEncode(GW_ItemSlotBase *this, COutPacket *oPacket)
{
  signed int v2; // [sp+0h] [bp-10h]@2
  GW_ItemSlotBase *thisa; // [sp+8h] [bp-8h]@1

  thisa = this;
  COutPacket::Encode4(oPacket, this->nItemID);
  if ( *(_QWORD *)&thisa->liCashItemSN._s0.LowPart )
    v2 = 1;
  else
    v2 = 0;
  COutPacket::Encode1(oPacket, v2);
  if ( v2 )
    COutPacket::EncodeBuffer(oPacket, &thisa->liCashItemSN, 8u);
  COutPacket::EncodeBuffer(oPacket, &thisa->dateExpire, 8u);
}

So as you can see you start out encoding the base of the item. int ItemID as you have, then a boolean for if the liCashItemSN > 0 or not. What you SHOULD do at least is add a check for ii.isCash(item.getItemId()) and write a long for it, otherwise just do nothing. Then you have another long which is written for all items, this is the expiration date long. You don't use expirations since I can see hard-coded longs there, but you could always just write a hard-coded long for infinite time so it looks better imo.

Moving on to the actual equip data, we then have this sub:
PHP:
void __thiscall GW_ItemSlotEquip::RawEncode(GW_ItemSlotEquip *this, COutPacket *oPacket)
{
  char *v2; // ecx@1
  ZXString<char> v3; // [sp-4h] [bp-10h]@1
  int v4; // [sp+0h] [bp-Ch]@1
  GW_ItemSlotEquip *thisa; // [sp+4h] [bp-8h]@1
  ZXString<char> *v6; // [sp+8h] [bp-4h]@1

  thisa = this;
  GW_ItemSlotBase::RawEncode(&this->baseclass_0, oPacket);
  COutPacket::Encode1(oPacket, thisa->nRUC);
  COutPacket::Encode1(oPacket, thisa->nCUC);
  COutPacket::Encode2(oPacket, thisa->niSTR);
  COutPacket::Encode2(oPacket, thisa->niDEX);
  COutPacket::Encode2(oPacket, thisa->niINT);
  COutPacket::Encode2(oPacket, thisa->niLUK);
  COutPacket::Encode2(oPacket, thisa->niMaxHP);
  COutPacket::Encode2(oPacket, thisa->niMaxMP);
  COutPacket::Encode2(oPacket, thisa->niPAD);
  COutPacket::Encode2(oPacket, thisa->niMAD);
  COutPacket::Encode2(oPacket, thisa->niPDD);
  COutPacket::Encode2(oPacket, thisa->niMDD);
  COutPacket::Encode2(oPacket, thisa->niACC);
  COutPacket::Encode2(oPacket, thisa->niEVA);
  COutPacket::Encode2(oPacket, thisa->niCraft);
  COutPacket::Encode2(oPacket, thisa->niSpeed);
  COutPacket::Encode2(oPacket, thisa->niJump);
  v3._m_pStr = v2;
  v6 = &v3;
  v4 = ZXString<char>::ZXString<char>(thisa->sTitle, -1);
  COutPacket::EncodeStr(oPacket, v3);
  COutPacket::Encode2(oPacket, thisa->nAttribute);
  if ( !GW_ItemSlotBase::IsCashItem(&thisa->baseclass_0) )
    COutPacket::EncodeBuffer(oPacket, &thisa->liSN, 8u);
}

Which following this does not match your packet because of the ring and "masked" booleans. So why won't "masked" always work the way it should? Well, simply put, you're assuming that the item is a "Cash Item" (NX) only if the slot position > 100. While this is true, it is ONLY true when you EQUIP that NX item. If it is otherwise being moved in your inventory or within the Cash Shop etc, masked will result to false because it is being moved to the slot of the inventory which would be 1~96. The issue with this is that cash items hold their own structure, different to a non-NX item. They should be handled regardless of position pretty much.

Moving on.. so the whole reason it isn't working is because of this part here:
PHP:
// 0 normal; 1 locked
mplew.write(equip.getLocked());

if (ring && !equipped) {
	mplew.write(0); //This byte is irrelevant. 
}
if (!masking && !ring) {
	mplew.write(0); //This byte is irrelevant.
	mplew.writeLong(0);
}

For starters, reference BMS's RawEncode. Notice a difference? The byte for locked items (even though it isn't actually for locked items lol it's the item flag/attribute mask for multiple things) is a single byte here, but in reality it is actually a short. Instead of having two random conditionals adding the same byte, just change that into a short and remove the irrelevant byte. Now we are back to the masking issue. What I suggest is that where you declare masking as false, you change it to what I suggested earlier:

Code:
boolean masking = ii.isCash(item.getItemId());

If you can transition into determining real cash items, then you can utilize the checks properly and have no issue. Now let's go back to BMS's RawEncode. Notice how that long is written ONLY if it is NON-NX? Well, because "masking" returns false here even if it is NX, it will always write that long. In that sense, your packet is actually writing NX and telling the client it is non-NX. In the actual game that is fine because SN's are generally useless there, it's mainly for tracking/logging purposes for Nexon. However, if you're making an item Non-NX when slot position isn't 100, and you're placing this "Non-NX" (which is actually NX) into the Cash Shop, then it won't do anything because you can't move NX into the Cash Storage. The only reason it isn't grayed out and un-clickable like the regular Non-NX is because the client knows in the WZ files itself that the item is "cash".

TL;DR, your "masking" boolean needs to determine real NX through something like ii.isCash rather than the position, and you need to remove the two irrelevant bytes and make the getLock() byte into a short (aka nAttribute). If you do want to truly update that packet then yeah a lot more could be done, but now you hopefully know why that addItemInfo won't work in its current state and that referencing BMS is fairly easy once you get the hang of it. If your cash item handling is otherwise working and the changes to addItemInfo are done correctly, then moving them should work perfectly fine.

Thanks for the help, I'll try is to update the topic!
 
Upvote 0
Newbie Spellweaver
Joined
Mar 7, 2011
Messages
62
Reaction score
0
Thanks for the help, I'll try is to update the topic!
Eric

I did everything you said to me, but something does not seem to work yet, look here:

Code:
 /**
     * Adds item info to existing MaplePacketLittleEndianWriter.
     *
     * [USER=2000183830]para[/USER]m mplew The MaplePacketLittleEndianWriter to write to.
     * [USER=2000183830]para[/USER]m item The item to add info about.
     * [USER=2000183830]para[/USER]m zeroPosition Is the position zero?
     * [USER=2000183830]para[/USER]m leaveOut Leave out the item if position is zero?
     */
    private static void addItemInfo(WritingPacket mplew, IItem item, boolean zeroPosition, boolean leaveOut) {
        MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
        boolean ring = false;
        IEquip equip = null;
        if (item.getType() == IItem.EQUIP) {
            equip = (IEquip) item;
            if (equip.getRingId() > -1) {
                ring = true;
            }
        }
        byte pos = item.getPosition();
        boolean masking = ItemConstants.isCash(item.getItemId());
        boolean equipped = false;
        if (zeroPosition) {
            if (!leaveOut) {
                mplew.write(0);
            }
        } else if (pos <= (byte) -1) {
            pos *= -1;
            if (pos > 100 || ring) {
               // masking = true;
                //mplew.write(0);
                mplew.write(pos - 100);
            } else {
                mplew.write(pos);
            }
            equipped = true;
        } else {
            mplew.write(item.getPosition());
        }
        if (item.getPetId() > -1) {
            mplew.write(3);
        } else {
            mplew.write(item.getType());
        }
        mplew.writeInt(item.getItemId());
       if (ring) {
            mplew.write(1);
            mplew.writeInt(equip.getRingId());
            mplew.writeInt(0);
        }
        if (item.getPetId() > -1) {
            ItemPet pet = ItemPet.loadFromDb(item.getItemId(), item.getPosition(), item.getPetId());
            String petname = pet.getName();
            mplew.write(1);
            mplew.writeInt(item.getPetId());
            mplew.writeInt(0);
            mplew.write(0);
            mplew.write(ITEM_MAGIC);
            mplew.write(HexTool.getByteArrayFromHexString("BB 46 E6 17 02"));
            if (petname.length() > 13) {
                petname = petname.substring(0, 13);
            }
            mplew.writeAsciiString(petname);
            for (int i = petname.length(); i < 13; i++) {
                mplew.write(0);
            }
            mplew.write(pet.getLevel());
            mplew.writeShort(pet.getCloseness());
            mplew.write(pet.getFullness());

            mplew.writeLong(getKoreanTimestamp((long) (System.currentTimeMillis() * 1.2)));
            mplew.writeInt(0);

            return;
        }
         if (masking && !ring) {
            mplew.write(HexTool.getByteArrayFromHexString("01 41 B4 38 00 00 00 00 00 80 20 6F"));
            addExpirationTime(mplew, item.getExpiration(), false);
        } else if (ring) {
            mplew.writeLong(getKoreanTimestamp((long) (System.currentTimeMillis() * 1.2)));
        } else {
            mplew.writeShort(0);
            mplew.write(ITEM_MAGIC);
            addExpirationTime(mplew, item.getExpiration(), false);
        }
        if (item.getType() == IItem.EQUIP) {
            mplew.write(equip.getUpgradeSlots());
            mplew.write(equip.getLevel());
            mplew.writeShort(equip.getStr()); // str
            mplew.writeShort(equip.getDex()); // dex
            mplew.writeShort(equip.getInt()); // int
            mplew.writeShort(equip.getLuk()); // luk
            mplew.writeShort(equip.getHp()); // hp
            mplew.writeShort(equip.getMp()); // mp
            mplew.writeShort(equip.getWatk()); // watk
            mplew.writeShort(equip.getMatk()); // matk
            mplew.writeShort(equip.getWdef()); // wdef
            mplew.writeShort(equip.getMdef()); // mdef
            mplew.writeShort(equip.getAcc()); // accuracy
            mplew.writeShort(equip.getAvoid()); // avoid
            mplew.writeShort(equip.getHands()); // hands
            mplew.writeShort(equip.getSpeed()); // speed
            mplew.writeShort(equip.getJump()); // jump
            mplew.writeMapleAsciiString(equip.getOwner());
            mplew.writeShort(equip.getLocked());
            if (!masking) {
                mplew.writeLong(0);
            }
        } else {
            mplew.writeShort(item.getQuantity());
            mplew.writeMapleAsciiString(item.getOwner());
            mplew.writeShort(0); 
            if (ii.isThrowingStar(item.getItemId()) || ii.isBullet(item.getItemId())) {
                mplew.write(HexTool.getByteArrayFromHexString("02 00 00 00 54 00 00 34"));
            }
        }
    }
 
Upvote 0
Newbie Spellweaver
Joined
Mar 7, 2011
Messages
62
Reaction score
0
Eric

I did everything you said to me, but something does not seem to work yet, look here:

Code:
 /**
     * Adds item info to existing MaplePacketLittleEndianWriter.
     *
     * [USER=2000183830]para[/USER]m mplew The MaplePacketLittleEndianWriter to write to.
     * [USER=2000183830]para[/USER]m item The item to add info about.
     * [USER=2000183830]para[/USER]m zeroPosition Is the position zero?
     * [USER=2000183830]para[/USER]m leaveOut Leave out the item if position is zero?
     */
    private static void addItemInfo(WritingPacket mplew, IItem item, boolean zeroPosition, boolean leaveOut) {
        MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
        boolean ring = false;
        IEquip equip = null;
        if (item.getType() == IItem.EQUIP) {
            equip = (IEquip) item;
            if (equip.getRingId() > -1) {
                ring = true;
            }
        }
        byte pos = item.getPosition();
        boolean masking = ItemConstants.isCash(item.getItemId());
        boolean equipped = false;
        if (zeroPosition) {
            if (!leaveOut) {
                mplew.write(0);
            }
        } else if (pos <= (byte) -1) {
            pos *= -1;
            if (pos > 100 || ring) {
               // masking = true;
                //mplew.write(0);
                mplew.write(pos - 100);
            } else {
                mplew.write(pos);
            }
            equipped = true;
        } else {
            mplew.write(item.getPosition());
        }
        if (item.getPetId() > -1) {
            mplew.write(3);
        } else {
            mplew.write(item.getType());
        }
        mplew.writeInt(item.getItemId());
       if (ring) {
            mplew.write(1);
            mplew.writeInt(equip.getRingId());
            mplew.writeInt(0);
        }
        if (item.getPetId() > -1) {
            ItemPet pet = ItemPet.loadFromDb(item.getItemId(), item.getPosition(), item.getPetId());
            String petname = pet.getName();
            mplew.write(1);
            mplew.writeInt(item.getPetId());
            mplew.writeInt(0);
            mplew.write(0);
            mplew.write(ITEM_MAGIC);
            mplew.write(HexTool.getByteArrayFromHexString("BB 46 E6 17 02"));
            if (petname.length() > 13) {
                petname = petname.substring(0, 13);
            }
            mplew.writeAsciiString(petname);
            for (int i = petname.length(); i < 13; i++) {
                mplew.write(0);
            }
            mplew.write(pet.getLevel());
            mplew.writeShort(pet.getCloseness());
            mplew.write(pet.getFullness());

            mplew.writeLong(getKoreanTimestamp((long) (System.currentTimeMillis() * 1.2)));
            mplew.writeInt(0);

            return;
        }
         if (masking && !ring) {
            mplew.write(HexTool.getByteArrayFromHexString("01 41 B4 38 00 00 00 00 00 80 20 6F"));
            addExpirationTime(mplew, item.getExpiration(), false);
        } else if (ring) {
            mplew.writeLong(getKoreanTimestamp((long) (System.currentTimeMillis() * 1.2)));
        } else {
            mplew.writeShort(0);
            mplew.write(ITEM_MAGIC);
            addExpirationTime(mplew, item.getExpiration(), false);
        }
        if (item.getType() == IItem.EQUIP) {
            mplew.write(equip.getUpgradeSlots());
            mplew.write(equip.getLevel());
            mplew.writeShort(equip.getStr()); // str
            mplew.writeShort(equip.getDex()); // dex
            mplew.writeShort(equip.getInt()); // int
            mplew.writeShort(equip.getLuk()); // luk
            mplew.writeShort(equip.getHp()); // hp
            mplew.writeShort(equip.getMp()); // mp
            mplew.writeShort(equip.getWatk()); // watk
            mplew.writeShort(equip.getMatk()); // matk
            mplew.writeShort(equip.getWdef()); // wdef
            mplew.writeShort(equip.getMdef()); // mdef
            mplew.writeShort(equip.getAcc()); // accuracy
            mplew.writeShort(equip.getAvoid()); // avoid
            mplew.writeShort(equip.getHands()); // hands
            mplew.writeShort(equip.getSpeed()); // speed
            mplew.writeShort(equip.getJump()); // jump
            mplew.writeMapleAsciiString(equip.getOwner());
            mplew.writeShort(equip.getLocked());
            if (!masking) {
                mplew.writeLong(0);
            }
        } else {
            mplew.writeShort(item.getQuantity());
            mplew.writeMapleAsciiString(item.getOwner());
            mplew.writeShort(0); 
            if (ii.isThrowingStar(item.getItemId()) || ii.isBullet(item.getItemId())) {
                mplew.write(HexTool.getByteArrayFromHexString("02 00 00 00 54 00 00 34"));
            }
        }
    }


Hello Eric correct me if I'm wrong, look at this function!
 
Upvote 0
Back
Top