- Joined
- Aug 21, 2009
- Messages
- 1,149
- Reaction score
- 599
Hello there.
I just... lost another bet with Moogra, as most of the times (all of them), so, as a loser... I gotta release something to RaGEZONE, doesn't matter what.
So, this time (3th time?) I'm releasing a NPC script generator.
Let me explain what is this about.
This will generate a NPC based on the packets you obtain from sniffing the NEXON's packets, obviously, it will be in our Java base NPC scripting version, otherwise it would be pointless.
Things you gotta know, is that this code was made to work only in Snow sniffer format, as I really dislike MapleShark format, so I regularly re-format the logs from MapleShark to Snow Format, because MapleShark is actually pretty good as a sniffer. You can change this anytime, because the source is technically yours.
Another thing to know, is that it's not 100% complete, as it may not completely do ALL the NPC's scripts, it was made for basic NPC's that give or takes mesos and warp you. The rest can be easily done as well, it's not that complex. However, as it's a release, it works as expected, it may just not fulfill all your needs.
Another thing is, the code is not "pro", so feel free to critic it, I just spend actually 1 hour on this to pay my debt.
Enough talk...
EDIT:
Oh yeah, I made the file to read "RandomLog.txt", you can change it to whatever you want, or make it read a whole directory, it's up to however wants to use this.
UPDATE:
The code has been updated to work even better and fixed tons of bugs it originally had. Still, it may not always works, but the range of non-working cases have been reduced.
Screenshot
I just... lost another bet with Moogra, as most of the times (all of them), so, as a loser... I gotta release something to RaGEZONE, doesn't matter what.
So, this time (3th time?) I'm releasing a NPC script generator.
Let me explain what is this about.
This will generate a NPC based on the packets you obtain from sniffing the NEXON's packets, obviously, it will be in our Java base NPC scripting version, otherwise it would be pointless.
Things you gotta know, is that this code was made to work only in Snow sniffer format, as I really dislike MapleShark format, so I regularly re-format the logs from MapleShark to Snow Format, because MapleShark is actually pretty good as a sniffer. You can change this anytime, because the source is technically yours.
Another thing to know, is that it's not 100% complete, as it may not completely do ALL the NPC's scripts, it was made for basic NPC's that give or takes mesos and warp you. The rest can be easily done as well, it's not that complex. However, as it's a release, it works as expected, it may just not fulfill all your needs.
Another thing is, the code is not "pro", so feel free to critic it, I just spend actually 1 hour on this to pay my debt.
Enough talk...
EDIT:
Oh yeah, I made the file to read "RandomLog.txt", you can change it to whatever you want, or make it read a whole directory, it's up to however wants to use this.
UPDATE:
The code has been updated to work even better and fixed tons of bugs it originally had. Still, it may not always works, but the range of non-working cases have been reduced.
NPCScriptGenerator.java
NPC.java
NPCMessage.java
Instruction.java
Mesos.java
Warp.java
Player.java
Reader.java
Character.java
Condition.java
Location.java
PHP:
package server;
import condition.Condition;
import condition.Location;
import npc.NPCMessage;
import npc.NPC;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.commons.codec.DecoderException;
import packet.Reader;
import player.Player;
import server.instruction.Instruction;
import server.instruction.Mesos;
import server.instruction.Warp;
/**
*
* @author XxOsirisxX
*/
public class NPCScriptGenerator {
private Player player;
private Map<Integer, NPC> npcs;
private int npcMessageFlow;
private int currentNpcId;
private boolean npcTalk;
private boolean npcMoreTalk;
private boolean readNpc;
private boolean readPlayer;
private int instructionType = -1;
private int selectedOption = -1;
public static void main(String[] args) {
new NPCScriptGenerator().start();
}
private void start() {
initiate();
String filename = "dkpackets.txt";
Reader read;
try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
String line;
restartAttributes();
List<Instruction> instructions = new LinkedList<Instruction>();
while ((line = br.readLine()) != null) {
if (line.contains("Sent")) {
if (line.contains("NPC_TALK ")) {
npcTalk = true;
npcMessageFlow = 0;
} else if (line.contains("NPC_TALK_MORE ") && npcTalk) {
npcMoreTalk = true;
}
continue;
}
if (readPlayer) {
boolean playerLoggedIn = line.length() > 200;
read = new Reader(line);
read.skip(2);
int channel = read.readInt();
int mapId;
if (!playerLoggedIn) {
read.skip(5);
mapId = read.readInt();
} else {
read.skip(109);
mapId = read.readInt();
}
player = new Player();
player.setMapId(mapId);
player.setChannel(channel + 1);
readPlayer = false;
}
if (npcMoreTalk) {
read = new Reader(line);
read.skip(2);
byte previousMessageType = read.readByte();
npcTalk = read.readByte() == 1;
switch (previousMessageType) {
case 0x02: //Text
String textInput = read.readMapleString();
break;
case 0x03: //Numbers
case 0x04: //Options
int selection = read.readInt();
selectedOption = selection;
break;
}
npcMoreTalk = false;
} else {
if (instructionType > -1) {
read = new Reader(line);
switch (instructionType) {
case 0x00: //WARP
read.skip(11);
int mapId = read.readInt();
byte spawnPoint = read.readByte();
Warp warp = new Warp();
warp.setMapId(mapId);
warp.setSpawnPoint(spawnPoint);
instructions.add(warp);
applyDummyConversation(instructions);
instructions = new LinkedList<Instruction>();
restartAttributes();
break;
case 0x01: //SHOW_STATUS_INFO
byte type = read.readByte();
if (type == 5) { //Mesos
int amount = read.readInt();
Mesos mesos = new Mesos();
mesos.setAmount(amount);
instructions.add(mesos);
} else if (type == 0) { //Inventory - Mesos Included
}
break;
}
instructionType = -1;
continue;
}
if (!npcTalk) {
if (line.contains("Received")) {
if (line.contains("WARP_TO_MAP ")) {
readPlayer = true;
}
continue;
}
}
if (npcTalk) {
if (line.contains("Received")) {
if (line.contains("NPC_TALK ")) {
readNpc = true;
} else if (line.contains("WARP_TO_MAP ")) {
instructionType = 0;
} else if (line.contains("SHOW_STATUS_INFO ")) {
instructionType = 1;
} else if (line.contains("OPEN_NPC_SHOP ")) {
restartAttributes();
} else if (line.contains("UPDATE_SKILLS")) {
restartAttributes();
} else if (line.contains("OPEN_STORAGE")) {
restartAttributes();
}
continue;
}
if (readNpc) {
read = new Reader(line);
read.skip(3);
int npcId = read.readInt();
byte messageType = read.readByte();
read.skip(1);
String message = read.readMapleString();
byte messageButtons = 0;
if (messageType == 0) {
messageButtons |= read.readByte() << 1; //Previous
messageButtons |= read.readByte(); //Next
}
currentNpcId = npcId;
NPC npc;
if (npcs.containsKey(npcId)) {
npc = npcs.get(npcId);
} else {
npc = new NPC(npcId);
}
npc.applyConversation(npcMessageFlow, player, messageType, message, messageButtons, selectedOption, instructions);
npcs.put(npcId, npc);
npcMessageFlow++;
instructions = new LinkedList<Instruction>();
readNpc = false;
}
}
}
}
adjustCondition();
generateScripts();
} catch (IOException ioe) {
System.err.println("Error while reading the file " + filename + ".");
} catch (DecoderException de) {
System.err.println("Error while trying to decode string in file " + filename + ".");
}
}
private void adjustCondition() {
for (NPC npc : npcs.values()) {
npc.adjustConditions();
}
}
private void initiate() {
npcs = new LinkedHashMap<Integer, NPC>();
}
private void restartAttributes() {
npcMessageFlow = 0;
currentNpcId = 0;
npcTalk = false;
npcMoreTalk = false;
readNpc = false;
instructionType = -1;
selectedOption = -1;
readPlayer = false;
}
private void generateScripts() {
for (NPC npc : npcs.values()) {
String filename = npc.getId() + ".txt";
try (final BufferedWriter bw = new BufferedWriter(new FileWriter(filename))) {
StringBuilder sb = new StringBuilder();
sb.append("status = -1;\r\n\r\n");
sb.append("function start() {\r\n\t");
for (int n = 0; n < npc.getMessages().get(0).size(); n++) {
List<Condition> conditions = npc.getMessages().get(0).get(n).getConditions();
int conditionSize = 0;
if (npc.getMessages().get(0).get(n).getConditions() != null) {
conditionSize = conditions.size();
sb.append(getScriptConditions(conditions));
}
sb.append(getScriptMessageType(npc.getMessages().get(0).get(n)));
for (int x = 0; x < conditionSize; x++) {
sb.append("\t}\r\n");
}
}
if (npc.getMessages().size() == 1) {
sb.append("\tcm.dispose();\r\n");
}
sb.append("}\r\n\r\n");
if (npc.getMessages().size() > 1) {
sb.append("function action(mode, type, selection) {\r\n");
sb.append("\tstatus++;\r\n");
sb.append("\tif ((mode == 0 && type == 4) || mode == -1) {\r\n");
sb.append("\t\tcm.dispose();\r\n");
sb.append("\t\treturn;\r\n");
sb.append("\t}\r\n");
sb.append("\tif (mode == 0 && type == 0) {\r\n");
sb.append("\t\tstatus -= 2;\r\n");
sb.append("\t}\r\n");
for (int i = 1; i < npc.getMessages().size(); i++) {
if (i > 1) {
sb.append(" else if (status == ").append(i - 1).append(") {\r\n\t");
} else {
sb.append("\tif (status == ").append(i - 1).append(") {\r\n\t");
}
for (int n = 0; n < npc.getMessages().get(i).size(); n++) {
NPCMessage data = npc.getMessages().get(i).get(n);
if (data.getConditions() != null) {
sb.append(getScriptConditions(data.getConditions()));
}
sb.append(getScriptOptions(data.getSelectedOption()));
sb.append("\t");
if (data.getSelectedOption() > -1) {
sb.append("\t\t");
}
sb.append(getScriptInstructions(data.getInstructions()));
sb.append(getScriptMessageType(data));
if (data.getSelectedOption() > -1) {
sb.append("\t\t}\r\n");
}
if (data.getConditions() != null) {
for (int x = 0; x < data.getConditions().size(); x++) {
sb.append("\t\t}\r\n");
}
}
}
sb.append("\t}");
}
sb.append("\r\n");
sb.append("}");
}
bw.append(sb);
bw.close();
} catch (IOException ioe) {
System.err.println("Error while writing the file " + filename + ".");
}
}
}
private void applyDummyConversation(List<Instruction> instructions) {
NPC npc;
if (npcs.containsKey(currentNpcId)) {
npc = npcs.get(currentNpcId);
} else {
npc = new NPC(currentNpcId);
}
npc.applyConversation(npcMessageFlow, player, -1, "", (byte) 0, -1, instructions);
}
private StringBuilder getScriptConditions(List<Condition> conditions) {
StringBuilder ret = new StringBuilder();
for (Condition condition : conditions) {
if (condition instanceof Location) {
Location location = (Location) condition;
ret.append("\tif (cm.getMapId() == ").append(location.getMapId()).append(") {\r\n");
}
}
return ret;
}
private StringBuilder getScriptOptions(int selectedOption) {
StringBuilder ret = new StringBuilder();
if (selectedOption > -1) {
ret.append("\tif (selection == ").append(selectedOption).append(") {\r\n");
}
return ret;
}
private StringBuilder getScriptInstructions(List<Instruction> instructions) {
StringBuilder ret = new StringBuilder();
for (Instruction instruction : instructions) {
if (instruction instanceof Mesos) {
Mesos mesos = (Mesos) instruction;
ret.append("\t\tcm.gainMesos(").append(mesos.getAmount()).append(");\r\n");
} else if (instruction instanceof Warp) {
Warp warp = (Warp) instruction;
ret.append("\t\tcm.warp(").append(warp.getMapId()).append(", ").append(warp.getSpawnPoint()).append(");\r\n");
}
if (instruction.isDispose()) {
ret.append("\t\tcm.dispose();\r\n");
}
}
return ret;
}
private StringBuilder getScriptMessageType(NPCMessage message) {
StringBuilder ret = new StringBuilder();
switch (message.getMessageType()) {
case 0x00: //Next | Prev | OK
switch (message.getMessageButtons()) {
case 0x00: //OK
ret.append("cm.sendOk(\"").append(message.getMessage()).append("\");\r\n");
break;
case 0x01: //Next
ret.append("cm.sendNext(\"").append(message.getMessage()).append("\");\r\n");
break;
case 0x02: //Previous
ret.append("cm.sendPrev(\"").append(message.getMessage()).append("\");\r\n");
break;
case 0x03: //Next | Previous
ret.append("cm.sendNextPrev(\"").append(message.getMessage()).append("\");\r\n");
break;
}
break;
case 0x01: //Yes | No
ret.append("cm.sendYesNo(\"").append(message.getMessage()).append("\");\r\n");
break;
case 0x02: //Text
ret.append("cm.sendGetText(\"").append(message.getMessage()).append("\");\r\n");
break;
case 0x03: //Numbers
ret.append("cm.sendGetNumber(\"").append(message.getMessage()).append("\");\r\n");
break;
case 0x04: //Options
ret.append("cm.sendSimple(\"").append(message.getMessage()).append("\");\r\n");
break;
case 0x07: //Style
ret.append("cm.sendStyle(\"").append(message.getMessage()).append("\");\r\n");
break;
case 0x0C: //Accept | Decline
ret.append("cm.sendAcceptDecline(\"").append(message.getMessage()).append("\");\r\n");
break;
case 0x0E: //Locations
ret.append("cm.sendPlaces();\r\n");
break;
}
return ret;
}
}
NPC.java
PHP:
package npc;
import condition.Condition;
import condition.Location;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import player.Player;
import server.instruction.Instruction;
/**
*
* @author XxOsirisxX
*/
public class NPC {
private static int messageChainId = 0;
private final int id;
private Map<Integer, List<NPCMessage>> messages;
public NPC(int id) {
this.id = id;
}
public void applyConversation(int flow, Player player, int messageType, String message, byte messageButtons, int selectedOption, List<Instruction> instructions) {
if (messages == null) {
messages = new LinkedHashMap<Integer, List<NPCMessage>>();
}
if (flow == 0) {
messageChainId++;
}
NPCMessage newMessage = new NPCMessage(messageChainId, player, messageType, message, messageButtons, selectedOption, instructions, null);
List<NPCMessage> actualMessages = new LinkedList<NPCMessage>();
if (messages.get(flow) != null) {
boolean repeated = false;
for (NPCMessage m : messages.get(flow)) {
if (m.getMessage().equals(newMessage.getMessage())) {
repeated = true;
break;
}
}
if (repeated) {
return;
}
actualMessages = messages.get(flow);
byte conditionType = 0;
for (int i = 0; i < messages.get(flow).size(); i++) {
List<Condition> oldCondition;
NPCMessage storedMessage = messages.get(flow).get(i);
if (storedMessage.getConditions() != null) {
oldCondition = storedMessage.getConditions();
} else {
oldCondition = new LinkedList<Condition>();
}
if (storedMessage.getPlayer().getMapId() != player.getMapId()) {
conditionType |= 1;
boolean repeatedCondition = false;
if (storedMessage.getConditions() != null) {
for (Condition c : storedMessage.getConditions()) {
if (c instanceof Location) {
repeatedCondition = true;
break;
}
}
}
if (repeatedCondition) {
continue;
}
Location location = new Location();
location.setMapId(storedMessage.getPlayer().getMapId());
oldCondition.add(location);
storedMessage.setConditions(oldCondition);
}
}
List<Condition> newCondition = new LinkedList<Condition>();
if ((conditionType & 0x01) == 0x01) {
Location location = new Location();
location.setMapId(player.getMapId());
newCondition.add(location);
newMessage.setConditions(newCondition);
}
}
actualMessages.add(newMessage);
messages.put(flow, actualMessages);
}
public void adjustConditions() {
for (Entry allMessages : messages.entrySet()) {
List<NPCMessage> npcMessages = (List<NPCMessage>) allMessages.getValue();
List<NPCMessage> npcMessages2 = (List<NPCMessage>) allMessages.getValue();
for (NPCMessage message : npcMessages) {
if (message.getConditions() != null) {
int uniqueId = 0;
Condition locationCondition = null;
for (Condition conditions : message.getConditions()) {
if (conditions instanceof Location) {
uniqueId = message.getMessageChainId();
locationCondition = conditions;
}
}
if (uniqueId > 0) {
for (List<NPCMessage> msg : messages.values()) {
for (NPCMessage messagex : msg) {
if (messagex.getMessageChainId() == uniqueId) {
if (messagex.getConditions() == null) {
messagex.setConditions(message.getConditions());
} else {
List<Condition> conditions = messagex.getConditions();
boolean ignore = false;
for (Condition all : conditions) {
if (all instanceof Location) {
ignore = true;
break;
}
}
if (ignore) {
continue;
}
conditions.add(locationCondition);
messagex.setConditions(conditions);
}
}
}
}
}
}
}
}
}
public int getId() {
return id;
}
public Map<Integer, List<NPCMessage>> getMessages() {
return messages;
}
}
NPCMessage.java
PHP:
package npc;
import condition.Condition;
import java.util.List;
import player.Player;
import server.instruction.Instruction;
/**
*
* @author XxOsirisxX
*/
public class NPCMessage {
private int messageChainId;
private int messageType;
private int selectedOption;
private byte messageButtons;
private String message;
private List<Instruction> instructions;
private List<Condition> conditions;
private Player player;
public NPCMessage(int messageChainId, Player player, int messageType, String message, byte messageButtons, int selectedOption, List<Instruction> instructions, List<Condition> conditions) {
this.messageChainId = messageChainId;
this.player = player;
this.messageType = messageType;
this.message = message;
this.messageButtons = messageButtons;
this.instructions = instructions;
this.selectedOption = selectedOption;
this.conditions = conditions;
}
public Player getPlayer() {
return player;
}
public void setPlayer(Player player) {
this.player = player;
}
public int getMessageType() {
return messageType;
}
public void setMessageType(int messageType) {
this.messageType = messageType;
}
public byte getMessageButtons() {
return messageButtons;
}
public void setMessageButtons(byte messageButtons) {
this.messageButtons = messageButtons;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getSelectedOption() {
return selectedOption;
}
public void setSelectedOption(int selectedOption) {
this.selectedOption = selectedOption;
}
public List<Instruction> getInstructions() {
return instructions;
}
public void setInstructions(List<Instruction> instructions) {
this.instructions = instructions;
}
public List<Condition> getConditions() {
return conditions;
}
public void setConditions(List<Condition> conditions) {
this.conditions = conditions;
}
public int getMessageChainId() {
return messageChainId;
}
public void setMessageChainId(int messageChainId) {
this.messageChainId = messageChainId;
}
}
Instruction.java
PHP:
package server.instruction;
/**
*
* @author XxOsirisxX
*/
public abstract class Instruction {
private boolean dispose;
public Instruction(boolean dispose) {
this.dispose = dispose;
}
public boolean isDispose() {
return dispose;
}
}
Mesos.java
PHP:
package server.instruction;
/**
*
* @author XxOsirisxX
*/
public class Mesos extends Instruction {
private int amount;
public Mesos() {
super(false);
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
}
Warp.java
PHP:
package server.instruction;
/**
*
* @author XxOsirisxX
*/
public class Warp extends Instruction {
private int mapId;
private short spawnPoint;
public Warp() {
super(true);
}
public int getMapId() {
return mapId;
}
public void setMapId(int mapId) {
this.mapId = mapId;
}
public short getSpawnPoint() {
return spawnPoint;
}
public void setSpawnPoint(short spawnPoint) {
this.spawnPoint = spawnPoint;
}
}
Player.java
PHP:
package player;
/**
*
* @author XxOsirisxX
*/
public class Player {
private int channel;
private int mapId;
public int getMapId() {
return mapId;
}
public void setMapId(int mapId) {
this.mapId = mapId;
}
public int getChannel() {
return channel;
}
public void setChannel(int channel) {
this.channel = channel;
}
}
Reader.java
PHP:
package packet;
import java.io.UnsupportedEncodingException;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
/**
*
* @author XxOsirisxX
*/
public class Reader {
private StringBuilder packetString;
public Reader(String packetString) {
this.packetString = new StringBuilder(packetString.replace(" ", ""));
}
public byte readByte() {
return Byte.parseByte(readString(0, 2), 16);
}
public short readShort() {
return Short.parseShort(readString(2, 4) + readString(0, 2), 16);
}
public int readInt() {
return (int) Long.parseLong(readString(6, 8) + readString(4, 6) + readString(2, 4) + readString(0, 2), 16);
}
public long readLong() {
return Long.parseLong(readString(14, 16) + readString(12, 14) + readString(10, 12) + readString(8, 10) + readString(6, 8) + readString(4, 6) + readString(2, 4) + readString(0, 2), 16);
}
public String readMapleString() throws DecoderException, UnsupportedEncodingException {
short messageLenght = readShort();
return new String(Hex.decodeHex(readString(0, messageLenght * 2).toCharArray()), "UTF8");
}
public void skip(int pairs) {
readString(0, pairs * 2);
}
private String readString(int start, int end) {
String data = packetString.substring(start, end);
packetString = packetString.delete(start, end);
return data;
}
}
Character.java
PHP:
package condition;
/**
*
* @author XxOsirisxX
*/
public class Character extends Condition {
private int level;
private int mesos;
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public int getMesos() {
return mesos;
}
public void setMesos(int mesos) {
this.mesos = mesos;
}
}
Condition.java
PHP:
package condition;
/**
*
* @author XxOsirisxX
*/
public abstract class Condition {
}
Location.java
PHP:
package condition;
/**
*
* @author XxOsirisxX
*/
public class Location extends Condition {
private int mapId;
private short xposition;
private short yposition;
public int getMapId() {
return mapId;
}
public void setMapId(int mapId) {
this.mapId = mapId;
}
public short getXposition() {
return xposition;
}
public void setXposition(short xposition) {
this.xposition = xposition;
}
public short getYposition() {
return yposition;
}
public void setYposition(short yposition) {
this.yposition = yposition;
}
}
Screenshot
Last edited:

