- Joined
- Jul 17, 2010
- Messages
- 333
- Reaction score
- 165
Hi, everyone. Today I introduce some packets related to Commander-Lucid-
*Note: I won't release just-copy-pastable codes (I mean, no compatibility to released servers), but a few pieces of code. Besides, since this is not from official-server sniffing, these implementation(used values) are most likely incorrect and missing some functions.
Though, I believe it will trigger you to update your server to up-to-date (v178+). (or, at least so as to attract attention of some people ;P)
Send Opcodes (v178)
Uh, it's bother to find actual values for each versions. So I provide a way to find them easily on IDA.
Recv Opcodes
Packet
Handler
Some material examples
Butterfly.java
FairyDust
STAINED_GLASS
MobSkill.java (applyEffect)
SpineAnimation
Video
Thanks for reading
*Note: I won't release just-copy-pastable codes (I mean, no compatibility to released servers), but a few pieces of code. Besides, since this is not from official-server sniffing, these implementation(used values) are most likely incorrect and missing some functions.
Though, I believe it will trigger you to update your server to up-to-date (v178+). (or, at least so as to attract attention of some people ;P)
Send Opcodes (v178)
Code:
//BEGIN_FIELD_LUCID
LUCID_BUTTERFLY_CREATE(0x4D0),
LUCID_BUTTERFLY_ACTION(0x4D1),
//none? xd
LUCID_DRAGON_CREATE(0x4D3),
LUCID_DO_SKILL(0x4D4),
LUCID2_STAINED_GLASS_ON_OFF(0x4D5),
LUCID2_STAINED_GLASS_BREAK(0x4D6),
LUCID_STATUE_STATE_CHANGE(0x4D7),
LUCID2_SET_FLYING_MODE(0x4D8),
LUCID2_WELCOME_BARRAGE(0x4D9),
//END_FIELD_LUCID
//BEGIN_FIELD_HUNDREDBINGO //just for ref.
1. Open the 'string window' and search "BossLucid.img" (press Alt+T on the window).
2. Select something like "___:0249FA58 00000021C Etc/BossLucid.img/Dragon/phase%d").
3. You'll see a tag? "aEtcBosslucid_0", then xref (press X key on the tag) and goto the address found.
4. Press F5 key to see pseudocode, now I assume you're on ("v7 = ZXString<char>::Format(&v134, "Etc/BossLucid.img/Dragon/phase%d", a3);").
5. Press X key on the function name (at the top of the code), it's the main function of LUCID_DRAGON_CREATE.
6. Repeat 5. twice. You can finally find two fuctions, which are CField_Lucid::OnPacket and CField_Lucid2::OnPacket (or what I call so).
2. Select something like "___:0249FA58 00000021C Etc/BossLucid.img/Dragon/phase%d").
3. You'll see a tag? "aEtcBosslucid_0", then xref (press X key on the tag) and goto the address found.
4. Press F5 key to see pseudocode, now I assume you're on ("v7 = ZXString<char>::Format(&v134, "Etc/BossLucid.img/Dragon/phase%d", a3);").
5. Press X key on the function name (at the top of the code), it's the main function of LUCID_DRAGON_CREATE.
6. Repeat 5. twice. You can finally find two fuctions, which are CField_Lucid::OnPacket and CField_Lucid2::OnPacket (or what I call so).
Recv Opcodes
Code:
END_BOSS_DEMIAN_FLYINGSWORD,//just for ref.
BEGIN_BOSS_LUCID,
LUCID_FAIRY_DUST_CHECK,
LUCID_ACTIVATE_STATUE_REQUEST,
LUCID_WELCOME_BARRAGE_END,
END_BOSS_LUCID,
Packet
Code:
public static class BossLucid {
public static byte[] createButterfly(int initId, List<Butterfly> butterflies) {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.LUCID_BUTTERFLY_CREATE.getValue());
mplew.writeInt(initId);//not sure
mplew.writeInt(butterflies.size());
for (Butterfly butterfly : butterflies) {
mplew.writeInt(butterfly.type);
mplew.writeInt(butterfly.pos.x);
mplew.writeInt(butterfly.pos.y);
}
return mplew.getPacket();
}
/**
* [USER=2000183830]para[/USER]m mode Butterfly.Mode ADD/MOVE/ATTACK/ERASE
* [USER=2000183830]para[/USER]m args <br /> ADD: initId(not sure), typeId, posX, posY<br />
* MOVE: posX, poxY<br />ATTACK: count, startDelay
* [USER=850422]return[/USER]
*/
public static byte[] setButterflyAction(Butterfly.Mode mode, int... args) {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.LUCID_BUTTERFLY_ACTION.getValue());
mplew.writeInt(mode.code);
switch (mode) {
case ADD:
mplew.writeInt(args[0]);
mplew.writeInt(args[1]);
mplew.writeInt(args[2]);
mplew.writeInt(args[3]);
break;
case MOVE:
mplew.writeInt(args[0]);
mplew.writeInt(args[1]);
break;
case ATTACK:
mplew.writeInt(args[0]);
mplew.writeInt(args[1]);
break;
}
return mplew.getPacket();
}
public static byte[] createDragon(int phase, int posX, int posY, int createPosX, int createPosY, boolean isLeft) {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.LUCID_DRAGON_CREATE.getValue());
mplew.writeInt(phase);
mplew.writeInt(posX);
mplew.writeInt(posY);
mplew.writeInt(createPosX);
mplew.writeInt(createPosY);
mplew.writeBool(isLeft);
return mplew.getPacket();
}
public static byte[] doFlowerTrapSkill(int level, int pattern, int x, int y, boolean flip) {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.LUCID_DO_SKILL.getValue());
mplew.writeInt(238);
mplew.writeInt(level);//1~3
mplew.writeInt(pattern);//0~2
mplew.writeInt(x);
mplew.writeInt(y);
mplew.writeBool(flip);//not sure
return mplew.getPacket();
}
public static byte[] doLaserRainSkill(int startDelay, List<Integer> intervals) {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.LUCID_DO_SKILL.getValue());
mplew.writeInt(238);
mplew.writeInt(5);
mplew.writeInt(startDelay);
mplew.writeInt(intervals.size());
for (int interval : intervals) {
mplew.writeInt(interval);
}
return mplew.getPacket();
}
public static byte[] doFairyDustSkill(int level, List<FairyDust> fairyDust) {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.LUCID_DO_SKILL.getValue());
mplew.writeInt(238);
mplew.writeInt(level);//4 or 10
mplew.writeInt(fairyDust.size());
for (FairyDust fd : fairyDust) {
mplew.writeInt(fd.scale);
mplew.writeInt(fd.createDelay);
mplew.writeInt(fd.moveSpeed);
mplew.writeInt(fd.angle);
}
return mplew.getPacket();
}
public static byte[] doForcedTeleportSkill(int splitId) {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.LUCID_DO_SKILL.getValue());
mplew.writeInt(238);
mplew.writeInt(6);
mplew.writeInt(splitId);//0~7
return mplew.getPacket();
}
public static byte[] doRushSkill() {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.LUCID_DO_SKILL.getValue());
mplew.writeInt(238);
mplew.writeInt(8);
mplew.writeInt(0);//only path0 exists o.O
return mplew.getPacket();
}
public static byte[] setStainedGlassOnOff(boolean enable, List<String> tags) {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.LUCID2_STAINED_GLASS_ON_OFF.getValue());
mplew.writeBool(enable);
mplew.writeInt(tags.size());
for (String name : tags) {
mplew.writeMapleString(name);
}
return mplew.getPacket();
}
public static byte[] breakStainedGlass(List<String> tags) {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.LUCID2_STAINED_GLASS_BREAK.getValue());
mplew.writeInt(tags.size());
for (String name : tags) {
mplew.writeMapleString(name);
}
return mplew.getPacket();
}
public static byte[] setFlyingMode(boolean enable) {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.LUCID2_SET_FLYING_MODE.getValue());
mplew.writeBool(enable);
return mplew.getPacket();
}
/**
* [USER=2000183830]para[/USER]m placement true:show/hide the statue, false:update the gauge
* [USER=2000183830]para[/USER]m gauge 0~3
* [USER=2000183830]para[/USER]m enable when the 'placement' is true, a boolean means put/remove, otherwise it means displaying horn effect.
* [USER=850422]return[/USER]
*/
public static byte[] changeStatueState(boolean placement, int gauge, boolean enable) {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.LUCID_STATUE_STATE_CHANGE.getValue());
mplew.writeInt(placement ? 1 : 0);
if (placement) {
mplew.writeBool(enable);
} else {
mplew.writeInt(gauge);
mplew.writeBool(enable);
}
return mplew.getPacket();
}
public static byte[] doShoot(int angle, int speed) {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.LUCID2_WELCOME_BARRAGE.getValue());
mplew.writeInt(0);
mplew.writeInt(angle);
mplew.writeInt(speed);
return mplew.getPacket();
}
public static byte[] doBidirectionShoot(int angleRate, int speed, int interval, int shotCount) {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.LUCID2_WELCOME_BARRAGE.getValue());
mplew.writeInt(3);
mplew.writeInt(angleRate);
mplew.writeInt(speed);
mplew.writeInt(interval);
mplew.writeInt(shotCount);
return mplew.getPacket();
}
public static byte[] doSpiralShoot(int angle, int angleRate, int angleDiff, int speed, int interval, int shotCount, int bulletAngleRate, int bulletSpeedRate) {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.LUCID2_WELCOME_BARRAGE.getValue());
mplew.writeInt(4);
mplew.writeInt(angle);
mplew.writeInt(angleRate);
mplew.writeInt(angleDiff);
mplew.writeInt(speed);
mplew.writeInt(interval);
mplew.writeInt(shotCount);
mplew.writeInt(bulletAngleRate);
mplew.writeInt(bulletSpeedRate);
return mplew.getPacket();
}
/**
* [USER=2000183830]para[/USER]m type should be 1/2/5.<br />
* 1 : ???<br />
* 2 : Start skill action<br />
* 5 : Stop skill action (for delaying or... idk)<br />
* 0/3/4 see below
* [USER=287391]See[/USER] FieldPacket.BossLucid.doShoot
* [USER=287391]See[/USER] FieldPacket.BossLucid.doBidirectionShoot
* [USER=287391]See[/USER] FieldPacket.BossLucid.doSpiralShoot
*/
public static byte[] doWelcomeBarrageSkill(int type) {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.LUCID2_WELCOME_BARRAGE.getValue());
mplew.writeInt(type);
return mplew.getPacket();
}
}
Handler
Implementing handlers is completely depend on how you implement/handle bosses... so honestly I will not answer (or would rather say I can't answer) what you should do.
LucidFairyDustCheck : oh, sorry I don't even know what will happen with this.
LucidActivateStatueRequest.java
LucidWelcomeBarrageEnd.java
LucidFairyDustCheck : oh, sorry I don't even know what will happen with this.
LucidActivateStatueRequest.java
Code:
package net.server.channel.handlers;
import client.MapleClient;
import net.AbstractMaplePacketHandler;
import server.field.Field;
import server.field.bosslucid.FieldLucid;
import tools.data.input.SeekableLittleEndianAccessor;
public class LucidActivateStatueRequest extends AbstractMaplePacketHandler {
@Override
public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
Field field = c.getUser().getField();
if (field instanceof FieldLucid) {
((FieldLucid) field).eraseButterflies(true);
}
}
}
Code:
package net.server.channel.handlers;
import client.MapleClient;
import net.AbstractMaplePacketHandler;
import server.field.Field;
import server.field.bosslucid.FieldLucid;
import tools.data.input.SeekableLittleEndianAccessor;
import tools.packet.FieldPacket;
public class LucidWelcomeBarrageEnd extends AbstractMaplePacketHandler {
@Override
public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
Field field = c.getUser().getField();
if (field instanceof FieldLucid) {
FieldLucid fieldLucid = (FieldLucid) field;
if (fieldLucid.getLucidState() != FieldLucid.LucidState.END_SKILL) {
c.sendPacket(FieldPacket.BossLucid.doWelcomeBarrageSkill(5));
return;
}
fieldLucid.setLucidState(FieldLucid.LucidState.NORMAL);
fieldLucid.broadcastPacket(FieldPacket.BossLucid.setStainedGlassOnOff(true, FieldLucid.STAINED_GLASS));
fieldLucid.broadcastPacket(FieldPacket.BossLucid.setFlyingMode(false));
}
}
}
Some material examples
Butterfly.java
Code:
package server.field.bosslucid;
import java.awt.Point;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import provider.MapleData;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
public class Butterfly {
private static List<Point> BUTTERFLY_POS1;
private static List<Point> BUTTERFLY_POS2;
[U]//Load at Server launching[/U]
public static void load() {
BUTTERFLY_POS1 = new ArrayList<>();
BUTTERFLY_POS2 = new ArrayList<>();
try {
MapleData butterflyData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath")+"/Etc.wz")).getData("BossLucid.img").getChildByPath("Butterfly");
for (MapleData d : butterflyData.getChildByPath("phase1_pos")) {
BUTTERFLY_POS1.add(MapleDataTool.getPoint("pos", d));
}
for (MapleData d : butterflyData.getChildByPath("phase2_pos")) {
BUTTERFLY_POS2.add(MapleDataTool.getPoint("pos", d));
}
} catch (NullPointerException e) {
System.err.println("[Butterfly] "+System.getProperty("wzpath")+"/Etc.wz/BossLucid.img/Butterfly is not found.");
}
}
public static Point getPosition(boolean isFirstPhase, int index) {
if (isFirstPhase && index < BUTTERFLY_POS1.size()) {
return BUTTERFLY_POS1.get(index);
} else if (index < BUTTERFLY_POS2.size()) {
return BUTTERFLY_POS2.get(index);
} else {
return new Point(0, 0);
}
}
public final int type;//templateId 0~8
public final Point pos;
public Butterfly(int type, boolean isFirstPhase, int index) {
this(type, getPosition(isFirstPhase, index));
}
public Butterfly(int type, Point pos) {
this.type = type;
this.pos = pos;
}
public static enum Mode {
ADD(0), MOVE(1), ATTACK(2), ERASE(3);
public final int code;
private Mode(int code) {
this.code = code;
}
}
}
Code:
public class FairyDust {
public final int scale;
public final int createDelay;
public final int moveSpeed;
public final int angle;
public FairyDust(int scale, int createDelay, int moveSpeed, int angle) {
this.scale = scale;
this.createDelay = createDelay;
this.moveSpeed = moveSpeed;
this.angle = angle;
}
}
Code:
public static final List<String> STAINED_GLASS = Arrays.asList("Bblue1", "Bblue2", "Bblue3", "Bred1", "Bred2", "Bred3", "Mred2", "Mred3", "Myellow1","Myellow2", "Myellow3");
*Disclaimer: values/formulas are incorrect, [STRIKE]or rather, who knows the right ones?[/STRIKE]
Code:
case 234://CONTAGION
//A buffstat named CONTAGION (infection)
break;
case 238://LUCID
if (!(field instanceof FieldLucid)) {
return;
}
FieldLucid fieldLucid = (FieldLucid) field;
switch(skillLv) {
case 1://Flower Trap
case 2:
case 3:
fieldLucid.broadcastPacket(FieldPacket.BossLucid.doFlowerTrapSkill(skillLv, Randomizer.nextInt(3), 1000, 48, Randomizer.nextBoolean()));
break;
case 4://Fairy Dust
case 10:
//Oh, I'm fairly serious.
List<FairyDust> fairyDust = new ArrayList<FairyDust>() {
{
int x, s, s2, v, v2, w, w2, u = 2640;
if (skillLv == 4) { s = 180; s2 = 240; v = 100; v2 = 5; w = 3; w2 = 1; x = 40;
} else { s = 30; s2 = 330; v = 250; v2 = 100; w = 6; w2 = 3; x = 5;}
for (int i = 0, max = Randomizer.rand(w, w + w2); i < max; i++) {
x += Randomizer.nextInt(x);
add(new FairyDust(Randomizer.nextInt(3), u, v + Randomizer.nextInt(v2), x + Randomizer.rand(s, s2)));
}
}
};
fieldLucid.broadcastPacket(FieldPacket.BossLucid.doFairyDustSkill(skillLv, fairyDust));
break;
case 5://Laser Rain
List<Integer> laserIntervals = new ArrayList<Integer>() {{for (int i = 0; i < 15; i++) add(500);}};
fieldLucid.broadcastPacket(FieldPacket.BossLucid.doLaserRainSkill(4500, laserIntervals));
break;
case 6://Forced Teleport
c.sendPacket(FieldPacket.BossLucid.doForcedTeleportSkill(Randomizer.nextInt(8)));//let's abuse the controller
break;
case 7://Dragon
boolean isLeft= Randomizer.isSuccess(70);
if (fieldLucid.getId() == FieldLucid.PHASE1_MAP) {//450004150?
fieldLucid.broadcastPacket(FieldPacket.BossLucid.createDragon(1, 0, 0, 0, 0, isLeft));
} else {
int createPosX = isLeft ? -138 : 1498;
int createPosY = Randomizer.nextBoolean() ? -1312 : 238;
int posX = createPosX;
int posY = mob.getPosition().y;
fieldLucid.broadcastPacket(FieldPacket.BossLucid.createDragon(2, posX, posY, createPosX, createPosY, isLeft));
}
break;
case 8://Rush
fieldLucid.broadcastPacket(FieldPacket.BossLucid.doRushSkill());
break;
case 9://Welcome Barrage
if (fieldLucid.getButterflyCount() == 0) {
fieldLucid.setLucidState(FieldLucid.LucidState.NORMAL);
return;
}
fieldLucid.setLucidState(FieldLucid.LucidState.DO_SKILL);
fieldLucid.broadcastPacket(FieldPacket.BossLucid.setButterflyAction(Butterfly.Mode.MOVE, 700, -600));
fieldLucid.resetButterflyPositions();
fieldLucid.getLifePool().removeAllMobsExcept(Collections.singletonList(mob));
fieldLucid.broadcastPacket(FieldPacket.BossLucid.doWelcomeBarrageSkill(2));
fieldLucid.broadcastPacket(FieldPacket.BossLucid.setStainedGlassOnOff(false, FieldLucid.STAINED_GLASS));
fieldLucid.broadcastPacket(FieldPacket.BossLucid.setFlyingMode(true));
fieldLucid.broadcastPacket(FieldPacket.BossLucid.doBidirectionShoot(50, 120, 500, 3));
fieldLucid.broadcastPacket(FieldPacket.BossLucid.doSpiralShoot(180, 150, 30, 30, 700, 12, 5, 1));
fieldLucid.broadcastPacket(FieldPacket.BossLucid.doBidirectionShoot(50, 100, 500, 4));
fieldLucid.broadcastPacket(FieldPacket.BossLucid.doSpiralShoot(180, 150, 30, 70, 1000, 12, 10, 0));
fieldLucid.broadcastPacket(FieldPacket.BossLucid.doBidirectionShoot(50, 90, 700, 8));
fieldLucid.broadcastPacket(FieldPacket.BossLucid.doSpiralShoot(180, 100, 30, 100, 700, 12, 0, 0));
TimerManager.getInstance().schedule(() -> fieldLucid.setLucidState(FieldLucid.LucidState.END_SKILL), 15700);
break;
}
break;
Code:
public static byte[] showSpineScreen(boolean isBinary, boolean isLoop, boolean isPostRender, String path, String animationName, int endDelay) {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.FIELD_EFFECT.getValue());
mplew.write(FieldEffectType.SPINE_SCREEN.type);
mplew.writeBool(isBinary);//not .json file
mplew.writeBool(isLoop);
mplew.writeBool(isPostRender);
mplew.writeInt(endDelay);
mplew.writeMapleString(path);//e.g. "Map/Effect3.img/BossLucid/Lucid/lusi"
mplew.writeMapleString(animationName);//e.g. "animation"
mplew.writeBool(false);//use key
//mplew.writeMapleString("");//the key to stop animation?
return mplew.getPacket();
}
Video
Thanks for reading
Last edited: