package javastory.channel.packet; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javastory.channel.ChannelCharacter; import javastory.channel.client.ISkill; import javastory.channel.client.Pet; import javastory.channel.client.Ring; import javastory.channel.client.SkillEntry; import javastory.channel.movement.LifeMovementFragment; import javastory.game.Equip; import javastory.game.GameConstants; import javastory.game.Inventory; import javastory.game.Item; import javastory.game.ItemType; import javastory.game.quest.QuestStatus; import javastory.io.PacketBuilder; import javastory.tools.FiletimeUtil; import javastory.world.core.PlayerCooldownValueHolder; import com.google.common.collect.Lists; public class PacketHelper { private static final long FT_UT_OFFSET = 116444592000000000L; // EDT public static final byte WEIRD_BYTE_MAGIC[] = new byte[] { (byte) 0x00, (byte) 0x40, (byte) 0xE0, (byte) 0xFD, (byte) 0x3B, (byte) 0x37, (byte) 0x4F, (byte) 0x01 }; public static long getFiletimeFromMillis(final long realTimestamp) { final long time = realTimestamp / 1000 / 60; // convert to minutes return time * 600000000 + FT_UT_OFFSET; } public static long getTime(final long realTimestamp) { final long time = realTimestamp / 1000; // convert to seconds return time * 10000000 + FT_UT_OFFSET; } public static void addQuestInfo(final PacketBuilder builder, final ChannelCharacter chr) { final List<QuestStatus> started = chr.getStartedQuests(); builder.writeAsShort(started.size()); for (final QuestStatus q : started) { builder.writeAsShort(q.getQuestId()); builder.writeLengthPrefixedString(q.getCustomData() != null ? q.getCustomData() : ""); } final List<QuestStatus> completed = chr.getCompletedQuests(); long time; builder.writeAsShort(completed.size()); for (final QuestStatus q : completed) { builder.writeAsShort(q.getQuestId()); time = FiletimeUtil.getFiletime(q.getCompletionTime()); builder.writeLong(time); // maybe start time? no effect. } } public static void addSkillInfo(final PacketBuilder builder, final ChannelCharacter chr) { final Map<ISkill, SkillEntry> skills = chr.getSkills(); builder.writeAsShort(skills.size()); for (final Entry<ISkill, SkillEntry> skill : skills.entrySet()) { builder.writeInt(skill.getKey().getId()); builder.writeInt(skill.getValue().getCurrentLevel()); addExpirationTime(builder, -1); if (skill.getKey().isFourthJob()) { builder.writeInt(skill.getValue().getMasterLevel()); } } } public static void addCoolDownInfo(final PacketBuilder builder, final ChannelCharacter chr) { builder.writeAsShort(chr.getAllCooldowns().size()); for (final PlayerCooldownValueHolder cooling : chr.getAllCooldowns()) { builder.writeInt(cooling.SkillId); builder.writeAsShort((int) (cooling.Length + cooling.StartTime - System.currentTimeMillis()) / 1000); } } public static void addRocksInfo(final PacketBuilder builder, final ChannelCharacter chr) { builder.writeInt(999999999); // Teleport maps (TODO) builder.writeInt(999999999); builder.writeInt(999999999); builder.writeInt(999999999); builder.writeInt(999999999); final int[] map = chr.getRocks(); for (int i = 0; i < 10; i++) { // VIP teleport map builder.writeInt(map[i]); } } public static void addMonsterBookInfo(final PacketBuilder builder, final ChannelCharacter chr) { builder.writeInt(chr.getMonsterBookCover()); builder.writeAsByte(0); chr.getMonsterBook().addCardPacket(builder); } public static void addRingInfo(final PacketBuilder builder, final ChannelCharacter chr) { final List<Ring> rings = Lists.newArrayList(); final Inventory equipped = chr.getEquippedItemsInventory(); for (final Item item : equipped) { final Equip equip = (Equip) item; if (equip.getRingId() > -1) { rings.add(Ring.loadFromDb(equip.getRingId())); } } final Inventory equips = chr.getEquipInventory(); for (final Item item : equips) { final Equip equip = (Equip) item; if (equip.getRingId() > -1) { rings.add(Ring.loadFromDb(equip.getRingId())); } } Collections.sort(rings); boolean FR_last = false; for (final Ring ring : rings) { final int ringItemId = ring.getItemId(); if ((ringItemId >= 1112800 && ringItemId <= 1112803 || ringItemId <= 1112806 || ringItemId <= 1112807 || ringItemId <= 1112809) && rings.indexOf(ring) == 0) { builder.writeAsShort(0); } builder.writeAsShort(0); builder.writeAsShort(1); builder.writeInt(ring.getPartnerCharacterId()); builder.writePaddedString(ring.getPartnerName(), 13); builder.writeInt(ring.getRingId()); builder.writeInt(0); builder.writeInt(ring.getPartnerRingId()); if (ringItemId >= 1112800 && ringItemId <= 1112803 || ringItemId <= 1112806 || ringItemId <= 1112807 || ringItemId <= 1112809) { FR_last = true; builder.writeInt(0); builder.writeInt(ringItemId); builder.writeAsShort(0); } else { if (rings.size() > 1) { builder.writeAsShort(0); } FR_last = false; } } if (!FR_last) { builder.writeLong(0); } } public static void addInventoryInfo(final PacketBuilder builder, final ChannelCharacter chr) { builder.writeInt(chr.getMeso()); // mesos builder.writeByte(chr.getEquipInventory().getSlotLimit()); // equip // slots builder.writeByte(chr.getUseInventory().getSlotLimit()); // use slots builder.writeByte(chr.getSetupInventory().getSlotLimit()); // set-up // slots builder.writeByte(chr.getEtcInventory().getSlotLimit()); // etc slots builder.writeByte(chr.getCashInventory().getSlotLimit()); // cash slots builder.writeBytes(WEIRD_BYTE_MAGIC); Inventory inventory = chr.getEquippedItemsInventory(); final List<Item> equipped = Lists.newArrayList(inventory); Collections.sort(equipped); for (final Item item : equipped) { if (item.getPosition() < 0 && item.getPosition() > -100) { addItemInfo(builder, item, false, false); } } builder.writeAsShort(0); // start of equipped nx for (final Item item : equipped) { if (item.getPosition() < -100) { addItemInfo(builder, item, false, false); } } builder.writeAsShort(0); // start of equip inventory inventory = chr.getEquipInventory(); for (final Item item : inventory) { addItemInfo(builder, item, false, false); } builder.writeInt(0); // start of use inventory inventory = chr.getUseInventory(); for (final Item item : inventory) { addItemInfo(builder, item, false, false); } builder.writeAsByte(0); // start of set-up inventory inventory = chr.getSetupInventory(); for (final Item item : inventory) { addItemInfo(builder, item, false, false); } builder.writeAsByte(0); // start of etc inventory inventory = chr.getEtcInventory(); for (final Item item : inventory) { addItemInfo(builder, item, false, false); } builder.writeAsByte(0); // start of cash inventory inventory = chr.getCashInventory(); for (final Item item : inventory) { addItemInfo(builder, item, false, false); } builder.writeAsByte(0); } public static void addExpirationTime(final PacketBuilder builder, final long time) { if (time != -1) { builder.writeLong(FiletimeUtil.getFiletime(time)); } else { builder.writeLong(FiletimeUtil.ITEM_EXPIRATION); } } public static void addItemInfo(final PacketBuilder builder, final Item item, final boolean zeroPosition, final boolean leaveOut) { addItemInfo(builder, item, zeroPosition, leaveOut, false); } public static void addItemInfo(final PacketBuilder builder, final Item item, final boolean zeroPosition, final boolean leaveOut, final boolean trade) { short pos = item.getPosition(); if (zeroPosition) { if (!leaveOut) { builder.writeAsByte(0); } } else { if (pos <= -1) { pos *= -1; if (pos > 100) { pos -= 100; } } if (!trade && item.getType() == ItemType.EQUIP) { builder.writeAsShort(pos); } else { builder.writeAsByte(pos); } } builder.writeByte(item.getType().asNumber()); builder.writeInt(item.getItemId()); if (item.getPet() != null) { // Pet final Pet pet = item.getPet(); builder.writeAsByte(1); builder.writeInt(pet.getUniqueId()); builder.writeZeroBytes(4); addExpirationTime(builder, item.getExpiration()); // 80 F9 58 8D 3B // C7 24 Time // where it // expired? builder.writePaddedString(pet.getName(), 13); builder.writeAsByte(pet.getLevel()); builder.writeAsShort(pet.getCloseness()); builder.writeAsByte(pet.getFullness()); builder.writeLong(getTime(System.currentTimeMillis())); builder.writeAsShort(0); builder.writeInt(1); builder.writeInt(0); builder.writeZeroBytes(5); } else { builder.writeAsByte(0); addExpirationTime(builder, item.getExpiration()); if (item.getType() == ItemType.EQUIP) { final Equip equip = (Equip) item; builder.writeByte(equip.getUpgradeSlots()); builder.writeByte(equip.getLevel()); builder.writeAsShort(equip.getStr()); builder.writeAsShort(equip.getDex()); builder.writeAsShort(equip.getInt()); builder.writeAsShort(equip.getLuk()); builder.writeAsShort(equip.getHp()); builder.writeAsShort(equip.getMp()); builder.writeAsShort(equip.getWatk()); builder.writeAsShort(equip.getMatk()); builder.writeAsShort(equip.getWdef()); builder.writeAsShort(equip.getMdef()); builder.writeAsShort(equip.getAcc()); builder.writeAsShort(equip.getAvoid()); builder.writeAsShort(equip.getHands()); builder.writeAsShort(equip.getSpeed()); builder.writeAsShort(equip.getJump()); builder.writeLengthPrefixedString(equip.getOwner()); builder.writeAsShort(equip.getFlag()); builder.writeAsByte(0); builder.writeByte(equip.getItemLevel()); // Item level builder.writeAsShort(0); builder.writeAsShort(equip.getItemEXP()); // Item Exp builder.writeInt(-1); builder.writeInt(equip.getViciousHammer()); builder.writeZeroBytes(12); builder.writeLong(0); builder.writeBytes(WEIRD_BYTE_MAGIC); builder.writeInt(-1); } else { builder.writeAsShort(item.getQuantity()); builder.writeLengthPrefixedString(item.getOwner()); builder.writeAsShort(item.getFlag()); if (GameConstants.isThrowingStar(item.getItemId()) || GameConstants.isBullet(item.getItemId())) { builder.writeInt(2); builder.writeAsShort(0x54); builder.writeAsByte(0); builder.writeAsByte(0x34); } } } } public static void serializeMovementList(final PacketBuilder lew, final List<LifeMovementFragment> moves) { lew.writeAsByte(moves.size()); for (final LifeMovementFragment move : moves) { move.serialize(lew); } } }