Merge branch 'master' into netty
This commit is contained in:
@@ -23,16 +23,16 @@ package client;
|
||||
|
||||
import constants.skills.*;
|
||||
import provider.*;
|
||||
import provider.wz.WZFiles;
|
||||
import server.MapleStatEffect;
|
||||
import server.life.Element;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class SkillFactory {
|
||||
private static volatile Map<Integer, Skill> skills = new HashMap<>();
|
||||
private static final MapleDataProvider datasource = MapleDataProviderFactory.getDataProvider(MapleDataProviderFactory.fileInWZPath("Skill.wz"));
|
||||
private static final MapleDataProvider datasource = MapleDataProviderFactory.getDataProvider(WZFiles.SKILL);
|
||||
|
||||
public static Skill getSkill(int id) {
|
||||
return skills.get(id);
|
||||
@@ -330,7 +330,7 @@ public class SkillFactory {
|
||||
}
|
||||
|
||||
public static String getSkillName(int skillid) {
|
||||
MapleData data = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "String.wz")).getData("Skill.img");
|
||||
MapleData data = MapleDataProviderFactory.getDataProvider(WZFiles.STRING).getData("Skill.img");
|
||||
StringBuilder skill = new StringBuilder();
|
||||
skill.append(skillid);
|
||||
if (skill.length() == 4) {
|
||||
|
||||
@@ -27,8 +27,7 @@ import client.*;
|
||||
import client.command.Command;
|
||||
import provider.MapleData;
|
||||
import provider.MapleDataProviderFactory;
|
||||
|
||||
import java.io.File;
|
||||
import provider.wz.WZFiles;
|
||||
|
||||
public class MaxSkillCommand extends Command {
|
||||
{
|
||||
@@ -38,7 +37,7 @@ public class MaxSkillCommand extends Command {
|
||||
@Override
|
||||
public void execute(MapleClient c, String[] params) {
|
||||
MapleCharacter player = c.getPlayer();
|
||||
for (MapleData skill_ : MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "String.wz")).getData("Skill.img").getChildren()) {
|
||||
for (MapleData skill_ : MapleDataProviderFactory.getDataProvider(WZFiles.STRING).getData("Skill.img").getChildren()) {
|
||||
try {
|
||||
Skill skill = SkillFactory.getSkill(Integer.parseInt(skill_.getName()));
|
||||
player.changeSkillLevel(skill, (byte) skill.getMaxLevel(), skill.getMaxLevel(), -1);
|
||||
|
||||
@@ -27,8 +27,7 @@ import client.*;
|
||||
import client.command.Command;
|
||||
import provider.MapleData;
|
||||
import provider.MapleDataProviderFactory;
|
||||
|
||||
import java.io.File;
|
||||
import provider.wz.WZFiles;
|
||||
|
||||
public class ResetSkillCommand extends Command {
|
||||
{
|
||||
@@ -38,7 +37,7 @@ public class ResetSkillCommand extends Command {
|
||||
@Override
|
||||
public void execute(MapleClient c, String[] params) {
|
||||
MapleCharacter player = c.getPlayer();
|
||||
for (MapleData skill_ : MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "String.wz")).getData("Skill.img").getChildren()) {
|
||||
for (MapleData skill_ : MapleDataProviderFactory.getDataProvider(WZFiles.STRING).getData("Skill.img").getChildren()) {
|
||||
try {
|
||||
Skill skill = SkillFactory.getSkill(Integer.parseInt(skill_.getName()));
|
||||
player.changeSkillLevel(skill, (byte) 0, skill.getMaxLevel(), -1);
|
||||
|
||||
@@ -30,12 +30,11 @@ import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import provider.wz.WZFiles;
|
||||
import server.MapleItemInformationProvider;
|
||||
import server.quest.MapleQuest;
|
||||
import tools.Pair;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class SearchCommand extends Command {
|
||||
private static MapleData npcStringData;
|
||||
private static MapleData mobStringData;
|
||||
@@ -45,7 +44,7 @@ public class SearchCommand extends Command {
|
||||
{
|
||||
setDescription("Search String.wz.");
|
||||
|
||||
MapleDataProvider dataProvider = MapleDataProviderFactory.getDataProvider(new File("wz/String.wz"));
|
||||
MapleDataProvider dataProvider = MapleDataProviderFactory.getDataProvider(WZFiles.STRING);
|
||||
npcStringData = dataProvider.getData("Npc.img");
|
||||
mobStringData = dataProvider.getData("Mob.img");
|
||||
skillStringData = dataProvider.getData("Skill.img");
|
||||
|
||||
@@ -25,8 +25,8 @@ import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import provider.wz.WZFiles;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -35,7 +35,7 @@ import java.util.Map;
|
||||
* @author Danny (Leifde)
|
||||
*/
|
||||
public class PetDataFactory {
|
||||
private static MapleDataProvider dataRoot = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Item.wz"));
|
||||
private static MapleDataProvider dataRoot = MapleDataProviderFactory.getDataProvider(WZFiles.ITEM);
|
||||
private static Map<String, PetCommand> petCommands = new HashMap<>();
|
||||
private static Map<Integer, Integer> petHunger = new HashMap<>();
|
||||
|
||||
|
||||
@@ -20,24 +20,25 @@
|
||||
package client.processor.action;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import java.awt.Point;
|
||||
import client.MapleClient;
|
||||
import client.SkillFactory;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import client.inventory.MaplePet;
|
||||
import client.SkillFactory;
|
||||
import provider.MapleDataTool;
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import java.io.File;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import provider.wz.WZFiles;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author RonanLana - just added locking on OdinMS' SpawnPetHandler method body
|
||||
*/
|
||||
public class SpawnPetProcessor {
|
||||
private static MapleDataProvider dataRoot = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Item.wz"));
|
||||
private static final MapleDataProvider dataRoot = MapleDataProviderFactory.getDataProvider(WZFiles.ITEM);
|
||||
|
||||
public static void processSpawnPet(MapleClient c, byte slot, boolean lead) {
|
||||
if (c.tryacquireClient()) {
|
||||
|
||||
@@ -5,11 +5,11 @@ import client.MapleJob;
|
||||
import config.YamlConfig;
|
||||
import constants.skills.Aran;
|
||||
import provider.*;
|
||||
import provider.wz.WZFiles;
|
||||
import server.maps.FieldLimit;
|
||||
import server.maps.MapleMap;
|
||||
import server.quest.MapleQuest;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.*;
|
||||
@@ -676,7 +676,7 @@ public class GameConstants {
|
||||
}
|
||||
|
||||
private static int getMaxObstacleMobDamageFromWz() {
|
||||
MapleDataProvider mapSource = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Map.wz"));
|
||||
MapleDataProvider mapSource = MapleDataProviderFactory.getDataProvider(WZFiles.MAP);
|
||||
int maxMobDmg = 0;
|
||||
|
||||
MapleDataDirectoryEntry root = mapSource.getRoot();
|
||||
|
||||
@@ -985,7 +985,6 @@ public class Server {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.setProperty("wzpath", "wz");
|
||||
Security.setProperty("crypto.policy", "unlimited");
|
||||
AutoJCE.removeCryptographyRestrictions();
|
||||
Server.getInstance().init();
|
||||
|
||||
@@ -34,10 +34,10 @@ import net.server.world.MapleParty;
|
||||
import provider.MapleData;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import provider.wz.WZFiles;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Pair;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
@@ -68,7 +68,7 @@ public class MaplePartySearchCoordinator {
|
||||
private static Map<Integer, Set<Integer>> fetchNeighbouringMaps() {
|
||||
Map<Integer, Set<Integer>> mapLinks = new HashMap<>();
|
||||
|
||||
MapleData data = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "Etc.wz")).getData("MapNeighbors.img");
|
||||
MapleData data = MapleDataProviderFactory.getDataProvider(WZFiles.ETC).getData("MapNeighbors.img");
|
||||
if (data != null) {
|
||||
for (MapleData mapdata : data.getChildren()) {
|
||||
int mapid = Integer.parseInt(mapdata.getName());
|
||||
|
||||
@@ -21,18 +21,18 @@
|
||||
*/
|
||||
package provider;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import provider.wz.WZFile;
|
||||
import provider.wz.WZFiles;
|
||||
import provider.wz.XMLWZFile;
|
||||
|
||||
public class MapleDataProviderFactory {
|
||||
private final static String wzPath = System.getProperty("wzpath");
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
private static MapleDataProvider getWZ(File in, boolean provideImages) {
|
||||
public class MapleDataProviderFactory {
|
||||
private static MapleDataProvider getWZ(File in) {
|
||||
if (in.getName().toLowerCase().endsWith("wz") && !in.isDirectory()) {
|
||||
try {
|
||||
return new WZFile(in, provideImages);
|
||||
return new WZFile(in, false);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Loading WZ File failed", e);
|
||||
}
|
||||
@@ -41,15 +41,7 @@ public class MapleDataProviderFactory {
|
||||
}
|
||||
}
|
||||
|
||||
public static MapleDataProvider getDataProvider(File in) {
|
||||
return getWZ(in, false);
|
||||
}
|
||||
|
||||
public static MapleDataProvider getImageProvidingDataProvider(File in) {
|
||||
return getWZ(in, true);
|
||||
}
|
||||
|
||||
public static File fileInWZPath(String filename) {
|
||||
return new File(wzPath, filename);
|
||||
public static MapleDataProvider getDataProvider(WZFiles in) {
|
||||
return getWZ(in.getFile());
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,6 @@
|
||||
*/
|
||||
package provider.wz;
|
||||
|
||||
import provider.MapleDataProviderFactory;
|
||||
import tools.data.input.GenericLittleEndianAccessor;
|
||||
import tools.data.input.InputStreamByteStream;
|
||||
import tools.data.input.LittleEndianAccessor;
|
||||
@@ -69,7 +68,7 @@ public class ListWZFile {
|
||||
if (listWz != null) {
|
||||
ListWZFile listwz;
|
||||
try {
|
||||
listwz = new ListWZFile(MapleDataProviderFactory.fileInWZPath("List.wz"));
|
||||
listwz = new ListWZFile(WZFiles.LIST.getFile());
|
||||
modernImgs = new HashSet<>(listwz.getEntries());
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
35
src/main/java/provider/wz/WZFiles.java
Normal file
35
src/main/java/provider/wz/WZFiles.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package provider.wz;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public enum WZFiles {
|
||||
QUEST("Quest"),
|
||||
ETC("Etc"),
|
||||
ITEM("Item"),
|
||||
CHARACTER("Character"),
|
||||
STRING("String"),
|
||||
LIST("List"),
|
||||
MOB("Mob"),
|
||||
MAP("Map"),
|
||||
NPC("Npc"),
|
||||
REACTOR("Reactor"),
|
||||
SKILL("Skill"),
|
||||
SOUND("Sound"),
|
||||
UI("UI");
|
||||
|
||||
public static final String DIRECTORY = "wz";
|
||||
|
||||
private final String fileName;
|
||||
|
||||
WZFiles(String name) {
|
||||
this.fileName = name + ".wz";
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return new File(DIRECTORY, fileName);
|
||||
}
|
||||
|
||||
public String getFilePath() {
|
||||
return getFile().getPath();
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,7 @@ import net.server.world.MapleParty;
|
||||
import net.server.world.MaplePartyCharacter;
|
||||
import provider.MapleData;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.wz.WZFiles;
|
||||
import scripting.AbstractPlayerInteraction;
|
||||
import server.*;
|
||||
import server.MapleSkillbookInformationProvider.SkillBookEntry;
|
||||
@@ -62,7 +63,6 @@ import tools.MaplePacketCreator;
|
||||
import tools.packets.Wedding;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
@@ -382,7 +382,7 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
|
||||
}
|
||||
|
||||
public void maxMastery() {
|
||||
for (MapleData skill_ : MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "String.wz")).getData("Skill.img").getChildren()) {
|
||||
for (MapleData skill_ : MapleDataProviderFactory.getDataProvider(WZFiles.STRING).getData("Skill.img").getChildren()) {
|
||||
try {
|
||||
Skill skill = SkillFactory.getSkill(Integer.parseInt(skill_.getName()));
|
||||
getPlayer().changeSkillLevel(skill, (byte) 0, skill.getMaxLevel(), -1);
|
||||
|
||||
@@ -31,10 +31,10 @@ import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import provider.wz.WZFiles;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.Pair;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
@@ -149,7 +149,7 @@ public class CashShop {
|
||||
private static volatile List<SpecialCashItem> specialcashitems = new ArrayList<>();
|
||||
|
||||
public static void loadAllCashItems() {
|
||||
MapleDataProvider etc = MapleDataProviderFactory.getDataProvider(new File("wz/Etc.wz"));
|
||||
MapleDataProvider etc = MapleDataProviderFactory.getDataProvider(WZFiles.ETC);
|
||||
|
||||
Map<Integer, CashItem> loadedItems = new HashMap<>();
|
||||
List<Integer> onSaleItems = new ArrayList<>();
|
||||
|
||||
@@ -32,12 +32,12 @@ import constants.skills.Gunslinger;
|
||||
import constants.skills.NightWalker;
|
||||
import net.server.Server;
|
||||
import provider.*;
|
||||
import provider.wz.WZFiles;
|
||||
import server.MakerItemFactory.MakerItemCreateEntry;
|
||||
import server.life.MapleLifeFactory;
|
||||
import server.life.MapleMonsterInformationProvider;
|
||||
import tools.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
@@ -116,10 +116,10 @@ public class MapleItemInformationProvider {
|
||||
|
||||
private MapleItemInformationProvider() {
|
||||
loadCardIdData();
|
||||
itemData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Item.wz"));
|
||||
equipData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Character.wz"));
|
||||
stringData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/String.wz"));
|
||||
etcData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Etc.wz"));
|
||||
itemData = MapleDataProviderFactory.getDataProvider(WZFiles.ITEM);
|
||||
equipData = MapleDataProviderFactory.getDataProvider(WZFiles.CHARACTER);
|
||||
stringData = MapleDataProviderFactory.getDataProvider(WZFiles.STRING);
|
||||
etcData = MapleDataProviderFactory.getDataProvider(WZFiles.ETC);
|
||||
cashStringData = stringData.getData("Cash.img");
|
||||
consumeStringData = stringData.getData("Consume.img");
|
||||
eqpStringData = stringData.getData("Eqp.img");
|
||||
|
||||
@@ -24,6 +24,7 @@ import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import provider.wz.WZFiles;
|
||||
import tools.DatabaseConnection;
|
||||
|
||||
import java.io.File;
|
||||
@@ -115,7 +116,7 @@ public class MapleSkillbookInformationProvider {
|
||||
}
|
||||
|
||||
private static Map<Integer, SkillBookEntry> fetchSkillbooksFromQuests() {
|
||||
MapleDataProvider questDataProvider = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "Quest.wz"));
|
||||
MapleDataProvider questDataProvider = MapleDataProviderFactory.getDataProvider(WZFiles.QUEST);
|
||||
MapleData actData = questDataProvider.getData("Act.img");
|
||||
MapleData checkData = questDataProvider.getData("Check.img");
|
||||
|
||||
|
||||
@@ -29,12 +29,12 @@ import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import provider.wz.WZFiles;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.FilePrinter;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Pair;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
@@ -310,7 +310,7 @@ public class MapleStorage {
|
||||
if(fee == null) {
|
||||
fee = 100;
|
||||
|
||||
MapleDataProvider npc = MapleDataProviderFactory.getDataProvider(new File("wz/Npc.wz"));
|
||||
MapleDataProvider npc = MapleDataProviderFactory.getDataProvider(WZFiles.NPC);
|
||||
MapleData npcData = npc.getData(npcId + ".img");
|
||||
if(npcData != null) {
|
||||
fee = MapleDataTool.getIntConvert("info/trunkPut", npcData, 100);
|
||||
@@ -328,7 +328,7 @@ public class MapleStorage {
|
||||
if(fee == null) {
|
||||
fee = 0;
|
||||
|
||||
MapleDataProvider npc = MapleDataProviderFactory.getDataProvider(new File("wz/Npc.wz"));
|
||||
MapleDataProvider npc = MapleDataProviderFactory.getDataProvider(WZFiles.NPC);
|
||||
MapleData npcData = npc.getData(npcId + ".img");
|
||||
if(npcData != null) {
|
||||
fee = MapleDataTool.getIntConvert("info/trunkGet", npcData, 0);
|
||||
|
||||
@@ -25,12 +25,12 @@ import client.MapleCharacter;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import provider.wz.WZFiles;
|
||||
import server.TimerManager;
|
||||
import server.maps.MapleMap;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Randomizer;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -43,7 +43,7 @@ public final class MapleOxQuiz {
|
||||
private int question = 1;
|
||||
private MapleMap map = null;
|
||||
private int expGain = 200;
|
||||
private static MapleDataProvider stringData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Etc.wz"));
|
||||
private static MapleDataProvider stringData = MapleDataProviderFactory.getDataProvider(WZFiles.ETC);
|
||||
|
||||
public MapleOxQuiz(MapleMap map) {
|
||||
this.map = map;
|
||||
|
||||
@@ -26,18 +26,18 @@ import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import provider.wz.MapleDataType;
|
||||
import provider.wz.WZFiles;
|
||||
import tools.Pair;
|
||||
import tools.StringUtil;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
public class MapleLifeFactory {
|
||||
|
||||
private static MapleDataProvider data = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Mob.wz"));
|
||||
private final static MapleDataProvider stringDataWZ = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/String.wz"));
|
||||
private static MapleDataProvider data = MapleDataProviderFactory.getDataProvider(WZFiles.MOB);
|
||||
private final static MapleDataProvider stringDataWZ = MapleDataProviderFactory.getDataProvider(WZFiles.STRING);
|
||||
private static MapleData mobStringData = stringDataWZ.getData("Mob.img");
|
||||
private static MapleData npcStringData = stringDataWZ.getData("Npc.img");
|
||||
private static Map<Integer, MapleMonsterStats> monsterStats = new HashMap<>();
|
||||
@@ -46,7 +46,7 @@ public class MapleLifeFactory {
|
||||
private static Set<Integer> getHpBarBosses() {
|
||||
Set<Integer> ret = new HashSet<>();
|
||||
|
||||
MapleDataProvider uiDataWZ = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/UI.wz"));
|
||||
MapleDataProvider uiDataWZ = MapleDataProviderFactory.getDataProvider(WZFiles.UI);
|
||||
for (MapleData bossData : uiDataWZ.getData("UIWindow.img").getChildByPath("MobGage/Mob").getChildren()) {
|
||||
ret.add(Integer.valueOf(bossData.getName()));
|
||||
}
|
||||
@@ -322,7 +322,7 @@ public class MapleLifeFactory {
|
||||
private int id;
|
||||
private byte chance, x;
|
||||
|
||||
private loseItem(int id, byte chance, byte x) {
|
||||
public loseItem(int id, byte chance, byte x) {
|
||||
this.id = id;
|
||||
this.chance = chance;
|
||||
this.x = x;
|
||||
@@ -347,7 +347,7 @@ public class MapleLifeFactory {
|
||||
private int removeAfter;
|
||||
private int hp;
|
||||
|
||||
private selfDestruction(byte action, int removeAfter, int hp) {
|
||||
public selfDestruction(byte action, int removeAfter, int hp) {
|
||||
this.action = action;
|
||||
this.removeAfter = removeAfter;
|
||||
this.hp = hp;
|
||||
|
||||
@@ -26,12 +26,12 @@ import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import provider.wz.WZFiles;
|
||||
import server.MapleItemInformationProvider;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.Pair;
|
||||
import tools.Randomizer;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
@@ -227,7 +227,7 @@ public class MapleMonsterInformationProvider {
|
||||
}
|
||||
|
||||
public static ArrayList<Pair<Integer, String>> getMobsIDsFromName(String search) {
|
||||
MapleDataProvider dataProvider = MapleDataProviderFactory.getDataProvider(new File("wz/String.wz"));
|
||||
MapleDataProvider dataProvider = MapleDataProviderFactory.getDataProvider(WZFiles.STRING);
|
||||
ArrayList<Pair<Integer, String>> retMobs = new ArrayList<>();
|
||||
MapleData data = dataProvider.getData("Mob.img");
|
||||
List<Pair<Integer, String>> mobPairList = new LinkedList<>();
|
||||
@@ -267,7 +267,7 @@ public class MapleMonsterInformationProvider {
|
||||
public String getMobNameFromId(int id) {
|
||||
String mobName = mobNameCache.get(id);
|
||||
if (mobName == null) {
|
||||
MapleDataProvider dataProvider = MapleDataProviderFactory.getDataProvider(new File("wz/String.wz"));
|
||||
MapleDataProvider dataProvider = MapleDataProviderFactory.getDataProvider(WZFiles.STRING);
|
||||
MapleData mobData = dataProvider.getData("Mob.img");
|
||||
|
||||
mobName = MapleDataTool.getString(mobData.getChildByPath(id + "/name"), "");
|
||||
|
||||
@@ -24,8 +24,8 @@ import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import provider.wz.WZFiles;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@@ -37,7 +37,7 @@ import java.util.Map;
|
||||
*/
|
||||
public class MaplePlayerNPCFactory {
|
||||
|
||||
private static MapleDataProvider npcData = MapleDataProviderFactory.getDataProvider(new File("wz/Npc.wz"));
|
||||
private static MapleDataProvider npcData = MapleDataProviderFactory.getDataProvider(WZFiles.NPC);
|
||||
|
||||
private static final Map<Integer, List<MaplePlayerNPC>> dnpcMaps = new HashMap<>();
|
||||
private static Integer runningDeveloperOid = 2147483000; // 647 slots, long enough
|
||||
@@ -49,11 +49,11 @@ public class MaplePlayerNPCFactory {
|
||||
private static void loadDeveloperRoomMetadata(MapleDataProvider npc) {
|
||||
MapleData thisData = npc.getData("9977777.img");
|
||||
if(thisData != null) {
|
||||
MapleDataProvider map = MapleDataProviderFactory.getDataProvider(new File("wz/Map.wz"));
|
||||
MapleDataProvider map = MapleDataProviderFactory.getDataProvider(WZFiles.MAP);
|
||||
|
||||
thisData = map.getData("Map/Map7/777777777.img");
|
||||
if(thisData != null) {
|
||||
MapleDataProvider sound = MapleDataProviderFactory.getDataProvider(new File("wz/Sound.wz"));
|
||||
MapleDataProvider sound = MapleDataProviderFactory.getDataProvider(WZFiles.SOUND);
|
||||
|
||||
thisData = sound.getData("Field.img");
|
||||
if(thisData != null) {
|
||||
@@ -70,7 +70,7 @@ public class MaplePlayerNPCFactory {
|
||||
MapleDataProvider npc = npcData;
|
||||
loadDeveloperRoomMetadata(npc);
|
||||
|
||||
MapleDataProvider etc = MapleDataProviderFactory.getDataProvider(new File("wz/Etc.wz"));
|
||||
MapleDataProvider etc = MapleDataProviderFactory.getDataProvider(WZFiles.ETC);
|
||||
MapleData dnpcData = etc.getData("DeveloperNpc.img");
|
||||
if(dnpcData != null) {
|
||||
for (MapleData data : dnpcData.getChildren()) {
|
||||
|
||||
@@ -25,9 +25,9 @@ import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import provider.wz.WZFiles;
|
||||
import tools.StringUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -37,7 +37,7 @@ import java.util.Map;
|
||||
*/
|
||||
public class MobAttackInfoFactory {
|
||||
private static Map<String, MobAttackInfo> mobAttacks = new HashMap<>();
|
||||
private static MapleDataProvider dataSource = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Mob.wz"));
|
||||
private static MapleDataProvider dataSource = MapleDataProviderFactory.getDataProvider(WZFiles.MOB);
|
||||
|
||||
public static MobAttackInfo getMobAttackInfo(MapleMonster mob, int attack) {
|
||||
MobAttackInfo ret = mobAttacks.get(mob.getId() + "" + attack);
|
||||
|
||||
@@ -31,9 +31,9 @@ import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import provider.wz.WZFiles;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -46,7 +46,7 @@ import java.util.Map;
|
||||
public class MobSkillFactory {
|
||||
|
||||
private static Map<String, MobSkill> mobSkills = new HashMap<>();
|
||||
private final static MapleDataProvider dataSource = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Skill.wz"));
|
||||
private final static MapleDataProvider dataSource = MapleDataProviderFactory.getDataProvider(WZFiles.SKILL);
|
||||
private static MapleData skillRoot = dataSource.getData("MobSkill.img");
|
||||
private final static MonitoredReentrantReadWriteLock dataLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.MOBSKILL_FACTORY);
|
||||
private final static MonitoredReadLock rL = MonitoredReadLockFactory.createLock(dataLock);
|
||||
|
||||
@@ -25,6 +25,7 @@ import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import provider.wz.WZFiles;
|
||||
import scripting.event.EventInstanceManager;
|
||||
import server.life.*;
|
||||
import server.partyquest.GuardianSpawnPoint;
|
||||
@@ -32,7 +33,6 @@ import tools.DatabaseConnection;
|
||||
import tools.StringUtil;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
@@ -47,8 +47,8 @@ public class MapleMapFactory {
|
||||
private static MapleDataProvider mapSource;
|
||||
|
||||
static {
|
||||
nameData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/String.wz")).getData("Map.img");
|
||||
mapSource = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Map.wz"));
|
||||
nameData = MapleDataProviderFactory.getDataProvider(WZFiles.STRING).getData("Map.img");
|
||||
mapSource = MapleDataProviderFactory.getDataProvider(WZFiles.MAP);
|
||||
}
|
||||
|
||||
private static void loadLifeFromWz(MapleMap map, MapleData mapData) {
|
||||
|
||||
@@ -25,18 +25,18 @@ import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import provider.wz.WZFiles;
|
||||
import server.maps.MapleReactorStats.StateData;
|
||||
import tools.Pair;
|
||||
import tools.StringUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class MapleReactorFactory {
|
||||
private static MapleDataProvider data = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Reactor.wz"));
|
||||
private static MapleDataProvider data = MapleDataProviderFactory.getDataProvider(WZFiles.REACTOR);
|
||||
private static Map<Integer, MapleReactorStats> reactorStats = new HashMap<>();
|
||||
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@ import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import provider.wz.WZFiles;
|
||||
import server.life.MobSkill;
|
||||
import server.life.MobSkillFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -22,7 +22,7 @@ public class MapleCarnivalFactory {
|
||||
private final static MapleCarnivalFactory instance = new MapleCarnivalFactory();
|
||||
private final Map<Integer, MCSkill> skills = new HashMap<>();
|
||||
private final Map<Integer, MCSkill> guardians = new HashMap<>();
|
||||
private final MapleDataProvider dataRoot = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Skill.wz"));
|
||||
private final MapleDataProvider dataRoot = MapleDataProviderFactory.getDataProvider(WZFiles.SKILL);
|
||||
|
||||
private final List<Integer> singleTargetedSkills = new ArrayList<>();
|
||||
private final List<Integer> multiTargetedSkills = new ArrayList<>();
|
||||
|
||||
@@ -29,12 +29,12 @@ import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import provider.wz.WZFiles;
|
||||
import server.quest.actions.*;
|
||||
import server.quest.requirements.*;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.StringUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
@@ -68,7 +68,7 @@ public class MapleQuest {
|
||||
private boolean autoPreComplete, autoComplete;
|
||||
private boolean repeatable = false;
|
||||
private String name = "", parent = "";
|
||||
private final static MapleDataProvider questData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Quest.wz"));
|
||||
private final static MapleDataProvider questData = MapleDataProviderFactory.getDataProvider(WZFiles.QUEST);
|
||||
private final static MapleData questInfo = questData.getData("QuestInfo.img");
|
||||
private final static MapleData questAct = questData.getData("Act.img");
|
||||
private final static MapleData questReq = questData.getData("Check.img");
|
||||
|
||||
@@ -63,6 +63,10 @@ public class DatabaseConnection {
|
||||
* @return true if connection to the database initiated successfully, false if not successful
|
||||
*/
|
||||
public static boolean initializeConnectionPool() {
|
||||
if (dataSource != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
log.info("Initializing connection pool...");
|
||||
final HikariConfig config = getConfig();
|
||||
Instant initStart = Instant.now();
|
||||
|
||||
220
src/main/java/tools/mapletools/ArrowFetcher.java
Normal file
220
src/main/java/tools/mapletools/ArrowFetcher.java
Normal file
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package tools.mapletools;
|
||||
|
||||
import server.life.MapleMonsterStats;
|
||||
import tools.Pair;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application traces arrow drop data on the underlying DB (that must be
|
||||
* defined on the DatabaseConnection file of this project) and generates a SQL file
|
||||
* that proposes updated arrow quantitty on drop entries for the drop_data table.
|
||||
* <p>
|
||||
* The arrow quantity range is calculated accordingly with the target mob stats, such
|
||||
* as level and if it's a boss or not.
|
||||
*/
|
||||
|
||||
public class ArrowFetcher {
|
||||
private static final String OUTPUT_FILE_NAME = "arrow_drop_data.sql";
|
||||
private static final int MIN_ARROW_ID = 2060000;
|
||||
private static final int MAX_ARROW_ID = 2061004;
|
||||
private static final float CORRECTION_FACTOR = 2.2f;
|
||||
|
||||
private static final Map<Integer, Pair<Integer, Integer>> mobRange = new HashMap<>();
|
||||
private static PrintWriter printWriter;
|
||||
private static Map<Integer, MapleMonsterStats> mobStats;
|
||||
|
||||
private static Pair<Integer, Integer> calcArrowRange(int level, boolean boss) {
|
||||
int minRange, maxRange;
|
||||
|
||||
// MIN range
|
||||
minRange = (int) Math.ceil(((2.870503597 * level) - 1.870503597) * (boss ? 1.4 : 1.0) / CORRECTION_FACTOR);
|
||||
|
||||
// MAX range
|
||||
maxRange = (int) Math.ceil(1.25 * minRange);
|
||||
|
||||
return new Pair<>(minRange, maxRange);
|
||||
}
|
||||
|
||||
private static void calcAllMobsArrowRange() {
|
||||
System.out.print("Calculating range... ");
|
||||
|
||||
for (Entry<Integer, MapleMonsterStats> mobStat : mobStats.entrySet()) {
|
||||
MapleMonsterStats mms = mobStat.getValue();
|
||||
Pair<Integer, Integer> arrowRange;
|
||||
|
||||
arrowRange = calcArrowRange(mms.getLevel(), mms.isBoss());
|
||||
mobRange.put(mobStat.getKey(), arrowRange);
|
||||
}
|
||||
|
||||
System.out.println("done!");
|
||||
}
|
||||
|
||||
private static void printSqlHeader() {
|
||||
printWriter.println(" # SQL File autogenerated from the MapleArrowFetcher feature by Ronan Lana.");
|
||||
printWriter.println(" # Generated data takes into account mob stats such as level and boss for the raw arrow ranges.");
|
||||
printWriter.println(" # Only current arrows entries on the DB it was compiled are being updated here.");
|
||||
printWriter.println();
|
||||
|
||||
printWriter.println("UPDATE drop_data");
|
||||
printWriter.println("SET minimum_quantity = CASE");
|
||||
}
|
||||
|
||||
private static void printSqlMiddle() {
|
||||
printWriter.println(" ELSE minimum_quantity END,");
|
||||
printWriter.println(" maximum_quantity = CASE");
|
||||
}
|
||||
|
||||
private static void printSqlFooter() {
|
||||
printWriter.println(" ELSE maximum_quantity END");
|
||||
printWriter.println(";");
|
||||
}
|
||||
|
||||
private static void updateSqlMobArrowMinEntry(int[] entry) {
|
||||
printWriter.println(" WHEN dropperid = " + entry[0] + " AND itemid = " + entry[1] + " THEN " + entry[2]);
|
||||
}
|
||||
|
||||
private static void updateSqlMobArrowMaxEntry(int[] entry) {
|
||||
printWriter.println(" WHEN dropperid = " + entry[0] + " AND itemid = " + entry[1] + " THEN " + entry[3]);
|
||||
}
|
||||
|
||||
private static List<int[]> getArrowEntryValues(Map<Integer, List<Integer>> existingEntries) {
|
||||
List<int[]> entryValues = new ArrayList<>(200);
|
||||
|
||||
List<Entry<Integer, List<Integer>>> listEntries = new ArrayList<>(existingEntries.entrySet());
|
||||
|
||||
listEntries.sort((o1, o2) -> {
|
||||
int val1 = o1.getKey();
|
||||
int val2 = o2.getKey();
|
||||
return Integer.compare(val1, val2);
|
||||
});
|
||||
|
||||
for (Entry<Integer, List<Integer>> ee : listEntries) {
|
||||
int mobid = ee.getKey();
|
||||
Pair<Integer, Integer> mr = mobRange.get(mobid);
|
||||
|
||||
for (Integer itemid : ee.getValue()) {
|
||||
int itemWeight = (itemid % 10) + 1;
|
||||
|
||||
int[] values = new int[4];
|
||||
values[0] = mobid;
|
||||
values[1] = itemid;
|
||||
|
||||
values[2] = (int) Math.ceil(mr.getLeft() / itemWeight); // weighted min quantity
|
||||
values[3] = (int) Math.ceil(mr.getRight() / itemWeight); // weighted max quantity
|
||||
|
||||
entryValues.add(values);
|
||||
}
|
||||
}
|
||||
|
||||
return entryValues;
|
||||
}
|
||||
|
||||
private static void updateMobsArrowRange() {
|
||||
System.out.print("Generating updated ranges... ");
|
||||
final Connection con = SimpleDatabaseConnection.getConnection();
|
||||
|
||||
Map<Integer, List<Integer>> existingEntries = new HashMap<>(200);
|
||||
|
||||
try {
|
||||
// select all arrow drop entries on the DB, to update their values
|
||||
PreparedStatement ps = con.prepareStatement("SELECT dropperid, itemid FROM drop_data WHERE itemid >= " + MIN_ARROW_ID + " AND itemid <= " + MAX_ARROW_ID + " ORDER BY itemid;");
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
if (rs.isBeforeFirst()) {
|
||||
while (rs.next()) {
|
||||
int mobid = rs.getInt(1);
|
||||
int itemid = rs.getInt(2);
|
||||
|
||||
if (mobRange.containsKey(mobid)) {
|
||||
List<Integer> em = existingEntries.get(mobid);
|
||||
|
||||
if (em == null) {
|
||||
em = new ArrayList<>(2);
|
||||
existingEntries.put(mobid, em);
|
||||
}
|
||||
|
||||
em.add(itemid);
|
||||
}
|
||||
}
|
||||
|
||||
if (!existingEntries.isEmpty()) {
|
||||
List<int[]> entryValues = getArrowEntryValues(existingEntries);
|
||||
|
||||
printWriter = new PrintWriter(ToolConstants.getOutputFile(OUTPUT_FILE_NAME), StandardCharsets.UTF_8);
|
||||
printSqlHeader();
|
||||
|
||||
for (int[] arrowEntry : entryValues) {
|
||||
updateSqlMobArrowMinEntry(arrowEntry);
|
||||
}
|
||||
|
||||
printSqlMiddle();
|
||||
|
||||
for (int[] arrowEntry : entryValues) {
|
||||
updateSqlMobArrowMaxEntry(arrowEntry);
|
||||
}
|
||||
|
||||
printSqlFooter();
|
||||
|
||||
printWriter.close();
|
||||
} else {
|
||||
throw new Exception("NO DATA");
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new Exception("NO DATA");
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
con.close();
|
||||
|
||||
System.out.println("done!");
|
||||
|
||||
} catch (Exception e) {
|
||||
if (e.getMessage() != null && e.getMessage().equals("NO DATA")) {
|
||||
System.out.println("failed! The DB has no arrow entry to be updated.");
|
||||
} else {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// load mob stats from WZ
|
||||
mobStats = MonsterStatFetcher.getAllMonsterStats();
|
||||
|
||||
calcAllMobsArrowRange();
|
||||
updateMobsArrowRange();
|
||||
}
|
||||
}
|
||||
190
src/main/java/tools/mapletools/BossHpBarFetcher.java
Normal file
190
src/main/java/tools/mapletools/BossHpBarFetcher.java
Normal file
@@ -0,0 +1,190 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import provider.wz.WZFiles;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application parses the Mob.wz file inputted and generates a report showing
|
||||
* all cases where a mob has a boss HP bar and doesn't have a "boss" label.
|
||||
* <p>
|
||||
* Running it should generate a report file under "lib" folder with the search results.
|
||||
*/
|
||||
public class BossHpBarFetcher {
|
||||
private static final String OUTPUT_FILE_NAME = "boss_hp_bar_report.txt";
|
||||
private static final int INITIAL_STRING_LENGTH = 50;
|
||||
private static final List<Integer> missingBosses = new ArrayList<>();
|
||||
|
||||
private static BufferedReader bufferedReader = null;
|
||||
private static byte status = 0;
|
||||
private static int curBoss;
|
||||
private static int curHpTag;
|
||||
private static int curMobId;
|
||||
|
||||
private static String getName(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("name");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static String getValue(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("value");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static void forwardCursor(int st) {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
simpleToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void simpleToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void readMobLabel(String token) {
|
||||
String name = getName(token);
|
||||
String value = getValue(token);
|
||||
|
||||
switch (name) {
|
||||
case "boss" -> curBoss = Integer.parseInt(value);
|
||||
case "hpTagColor" -> curHpTag = Integer.parseInt(value);
|
||||
}
|
||||
}
|
||||
|
||||
private static void processMobData() {
|
||||
if (curHpTag != Integer.MAX_VALUE && curBoss == Integer.MAX_VALUE) {
|
||||
missingBosses.add(curMobId);
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateToken(String token) {
|
||||
String d;
|
||||
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
|
||||
if (status == 1) {
|
||||
processMobData();
|
||||
}
|
||||
} else if (token.contains("imgdir")) {
|
||||
if (status == 0) {
|
||||
String mobText = getName(token);
|
||||
curMobId = Integer.parseInt(mobText.substring(0, mobText.lastIndexOf('.')));
|
||||
} else if (status == 1) { //getting info
|
||||
d = getName(token);
|
||||
|
||||
if (!d.contentEquals("info")) {
|
||||
forwardCursor(status);
|
||||
} else {
|
||||
curBoss = Integer.MAX_VALUE;
|
||||
curHpTag = Integer.MAX_VALUE;
|
||||
}
|
||||
} else if (status == 2) {
|
||||
forwardCursor(status);
|
||||
}
|
||||
|
||||
status += 1;
|
||||
} else {
|
||||
if (status == 2) { //info tags
|
||||
readMobLabel(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void readBossHpBarData() throws IOException {
|
||||
String line;
|
||||
|
||||
final File mobDirectory = WZFiles.MOB.getFile();
|
||||
for (File file : mobDirectory.listFiles()) {
|
||||
if (file.isFile()) {
|
||||
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
translateToken(line);
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void printReportFileHeader(PrintWriter writer) {
|
||||
writer.println(" # Report File autogenerated from the MapleBossHpBarFetcher feature by Ronan Lana.");
|
||||
writer.println(" # Generated data takes into account several data info from the server-side WZ.xmls.");
|
||||
writer.println();
|
||||
}
|
||||
|
||||
private static void printReportFileResults(PrintWriter writer) {
|
||||
for (int mobId : missingBosses) {
|
||||
writer.println("Missing 'isBoss' on id: " + mobId);
|
||||
}
|
||||
}
|
||||
|
||||
private static void reportBossHpBarData() {
|
||||
// This will reference one line at a time
|
||||
|
||||
try {
|
||||
System.out.println("Reading WZs...");
|
||||
readBossHpBarData();
|
||||
|
||||
System.out.println("Reporting results...");
|
||||
final PrintWriter printWriter = new PrintWriter(ToolConstants.getOutputFile(OUTPUT_FILE_NAME), StandardCharsets.UTF_8);
|
||||
|
||||
printReportFileHeader(printWriter);
|
||||
printReportFileResults(printWriter);
|
||||
|
||||
printWriter.close();
|
||||
System.out.println("Done!");
|
||||
} catch (FileNotFoundException ex) {
|
||||
System.out.println("Unable to open mob file.");
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Error reading mob file.");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
reportBossHpBarData();
|
||||
}
|
||||
|
||||
}
|
||||
680
src/main/java/tools/mapletools/CashCosmeticsChecker.java
Normal file
680
src/main/java/tools/mapletools/CashCosmeticsChecker.java
Normal file
@@ -0,0 +1,680 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import provider.wz.WZFiles;
|
||||
import tools.Pair;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application parses the cosmetic recipes defined within "lib/care" folder, loads
|
||||
* every present cosmetic itemid from the XML data, then checks the scripts for missed
|
||||
* cosmetics within the stylist/surgeon. Results from the search are reported in a report
|
||||
* file.
|
||||
* <p>
|
||||
* Note: to best make use of this feature, set IGNORE_CURRENT_SCRIPT_COSMETICS = true. This
|
||||
* way, every available cosmetic present on the recipes will be listed on the report.
|
||||
* <p>
|
||||
* Estimated parse time: 1 minute
|
||||
*/
|
||||
public class CashCosmeticsChecker {
|
||||
private static final String INPUT_DIRECTORY_PATH = ToolConstants.getInputFile("care").getPath();
|
||||
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("cash_cosmetics_result.txt");
|
||||
private static final boolean IGNORE_CURRENT_SCRIPT_COSMETICS = false; // Toggle to preference
|
||||
private static final int INITIAL_STRING_LENGTH = 50;
|
||||
|
||||
private static final Map<Integer, Set<Integer>> scriptCosmetics = new HashMap<>();
|
||||
private static final Map<Integer, String> scriptEntries = new HashMap<>(500);
|
||||
private static final Set<Integer> allCosmetics = new HashSet<>();
|
||||
private static final Set<Integer> unusedCosmetics = new HashSet<>();
|
||||
private static final Map<Integer, List<Integer>> usedCosmetics = new HashMap<>();
|
||||
private static final Map<Integer, String> couponNames = new HashMap<>();
|
||||
private static final Map<Integer, Integer> cosmeticNpcs = new HashMap<>(); // expected only 1 NPC per cosmetic coupon (town care/salon)
|
||||
private static final Map<List<String>, Integer> cosmeticNpcids = new HashMap<>();
|
||||
private static final Set<String> missingCosmeticNames = new HashSet<>();
|
||||
private static final Map<String, Integer> cosmeticNameIds = new HashMap<>();
|
||||
private static final Map<Integer, String> cosmeticIdNames = new HashMap<>();
|
||||
private static final Map<Pair<Integer, String>, Set<Integer>> missingCosmeticsNpcTypes = new HashMap<>();
|
||||
|
||||
private static PrintWriter printWriter = null;
|
||||
private static InputStreamReader fileReader = null;
|
||||
private static BufferedReader bufferedReader = null;
|
||||
private static byte status = 0;
|
||||
|
||||
private static String getName(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("name");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static String getValue(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("value");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static void forwardCursor(int st) {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
simpleToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void simpleToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
|
||||
if (status == 3) {
|
||||
String d = getName(token);
|
||||
|
||||
if (!(d.contentEquals("Face") || d.contentEquals("Hair"))) {
|
||||
forwardCursor(status);
|
||||
}
|
||||
} else if (status == 4) {
|
||||
String d = getName(token);
|
||||
int itemid = Integer.parseInt(d);
|
||||
|
||||
int cosmeticid;
|
||||
if (itemid >= 30000) {
|
||||
cosmeticid = (itemid / 10) * 10;
|
||||
} else {
|
||||
cosmeticid = itemid - ((itemid / 100) % 10) * 100;
|
||||
}
|
||||
|
||||
allCosmetics.add(cosmeticid);
|
||||
forwardCursor(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void readEqpStringData(String eqpStringDirectory) throws IOException {
|
||||
String line;
|
||||
|
||||
fileReader = new InputStreamReader(new FileInputStream(eqpStringDirectory), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
translateToken(line);
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
}
|
||||
|
||||
private static void loadCosmeticWzData() throws IOException {
|
||||
System.out.println("Reading String.wz ...");
|
||||
readEqpStringData(WZFiles.STRING.getFilePath() + "/Eqp.img.xml");
|
||||
}
|
||||
|
||||
private static void setCosmeticUsage(List<Integer> usedByNpcids, int cosmeticid) {
|
||||
if (!usedByNpcids.isEmpty()) {
|
||||
usedCosmetics.put(cosmeticid, usedByNpcids);
|
||||
} else {
|
||||
unusedCosmetics.add(cosmeticid);
|
||||
}
|
||||
}
|
||||
|
||||
private static void listFiles(String directoryName, ArrayList<File> files) {
|
||||
File directory = new File(directoryName);
|
||||
|
||||
// get all the files from a directory
|
||||
File[] fList = directory.listFiles();
|
||||
for (File file : fList) {
|
||||
if (file.isFile()) {
|
||||
files.add(file);
|
||||
} else if (file.isDirectory()) {
|
||||
listFiles(file.getAbsolutePath(), files);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int getNpcIdFromFilename(String name) {
|
||||
try {
|
||||
return Integer.parseInt(name.substring(0, name.indexOf('.')));
|
||||
} catch (Exception e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Integer> findCosmeticDataNpcids(int itemid) {
|
||||
List<Integer> npcids = new LinkedList<>();
|
||||
for (Map.Entry<Integer, Set<Integer>> sc : scriptCosmetics.entrySet()) {
|
||||
if (sc.getValue().contains(itemid)) {
|
||||
npcids.add(itemid);
|
||||
}
|
||||
}
|
||||
|
||||
return npcids;
|
||||
}
|
||||
|
||||
private static void loadScripts() throws IOException {
|
||||
ArrayList<File> files = new ArrayList<>();
|
||||
listFiles(ToolConstants.SCRIPTS_PATH + "/npc", files);
|
||||
|
||||
for (File f : files) {
|
||||
Integer npcid = getNpcIdFromFilename(f.getName());
|
||||
|
||||
//System.out.println("Parsing " + f.getAbsolutePath());
|
||||
fileReader = new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
String line;
|
||||
|
||||
StringBuilder stringBuffer = new StringBuilder();
|
||||
|
||||
boolean cosmeticNpc = false;
|
||||
Set<Integer> cosmeticids = new HashSet<>();
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
String[] s = line.split("hair_. = Array\\(", 2);
|
||||
|
||||
if (s.length > 1) {
|
||||
cosmeticNpc = true;
|
||||
s = s[1].split("\\)", 2);
|
||||
s = s[0].split(", ");
|
||||
|
||||
for (String st : s) {
|
||||
if (!st.isEmpty()) {
|
||||
int itemid = Integer.parseInt(st);
|
||||
cosmeticids.add(itemid);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s = line.split("face_. = Array\\(", 2);
|
||||
|
||||
if (s.length > 1) {
|
||||
cosmeticNpc = true;
|
||||
s = s[1].split("\\)", 2);
|
||||
s = s[0].split(", ");
|
||||
|
||||
for (String st : s) {
|
||||
if (!st.isEmpty()) {
|
||||
int itemid = Integer.parseInt(st);
|
||||
cosmeticids.add(itemid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stringBuffer.append(line).append("\n");
|
||||
}
|
||||
|
||||
scriptEntries.put(npcid, stringBuffer.toString());
|
||||
|
||||
if (cosmeticNpc) {
|
||||
scriptCosmetics.put(npcid, cosmeticids);
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static void processCosmeticScriptData() throws IOException {
|
||||
System.out.println("Reading script files ...");
|
||||
loadScripts();
|
||||
|
||||
if (IGNORE_CURRENT_SCRIPT_COSMETICS) {
|
||||
for (Set<Integer> npcCosmetics : scriptCosmetics.values()) {
|
||||
npcCosmetics.clear();
|
||||
}
|
||||
}
|
||||
|
||||
for (Integer itemid : allCosmetics) {
|
||||
List<Integer> npcids = findCosmeticDataNpcids(itemid);
|
||||
setCosmeticUsage(npcids, itemid);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Integer> loadCosmeticCouponids() throws IOException {
|
||||
List<Integer> couponItemids = new LinkedList<>();
|
||||
|
||||
fileReader = new InputStreamReader(new FileInputStream(getHandbookFileName("/Cash.txt")), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
if (line.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
String[] s = line.split(" - ", 3);
|
||||
|
||||
int itemid = Integer.parseInt(s[0]);
|
||||
if (itemid >= 5150000 && itemid < 5160000) {
|
||||
couponItemids.add(itemid);
|
||||
couponNames.put(itemid, s[1]);
|
||||
}
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
|
||||
return couponItemids;
|
||||
}
|
||||
|
||||
private static List<Integer> findItemidOnScript(int itemid) {
|
||||
List<Integer> files = new LinkedList<>();
|
||||
String t = String.valueOf(itemid);
|
||||
|
||||
for (Map.Entry<Integer, String> text : scriptEntries.entrySet()) {
|
||||
if (text.getValue().contains(t)) {
|
||||
files.add(text.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
private static void loadCosmeticCouponNpcs() throws IOException {
|
||||
System.out.println("Locating cosmetic NPCs ...");
|
||||
|
||||
for (Integer itemid : loadCosmeticCouponids()) {
|
||||
List<Integer> npcids = findItemidOnScript(itemid);
|
||||
|
||||
if (!npcids.isEmpty()) {
|
||||
cosmeticNpcs.put(itemid, npcids.get(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum CosmeticType {
|
||||
HAIRSTYLE,
|
||||
HAIRCOLOR,
|
||||
DIRTYHAIR,
|
||||
FACE_SURGERY,
|
||||
EYE_COLOR,
|
||||
SKIN_CARE
|
||||
}
|
||||
|
||||
private static Pair<Integer, CosmeticType> parseCosmeticCoupon(String[] tokens) {
|
||||
for (int i = 0; i < tokens.length; i++) {
|
||||
String s = tokens[i];
|
||||
|
||||
if (s.startsWith("Hair")) {
|
||||
if (s.contentEquals("Hairstyle")) {
|
||||
return new Pair<>(i, CosmeticType.HAIRSTYLE);
|
||||
} else {
|
||||
if (i - 1 >= 0 && tokens[i - 1].contentEquals("Dirty")) {
|
||||
return new Pair<>(i - 1, CosmeticType.DIRTYHAIR);
|
||||
} else if (i + 1 < tokens.length && tokens[i + 1].contentEquals("Color")) {
|
||||
return new Pair<>(i, CosmeticType.HAIRCOLOR);
|
||||
} else {
|
||||
return new Pair<>(i, CosmeticType.HAIRSTYLE);
|
||||
}
|
||||
}
|
||||
} else if (s.startsWith("Face")) {
|
||||
return new Pair<>(i, CosmeticType.FACE_SURGERY);
|
||||
} else if (s.startsWith("Cosmetic")) {
|
||||
return new Pair<>(i, CosmeticType.EYE_COLOR);
|
||||
} else if (s.startsWith("Plastic")) {
|
||||
return new Pair<>(i, CosmeticType.FACE_SURGERY);
|
||||
} else if (s.startsWith("Skin")) {
|
||||
return new Pair<>(i, CosmeticType.SKIN_CARE);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static List<String> getCosmeticCouponData(String town, String type, String subtype) {
|
||||
List<String> ret = new ArrayList<>(3);
|
||||
ret.add(town);
|
||||
ret.add(type);
|
||||
ret.add(subtype);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static List<String> parseCosmeticCoupon(String couponName) {
|
||||
String town, type, subtype = "EXP";
|
||||
|
||||
String[] s = couponName.split(" Coupon ", 2);
|
||||
|
||||
if (s.length > 1) {
|
||||
subtype = s[1].substring(1, s[1].length() - 1);
|
||||
}
|
||||
|
||||
String[] tokens = s[0].split(" ");
|
||||
Pair<Integer, CosmeticType> cosmeticData = parseCosmeticCoupon(tokens);
|
||||
if (cosmeticData == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
town = "";
|
||||
for (int i = 0; i < cosmeticData.left; i++) {
|
||||
town += (tokens[i] + "_");
|
||||
}
|
||||
town = town.substring(0, town.length() - 1).toLowerCase();
|
||||
|
||||
switch (cosmeticData.right) {
|
||||
case HAIRSTYLE:
|
||||
type = "hair";
|
||||
break;
|
||||
|
||||
case FACE_SURGERY:
|
||||
type = "face";
|
||||
break;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
return getCosmeticCouponData(town, type, subtype);
|
||||
}
|
||||
|
||||
private static void generateCosmeticPlaceNpcs() {
|
||||
for (Map.Entry<Integer, String> e : couponNames.entrySet()) {
|
||||
Integer npcid = cosmeticNpcs.get(e.getKey());
|
||||
if (npcid == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String couponName = e.getValue();
|
||||
List<String> couponData = parseCosmeticCoupon(couponName);
|
||||
|
||||
if (couponData == null) {
|
||||
continue;
|
||||
}
|
||||
cosmeticNpcids.put(couponData, npcid);
|
||||
}
|
||||
}
|
||||
|
||||
private static Integer getCosmeticNpcid(String townName, String typeCosmetic, String typeCoupon) {
|
||||
return cosmeticNpcids.get(getCosmeticCouponData(townName, typeCosmetic, typeCoupon));
|
||||
}
|
||||
|
||||
private static String getCosmeticName(String name, boolean gender) {
|
||||
final String genderString = gender ? "F" : "M";
|
||||
return String.format("%s (%s)", name, genderString);
|
||||
}
|
||||
|
||||
private static void loadCosmeticNames(String cosmeticPath) throws IOException {
|
||||
fileReader = new InputStreamReader(new FileInputStream(cosmeticPath), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
String[] s = line.split(" - ", 3);
|
||||
int itemid = Integer.parseInt(s[0]);
|
||||
|
||||
String name;
|
||||
if (itemid < 30000) {
|
||||
itemid = itemid - ((itemid / 100) % 10) * 100;
|
||||
|
||||
int idx = s[1].lastIndexOf(" ");
|
||||
if (idx > -1) {
|
||||
name = s[1].substring(0, idx);
|
||||
} else {
|
||||
name = s[1];
|
||||
}
|
||||
} else {
|
||||
itemid = (Integer.valueOf(s[0]) / 10) * 10;
|
||||
|
||||
int idx = s[1].indexOf(" ");
|
||||
if (idx > -1) {
|
||||
name = s[1].substring(idx + 1);
|
||||
} else {
|
||||
name = s[1];
|
||||
}
|
||||
}
|
||||
|
||||
name = name.trim();
|
||||
|
||||
String cname = getCosmeticName(name, (((itemid / 1000) % 10) % 3) != 0);
|
||||
|
||||
/*
|
||||
if (cosmeticNameIds.containsKey(cname) && Math.abs(cosmeticNameIds.get(cname) - itemid) > 50) {
|
||||
System.out.println("Clashing '" + name + "' " + itemid + "/" + cosmeticNameIds.get(cname));
|
||||
}
|
||||
*/
|
||||
|
||||
cosmeticNameIds.put(cname, itemid);
|
||||
cosmeticIdNames.put(itemid, name);
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
}
|
||||
|
||||
private static void loadCosmeticNames() throws IOException {
|
||||
System.out.println("Reading cosmetics from handbook ...");
|
||||
|
||||
loadCosmeticNames(getHandbookFileName("/Equip/Face.txt"));
|
||||
loadCosmeticNames(getHandbookFileName("/Equip/Hair.txt"));
|
||||
}
|
||||
|
||||
private static String getHandbookFileName(String fileName) {
|
||||
return ToolConstants.HANDBOOK_PATH + fileName;
|
||||
}
|
||||
|
||||
private static List<Integer> fetchExpectedCosmetics(String[] cosmeticList, boolean gender) {
|
||||
List<Integer> list = new LinkedList<>();
|
||||
|
||||
for (String cosmetic : cosmeticList) {
|
||||
String cname = getCosmeticName(cosmetic, gender);
|
||||
Integer itemid = cosmeticNameIds.get(cname);
|
||||
if (itemid != null) {
|
||||
list.add(itemid);
|
||||
} else {
|
||||
missingCosmeticNames.add(cosmetic);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static void verifyCosmeticExpectedFile(File f) throws IOException {
|
||||
String townName = f.getParent().substring(f.getParent().lastIndexOf("\\") + 1);
|
||||
String typeCosmetic = f.getName().substring(0, f.getName().indexOf("."));
|
||||
|
||||
fileReader = new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
String[] s = line.split(": ", 2);
|
||||
String[] t = s[0].split("ale ");
|
||||
|
||||
String typeCoupon = t[1];
|
||||
boolean gender = !t[0].contentEquals("M");
|
||||
|
||||
Integer npcid = getCosmeticNpcid(townName, typeCosmetic, typeCoupon);
|
||||
if (npcid != null) {
|
||||
String[] cosmetics = s[1].split(", ");
|
||||
List<Integer> cosmeticItemids = fetchExpectedCosmetics(cosmetics, gender);
|
||||
|
||||
Set<Integer> npcCosmetics = scriptCosmetics.get(npcid);
|
||||
Set<Integer> missingCosmetics = new HashSet<>();
|
||||
for (Integer itemid : cosmeticItemids) {
|
||||
if (!npcCosmetics.contains(itemid)) {
|
||||
missingCosmetics.add(itemid);
|
||||
}
|
||||
}
|
||||
|
||||
if (!missingCosmetics.isEmpty()) {
|
||||
Pair<Integer, String> key = new Pair<>(npcid, typeCoupon);
|
||||
|
||||
Set<Integer> list = missingCosmeticsNpcTypes.get(key);
|
||||
if (list == null) {
|
||||
missingCosmeticsNpcTypes.put(key, missingCosmetics);
|
||||
} else {
|
||||
list.addAll(missingCosmetics);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
}
|
||||
|
||||
private static void verifyCosmeticExpectedData() throws IOException {
|
||||
System.out.println("Analyzing cosmetic NPC scripts ...");
|
||||
|
||||
ArrayList<File> cosmeticRecipes = new ArrayList<>();
|
||||
listFiles(INPUT_DIRECTORY_PATH, cosmeticRecipes);
|
||||
|
||||
for (File f : cosmeticRecipes) {
|
||||
verifyCosmeticExpectedFile(f);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Pair<Pair<Integer, String>, List<Integer>>> getSortedMapEntries(Map<Pair<Integer, String>, Set<Integer>> map) {
|
||||
List<Pair<Pair<Integer, String>, List<Integer>>> list = new ArrayList<>(map.size());
|
||||
for (Map.Entry<Pair<Integer, String>, Set<Integer>> e : map.entrySet()) {
|
||||
List<Integer> il = new ArrayList<>(2);
|
||||
il.addAll(e.getValue());
|
||||
|
||||
il.sort((o1, o2) -> o1 - o2);
|
||||
|
||||
list.add(new Pair<>(e.getKey(), il));
|
||||
}
|
||||
|
||||
list.sort((o1, o2) -> {
|
||||
int cmp = o1.getLeft().getLeft() - o2.getLeft().getLeft();
|
||||
if (cmp == 0) {
|
||||
return o1.getLeft().getRight().compareTo(o2.getLeft().getRight());
|
||||
} else {
|
||||
return cmp;
|
||||
}
|
||||
});
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static void printReportFileHeader() {
|
||||
printWriter.println(" # Report File autogenerated from the MapleCashCosmeticsChecker feature by Ronan Lana.");
|
||||
printWriter.println(" # Generated data takes into account several data info from the server source files and the server-side WZ.xmls.");
|
||||
printWriter.println();
|
||||
}
|
||||
|
||||
private static Pair<List<Integer>, List<Integer>> getCosmeticReport(List<Integer> itemids) {
|
||||
List<Integer> maleItemids = new LinkedList<>();
|
||||
List<Integer> femaleItemids = new LinkedList<>();
|
||||
|
||||
for (Integer i : itemids) {
|
||||
if ((((i / 1000) % 10) % 3) == 0) {
|
||||
maleItemids.add(i);
|
||||
} else {
|
||||
femaleItemids.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
return new Pair<>(maleItemids, femaleItemids);
|
||||
}
|
||||
|
||||
private static void reportNpcCosmetics(List<Integer> itemids) {
|
||||
if (!itemids.isEmpty()) {
|
||||
String res = " ";
|
||||
for (Integer i : itemids) {
|
||||
res += (i + ", ");
|
||||
unusedCosmetics.remove(i);
|
||||
}
|
||||
|
||||
printWriter.println(res.substring(0, res.length() - 2));
|
||||
}
|
||||
}
|
||||
|
||||
private static void reportCosmeticResults() throws IOException {
|
||||
System.out.println("Reporting results ...");
|
||||
|
||||
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
|
||||
|
||||
printReportFileHeader();
|
||||
|
||||
if (!missingCosmeticsNpcTypes.isEmpty()) {
|
||||
printWriter.println("Found " + missingCosmeticsNpcTypes.size() + " entries with missing cosmetic entries.");
|
||||
|
||||
for (Pair<Pair<Integer, String>, List<Integer>> mcn : getSortedMapEntries(missingCosmeticsNpcTypes)) {
|
||||
printWriter.println(" NPC " + mcn.getLeft());
|
||||
|
||||
Pair<List<Integer>, List<Integer>> genderItemids = getCosmeticReport(mcn.getRight());
|
||||
reportNpcCosmetics(genderItemids.getLeft());
|
||||
reportNpcCosmetics(genderItemids.getRight());
|
||||
printWriter.println();
|
||||
}
|
||||
}
|
||||
|
||||
if (!unusedCosmetics.isEmpty()) {
|
||||
printWriter.println("Unused cosmetics: " + unusedCosmetics.size());
|
||||
|
||||
List<Integer> list = new ArrayList<>(unusedCosmetics);
|
||||
Collections.sort(list);
|
||||
|
||||
for (Integer i : list) {
|
||||
printWriter.println(i + " " + cosmeticIdNames.get(i));
|
||||
}
|
||||
|
||||
printWriter.println();
|
||||
}
|
||||
|
||||
if (!missingCosmeticNames.isEmpty()) {
|
||||
printWriter.println("Missing cosmetic itemids: " + missingCosmeticNames.size());
|
||||
|
||||
List<String> listString = new ArrayList<>(missingCosmeticNames);
|
||||
Collections.sort(listString);
|
||||
|
||||
for (String c : listString) {
|
||||
printWriter.println(c);
|
||||
}
|
||||
|
||||
printWriter.println();
|
||||
}
|
||||
|
||||
printWriter.close();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
loadCosmeticWzData();
|
||||
processCosmeticScriptData();
|
||||
|
||||
loadCosmeticCouponNpcs();
|
||||
generateCosmeticPlaceNpcs();
|
||||
|
||||
loadCosmeticNames();
|
||||
verifyCosmeticExpectedData();
|
||||
|
||||
reportCosmeticResults();
|
||||
System.out.println("Done!");
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
115
src/main/java/tools/mapletools/CashCosmeticsFetcher.java
Normal file
115
src/main/java/tools/mapletools/CashCosmeticsFetcher.java
Normal file
@@ -0,0 +1,115 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import server.MapleItemInformationProvider;
|
||||
import tools.DatabaseConnection;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author RonanLana
|
||||
|
||||
This application gathers info from the WZ.XML files, fetching all cosmetic coupons and tickets from there, and then
|
||||
searches the NPC script files, identifying the stylish NPCs that supposedly uses them. It will reports all NPCs that
|
||||
uses up a card, as well as report those currently unused.
|
||||
|
||||
Estimated parse time: 10 seconds
|
||||
|
||||
*/
|
||||
public class CashCosmeticsFetcher {
|
||||
private static final Map<Integer, String> scriptEntries = new HashMap<>(500);
|
||||
|
||||
private static void listFiles(String directoryName, ArrayList<File> files) {
|
||||
File directory = new File(directoryName);
|
||||
|
||||
// get all the files from a directory
|
||||
File[] fList = directory.listFiles();
|
||||
for (File file : fList) {
|
||||
if (file.isFile()) {
|
||||
files.add(file);
|
||||
} else if (file.isDirectory()) {
|
||||
listFiles(file.getAbsolutePath(), files);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int getNpcIdFromFilename(String name) {
|
||||
try {
|
||||
return Integer.parseInt(name.substring(0, name.indexOf('.')));
|
||||
} catch(Exception e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadScripts() throws Exception {
|
||||
ArrayList<File> files = new ArrayList<>();
|
||||
listFiles(ToolConstants.SCRIPTS_PATH + "/npc", files);
|
||||
|
||||
for(File f : files) {
|
||||
Integer npcid = getNpcIdFromFilename(f.getName());
|
||||
|
||||
//System.out.println("Parsing " + f.getAbsolutePath());
|
||||
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8);
|
||||
BufferedReader bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
StringBuilder stringBuffer = new StringBuilder();
|
||||
String line;
|
||||
|
||||
while((line = bufferedReader.readLine())!=null){
|
||||
stringBuffer.append(line).append("\n");
|
||||
}
|
||||
|
||||
scriptEntries.put(npcid, stringBuffer.toString());
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Integer> findItemidOnScript(int itemid) {
|
||||
List<Integer> files = new LinkedList<>();
|
||||
String t = String.valueOf(itemid);
|
||||
|
||||
for (Map.Entry<Integer, String> text : scriptEntries.entrySet()) {
|
||||
if (text.getValue().contains(t)) {
|
||||
files.add(text.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
private static void reportCosmeticCouponResults() {
|
||||
final MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
for (int itemid = 5150000; itemid <= 5154000; itemid++) {
|
||||
String itemName = ii.getName(itemid);
|
||||
|
||||
if (itemName != null) {
|
||||
List<Integer> npcids = findItemidOnScript(itemid);
|
||||
|
||||
if (!npcids.isEmpty()) {
|
||||
System.out.println("Itemid " + itemid + " found on " + npcids + ". (" + itemName + ")");
|
||||
} else {
|
||||
System.out.println("NOT FOUND ITEMID " + itemid + " (" + itemName + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
DatabaseConnection.initializeConnectionPool(); // MapleItemInformationProvider loads unrelated stuff from the db
|
||||
try {
|
||||
loadScripts();
|
||||
System.out.println("Loaded scripts");
|
||||
|
||||
reportCosmeticCouponResults();
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
341
src/main/java/tools/mapletools/CashDropFetcher.java
Normal file
341
src/main/java/tools/mapletools/CashDropFetcher.java
Normal file
@@ -0,0 +1,341 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import provider.wz.WZFiles;
|
||||
import tools.Pair;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application gets info from the WZ.XML files regarding cash itemids then searches the drop data on the DB
|
||||
* after any NX (cash item) drops and reports them.
|
||||
* <p>
|
||||
* Estimated parse time: 2 minutes
|
||||
*/
|
||||
public class CashDropFetcher {
|
||||
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("cash_drop_report.txt");
|
||||
private static final Connection con = SimpleDatabaseConnection.getConnection();
|
||||
private static final int INITIAL_STRING_LENGTH = 50;
|
||||
private static final int ITEM_FILE_NAME_SIZE = 13;
|
||||
|
||||
private static final Set<Integer> nxItems = new HashSet<>();
|
||||
private static final Set<Integer> nxDrops = new HashSet<>();
|
||||
|
||||
private static PrintWriter printWriter = null;
|
||||
private static BufferedReader bufferedReader = null;
|
||||
|
||||
private static byte status = 0;
|
||||
private static int currentItemid = 0;
|
||||
|
||||
private static String getName(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("name");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
if (j < i) {
|
||||
return "0"; //node value containing 'name' in it's scope, cheap fix since we don't deal with strings anyway
|
||||
}
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static String getValue(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("value");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static void forwardCursor(int st) {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
simpleToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void simpleToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void inspectEquipWzEntry() {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
translateEquipToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateEquipToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
if (status == 1) {
|
||||
if (!getName(token).equals("info")) {
|
||||
forwardCursor(status);
|
||||
}
|
||||
}
|
||||
|
||||
status += 1;
|
||||
} else {
|
||||
if (status == 2) {
|
||||
String d = getName(token);
|
||||
|
||||
if (d.equals("cash")) {
|
||||
if (!getValue(token).equals("0")) {
|
||||
nxItems.add(currentItemid);
|
||||
}
|
||||
|
||||
forwardCursor(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void inspectItemWzEntry() {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
translateItemToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateItemToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
if (status == 1) {
|
||||
currentItemid = Integer.parseInt(getName(token));
|
||||
} else if (status == 2) {
|
||||
if (!getName(token).equals("info")) {
|
||||
forwardCursor(status);
|
||||
}
|
||||
}
|
||||
|
||||
status += 1;
|
||||
} else {
|
||||
if (status == 3) {
|
||||
String d = getName(token);
|
||||
|
||||
if (d.equals("cash")) {
|
||||
if (!getValue(token).equals("0")) {
|
||||
nxItems.add(currentItemid);
|
||||
}
|
||||
|
||||
forwardCursor(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void printReportFileHeader() {
|
||||
printWriter.println(" # Report File autogenerated from the MapleCashDropFetcher feature by Ronan Lana.");
|
||||
printWriter.println(" # Generated data takes into account several data info from the underlying DB and the server-side WZ.xmls.");
|
||||
printWriter.println();
|
||||
}
|
||||
|
||||
private static void listFiles(String directoryName, ArrayList<File> files) {
|
||||
File directory = new File(directoryName);
|
||||
|
||||
// get all the files from a directory
|
||||
File[] fList = directory.listFiles();
|
||||
for (File file : fList) {
|
||||
if (file.isFile()) {
|
||||
files.add(file);
|
||||
} else if (file.isDirectory()) {
|
||||
listFiles(file.getAbsolutePath(), files);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int getItemIdFromFilename(String name) {
|
||||
try {
|
||||
return Integer.parseInt(name.substring(0, name.indexOf('.')));
|
||||
} catch (Exception e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static String getDropTableName(boolean dropdata) {
|
||||
return (dropdata ? "drop_data" : "reactordrops");
|
||||
}
|
||||
|
||||
private static String getDropElementName(boolean dropdata) {
|
||||
return (dropdata ? "dropperid" : "reactorid");
|
||||
}
|
||||
|
||||
private static void filterNxDropsOnDB(boolean dropdata) throws SQLException {
|
||||
nxDrops.clear();
|
||||
|
||||
PreparedStatement ps = con.prepareStatement("SELECT DISTINCT itemid FROM " + getDropTableName(dropdata));
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
while (rs.next()) {
|
||||
int itemid = rs.getInt("itemid");
|
||||
|
||||
if (nxItems.contains(itemid)) {
|
||||
nxDrops.add(itemid);
|
||||
}
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
}
|
||||
|
||||
private static List<Pair<Integer, Integer>> getNxDropsEntries(boolean dropdata) throws SQLException {
|
||||
List<Pair<Integer, Integer>> entries = new ArrayList<>();
|
||||
|
||||
List<Integer> sortedNxDrops = new ArrayList<>(nxDrops);
|
||||
Collections.sort(sortedNxDrops);
|
||||
|
||||
for (Integer nx : sortedNxDrops) {
|
||||
PreparedStatement ps = con.prepareStatement("SELECT " + getDropElementName(dropdata) + " FROM " + getDropTableName(dropdata) + " WHERE itemid = ?");
|
||||
ps.setInt(1, nx);
|
||||
|
||||
ResultSet rs = ps.executeQuery();
|
||||
while (rs.next()) {
|
||||
entries.add(new Pair<>(nx, rs.getInt(getDropElementName(dropdata))));
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
private static void reportNxDropResults(boolean dropdata) throws SQLException {
|
||||
filterNxDropsOnDB(dropdata);
|
||||
|
||||
if (!nxDrops.isEmpty()) {
|
||||
List<Pair<Integer, Integer>> nxEntries = getNxDropsEntries(dropdata);
|
||||
|
||||
printWriter.println("NX DROPS ON " + getDropTableName(dropdata));
|
||||
for (Pair<Integer, Integer> nx : nxEntries) {
|
||||
printWriter.println(nx.left + " : " + nx.right);
|
||||
}
|
||||
printWriter.println("\n\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
private static void reportNxDropData() {
|
||||
try {
|
||||
System.out.println("Reading Character.wz ...");
|
||||
ArrayList<File> files = new ArrayList<>();
|
||||
listFiles(WZFiles.CHARACTER.getFilePath(), files);
|
||||
|
||||
InputStreamReader fileReader = null;
|
||||
for (File f : files) {
|
||||
//System.out.println("Parsing " + f.getAbsolutePath());
|
||||
int itemid = getItemIdFromFilename(f.getName());
|
||||
if (itemid < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fileReader = new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
currentItemid = itemid;
|
||||
inspectEquipWzEntry();
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
}
|
||||
|
||||
System.out.println("Reading Item.wz ...");
|
||||
files = new ArrayList<>();
|
||||
listFiles(WZFiles.ITEM.getFilePath(), files);
|
||||
|
||||
for (File f : files) {
|
||||
//System.out.println("Parsing " + f.getAbsolutePath());
|
||||
fileReader = new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
if (f.getName().length() <= ITEM_FILE_NAME_SIZE) {
|
||||
inspectItemWzEntry();
|
||||
} else { // pet file structure is similar to equips, maybe there are other item-types following this behaviour?
|
||||
int itemid = getItemIdFromFilename(f.getName());
|
||||
if (itemid < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
currentItemid = itemid;
|
||||
inspectEquipWzEntry();
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
}
|
||||
|
||||
System.out.println("Reporting results...");
|
||||
|
||||
// report suspects of missing quest drop data, as well as those drop data that may have incorrect questids.
|
||||
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
|
||||
printReportFileHeader();
|
||||
|
||||
reportNxDropResults(true);
|
||||
reportNxDropResults(false);
|
||||
|
||||
/*
|
||||
printWriter.println("NX LIST"); // list of all cash items found
|
||||
for(Integer nx : nxItems) {
|
||||
printWriter.println(nx);
|
||||
}
|
||||
*/
|
||||
|
||||
con.close();
|
||||
printWriter.close();
|
||||
System.out.println("Done!");
|
||||
} catch (SQLException e) {
|
||||
System.out.println("Warning: Could not establish connection to database to report quest data.");
|
||||
System.out.println(e.getMessage());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
reportNxDropData();
|
||||
}
|
||||
}
|
||||
188
src/main/java/tools/mapletools/CashVegaChecker.java
Normal file
188
src/main/java/tools/mapletools/CashVegaChecker.java
Normal file
@@ -0,0 +1,188 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import provider.wz.WZFiles;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author RonanLana
|
||||
*
|
||||
This application main objective is to read Vega-related information from
|
||||
the item's description report back missing nodes for these items.
|
||||
|
||||
Estimated parse time: 10 seconds
|
||||
*/
|
||||
public class CashVegaChecker {
|
||||
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("vega_checker_report.txt");
|
||||
private static final int INITIAL_STRING_LENGTH = 1000;
|
||||
private static final Set<Integer> vegaItems = new HashSet<>();
|
||||
|
||||
private static PrintWriter printWriter = null;
|
||||
private static InputStreamReader fileReader = null;
|
||||
private static BufferedReader bufferedReader = null;
|
||||
private static int currentItem;
|
||||
private static byte status = 0;
|
||||
|
||||
private static String getName(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("name");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return(d.trim());
|
||||
}
|
||||
|
||||
private static String getValue(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("value=");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return(d.trim());
|
||||
}
|
||||
|
||||
private static void forwardCursor(int st) {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while(status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
simpleToken(line);
|
||||
}
|
||||
}
|
||||
catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void simpleToken(String token) {
|
||||
if(token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
}
|
||||
else if(token.contains("imgdir")) {
|
||||
status += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateItemToken(String token) {
|
||||
if(token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
}
|
||||
else if(token.contains("imgdir")) {
|
||||
status += 1;
|
||||
|
||||
if (status == 2) {
|
||||
currentItem = Integer.parseInt(getName(token));
|
||||
}
|
||||
} else {
|
||||
if (status == 2) {
|
||||
if (getValue(token).endsWith("Vega's Spell.")) {
|
||||
vegaItems.add(currentItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateVegaToken(String token) {
|
||||
if(token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
}
|
||||
else if(token.contains("imgdir")) {
|
||||
status += 1;
|
||||
} else {
|
||||
if (status == 2) {
|
||||
if (getName(token).contentEquals("item")) {
|
||||
vegaItems.remove(Integer.valueOf(getValue(token)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void readItemDescriptionFile(File f) {
|
||||
System.out.print("Reading String.wz... ");
|
||||
try {
|
||||
fileReader = new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
String line;
|
||||
while((line = bufferedReader.readLine())!=null){
|
||||
translateItemToken(line);
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
System.out.println(vegaItems.size() + " Vega Scroll items found");
|
||||
}
|
||||
|
||||
private static void readVegaDescriptionFile(File f) {
|
||||
System.out.println("Reading Etc.wz...");
|
||||
try {
|
||||
fileReader = new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
String line;
|
||||
while((line = bufferedReader.readLine())!=null){
|
||||
translateVegaToken(line);
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void printReportFileHeader() {
|
||||
printWriter.println(" # Report File autogenerated from the MapleCashVegaChecker feature by Ronan Lana.");
|
||||
printWriter.println(" # Generated data takes into account several data info from the server-side WZ.xmls.");
|
||||
printWriter.println();
|
||||
}
|
||||
|
||||
private static void reportMissingVegaItems() {
|
||||
System.out.println("Reporting results ...");
|
||||
|
||||
try {
|
||||
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
|
||||
|
||||
printReportFileHeader();
|
||||
|
||||
for (Integer itemid : vegaItems) {
|
||||
printWriter.println(" " + itemid);
|
||||
}
|
||||
|
||||
printWriter.close();
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
readItemDescriptionFile(new File(WZFiles.STRING.getFilePath() + "/Consume.img.xml"));
|
||||
readVegaDescriptionFile(new File(WZFiles.ETC.getFilePath() + "/VegaSpell.img.xml"));
|
||||
|
||||
reportMissingVegaItems();
|
||||
}
|
||||
|
||||
}
|
||||
356
src/main/java/tools/mapletools/CodeCouponGenerator.java
Normal file
356
src/main/java/tools/mapletools/CodeCouponGenerator.java
Normal file
@@ -0,0 +1,356 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import tools.Pair;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application parses the coupon descriptor XML file and automatically generates
|
||||
* code entries on the DB reflecting the descriptions found. Parse time relies on the
|
||||
* sum of coupon codes created and amount of current codes on DB.
|
||||
* <p>
|
||||
* Estimated parse time: 2 minutes (for 100 code entries)
|
||||
*/
|
||||
public class CodeCouponGenerator {
|
||||
private static final File INPUT_FILE = ToolConstants.getInputFile("CouponCodes.img.xml");
|
||||
private static final int INITIAL_STRING_LENGTH = 250;
|
||||
private static final Connection con = SimpleDatabaseConnection.getConnection();
|
||||
|
||||
private static final List<CodeCouponDescriptor> activeCoupons = new ArrayList<>();
|
||||
private static final Set<String> usedCodes = new HashSet<>();
|
||||
private static final List<Pair<Integer, Integer>> itemList = new ArrayList<>();
|
||||
|
||||
private static BufferedReader bufferedReader = null;
|
||||
private static long currentTime;
|
||||
private static String name;
|
||||
private static boolean active;
|
||||
private static int quantity;
|
||||
private static int duration;
|
||||
private static int maplePoint;
|
||||
private static int nxCredit;
|
||||
private static int nxPrepaid;
|
||||
private static Pair<Integer, Integer> item;
|
||||
private static List<Integer> generatedKeys;
|
||||
private static byte status;
|
||||
|
||||
private static void resetCouponPackage() {
|
||||
name = null;
|
||||
active = false;
|
||||
quantity = 1;
|
||||
duration = 7;
|
||||
maplePoint = 0;
|
||||
nxCredit = 0;
|
||||
nxPrepaid = 0;
|
||||
itemList.clear();
|
||||
}
|
||||
|
||||
private static String getName(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("name");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
try {
|
||||
token.getChars(i, j, dest, 0);
|
||||
} catch (StringIndexOutOfBoundsException e) {
|
||||
// do nothing
|
||||
return "";
|
||||
} catch (Exception e) {
|
||||
System.out.println("error in: " + token + "");
|
||||
e.printStackTrace();
|
||||
try {
|
||||
Thread.sleep(100000000);
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
}
|
||||
|
||||
return new String(dest).trim();
|
||||
}
|
||||
|
||||
private static String getValue(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("value");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static void forwardCursor(int st) {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
simpleToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void simpleToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
|
||||
if (status == 1) {
|
||||
if (active) {
|
||||
activeCoupons.add(new CodeCouponDescriptor(name, quantity, duration, maplePoint, nxCredit, nxPrepaid, itemList));
|
||||
}
|
||||
|
||||
resetCouponPackage();
|
||||
} else if (status == 3) {
|
||||
itemList.add(item);
|
||||
}
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
|
||||
if (status == 4) {
|
||||
item = new Pair<>(-1, -1);
|
||||
} else if (status == 2) {
|
||||
String d = getName(token);
|
||||
|
||||
System.out.println(" Reading coupon '" + d + "'");
|
||||
name = d;
|
||||
}
|
||||
} else {
|
||||
String d = getName(token);
|
||||
|
||||
if (status == 2) {
|
||||
switch (d) {
|
||||
case "active":
|
||||
if (Integer.parseInt(getValue(token)) == 0) {
|
||||
forwardCursor(status);
|
||||
resetCouponPackage();
|
||||
} else {
|
||||
active = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case "quantity":
|
||||
quantity = Integer.parseInt(getValue(token));
|
||||
break;
|
||||
case "duration":
|
||||
duration = Integer.parseInt(getValue(token));
|
||||
break;
|
||||
case "maplePoint":
|
||||
maplePoint = Integer.parseInt(getValue(token));
|
||||
break;
|
||||
case "nxCredit":
|
||||
nxCredit = Integer.parseInt(getValue(token));
|
||||
break;
|
||||
case "nxPrepaid":
|
||||
nxPrepaid = Integer.parseInt(getValue(token));
|
||||
break;
|
||||
}
|
||||
} else if (status == 4) {
|
||||
switch (d) {
|
||||
case "count":
|
||||
item.right = Integer.valueOf(getValue(token));
|
||||
break;
|
||||
case "id":
|
||||
item.left = Integer.valueOf(getValue(token));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class CodeCouponDescriptor {
|
||||
protected String name;
|
||||
protected int quantity, duration;
|
||||
protected int nxCredit, maplePoint, nxPrepaid;
|
||||
protected List<Pair<Integer, Integer>> itemList;
|
||||
|
||||
protected CodeCouponDescriptor(String name, int quantity, int duration, int maplePoint, int nxCredit, int nxPrepaid, List<Pair<Integer, Integer>> itemList) {
|
||||
this.name = name;
|
||||
this.quantity = quantity;
|
||||
this.duration = duration;
|
||||
this.maplePoint = maplePoint;
|
||||
this.nxCredit = nxCredit;
|
||||
this.nxPrepaid = nxPrepaid;
|
||||
|
||||
this.itemList = new ArrayList<>(itemList);
|
||||
}
|
||||
}
|
||||
|
||||
private static String randomizeCouponCode() {
|
||||
return Long.toHexString(Double.doubleToLongBits(Math.random())).substring(0, 15);
|
||||
}
|
||||
|
||||
private static String generateCouponCode() {
|
||||
String newCode;
|
||||
do {
|
||||
newCode = randomizeCouponCode();
|
||||
} while (usedCodes.contains(newCode));
|
||||
|
||||
usedCodes.add(newCode);
|
||||
return newCode;
|
||||
}
|
||||
|
||||
private static List<Integer> getGeneratedKeys(PreparedStatement ps) throws SQLException {
|
||||
if (generatedKeys == null) {
|
||||
generatedKeys = new ArrayList<>();
|
||||
|
||||
ResultSet rs = ps.getGeneratedKeys();
|
||||
while (rs.next()) {
|
||||
generatedKeys.add(rs.getInt(1));
|
||||
}
|
||||
rs.close();
|
||||
}
|
||||
|
||||
return generatedKeys;
|
||||
}
|
||||
|
||||
private static void commitCodeCouponDescription(CodeCouponDescriptor recipe) throws SQLException {
|
||||
if (recipe.quantity < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println(" Generating coupon '" + recipe.name + "'");
|
||||
generatedKeys = null;
|
||||
|
||||
PreparedStatement ps = con.prepareStatement("INSERT IGNORE INTO `nxcode` (`code`, `expiration`) VALUES (?, ?)", Statement.RETURN_GENERATED_KEYS);
|
||||
ps.setLong(2, currentTime + ((long) recipe.duration * 60 * 60 * 1000));
|
||||
|
||||
for (int i = 0; i < recipe.quantity; i++) {
|
||||
ps.setString(1, generateCouponCode());
|
||||
ps.addBatch();
|
||||
}
|
||||
ps.executeBatch();
|
||||
|
||||
PreparedStatement ps2 = con.prepareStatement("INSERT IGNORE INTO `nxcode_items` (`codeid`, `type`, `item`, `quantity`) VALUES (?, ?, ?, ?)");
|
||||
if (!recipe.itemList.isEmpty()) {
|
||||
ps2.setInt(2, 5);
|
||||
List<Integer> keys = getGeneratedKeys(ps);
|
||||
|
||||
for (Pair<Integer, Integer> p : recipe.itemList) {
|
||||
ps2.setInt(3, p.getLeft());
|
||||
ps2.setInt(4, p.getRight());
|
||||
|
||||
for (Integer codeid : keys) {
|
||||
ps2.setInt(1, codeid);
|
||||
ps2.addBatch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ps2.setInt(3, 0);
|
||||
if (recipe.nxCredit > 0) {
|
||||
ps2.setInt(2, 0);
|
||||
ps2.setInt(4, recipe.nxCredit);
|
||||
List<Integer> keys = getGeneratedKeys(ps);
|
||||
|
||||
for (Integer codeid : keys) {
|
||||
ps2.setInt(1, codeid);
|
||||
ps2.addBatch();
|
||||
}
|
||||
}
|
||||
|
||||
if (recipe.maplePoint > 0) {
|
||||
ps2.setInt(2, 1);
|
||||
ps2.setInt(4, recipe.maplePoint);
|
||||
List<Integer> keys = getGeneratedKeys(ps);
|
||||
|
||||
for (Integer codeid : keys) {
|
||||
ps2.setInt(1, codeid);
|
||||
ps2.addBatch();
|
||||
}
|
||||
}
|
||||
|
||||
if (recipe.nxPrepaid > 0) {
|
||||
ps2.setInt(2, 2);
|
||||
ps2.setInt(4, recipe.nxPrepaid);
|
||||
List<Integer> keys = getGeneratedKeys(ps);
|
||||
|
||||
for (Integer codeid : keys) {
|
||||
ps2.setInt(1, codeid);
|
||||
ps2.addBatch();
|
||||
}
|
||||
}
|
||||
|
||||
ps2.executeBatch();
|
||||
ps2.close();
|
||||
ps.close();
|
||||
}
|
||||
|
||||
private static void loadUsedCouponCodes() throws SQLException {
|
||||
PreparedStatement ps = con.prepareStatement("SELECT code FROM nxcode", Statement.RETURN_GENERATED_KEYS);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
while (rs.next()) {
|
||||
usedCodes.add(rs.getString("code"));
|
||||
}
|
||||
rs.close();
|
||||
ps.close();
|
||||
}
|
||||
|
||||
private static void generateCodeCoupons(File file) throws IOException {
|
||||
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
resetCouponPackage();
|
||||
status = 0;
|
||||
|
||||
System.out.println("Reading XML coupon information...");
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
translateToken(line);
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
System.out.println();
|
||||
|
||||
try {
|
||||
System.out.println("Loading DB coupon codes...");
|
||||
loadUsedCouponCodes();
|
||||
System.out.println();
|
||||
|
||||
System.out.println("Saving generated coupons...");
|
||||
currentTime = System.currentTimeMillis();
|
||||
for (CodeCouponDescriptor ccd : activeCoupons) {
|
||||
commitCodeCouponDescription(ccd);
|
||||
}
|
||||
System.out.println();
|
||||
|
||||
con.close();
|
||||
System.out.println("Done.");
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
generateCodeCoupons(INPUT_FILE);
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Error reading file '" + INPUT_FILE.getAbsolutePath() + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
255
src/main/java/tools/mapletools/CouponInstaller.java
Normal file
255
src/main/java/tools/mapletools/CouponInstaller.java
Normal file
@@ -0,0 +1,255 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import provider.wz.WZFiles;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application gathers information about the Cash Shop's EXP & DROP coupons,
|
||||
* such as applied rates, active times of day and days of week and dumps them in
|
||||
* a SQL table that the server will make use.
|
||||
*/
|
||||
public class CouponInstaller {
|
||||
private static final File COUPON_INPUT_FILE_1 = new File(WZFiles.ITEM.getFilePath(), "/Cash/0521.img.xml");
|
||||
private static final File COUPON_INPUT_FILE_2 = new File(WZFiles.ITEM.getFilePath(), "/Cash/0536.img.xml");
|
||||
private static final Connection con = SimpleDatabaseConnection.getConnection();
|
||||
private static BufferedReader bufferedReader = null;
|
||||
private static byte status = 0;
|
||||
private static int itemId = -1;
|
||||
private static int itemMultiplier = 1;
|
||||
private static int startHour = -1;
|
||||
private static int endHour = -1;
|
||||
private static int activeDay = 0;
|
||||
|
||||
private static String getName(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("name");
|
||||
if (i < 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[8];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d);
|
||||
}
|
||||
|
||||
private static String getNodeValue(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("value=");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
if (j - i < 1) {
|
||||
return "";
|
||||
}
|
||||
|
||||
dest = new char[j - i];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d);
|
||||
}
|
||||
|
||||
private static void forwardCursor(int st) {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
simpleToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void simpleToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getDayOfWeek(String day) {
|
||||
return switch (day) {
|
||||
case "SUN" -> 1;
|
||||
case "MON" -> 2;
|
||||
case "TUE" -> 3;
|
||||
case "WED" -> 4;
|
||||
case "THU" -> 5;
|
||||
case "FRI" -> 6;
|
||||
case "SAT" -> 7;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
private static void processHourTimeString(String time) {
|
||||
startHour = Integer.parseInt(time.substring(4, 6));
|
||||
endHour = Integer.parseInt(time.substring(7, 9));
|
||||
}
|
||||
|
||||
private static void processDayTimeString(String time) {
|
||||
String day = time.substring(0, 3);
|
||||
int d = getDayOfWeek(day);
|
||||
|
||||
activeDay |= (1 << d);
|
||||
}
|
||||
|
||||
private static void loadTimeFromCoupon(int st) {
|
||||
System.out.println("Loading coupon id " + itemId + ". Rate: " + itemMultiplier + "x.");
|
||||
|
||||
String line = null;
|
||||
try {
|
||||
startHour = -1;
|
||||
endHour = -1;
|
||||
activeDay = 0;
|
||||
|
||||
String time = null;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
simpleToken(line);
|
||||
if (status < st) {
|
||||
break;
|
||||
}
|
||||
|
||||
time = getNodeValue(line);
|
||||
processDayTimeString(time);
|
||||
|
||||
simpleToken(line);
|
||||
}
|
||||
|
||||
if (time != null) {
|
||||
processHourTimeString(time);
|
||||
|
||||
PreparedStatement ps = con.prepareStatement("INSERT INTO nxcoupons (couponid, rate, activeday, starthour, endhour) VALUES (?, ?, ?, ?, ?)");
|
||||
ps.setInt(1, itemId);
|
||||
ps.setInt(2, itemMultiplier);
|
||||
ps.setInt(3, activeDay);
|
||||
ps.setInt(4, startHour);
|
||||
ps.setInt(5, endHour);
|
||||
ps.execute();
|
||||
|
||||
ps.close();
|
||||
}
|
||||
} catch (SQLException | IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateToken(String token) {
|
||||
String d;
|
||||
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
if (status == 1) { //getting ItemId
|
||||
d = getName(token);
|
||||
itemId = Integer.parseInt(d);
|
||||
} else if (status == 2) {
|
||||
d = getName(token);
|
||||
|
||||
if (!d.contains("info")) {
|
||||
forwardCursor(status);
|
||||
}
|
||||
} else if (status == 3) {
|
||||
d = getName(token);
|
||||
|
||||
if (!d.contains("time")) {
|
||||
forwardCursor(status);
|
||||
} else {
|
||||
loadTimeFromCoupon(status);
|
||||
}
|
||||
}
|
||||
|
||||
status += 1;
|
||||
} else {
|
||||
if (status == 3) {
|
||||
d = getName(token);
|
||||
|
||||
if (d.contains("rate")) {
|
||||
String r = getNodeValue(token);
|
||||
|
||||
double db = Double.parseDouble(r);
|
||||
itemMultiplier = (int) db;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void installRateCoupons(File file) {
|
||||
// This will reference one line at a time
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
translateToken(line);
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
} catch (FileNotFoundException ex) {
|
||||
System.out.println("Unable to open file '" + file + "'");
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Error reading file '" + file + "'");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void installCouponsTable() {
|
||||
try {
|
||||
PreparedStatement ps = con.prepareStatement("DROP TABLE IF EXISTS `nxcoupons`;");
|
||||
ps.execute();
|
||||
ps.close();
|
||||
|
||||
ps = con.prepareStatement(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `nxcoupons` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`couponid` int(11) NOT NULL DEFAULT '0',
|
||||
`rate` int(11) NOT NULL DEFAULT '0',
|
||||
`activeday` int(11) NOT NULL DEFAULT '0',
|
||||
`starthour` int(11) NOT NULL DEFAULT '0',
|
||||
`endhour` int(11) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;"""
|
||||
);
|
||||
|
||||
ps.execute();
|
||||
ps.close();
|
||||
|
||||
installRateCoupons(COUPON_INPUT_FILE_1);
|
||||
installRateCoupons(COUPON_INPUT_FILE_2);
|
||||
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
System.out.println("Warning: Could not establish connection to database to change card chance rate.");
|
||||
System.out.println(e.getMessage());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
installCouponsTable();
|
||||
}
|
||||
}
|
||||
182
src/main/java/tools/mapletools/DojoUpdate.java
Normal file
182
src/main/java/tools/mapletools/DojoUpdate.java
Normal file
@@ -0,0 +1,182 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import provider.wz.WZFiles;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application parses the Character.wz folder inputted and adds/updates the "info/level"
|
||||
* node on every known equipment id. This addition enables client-side view of the equipment
|
||||
* level attribute on every equipment in the game, given proper item visibility, be it from
|
||||
* own equipments or from other players.
|
||||
* <p>
|
||||
* Estimated parse time: 10 seconds
|
||||
*/
|
||||
public class DojoUpdate {
|
||||
private static final File INPUT_DIRECTORY = new File(WZFiles.MAP.getFile(), "/Map/Map9");
|
||||
private static final File OUTPUT_DIRECTORY = ToolConstants.getOutputFile("dojo-maps");
|
||||
private static final int DOJO_MIN_MAP_ID = 925_020_100;
|
||||
private static final int DOJO_MAX_MAP_ID = 925_033_804;
|
||||
private static final int INITIAL_STRING_LENGTH = 250;
|
||||
|
||||
private static PrintWriter printWriter = null;
|
||||
private static BufferedReader bufferedReader = null;
|
||||
private static boolean isDojoMapid;
|
||||
private static byte status;
|
||||
|
||||
private static String getName(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("name");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
try {
|
||||
token.getChars(i, j, dest, 0);
|
||||
} catch (StringIndexOutOfBoundsException e) {
|
||||
// do nothing
|
||||
return "";
|
||||
} catch (Exception e) {
|
||||
System.out.println("error in: " + token + "");
|
||||
e.printStackTrace();
|
||||
try {
|
||||
Thread.sleep(100000000);
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static void forwardCursor(int st) {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
simpleToken(line);
|
||||
printWriter.println(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void simpleToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
printWriter.println(token);
|
||||
} else if (token.contains("imgdir")) {
|
||||
printWriter.println(token);
|
||||
status += 1;
|
||||
|
||||
if (status == 2) {
|
||||
String d = getName(token);
|
||||
if (!d.contentEquals("info")) {
|
||||
forwardCursor(status);
|
||||
}
|
||||
} else if (status > 2) {
|
||||
forwardCursor(status);
|
||||
}
|
||||
} else {
|
||||
if (status == 2 && isDojoMapid) {
|
||||
String item = getName(token);
|
||||
|
||||
if (item.contentEquals("onFirstUserEnter")) {
|
||||
printWriter.println(" <string name=\"onFirstUserEnter\" value=\"dojang_1st\"/>");
|
||||
} else if (item.contentEquals("onUserEnter")) {
|
||||
printWriter.println(" <string name=\"onUserEnter\" value=\"dojang_Eff\"/>");
|
||||
} else {
|
||||
printWriter.println(token);
|
||||
}
|
||||
} else {
|
||||
printWriter.println(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int getMapId(String fileName) {
|
||||
return Integer.parseInt(fileName.substring(0, 9));
|
||||
}
|
||||
|
||||
private static void parseDojoData(File file, String curPath) throws IOException {
|
||||
int mapId = getMapId(file.getName());
|
||||
isDojoMapid = isDojoMapId(mapId);
|
||||
if (!isDojoMapid) {
|
||||
return;
|
||||
}
|
||||
|
||||
printWriter = new PrintWriter(OUTPUT_DIRECTORY.getPath() + "/" + curPath + file.getName(), StandardCharsets.UTF_8);
|
||||
|
||||
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
status = 0;
|
||||
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
translateToken(line);
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
|
||||
printFileFooter();
|
||||
printWriter.close();
|
||||
}
|
||||
|
||||
private static boolean isDojoMapId(int mapId) {
|
||||
return mapId >= DOJO_MIN_MAP_ID && mapId <= DOJO_MAX_MAP_ID;
|
||||
}
|
||||
|
||||
private static void printFileFooter() {
|
||||
printWriter.println("<!--");
|
||||
printWriter.println(" # WZ XML File updated by the MapleDojoUpdate feature by Ronan Lana.");
|
||||
printWriter.println(" # Generated data takes into account info from the server-side WZ.xmls.");
|
||||
printWriter.println("-->");
|
||||
}
|
||||
|
||||
private static void parseDirectoryDojoData(String curPath) {
|
||||
File folder = new File(OUTPUT_DIRECTORY, curPath);
|
||||
if (!folder.exists()) {
|
||||
folder.mkdir();
|
||||
}
|
||||
|
||||
System.out.println("Parsing directory '" + curPath + "'");
|
||||
folder = new File(INPUT_DIRECTORY, curPath);
|
||||
for (File file : folder.listFiles()) {
|
||||
if (file.isFile()) {
|
||||
try {
|
||||
parseDojoData(file, curPath);
|
||||
} catch (FileNotFoundException ex) {
|
||||
System.out.println("Unable to open dojo file " + file.getAbsolutePath() + ".");
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Error reading dojo file " + file.getAbsolutePath() + ".");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
parseDirectoryDojoData(curPath + file.getName() + "/");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
parseDirectoryDojoData("");
|
||||
}
|
||||
}
|
||||
437
src/main/java/tools/mapletools/EmptyItemWzChecker.java
Normal file
437
src/main/java/tools/mapletools/EmptyItemWzChecker.java
Normal file
@@ -0,0 +1,437 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import provider.wz.WZFiles;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application has two objectives: it reports in a detailed file all itemids which is
|
||||
* currently missing either a name entry in the String.wz or an item entry in the Item.wz;
|
||||
* And it removes from the String.wz XMLs all entries which misses properties on Item.wz.
|
||||
*/
|
||||
public class EmptyItemWzChecker {
|
||||
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("empty_item_wz_report.txt");
|
||||
private static final String OUTPUT_PATH = ToolConstants.OUTPUT_DIRECTORY.getPath();
|
||||
private static final int INITIAL_STRING_LENGTH = 50;
|
||||
private static final int ITEM_FILE_NAME_SIZE = 13;
|
||||
|
||||
private static final Stack<String> currentPath = new Stack<>();
|
||||
private static final Map<Integer, String> stringWzItems = new HashMap<>();
|
||||
private static final Map<Integer, String> contentWzItems = new HashMap<>();
|
||||
private static final Set<Integer> handbookItems = new HashSet<>();
|
||||
|
||||
private static PrintWriter printWriter = null;
|
||||
private static InputStreamReader fileReader = null;
|
||||
private static BufferedReader bufferedReader = null;
|
||||
private static byte status = 0;
|
||||
private static int currentItemid = 0;
|
||||
private static int currentDepth = 0;
|
||||
private static String currentFile;
|
||||
private static Set<Integer> nonPropItems;
|
||||
|
||||
private static String getName(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("name");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static void forwardCursor(int st) {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
simpleToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void simpleToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void listFiles(String directoryName, ArrayList<File> files) {
|
||||
File directory = new File(directoryName);
|
||||
|
||||
// get all the files from a directory
|
||||
File[] fList = directory.listFiles();
|
||||
for (File file : fList) {
|
||||
if (file.isFile()) {
|
||||
files.add(file);
|
||||
} else if (file.isDirectory()) {
|
||||
listFiles(file.getAbsolutePath(), files);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int getItemIdFromFilename(String name) {
|
||||
try {
|
||||
return Integer.parseInt(name.substring(0, name.indexOf('.')));
|
||||
} catch (Exception e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void inspectItemWzEntry() {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
translateItemToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static String currentItemPath() {
|
||||
String s = currentFile + " -> ";
|
||||
|
||||
for (String p : currentPath) {
|
||||
s += (p + "\\");
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private static void translateItemToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
|
||||
currentPath.pop();
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
String d = getName(token);
|
||||
|
||||
if (status == 2) {
|
||||
currentItemid = Integer.parseInt(d);
|
||||
contentWzItems.put(currentItemid, currentItemPath());
|
||||
|
||||
forwardCursor(status);
|
||||
} else {
|
||||
currentPath.push(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void inspectStringWzEntry() {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
translateStringToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateStringToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
currentPath.pop();
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
String d = getName(token);
|
||||
|
||||
if (status == currentDepth) {
|
||||
currentItemid = Integer.parseInt(d);
|
||||
stringWzItems.put(currentItemid, currentItemPath());
|
||||
//if (currentItemid >= 4000000) System.out.println(" " + currentItemid);
|
||||
|
||||
forwardCursor(status);
|
||||
} else {
|
||||
currentPath.push(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadStringWzFile(String filePath, int depth) throws IOException {
|
||||
fileReader = new InputStreamReader(new FileInputStream(filePath), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
currentFile = filePath;
|
||||
currentDepth = 2 + depth;
|
||||
//System.out.println(filePath + " depth " + depth);
|
||||
inspectStringWzEntry();
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
}
|
||||
|
||||
private static void loadStringWz() throws IOException {
|
||||
System.out.println("Reading String.wz ...");
|
||||
String[][] stringWzFiles = {{"Cash", "Consume", "Ins", "Pet"}, {"Etc"}, {"Eqp"}};
|
||||
|
||||
for (int i = 0; i < stringWzFiles.length; i++) {
|
||||
for (String dirFile : stringWzFiles[i]) {
|
||||
final String fileName = "/" + dirFile + ".img.xml";
|
||||
loadStringWzFile(WZFiles.STRING.getFilePath() + fileName, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadItemWz() throws IOException {
|
||||
System.out.println("Reading Item.wz ...");
|
||||
ArrayList<File> files = new ArrayList<>();
|
||||
listFiles(WZFiles.ITEM.getFilePath(), files);
|
||||
|
||||
for (File f : files) {
|
||||
if (f.getParentFile().getName().contentEquals("Special")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//System.out.println("Parsing " + f.getAbsolutePath());
|
||||
fileReader = new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
currentFile = f.getCanonicalPath();
|
||||
|
||||
if (f.getName().length() <= ITEM_FILE_NAME_SIZE) {
|
||||
inspectItemWzEntry();
|
||||
} else { // pet file structure is similar to equips, maybe there are other item-types following this behaviour?
|
||||
int itemid = getItemIdFromFilename(f.getName());
|
||||
if (itemid < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
currentItemid = itemid;
|
||||
contentWzItems.put(currentItemid, currentItemPath());
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadCharacterWz() throws IOException {
|
||||
System.out.println("Reading Character.wz ...");
|
||||
ArrayList<File> files = new ArrayList<>();
|
||||
listFiles(WZFiles.CHARACTER.getFilePath(), files);
|
||||
|
||||
for (File f : files) {
|
||||
if (f.getParentFile().getName().contentEquals("Character.wz")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int itemid = getItemIdFromFilename(f.getName());
|
||||
if (itemid < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
currentFile = f.getCanonicalPath();
|
||||
currentItemid = itemid;
|
||||
contentWzItems.put(currentItemid, currentItemPath());
|
||||
}
|
||||
}
|
||||
|
||||
private static void calculateItemNameDiff(Set<Integer> emptyItemNames, Set<Integer> emptyNameItems) {
|
||||
for (Integer i : contentWzItems.keySet()) {
|
||||
if (!stringWzItems.containsKey(i)) {
|
||||
emptyNameItems.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
for (Integer i : stringWzItems.keySet()) {
|
||||
if (!contentWzItems.containsKey(i)) {
|
||||
emptyItemNames.add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void readHandbookItems() throws IOException {
|
||||
System.out.println("Reading handbook ...");
|
||||
String[] handbookPaths = {"Equip", "Cash.txt", "Etc.txt", "Pet.txt", "Setup.txt", "Use.txt"};
|
||||
|
||||
for (String path : handbookPaths) {
|
||||
readHandbookPath(ToolConstants.HANDBOOK_PATH + "/" + path);
|
||||
}
|
||||
}
|
||||
|
||||
private static void readHandbookPath(String filePath) throws IOException {
|
||||
ArrayList<File> files = new ArrayList<>();
|
||||
|
||||
File testFile = new File(filePath);
|
||||
if (testFile.isDirectory()) {
|
||||
listFiles(filePath, files);
|
||||
} else {
|
||||
files.add(testFile);
|
||||
}
|
||||
|
||||
for (File f : files) {
|
||||
fileReader = new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
String[] tokens = line.split(" - ");
|
||||
|
||||
if (tokens[0].length() > 0) {
|
||||
int itemid = Integer.parseInt(tokens[0]);
|
||||
handbookItems.add(itemid);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static void printReportFileHeader() {
|
||||
printWriter.println(" # Report File autogenerated from the MapleEmptyItemWzChecker feature by Ronan Lana.");
|
||||
printWriter.println(" # Generated data takes into account several data info from the server-side WZ.xmls.");
|
||||
printWriter.println();
|
||||
}
|
||||
|
||||
private static List<Integer> getSortedItems(Set<Integer> items) {
|
||||
List<Integer> sortedItems = new ArrayList<>(items);
|
||||
Collections.sort(sortedItems);
|
||||
|
||||
return sortedItems;
|
||||
}
|
||||
|
||||
private static void printReportFileResults(Set<Integer> emptyItemNames, Set<Integer> emptyNameItems) {
|
||||
if (!emptyItemNames.isEmpty()) {
|
||||
printWriter.println("String.wz NAMES with no Item.wz node, " + emptyItemNames.size() + " entries:");
|
||||
|
||||
for (Integer itemid : getSortedItems(emptyItemNames)) {
|
||||
printWriter.println(" " + itemid + " " + stringWzItems.get(itemid) + (handbookItems.contains(itemid) ? "" : " NOT FOUND"));
|
||||
}
|
||||
|
||||
printWriter.println();
|
||||
}
|
||||
|
||||
if (!emptyNameItems.isEmpty()) {
|
||||
printWriter.println("Item.wz ITEMS with no String.wz node, " + emptyNameItems.size() + " entries:");
|
||||
|
||||
for (Integer itemid : getSortedItems(emptyNameItems)) {
|
||||
printWriter.println(" " + itemid + " " + contentWzItems.get(itemid) + (handbookItems.contains(itemid) ? "" : " NOT FOUND"));
|
||||
}
|
||||
|
||||
printWriter.println();
|
||||
}
|
||||
}
|
||||
|
||||
private static void reportItemNameDiff(Set<Integer> emptyItemNames, Set<Integer> emptyNameItems) throws IOException {
|
||||
System.out.println("Reporting results...");
|
||||
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
|
||||
|
||||
printReportFileHeader();
|
||||
printReportFileResults(emptyItemNames, emptyNameItems);
|
||||
|
||||
printWriter.close();
|
||||
}
|
||||
|
||||
private static void locateItemStringWzDiff() throws IOException {
|
||||
Set<Integer> emptyItemNames = new HashSet<>(), emptyNameItems = new HashSet<>();
|
||||
calculateItemNameDiff(emptyItemNames, emptyNameItems);
|
||||
|
||||
reportItemNameDiff(emptyItemNames, emptyNameItems);
|
||||
nonPropItems = emptyItemNames;
|
||||
}
|
||||
|
||||
private static void runEmptyItemWzChecker() throws IOException {
|
||||
readHandbookItems();
|
||||
|
||||
loadCharacterWz();
|
||||
loadItemWz();
|
||||
loadStringWz();
|
||||
|
||||
locateItemStringWzDiff();
|
||||
}
|
||||
|
||||
private static void generateStringWzEntry() {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
updateStringToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateStringToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
|
||||
if (status == currentDepth && nonPropItems.contains(Integer.valueOf(getName(token)))) {
|
||||
forwardCursor(status);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
printWriter.println(token);
|
||||
}
|
||||
|
||||
private static void generateStringWzFile(String filePath, int depth) throws IOException {
|
||||
fileReader = new InputStreamReader(new FileInputStream(WZFiles.DIRECTORY + filePath), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
printWriter = new PrintWriter(OUTPUT_PATH + filePath, StandardCharsets.UTF_8);
|
||||
currentDepth = 2 + depth;
|
||||
|
||||
//System.out.println(filePath + " depth " + depth);
|
||||
generateStringWzEntry();
|
||||
|
||||
printWriter.close();
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
}
|
||||
|
||||
private static void generateStringWz() throws IOException {
|
||||
System.out.println("Generating clean String.wz ...");
|
||||
String[][] stringWzFiles = {{"Cash", "Consume", "Ins", "Pet"}, {"Etc"}, {"Eqp"}};
|
||||
String stringWzPath = "/String.wz/";
|
||||
|
||||
File folder = new File(OUTPUT_PATH + "/String.wz/");
|
||||
if (!folder.exists()) {
|
||||
folder.mkdirs();
|
||||
}
|
||||
|
||||
for (int i = 0; i < stringWzFiles.length; i++) {
|
||||
for (String dirFile : stringWzFiles[i]) {
|
||||
generateStringWzFile(stringWzPath + dirFile + ".img.xml", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
runEmptyItemWzChecker();
|
||||
generateStringWz();
|
||||
|
||||
System.out.println("Done!");
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
443
src/main/java/tools/mapletools/EquipmentOmniLeveller.java
Normal file
443
src/main/java/tools/mapletools/EquipmentOmniLeveller.java
Normal file
@@ -0,0 +1,443 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import provider.wz.WZFiles;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application parses the Character.wz folder inputted and adds/updates the "info/level"
|
||||
* node on every known equipment id. This addition enables client-side view of the equipment
|
||||
* level attribute on every equipment in the game, given proper item visibility, be it from
|
||||
* own equipments or from other players.
|
||||
* <p>
|
||||
* Estimated parse time: 7 minutes
|
||||
*/
|
||||
public class EquipmentOmniLeveller {
|
||||
private static final File INPUT_DIRECTORY = WZFiles.CHARACTER.getFile();
|
||||
private static final File OUTPUT_DIRECTORY = ToolConstants.getOutputFile("equips-with-levels");
|
||||
private static final int INITIAL_STRING_LENGTH = 250;
|
||||
private static final int FIXED_EXP = 10000;
|
||||
private static final int MAX_EQP_LEVEL = 30;
|
||||
|
||||
private static PrintWriter printWriter = null;
|
||||
private static InputStreamReader fileReader = null;
|
||||
private static BufferedReader bufferedReader = null;
|
||||
|
||||
private static int infoTagState = -1;
|
||||
private static int infoTagExpState = -1;
|
||||
private static boolean infoTagLevel;
|
||||
private static boolean infoTagLevelExp;
|
||||
private static boolean infoTagLevelInfo;
|
||||
private static int parsedLevels = 0;
|
||||
private static byte status;
|
||||
private static boolean upgradeable;
|
||||
private static boolean cash;
|
||||
|
||||
private static String getName(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("name");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
try {
|
||||
token.getChars(i, j, dest, 0);
|
||||
} catch (StringIndexOutOfBoundsException e) {
|
||||
// do nothing
|
||||
return "";
|
||||
} catch (Exception e) {
|
||||
System.out.println("error in: " + token + "");
|
||||
e.printStackTrace();
|
||||
try {
|
||||
Thread.sleep(100000000);
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static String getValue(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("value");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static void forwardCursor(int st) {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
simpleToken(line);
|
||||
printWriter.println(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateLevelCursor(int st) {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
infoTagLevelInfo = false;
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
translateLevelToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateInfoTag(int st) {
|
||||
infoTagLevel = false;
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) { // skipping directory & canvas definition
|
||||
translateInfoToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (!upgradeable || cash) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
private static void simpleToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void printUpdatedLevelExp() {
|
||||
printWriter.println(" <int name=\"exp\" value=\"" + FIXED_EXP + "\"/>");
|
||||
}
|
||||
|
||||
private static void printDefaultLevel(int level) {
|
||||
printWriter.println(" <imgdir name=\"" + level + "\">");
|
||||
printUpdatedLevelExp();
|
||||
printWriter.println(" </imgdir>");
|
||||
}
|
||||
|
||||
private static void printDefaultLevelInfoTag() {
|
||||
printWriter.println(" <imgdir name=\"info\">");
|
||||
for (int i = 1; i <= MAX_EQP_LEVEL; i++) {
|
||||
printDefaultLevel(i);
|
||||
}
|
||||
printWriter.println(" </imgdir>");
|
||||
}
|
||||
|
||||
private static void printDefaultLevelTag() {
|
||||
printWriter.println(" <imgdir name=\"level\">");
|
||||
printDefaultLevelInfoTag();
|
||||
printWriter.println(" </imgdir>");
|
||||
}
|
||||
|
||||
private static void processLevelInfoTag(int st) {
|
||||
String line;
|
||||
try {
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
translateLevelExpToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void processLevelInfoSet(int st) {
|
||||
parsedLevels = (1 << MAX_EQP_LEVEL) - 1;
|
||||
|
||||
String line;
|
||||
try {
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
translateLevelInfoSetToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateLevelToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
if (status == 3) {
|
||||
if (!infoTagLevelInfo) {
|
||||
printDefaultLevelInfoTag();
|
||||
}
|
||||
}
|
||||
printWriter.println(token);
|
||||
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
printWriter.println(token);
|
||||
status += 1;
|
||||
|
||||
if (status == 4) {
|
||||
String d = getName(token);
|
||||
if (d.contentEquals("info")) {
|
||||
infoTagLevelInfo = true;
|
||||
processLevelInfoSet(status);
|
||||
} else {
|
||||
forwardCursor(status);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printWriter.println(token);
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateLevelInfoSetToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
|
||||
if (status == 3) {
|
||||
if (parsedLevels != 0) {
|
||||
for (int i = 0; i < MAX_EQP_LEVEL; i++) {
|
||||
if ((parsedLevels >> i) % 2 != 0) {
|
||||
int level = i + 1;
|
||||
printDefaultLevel(level);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printWriter.println(token);
|
||||
} else if (token.contains("imgdir")) {
|
||||
printWriter.println(token);
|
||||
status += 1;
|
||||
|
||||
if (status == 5) {
|
||||
int level = Integer.parseInt(getName(token)) - 1;
|
||||
parsedLevels ^= (1 << level);
|
||||
|
||||
infoTagLevelExp = false;
|
||||
infoTagExpState = status; // status: 5
|
||||
processLevelInfoTag(status);
|
||||
infoTagExpState = -1;
|
||||
}
|
||||
} else {
|
||||
printWriter.println(token);
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateLevelExpToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
|
||||
if (status < infoTagExpState) {
|
||||
if (!infoTagLevelExp) {
|
||||
printUpdatedLevelExp();
|
||||
}
|
||||
}
|
||||
|
||||
printWriter.println(token);
|
||||
} else if (token.contains("imgdir")) {
|
||||
printWriter.println(token);
|
||||
status += 1;
|
||||
|
||||
forwardCursor(status);
|
||||
} else {
|
||||
String name = getName(token);
|
||||
if (name.contentEquals("exp")) {
|
||||
infoTagLevelExp = true;
|
||||
printUpdatedLevelExp();
|
||||
} else {
|
||||
printWriter.println(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateInfoToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
|
||||
if (status < infoTagState) {
|
||||
if (!infoTagLevel) {
|
||||
printDefaultLevelTag();
|
||||
}
|
||||
}
|
||||
|
||||
printWriter.println(token);
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
printWriter.println(token);
|
||||
|
||||
String d = getName(token);
|
||||
if (d.contentEquals("level")) {
|
||||
infoTagLevel = true;
|
||||
translateLevelCursor(status);
|
||||
} else {
|
||||
forwardCursor(status);
|
||||
}
|
||||
} else {
|
||||
String name = getName(token);
|
||||
|
||||
switch (name) {
|
||||
case "cash":
|
||||
if (!getValue(token).contentEquals("0")) {
|
||||
cash = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case "tuc":
|
||||
case "incPAD":
|
||||
case "incMAD":
|
||||
case "incPDD":
|
||||
case "incMDD":
|
||||
case "incACC":
|
||||
case "incEVA":
|
||||
case "incSpeed":
|
||||
case "incJump":
|
||||
case "incMHP":
|
||||
case "incMMP":
|
||||
case "incSTR":
|
||||
case "incDEX":
|
||||
case "incINT":
|
||||
case "incLUK":
|
||||
if (!getValue(token).contentEquals("0")) {
|
||||
upgradeable = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
printWriter.println(token);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean translateToken(String token) {
|
||||
boolean accessInfoTag = false;
|
||||
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
printWriter.println(token);
|
||||
} else if (token.contains("imgdir")) {
|
||||
printWriter.println(token);
|
||||
status += 1;
|
||||
|
||||
if (status == 2) {
|
||||
String d = getName(token);
|
||||
if (!d.contentEquals("info")) {
|
||||
forwardCursor(status);
|
||||
} else {
|
||||
accessInfoTag = true;
|
||||
}
|
||||
} else if (status > 2) {
|
||||
forwardCursor(status);
|
||||
}
|
||||
} else {
|
||||
printWriter.println(token);
|
||||
}
|
||||
|
||||
return accessInfoTag;
|
||||
}
|
||||
|
||||
private static void copyCashItemData(File file, String curPath) throws IOException {
|
||||
printWriter = new PrintWriter(new File(OUTPUT_DIRECTORY, curPath + file.getName()), StandardCharsets.UTF_8);
|
||||
|
||||
fileReader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
printWriter.println(line);
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
|
||||
printWriter.close();
|
||||
}
|
||||
|
||||
private static void parseEquipData(File file, String curPath) throws IOException {
|
||||
printWriter = new PrintWriter(new File(OUTPUT_DIRECTORY, curPath + file.getName()), StandardCharsets.UTF_8);
|
||||
|
||||
fileReader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
try {
|
||||
status = 0;
|
||||
upgradeable = false;
|
||||
cash = false;
|
||||
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
if (translateToken(line)) {
|
||||
infoTagState = status; // status: 2
|
||||
translateInfoTag(status);
|
||||
infoTagState = -1;
|
||||
}
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
|
||||
printFileFooter();
|
||||
printWriter.close();
|
||||
} catch (RuntimeException e) {
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
|
||||
printWriter.close();
|
||||
|
||||
copyCashItemData(file, curPath);
|
||||
}
|
||||
}
|
||||
|
||||
private static void printFileFooter() {
|
||||
printWriter.println("<!--");
|
||||
printWriter.println(" # WZ XML File parsed by the MapleEquipmentOmnilever feature by Ronan Lana.");
|
||||
printWriter.println(" # Generated data takes into account info from the server-side WZ.xmls.");
|
||||
printWriter.println("-->");
|
||||
}
|
||||
|
||||
private static void parseDirectoryEquipData(String curPath) {
|
||||
File folder = new File(OUTPUT_DIRECTORY, curPath);
|
||||
if (!folder.exists()) {
|
||||
folder.mkdir();
|
||||
}
|
||||
|
||||
System.out.println("Parsing directory '" + curPath + "'");
|
||||
folder = new File(INPUT_DIRECTORY, curPath);
|
||||
for (File file : folder.listFiles()) {
|
||||
if (file.isFile()) {
|
||||
try {
|
||||
parseEquipData(file, curPath);
|
||||
} catch (FileNotFoundException ex) {
|
||||
System.out.println("Unable to open equip file " + file.getAbsolutePath() + ".");
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Error reading equip file " + file.getAbsolutePath() + ".");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
parseDirectoryEquipData(curPath + file.getName() + "/");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
parseDirectoryEquipData("");
|
||||
}
|
||||
}
|
||||
102
src/main/java/tools/mapletools/EventMethodFiller.java
Normal file
102
src/main/java/tools/mapletools/EventMethodFiller.java
Normal file
@@ -0,0 +1,102 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application objective is to read all scripts from the event folder
|
||||
* and fill empty functions for every function name not yet present in the
|
||||
* script.
|
||||
* <p>
|
||||
* Estimated parse time: 10 seconds
|
||||
*/
|
||||
public class EventMethodFiller {
|
||||
private static boolean foundMatchingDataOnFile(String fileContent, Pattern pattern) {
|
||||
Matcher matcher = pattern.matcher(fileContent);
|
||||
return matcher.find();
|
||||
}
|
||||
|
||||
private static void fileSearchMatchingData(File file, Map<Pattern, String> functions) {
|
||||
try {
|
||||
String fileContent = FileUtils.readFileToString(file, "UTF-8");
|
||||
List<String> fillFunctions = new LinkedList<>();
|
||||
|
||||
for (Map.Entry<Pattern, String> f : functions.entrySet()) {
|
||||
if (!foundMatchingDataOnFile(fileContent, f.getKey())) {
|
||||
fillFunctions.add(f.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (!fillFunctions.isEmpty()) {
|
||||
System.out.println("Filling out " + file.getName() + "...");
|
||||
|
||||
FileWriter fileWriter = new FileWriter(file, true);
|
||||
PrintWriter printWriter = new PrintWriter(fileWriter);
|
||||
|
||||
printWriter.println();
|
||||
printWriter.println();
|
||||
printWriter.println("// ---------- FILLER FUNCTIONS ----------");
|
||||
printWriter.println();
|
||||
|
||||
for (String s : fillFunctions) {
|
||||
printWriter.println(s);
|
||||
printWriter.println();
|
||||
}
|
||||
|
||||
printWriter.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void filterDirectorySearchMatchingData(String directoryPath, Map<Pattern, String> functions) {
|
||||
Iterator iter = FileUtils.iterateFiles(new File(directoryPath), new String[]{"sql", "js", "txt", "java"}, true);
|
||||
|
||||
while (iter.hasNext()) {
|
||||
File file = (File) iter.next();
|
||||
fileSearchMatchingData(file, functions);
|
||||
}
|
||||
}
|
||||
|
||||
private static Pattern compileJsFunctionPattern(String function) {
|
||||
String jsFunction = "function(\\s)+";
|
||||
return Pattern.compile(jsFunction + function);
|
||||
}
|
||||
|
||||
private static Map<Pattern, String> getFunctions() {
|
||||
Map<Pattern, String> functions = new HashMap<>();
|
||||
functions.put(compileJsFunctionPattern("playerEntry"), "function playerEntry(eim, player) {}");
|
||||
functions.put(compileJsFunctionPattern("playerExit"), "function playerExit(eim, player) {}");
|
||||
functions.put(compileJsFunctionPattern("scheduledTimeout"), "function scheduledTimeout(eim) {}");
|
||||
functions.put(compileJsFunctionPattern("playerUnregistered"), "function playerUnregistered(eim, player) {}");
|
||||
functions.put(compileJsFunctionPattern("changedLeader"), "function changedLeader(eim, leader) {}");
|
||||
functions.put(compileJsFunctionPattern("monsterKilled"), "function monsterKilled(mob, eim) {}");
|
||||
functions.put(compileJsFunctionPattern("allMonstersDead"), "function allMonstersDead(eim) {}");
|
||||
functions.put(compileJsFunctionPattern("playerDisconnected"), "function playerDisconnected(eim, player) {}");
|
||||
functions.put(compileJsFunctionPattern("monsterValue"), "function monsterValue(eim, mobid) {return 0;}");
|
||||
functions.put(compileJsFunctionPattern("dispose"), "function dispose() {}");
|
||||
functions.put(compileJsFunctionPattern("leftParty"), "function leftParty(eim, player) {}");
|
||||
functions.put(compileJsFunctionPattern("disbandParty"), "function disbandParty(eim, player) {}");
|
||||
functions.put(compileJsFunctionPattern("clearPQ"), "function clearPQ(eim) {}");
|
||||
functions.put(compileJsFunctionPattern("afterSetup"), "function afterSetup(eim) {}");
|
||||
functions.put(compileJsFunctionPattern("cancelSchedule"), "function cancelSchedule() {}");
|
||||
functions.put(compileJsFunctionPattern("setup"), "function setup(eim, leaderid) {}");
|
||||
//put(compileJsFunctionPattern("getEligibleParty"), "function getEligibleParty(party) {}"); not really needed
|
||||
return functions;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
filterDirectorySearchMatchingData(ToolConstants.SCRIPTS_PATH + "/event", getFunctions());
|
||||
}
|
||||
|
||||
}
|
||||
330
src/main/java/tools/mapletools/GachaponItemIdRetriever.java
Normal file
330
src/main/java/tools/mapletools/GachaponItemIdRetriever.java
Normal file
@@ -0,0 +1,330 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application reads metadata for the gachapons found on the "gachapon_items.txt"
|
||||
* recipe file, then checks up the Handbook DB (installed through MapleIdRetriever)
|
||||
* and translates the item names from the recipe file into their respective itemids.
|
||||
* The translated itemids are then stored in specific gachapon files inside the
|
||||
* "lib/gachapons" folder.
|
||||
* <p>
|
||||
* Estimated parse time: 1 minute
|
||||
*/
|
||||
public class GachaponItemIdRetriever {
|
||||
private static final File INPUT_FILE = ToolConstants.getInputFile("gachapon_items.txt");
|
||||
private static final File OUTPUT_DIRECTORY = ToolConstants.getOutputFile("gachapons");
|
||||
private static final Connection con = SimpleDatabaseConnection.getConnection();
|
||||
private static final Pattern pattern = Pattern.compile("(\\d*)%");
|
||||
private static final int[] scrollsChances = new int[]{10, 15, 30, 60, 65, 70, 100};
|
||||
private static final Map<GachaponScroll, List<Integer>> scrollItemids = new HashMap<>();
|
||||
|
||||
private static PrintWriter printWriter = null;
|
||||
|
||||
private static void insertGachaponScrollItemid(Integer id, String name, String description, boolean both) {
|
||||
GachaponScroll gachaScroll = getGachaponScroll(name, description, both);
|
||||
|
||||
List<Integer> list = scrollItemids.get(gachaScroll);
|
||||
if (list == null) {
|
||||
list = new LinkedList<>();
|
||||
scrollItemids.put(gachaScroll, list);
|
||||
}
|
||||
|
||||
list.add(id);
|
||||
}
|
||||
|
||||
private static void loadHandbookUseNames() throws SQLException {
|
||||
PreparedStatement ps = con.prepareStatement("SELECT * FROM `handbook` WHERE `id` >= 2040000 AND `id` < 2050000 ORDER BY `id` ASC;");
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
while (rs.next()) {
|
||||
Integer id = rs.getInt("id");
|
||||
String name = rs.getString("name");
|
||||
|
||||
if (isUpgradeScroll(name)) {
|
||||
String description = rs.getString("description");
|
||||
insertGachaponScrollItemid(id, name, description, false);
|
||||
insertGachaponScrollItemid(id, name, description, true);
|
||||
}
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
|
||||
/*
|
||||
for (Entry<GachaponScroll, List<Integer>> e : scrollItemids.entrySet()) {
|
||||
System.out.println(e);
|
||||
}
|
||||
System.out.println("------------");
|
||||
*/
|
||||
}
|
||||
|
||||
private static class GachaponScroll {
|
||||
private String header;
|
||||
private String target;
|
||||
private String buff;
|
||||
private int prop;
|
||||
|
||||
private GachaponScroll(GachaponScroll from, int prop) {
|
||||
this.header = from.header;
|
||||
this.target = from.target;
|
||||
this.buff = from.buff;
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
private GachaponScroll(String name, String description, boolean both) {
|
||||
String[] params = name.split(" for ");
|
||||
if (params.length < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
String header = both ? "scroll" : " " + params[0];
|
||||
String target = params[1];
|
||||
|
||||
int prop = 0;
|
||||
String buff = params[2];
|
||||
|
||||
Matcher m = pattern.matcher(buff);
|
||||
if (m.find()) {
|
||||
prop = Integer.parseInt(m.group(1));
|
||||
buff = buff.substring(0, m.start() - 1).trim();
|
||||
} else {
|
||||
m = pattern.matcher(description);
|
||||
|
||||
if (m.find()) {
|
||||
prop = Integer.parseInt(m.group(1));
|
||||
}
|
||||
}
|
||||
|
||||
int idx = buff.indexOf(" ("); // remove percentage & dots from name checking
|
||||
if (idx > -1) {
|
||||
buff = buff.substring(0, idx);
|
||||
}
|
||||
buff = buff.replace(".", "");
|
||||
|
||||
this.header = header;
|
||||
this.target = target;
|
||||
this.buff = buff;
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = prop ^ (prop >>> 32);
|
||||
result = 31 * result + (header != null ? header.hashCode() : 0);
|
||||
result = 31 * result + (target != null ? target.hashCode() : 0);
|
||||
result = 31 * result + (buff != null ? buff.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
GachaponScroll sc = (GachaponScroll) o;
|
||||
if (header != null ? !header.equals(sc.header) : sc.header != null) {
|
||||
return false;
|
||||
}
|
||||
if (target != null ? !target.equals(sc.target) : sc.target != null) {
|
||||
return false;
|
||||
}
|
||||
if (buff != null ? !buff.equals(sc.buff) : sc.buff != null) {
|
||||
return false;
|
||||
}
|
||||
return prop == sc.prop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return header + " for " + target + " for " + buff + " - " + prop + "%";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static String getGachaponScrollResults(String line, boolean both) {
|
||||
String str = "";
|
||||
List<GachaponScroll> gachaScrollList;
|
||||
|
||||
GachaponScroll gachaScroll = getGachaponScroll(line, "", both);
|
||||
if (gachaScroll.prop != 0) {
|
||||
gachaScrollList = Collections.singletonList(gachaScroll);
|
||||
} else {
|
||||
gachaScrollList = new ArrayList<>(scrollsChances.length);
|
||||
|
||||
for (int prop : scrollsChances) {
|
||||
gachaScrollList.add(new GachaponScroll(gachaScroll, prop));
|
||||
}
|
||||
}
|
||||
|
||||
for (GachaponScroll gs : gachaScrollList) {
|
||||
List<Integer> gachaItemids = scrollItemids.get(gs);
|
||||
if (gachaItemids != null) {
|
||||
String listStr = "";
|
||||
for (Integer id : gachaItemids) {
|
||||
listStr += id.toString();
|
||||
listStr += " ";
|
||||
}
|
||||
|
||||
if (gachaItemids.size() > 1) {
|
||||
str += "[" + listStr + "]";
|
||||
} else {
|
||||
str += listStr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
private static GachaponScroll getGachaponScroll(String name, String description, boolean both) {
|
||||
name = name.toLowerCase();
|
||||
name = name.replace("for acc ", "for accuracy ");
|
||||
name = name.replace("blunt weapon", "bw");
|
||||
name = name.replace("eye eqp.", "eye accessory");
|
||||
name = name.replace("face eqp.", "face accessory");
|
||||
name = name.replace("for attack", "for att");
|
||||
name = name.replace("1-handed", "one-handed");
|
||||
name = name.replace("2-handed", "two-handed");
|
||||
|
||||
return new GachaponScroll(name, description, both);
|
||||
}
|
||||
|
||||
private static boolean isUpgradeScroll(String name) {
|
||||
return name.matches("^(([D|d]ark )?[S|s]croll for).*");
|
||||
}
|
||||
|
||||
private static void fetchLineOnMapleHandbook(String line, String rarity) throws SQLException {
|
||||
String str = "";
|
||||
if (!isUpgradeScroll(line)) {
|
||||
PreparedStatement ps = con.prepareStatement("SELECT `id` FROM `handbook` WHERE `name` LIKE ? ORDER BY `id` ASC;");
|
||||
ps.setString(1, line);
|
||||
|
||||
ResultSet rs = ps.executeQuery();
|
||||
while (rs.next()) {
|
||||
int id = rs.getInt("id");
|
||||
|
||||
str += Integer.toString(id);
|
||||
str += " ";
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
} else {
|
||||
str += getGachaponScrollResults(line, false);
|
||||
if (str.isEmpty()) {
|
||||
str += getGachaponScrollResults(line, true);
|
||||
|
||||
if (str.isEmpty()) {
|
||||
System.out.println("NONE for '" + line + "' : " + getGachaponScroll(line, "", false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (str.isEmpty()) {
|
||||
str += line;
|
||||
}
|
||||
|
||||
if (rarity != null) {
|
||||
str += ("- " + rarity);
|
||||
}
|
||||
|
||||
printWriter.println(str);
|
||||
}
|
||||
|
||||
private static void fetchDataOnMapleHandbook() throws SQLException {
|
||||
String line;
|
||||
|
||||
try {
|
||||
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(INPUT_FILE), StandardCharsets.UTF_8);
|
||||
BufferedReader bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
int skip = 0;
|
||||
boolean lineHeader = false;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
if (skip > 0) {
|
||||
skip--;
|
||||
|
||||
if (lineHeader) {
|
||||
if (!line.isEmpty()) {
|
||||
lineHeader = false;
|
||||
printWriter.println();
|
||||
printWriter.println(line + ":");
|
||||
}
|
||||
}
|
||||
} else if (line.isEmpty()) {
|
||||
printWriter.println("");
|
||||
} else if (line.startsWith("Gachapon ")) {
|
||||
String[] s = line.split("<EFBFBD> ");
|
||||
String gachaponName = s[s.length - 1];
|
||||
gachaponName = gachaponName.replace(" ", "_");
|
||||
gachaponName = gachaponName.toLowerCase();
|
||||
|
||||
if (printWriter != null) {
|
||||
printWriter.close();
|
||||
}
|
||||
File outputFile = new File(OUTPUT_DIRECTORY, gachaponName + ".txt");
|
||||
setupDirectories(outputFile);
|
||||
|
||||
printWriter = new PrintWriter(outputFile, StandardCharsets.UTF_8);
|
||||
|
||||
skip = 2;
|
||||
lineHeader = true;
|
||||
} else if (line.startsWith(".")) {
|
||||
skip = 1;
|
||||
lineHeader = true;
|
||||
} else {
|
||||
line = line.replace("<EFBFBD>", "'");
|
||||
for (String item : line.split("\\s\\|\\s")) {
|
||||
item = item.trim();
|
||||
if (!item.contentEquals("n/a")) {
|
||||
String[] itemInfo = item.split(" - ");
|
||||
fetchLineOnMapleHandbook(itemInfo[0], itemInfo.length > 1 ? itemInfo[1] : null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (printWriter != null) {
|
||||
printWriter.close();
|
||||
}
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
} catch (IOException ex) {
|
||||
System.out.println(ex.getMessage());
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void setupDirectories(File file) {
|
||||
if (!file.getParentFile().exists()) {
|
||||
file.getParentFile().mkdirs();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
loadHandbookUseNames();
|
||||
fetchDataOnMapleHandbook();
|
||||
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
System.out.println("Error: invalid SQL syntax");
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
192
src/main/java/tools/mapletools/IdRetriever.java
Normal file
192
src/main/java/tools/mapletools/IdRetriever.java
Normal file
@@ -0,0 +1,192 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application acts two-way: first section sets up a table on the SQL Server with all the names used within MapleStory,
|
||||
* and the second queries all the names placed inside "fetch.txt", returning in the same line order the ids of the elements.
|
||||
* In case of multiple entries with the same name, multiple ids will be returned in the same line split by a simple space
|
||||
* in ascending order. An empty line means that no entry with the given name in a line has been found.
|
||||
* <p>
|
||||
* IMPORTANT: this will fail for fetching MAP ID (you shouldn't be using this program for these, just checking them up in the
|
||||
* handbook is enough anyway).
|
||||
* <p>
|
||||
* Set whether you are first installing the handbook on the SQL Server (TRUE) or just fetching whatever is on your "fetch_ids.txt"
|
||||
* file (FALSE) on the INSTALL_SQLTABLE property and build the project. With all done, run the Java executable.
|
||||
* <p>
|
||||
* Expected installing time: 30 minutes
|
||||
*/
|
||||
public class IdRetriever {
|
||||
private static final boolean INSTALL_SQLTABLE = true;
|
||||
private static final File INPUT_FILE = ToolConstants.getInputFile("fetch_ids.txt");
|
||||
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("fetched_ids.txt");
|
||||
private static final Connection con = SimpleDatabaseConnection.getConnection();
|
||||
|
||||
private static InputStreamReader fileReader = null;
|
||||
private static BufferedReader bufferedReader = null;
|
||||
|
||||
private static void listFiles(String directoryName, ArrayList<File> files) {
|
||||
File directory = new File(directoryName);
|
||||
|
||||
// get all the files from a directory
|
||||
File[] fList = directory.listFiles();
|
||||
for (File file : fList) {
|
||||
if (file.isFile()) {
|
||||
files.add(file);
|
||||
} else if (file.isDirectory()) {
|
||||
listFiles(file.getAbsolutePath(), files);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void parseMapleHandbookLine(String line) throws SQLException {
|
||||
String[] tokens = line.split(" - ", 3);
|
||||
|
||||
if (tokens.length > 1) {
|
||||
PreparedStatement ps = con.prepareStatement("INSERT INTO `handbook` (`id`, `name`, `description`) VALUES (?, ?, ?)");
|
||||
try {
|
||||
ps.setInt(1, Integer.parseInt(tokens[0]));
|
||||
} catch (NumberFormatException npe) { // odd...
|
||||
String num = tokens[0].substring(1);
|
||||
ps.setInt(1, Integer.parseInt(num));
|
||||
}
|
||||
ps.setString(2, tokens[1]);
|
||||
ps.setString(3, tokens.length > 2 ? tokens[2] : "");
|
||||
ps.execute();
|
||||
|
||||
ps.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static void parseMapleHandbookFile(File fileObj) throws SQLException {
|
||||
if (shouldSkipParsingFile(fileObj.getName())) {
|
||||
return;
|
||||
}
|
||||
|
||||
String line;
|
||||
|
||||
try {
|
||||
fileReader = new InputStreamReader(new FileInputStream(fileObj), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
System.out.println("Parsing file '" + fileObj.getCanonicalPath() + "'.");
|
||||
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
try {
|
||||
parseMapleHandbookLine(line);
|
||||
} catch (SQLException e) {
|
||||
System.err.println("Failed to parse line: " + line);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
} catch (IOException ex) {
|
||||
System.out.println(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Quest.txt has different formatting: id is last token on the line, instead of the first
|
||||
private static boolean shouldSkipParsingFile(String fileName) {
|
||||
return "Quest.txt".equals(fileName);
|
||||
}
|
||||
|
||||
private static void setupSqlTable() throws SQLException {
|
||||
PreparedStatement ps = con.prepareStatement("DROP TABLE IF EXISTS `handbook`;");
|
||||
ps.execute();
|
||||
ps.close();
|
||||
|
||||
ps = con.prepareStatement("CREATE TABLE `handbook` ("
|
||||
+ "`key` int(10) unsigned NOT NULL AUTO_INCREMENT,"
|
||||
+ "`id` int(10) DEFAULT NULL,"
|
||||
+ "`name` varchar(200) DEFAULT NULL,"
|
||||
+ "`description` varchar(1000) DEFAULT '',"
|
||||
+ "PRIMARY KEY (`key`)"
|
||||
+ ");");
|
||||
ps.execute();
|
||||
ps.close();
|
||||
}
|
||||
|
||||
private static void parseMapleHandbook() throws SQLException {
|
||||
ArrayList<File> files = new ArrayList<>();
|
||||
|
||||
listFiles(ToolConstants.HANDBOOK_PATH, files);
|
||||
if (files.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setupSqlTable();
|
||||
|
||||
for (File f : files) {
|
||||
parseMapleHandbookFile(f);
|
||||
}
|
||||
}
|
||||
|
||||
private static void fetchDataOnMapleHandbook() throws SQLException {
|
||||
String line;
|
||||
|
||||
try {
|
||||
fileReader = new InputStreamReader(new FileInputStream(INPUT_FILE), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
PrintWriter printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
|
||||
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
if (line.isEmpty()) {
|
||||
printWriter.println("");
|
||||
continue;
|
||||
}
|
||||
|
||||
PreparedStatement ps = con.prepareStatement("SELECT `id` FROM `handbook` WHERE `name` LIKE ? ORDER BY `id` ASC;");
|
||||
ps.setString(1, line);
|
||||
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
String str = "";
|
||||
while (rs.next()) {
|
||||
int id = rs.getInt("id");
|
||||
|
||||
str += Integer.toString(id);
|
||||
str += " ";
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
|
||||
printWriter.println(str);
|
||||
}
|
||||
|
||||
printWriter.close();
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
} catch (IOException ex) {
|
||||
System.out.println(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
try {
|
||||
if (INSTALL_SQLTABLE) {
|
||||
parseMapleHandbook();
|
||||
} else {
|
||||
fetchDataOnMapleHandbook();
|
||||
}
|
||||
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
System.out.println("Error: invalid SQL syntax");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
64
src/main/java/tools/mapletools/MakerItemEntry.java
Normal file
64
src/main/java/tools/mapletools/MakerItemEntry.java
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package tools.mapletools;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author RonanLana
|
||||
*/
|
||||
public class MakerItemEntry {
|
||||
public int id = -1;
|
||||
public int itemid = -1;
|
||||
public int reqLevel = -1;
|
||||
public int reqMakerLevel = -1;
|
||||
public int reqItem = -1;
|
||||
public int reqMeso = -1;
|
||||
public int reqEquip = -1;
|
||||
public int catalyst = -1;
|
||||
public int quantity = -1;
|
||||
public int tuc = -1;
|
||||
|
||||
public int recipeCount = -1;
|
||||
public int recipeItem = -1;
|
||||
|
||||
public List<int[]> recipeList = null;
|
||||
public List<int[]> randomList = null;
|
||||
|
||||
MakerItemEntry(int id, int itemid, int reqLevel, int reqMakerLevel, int reqItem, int reqMeso, int reqEquip, int catalyst, int quantity, int tuc, int recipeCount, int recipeItem, List<int[]> recipeList, List<int[]> randomList) {
|
||||
this.id = id;
|
||||
this.itemid = itemid;
|
||||
this.reqLevel = reqLevel;
|
||||
this.reqMakerLevel = reqMakerLevel;
|
||||
this.reqItem = reqItem;
|
||||
this.reqMeso = reqMeso;
|
||||
this.reqEquip = reqEquip;
|
||||
this.catalyst = catalyst;
|
||||
this.quantity = quantity;
|
||||
this.tuc = tuc;
|
||||
|
||||
this.recipeCount = recipeCount;
|
||||
this.recipeItem = recipeItem;
|
||||
|
||||
this.recipeList = recipeList;
|
||||
this.randomList = randomList;
|
||||
}
|
||||
}
|
||||
160
src/main/java/tools/mapletools/MapFieldLimitChecker.java
Normal file
160
src/main/java/tools/mapletools/MapFieldLimitChecker.java
Normal file
@@ -0,0 +1,160 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import provider.wz.WZFiles;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application seeks from the XMLs all mapid entries that holds the specified
|
||||
* fieldLimit.
|
||||
*/
|
||||
public class MapFieldLimitChecker {
|
||||
private static final int INITIAL_STRING_LENGTH = 50;
|
||||
private static final int FIELD_LIMIT = 0x400000;
|
||||
|
||||
private static BufferedReader bufferedReader = null;
|
||||
private static byte status = 0;
|
||||
private static int mapid = 0;
|
||||
|
||||
private static String getName(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("name");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static String getValue(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("value");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static void forwardCursor(int st) {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
simpleToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void simpleToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void listFiles(String directoryName, ArrayList<File> files) {
|
||||
File directory = new File(directoryName);
|
||||
|
||||
// get all the files from a directory
|
||||
File[] fList = directory.listFiles();
|
||||
for (File file : fList) {
|
||||
if (file.isFile()) {
|
||||
files.add(file);
|
||||
} else if (file.isDirectory()) {
|
||||
listFiles(file.getAbsolutePath(), files);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int getMapIdFromFilename(String name) {
|
||||
try {
|
||||
return Integer.parseInt(name.substring(0, name.indexOf('.')));
|
||||
} catch (Exception e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
|
||||
if (status == 2) {
|
||||
String d = getName(token);
|
||||
if (!d.contentEquals("info")) {
|
||||
forwardCursor(status);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (status == 2) {
|
||||
String d = getName(token);
|
||||
|
||||
if (d.contentEquals("fieldLimit")) {
|
||||
int value = Integer.parseInt(getValue(token));
|
||||
if ((value & FIELD_LIMIT) == FIELD_LIMIT) {
|
||||
System.out.println(mapid + " " + value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void inspectMapEntry() {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
translateToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadMapWz() throws IOException {
|
||||
System.out.println("Reading Map.wz ...");
|
||||
ArrayList<File> files = new ArrayList<>();
|
||||
listFiles(WZFiles.MAP.getFilePath() + "/Map", files);
|
||||
|
||||
for (File f : files) {
|
||||
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
mapid = getMapIdFromFilename(f.getName());
|
||||
inspectMapEntry();
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
loadMapWz();
|
||||
System.out.println("Done!");
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
157
src/main/java/tools/mapletools/MapInfoRetriever.java
Normal file
157
src/main/java/tools/mapletools/MapInfoRetriever.java
Normal file
@@ -0,0 +1,157 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import provider.wz.WZFiles;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* The main objective of this tool is to locate all mapids that doesn't have
|
||||
* the "info" node in their WZ node tree.
|
||||
*/
|
||||
public class MapInfoRetriever {
|
||||
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("map_info_report.txt");
|
||||
private static final List<Integer> missingInfo = new ArrayList<>();
|
||||
|
||||
private static BufferedReader bufferedReader = null;
|
||||
private static byte status = 0;
|
||||
private static boolean hasInfo;
|
||||
|
||||
private static String getName(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("name");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[50];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static void forwardCursor(int st) {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
simpleToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void simpleToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean translateToken(String token) {
|
||||
String d;
|
||||
int temp;
|
||||
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
if (status == 1) {
|
||||
d = getName(token);
|
||||
if (d.contains("info")) {
|
||||
hasInfo = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
temp = status;
|
||||
forwardCursor(temp);
|
||||
}
|
||||
|
||||
status += 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void searchMapDirectory(int mapArea) {
|
||||
final File mapDirectory = new File(WZFiles.MAP.getFilePath() + "/Map/Map" + mapArea);
|
||||
try {
|
||||
Iterator<File> iter = FileUtils.iterateFiles(mapDirectory, new String[]{"xml"}, true);
|
||||
System.out.println("Parsing map area " + mapArea);
|
||||
|
||||
while (iter.hasNext()) {
|
||||
File file = iter.next();
|
||||
searchMapFile(file);
|
||||
}
|
||||
} catch (UncheckedIOException e) {
|
||||
System.err.println("Directory " + mapDirectory.getPath() + " does not exist");
|
||||
}
|
||||
}
|
||||
|
||||
private static void searchMapFile(File file) {
|
||||
// This will reference one line at a time
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
hasInfo = false;
|
||||
status = 0;
|
||||
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
if (translateToken(line)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasInfo) {
|
||||
missingInfo.add(Integer.valueOf(file.getName().split(".img.xml")[0]));
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Error reading file '" + file.getName() + "'");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeReport() {
|
||||
try {
|
||||
PrintWriter printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
|
||||
|
||||
if (!missingInfo.isEmpty()) {
|
||||
for (Integer i : missingInfo) {
|
||||
printWriter.println(i);
|
||||
}
|
||||
} else {
|
||||
printWriter.println("All map files contain 'info' node.");
|
||||
}
|
||||
|
||||
printWriter.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
for (int i = 0; i <= 9; i++) {
|
||||
searchMapDirectory(i);
|
||||
}
|
||||
writeReport();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
184
src/main/java/tools/mapletools/MesoFetcher.java
Normal file
184
src/main/java/tools/mapletools/MesoFetcher.java
Normal file
@@ -0,0 +1,184 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import server.life.MapleMonsterStats;
|
||||
import tools.Pair;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application traces missing meso drop data on the underlying DB (that must be
|
||||
* defined on the DatabaseConnection file of this project) and generates a
|
||||
* SQL file that proposes missing drop entries for the drop_data table.
|
||||
* <p>
|
||||
* The meso range is calculated accordingly with the target mob stats, such as level
|
||||
* and if it's a boss or not, similarly as how it has been done for the actual meso
|
||||
* drops.
|
||||
*/
|
||||
|
||||
public class MesoFetcher {
|
||||
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("meso_drop_data.sql");
|
||||
private static final boolean PERMIT_MESOS_ON_DOJO_BOSSES = false;
|
||||
private static final int MESO_ID = 0;
|
||||
private static final int MIN_ITEMS = 4;
|
||||
private static final int CHANCE = 400000;
|
||||
|
||||
private static final Map<Integer, Pair<Integer, Integer>> mobRange = new HashMap<>();
|
||||
private static PrintWriter printWriter;
|
||||
private static Map<Integer, MapleMonsterStats> mobStats;
|
||||
|
||||
private static Pair<Integer, Integer> calcMesoRange90(int level, boolean boss) {
|
||||
int minRange, maxRange;
|
||||
|
||||
// MIN range
|
||||
minRange = (int) (72.70814714 * Math.exp(0.02284640619 * level));
|
||||
|
||||
// MAX range
|
||||
maxRange = (int) (133.8194881 * Math.exp(0.02059225059 * level));
|
||||
|
||||
// boss perks
|
||||
if (boss) {
|
||||
minRange *= 3;
|
||||
maxRange *= 10;
|
||||
}
|
||||
|
||||
return new Pair<>(minRange, maxRange);
|
||||
}
|
||||
|
||||
private static Pair<Integer, Integer> calcMesoRange(int level, boolean boss) {
|
||||
int minRange, maxRange;
|
||||
|
||||
// MIN range
|
||||
minRange = (int) (30.32032228 * Math.exp(0.03281144930 * level));
|
||||
|
||||
// MAX range
|
||||
maxRange = (int) (44.45878459 * Math.exp(0.03289611686 * level));
|
||||
|
||||
// boss perks
|
||||
if (boss) {
|
||||
minRange *= 3;
|
||||
maxRange *= 10;
|
||||
}
|
||||
|
||||
return new Pair<>(minRange, maxRange);
|
||||
}
|
||||
|
||||
private static void calcAllMobsMesoRange() {
|
||||
System.out.print("Calculating range... ");
|
||||
|
||||
for (Map.Entry<Integer, MapleMonsterStats> mobStat : mobStats.entrySet()) {
|
||||
MapleMonsterStats mms = mobStat.getValue();
|
||||
Pair<Integer, Integer> mesoRange;
|
||||
|
||||
if (mms.getLevel() < 90) {
|
||||
mesoRange = calcMesoRange(mms.getLevel(), mms.isBoss());
|
||||
} else {
|
||||
mesoRange = calcMesoRange90(mms.getLevel(), mms.isBoss());
|
||||
}
|
||||
|
||||
mobRange.put(mobStat.getKey(), mesoRange);
|
||||
}
|
||||
|
||||
System.out.println("done!");
|
||||
}
|
||||
|
||||
private static void printSqlHeader() {
|
||||
printWriter.println(" # SQL File autogenerated from the MapleMesoFetcher feature by Ronan Lana.");
|
||||
printWriter.println(" # Generated data takes into account mob stats such as level and boss for the meso ranges.");
|
||||
printWriter.println(" # Only mobs with " + MIN_ITEMS + " or more items with no meso entry on the DB it was compiled are presented here.");
|
||||
printWriter.println();
|
||||
|
||||
printWriter.println(" INSERT IGNORE INTO drop_data (`dropperid`, `itemid`, `minimum_quantity`, `maximum_quantity`, `questid`, `chance`) VALUES");
|
||||
}
|
||||
|
||||
private static void printSqlExceptions() {
|
||||
if (!PERMIT_MESOS_ON_DOJO_BOSSES) {
|
||||
printWriter.println("\r\n DELETE FROM drop_data WHERE dropperid >= 9300184 AND dropperid <= 9300215 AND itemid = " + MESO_ID + ";");
|
||||
}
|
||||
}
|
||||
|
||||
private static void printSqlMobMesoRange(int mobid) {
|
||||
Pair<Integer, Integer> mobmeso = mobRange.get(mobid);
|
||||
printWriter.println("(" + mobid + ", " + MESO_ID + ", " + mobmeso.left + ", " + mobmeso.right + ", 0, " + CHANCE + "),");
|
||||
}
|
||||
|
||||
private static void printSqlMobMesoRangeFinal(int mobid) {
|
||||
Pair<Integer, Integer> mobmeso = mobRange.get(mobid);
|
||||
printWriter.println("(" + mobid + ", " + MESO_ID + ", " + mobmeso.left + ", " + mobmeso.right + ", 0, " + CHANCE + ");");
|
||||
}
|
||||
|
||||
private static void generateMissingMobsMesoRange() {
|
||||
System.out.print("Generating missing ranges... ");
|
||||
Connection con = SimpleDatabaseConnection.getConnection();
|
||||
List<Integer> existingMobs = new ArrayList<>(200);
|
||||
|
||||
try {
|
||||
// select all mobs which doesn't drop mesos and have a fair amount of items dropping (meaning they are not an event mob)
|
||||
PreparedStatement ps = con.prepareStatement("SELECT dropperid FROM drop_data WHERE dropperid NOT IN (SELECT DISTINCT dropperid FROM drop_data WHERE itemid = 0) GROUP BY dropperid HAVING count(*) >= " + MIN_ITEMS + ";");
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
if (rs.isBeforeFirst()) {
|
||||
while (rs.next()) {
|
||||
int mobid = rs.getInt(1);
|
||||
|
||||
if (mobRange.containsKey(mobid)) {
|
||||
existingMobs.add(mobid);
|
||||
}
|
||||
}
|
||||
|
||||
if (!existingMobs.isEmpty()) {
|
||||
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
|
||||
printSqlHeader();
|
||||
|
||||
for (int i = 0; i < existingMobs.size() - 1; i++) {
|
||||
printSqlMobMesoRange(existingMobs.get(i));
|
||||
}
|
||||
|
||||
printSqlMobMesoRangeFinal(existingMobs.get(existingMobs.size() - 1));
|
||||
|
||||
printSqlExceptions();
|
||||
|
||||
printWriter.close();
|
||||
} else {
|
||||
throw new Exception("ALREADY UPDATED");
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new Exception("ALREADY UPDATED");
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
con.close();
|
||||
|
||||
System.out.println("done!");
|
||||
|
||||
} catch (Exception e) {
|
||||
if (e.getMessage() != null && e.getMessage().equals("ALREADY UPDATED")) {
|
||||
System.out.println("done! The DB is already up-to-date, no file generated.");
|
||||
} else {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// load mob stats from WZ
|
||||
mobStats = MonsterStatFetcher.getAllMonsterStats();
|
||||
|
||||
calcAllMobsMesoRange();
|
||||
generateMissingMobsMesoRange();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
169
src/main/java/tools/mapletools/MobBookIndexer.java
Normal file
169
src/main/java/tools/mapletools/MobBookIndexer.java
Normal file
@@ -0,0 +1,169 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import provider.wz.WZFiles;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application simply gets from the MonsterBook.img.xml all mobid's and
|
||||
* puts them on a SQL table with the correspondent mob cardid.
|
||||
*/
|
||||
public class MobBookIndexer {
|
||||
private static final File INPUT_FILE = new File(WZFiles.STRING.getFile(), "MonsterBook.img.xml");
|
||||
private static final Connection con = SimpleDatabaseConnection.getConnection();
|
||||
|
||||
private static BufferedReader bufferedReader = null;
|
||||
private static byte status = 0;
|
||||
private static int mobId = -1;
|
||||
|
||||
private static String getName(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("name");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
if (j - i < 7) {
|
||||
dest = new char[6];
|
||||
} else {
|
||||
dest = new char[7];
|
||||
}
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d);
|
||||
}
|
||||
|
||||
private static void forwardCursor(int st) {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
simpleToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void simpleToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isCard(int itemId) {
|
||||
return itemId / 10000 == 238;
|
||||
}
|
||||
|
||||
private static void loadPairFromMob() {
|
||||
System.out.println("Loading mob id " + mobId);
|
||||
|
||||
try {
|
||||
PreparedStatement ps, ps2;
|
||||
ResultSet rs;
|
||||
|
||||
ps = con.prepareStatement("SELECT itemid FROM drop_data WHERE (dropperid = ? AND itemid > 0) GROUP BY itemid;");
|
||||
ps.setInt(1, mobId);
|
||||
rs = ps.executeQuery();
|
||||
|
||||
while (rs.next()) {
|
||||
int itemId = rs.getInt("itemid");
|
||||
if (isCard(itemId)) {
|
||||
ps2 = con.prepareStatement("INSERT INTO `monstercardwz` (`cardid`, `mobid`) VALUES (?, ?)");
|
||||
ps2.setInt(1, itemId);
|
||||
ps2.setInt(2, mobId);
|
||||
|
||||
ps2.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateToken(String token) {
|
||||
String d;
|
||||
int temp;
|
||||
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
if (status == 1) { //getting MobId
|
||||
d = getName(token);
|
||||
mobId = Integer.parseInt(d);
|
||||
} else if (status == 2) {
|
||||
d = getName(token);
|
||||
|
||||
if (d.contains("reward")) {
|
||||
temp = status;
|
||||
|
||||
loadPairFromMob();
|
||||
forwardCursor(temp);
|
||||
}
|
||||
}
|
||||
|
||||
status += 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void indexFromDropData() {
|
||||
// This will reference one line at a time
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(INPUT_FILE), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
PreparedStatement ps = con.prepareStatement("DROP TABLE IF EXISTS monstercardwz;");
|
||||
ps.execute();
|
||||
|
||||
ps = con.prepareStatement("CREATE TABLE `monstercardwz` ("
|
||||
+ "`id` int(10) unsigned NOT NULL AUTO_INCREMENT,"
|
||||
+ "`cardid` int(10) NOT NULL DEFAULT '-1',"
|
||||
+ "`mobid` int(10) NOT NULL DEFAULT '-1',"
|
||||
+ "PRIMARY KEY (`id`)"
|
||||
+ ");");
|
||||
ps.execute();
|
||||
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
translateToken(line);
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
|
||||
con.close();
|
||||
} catch (FileNotFoundException ex) {
|
||||
System.out.println("Unable to open file '" + INPUT_FILE + "'");
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Error reading file '" + INPUT_FILE + "'");
|
||||
} catch (SQLException e) {
|
||||
System.out.println("Warning: Could not establish connection to database to change card chance rate.");
|
||||
System.out.println(e.getMessage());
|
||||
e.printStackTrace();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
indexFromDropData();
|
||||
}
|
||||
}
|
||||
|
||||
179
src/main/java/tools/mapletools/MobBookUpdate.java
Normal file
179
src/main/java/tools/mapletools/MobBookUpdate.java
Normal file
@@ -0,0 +1,179 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import provider.wz.WZFiles;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application updates the Monster Book drop data with the actual underlying drop data from
|
||||
* the Maplestory database specified in the URL below.
|
||||
* <p>
|
||||
* In other words all items drops from monsters listed inside the Mob Book feature will be patched to match exactly like the item
|
||||
* drop list specified in the URL's Maplestory database.
|
||||
* <p>
|
||||
* The original file "MonsterBook.img.xml" from String.wz must be copied to the directory of this application and only then
|
||||
* executed. This program will generate another file that must replace the original server file to make the effects take place
|
||||
* to on your server.
|
||||
* <p>
|
||||
* After replacing on server, this XML must be updated on the client via WZ Editor (HaRepack for instance). Once inside the repack,
|
||||
* remove the property 'MonsterBook.img' inside 'string.wz' and choose to import the xml generated with this software.
|
||||
*/
|
||||
public class MobBookUpdate {
|
||||
private static final File INPUT_FILE = new File(WZFiles.STRING.getFile(), "MonsterBook.img.xml");
|
||||
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("MonsterBook_updated.img.xml");
|
||||
private static final Connection con = SimpleDatabaseConnection.getConnection();
|
||||
|
||||
private static PrintWriter printWriter = null;
|
||||
private static BufferedReader bufferedReader = null;
|
||||
private static byte status = 0;
|
||||
private static int mobId = -1;
|
||||
|
||||
private static String getName(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("name");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
if (j - i < 7) {
|
||||
dest = new char[6];
|
||||
} else {
|
||||
dest = new char[7];
|
||||
}
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d);
|
||||
}
|
||||
|
||||
private static void forwardCursor(int st) {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
simpleToken(line);
|
||||
}
|
||||
if (line != null) {
|
||||
printWriter.println(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void simpleToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadDropsFromMob() {
|
||||
System.out.println("Loading mob id " + mobId);
|
||||
|
||||
try {
|
||||
String toPrint;
|
||||
int itemId, cont = 0;
|
||||
|
||||
PreparedStatement ps = con.prepareStatement("SELECT itemid FROM drop_data WHERE (dropperid = ? AND itemid > 0) GROUP BY itemid;");
|
||||
ps.setInt(1, mobId);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
while (rs.next()) {
|
||||
toPrint = "";
|
||||
for (int k = 0; k <= status; k++) {
|
||||
toPrint += " ";
|
||||
}
|
||||
|
||||
toPrint += "<int name=\"";
|
||||
toPrint += cont;
|
||||
toPrint += "\" value=\"";
|
||||
|
||||
itemId = rs.getInt("itemid");
|
||||
toPrint += itemId;
|
||||
toPrint += "\" />";
|
||||
|
||||
printWriter.println(toPrint);
|
||||
cont++;
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateToken(String token) {
|
||||
String d;
|
||||
int temp;
|
||||
|
||||
printWriter.println(token);
|
||||
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
if (status == 1) { //getting MobId
|
||||
d = getName(token);
|
||||
mobId = Integer.parseInt(d);
|
||||
} else if (status == 2) {
|
||||
d = getName(token);
|
||||
|
||||
if (d.contains("reward")) {
|
||||
temp = status;
|
||||
|
||||
loadDropsFromMob();
|
||||
forwardCursor(temp);
|
||||
}
|
||||
}
|
||||
|
||||
status += 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void updateFromDropData() {
|
||||
// This will reference one line at a time
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
|
||||
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(INPUT_FILE), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
translateToken(line);
|
||||
}
|
||||
|
||||
printWriter.close();
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
|
||||
con.close();
|
||||
} catch (FileNotFoundException ex) {
|
||||
System.out.println("Unable to open file '" + INPUT_FILE + "'");
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Error reading file '" + INPUT_FILE + "'");
|
||||
} catch (SQLException e) {
|
||||
System.out.println("Warning: Could not establish connection to database to change card chance rate.");
|
||||
System.out.println(e.getMessage());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
updateFromDropData();
|
||||
}
|
||||
}
|
||||
|
||||
143
src/main/java/tools/mapletools/MonsterStatFetcher.java
Normal file
143
src/main/java/tools/mapletools/MonsterStatFetcher.java
Normal file
@@ -0,0 +1,143 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import provider.*;
|
||||
import provider.wz.MapleDataType;
|
||||
import provider.wz.WZFiles;
|
||||
import server.life.Element;
|
||||
import server.life.ElementalEffectiveness;
|
||||
import server.life.MapleLifeFactory.BanishInfo;
|
||||
import server.life.MapleLifeFactory.loseItem;
|
||||
import server.life.MapleLifeFactory.selfDestruction;
|
||||
import server.life.MapleMonsterStats;
|
||||
import tools.Pair;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class MonsterStatFetcher {
|
||||
private static final MapleDataProvider data = MapleDataProviderFactory.getDataProvider(WZFiles.MOB);
|
||||
private static final MapleDataProvider stringDataWZ = MapleDataProviderFactory.getDataProvider(WZFiles.STRING);
|
||||
private static final MapleData mobStringData = stringDataWZ.getData("Mob.img");
|
||||
private static final Map<Integer, MapleMonsterStats> monsterStats = new HashMap<>();
|
||||
|
||||
static Map<Integer, MapleMonsterStats> getAllMonsterStats() {
|
||||
MapleDataDirectoryEntry root = data.getRoot();
|
||||
|
||||
System.out.print("Parsing mob stats... ");
|
||||
for (MapleDataFileEntry mFile : root.getFiles()) {
|
||||
try {
|
||||
String fileName = mFile.getName();
|
||||
|
||||
//System.out.println("Parsing '" + fileName + "'");
|
||||
MapleData monsterData = data.getData(fileName);
|
||||
if (monsterData == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Integer mid = getMonsterId(fileName);
|
||||
|
||||
MapleData monsterInfoData = monsterData.getChildByPath("info");
|
||||
MapleMonsterStats stats = new MapleMonsterStats();
|
||||
stats.setHp(MapleDataTool.getIntConvert("maxHP", monsterInfoData));
|
||||
stats.setFriendly(MapleDataTool.getIntConvert("damagedByMob", monsterInfoData, 0) == 1);
|
||||
stats.setPADamage(MapleDataTool.getIntConvert("PADamage", monsterInfoData));
|
||||
stats.setPDDamage(MapleDataTool.getIntConvert("PDDamage", monsterInfoData));
|
||||
stats.setMADamage(MapleDataTool.getIntConvert("MADamage", monsterInfoData));
|
||||
stats.setMDDamage(MapleDataTool.getIntConvert("MDDamage", monsterInfoData));
|
||||
stats.setMp(MapleDataTool.getIntConvert("maxMP", monsterInfoData, 0));
|
||||
stats.setExp(MapleDataTool.getIntConvert("exp", monsterInfoData, 0));
|
||||
stats.setLevel(MapleDataTool.getIntConvert("level", monsterInfoData));
|
||||
stats.setRemoveAfter(MapleDataTool.getIntConvert("removeAfter", monsterInfoData, 0));
|
||||
stats.setBoss(MapleDataTool.getIntConvert("boss", monsterInfoData, 0) > 0);
|
||||
stats.setExplosiveReward(MapleDataTool.getIntConvert("explosiveReward", monsterInfoData, 0) > 0);
|
||||
stats.setFfaLoot(MapleDataTool.getIntConvert("publicReward", monsterInfoData, 0) > 0);
|
||||
stats.setUndead(MapleDataTool.getIntConvert("undead", monsterInfoData, 0) > 0);
|
||||
stats.setName(MapleDataTool.getString(mid + "/name", mobStringData, "MISSINGNO"));
|
||||
stats.setBuffToGive(MapleDataTool.getIntConvert("buff", monsterInfoData, -1));
|
||||
stats.setCP(MapleDataTool.getIntConvert("getCP", monsterInfoData, 0));
|
||||
stats.setRemoveOnMiss(MapleDataTool.getIntConvert("removeOnMiss", monsterInfoData, 0) > 0);
|
||||
|
||||
MapleData special = monsterInfoData.getChildByPath("coolDamage");
|
||||
if (special != null) {
|
||||
int coolDmg = MapleDataTool.getIntConvert("coolDamage", monsterInfoData);
|
||||
int coolProb = MapleDataTool.getIntConvert("coolDamageProb", monsterInfoData, 0);
|
||||
stats.setCool(new Pair<>(coolDmg, coolProb));
|
||||
}
|
||||
special = monsterInfoData.getChildByPath("loseItem");
|
||||
if (special != null) {
|
||||
for (MapleData liData : special.getChildren()) {
|
||||
stats.addLoseItem(new loseItem(MapleDataTool.getInt(liData.getChildByPath("id")), (byte) MapleDataTool.getInt(liData.getChildByPath("prop")), (byte) MapleDataTool.getInt(liData.getChildByPath("x"))));
|
||||
}
|
||||
}
|
||||
special = monsterInfoData.getChildByPath("selfDestruction");
|
||||
if (special != null) {
|
||||
stats.setSelfDestruction(new selfDestruction((byte) MapleDataTool.getInt(special.getChildByPath("action")), MapleDataTool.getIntConvert("removeAfter", special, -1), MapleDataTool.getIntConvert("hp", special, -1)));
|
||||
}
|
||||
MapleData firstAttackData = monsterInfoData.getChildByPath("firstAttack");
|
||||
int firstAttack = 0;
|
||||
if (firstAttackData != null) {
|
||||
if (firstAttackData.getType() == MapleDataType.FLOAT) {
|
||||
firstAttack = Math.round(MapleDataTool.getFloat(firstAttackData));
|
||||
} else {
|
||||
firstAttack = MapleDataTool.getInt(firstAttackData);
|
||||
}
|
||||
}
|
||||
stats.setFirstAttack(firstAttack > 0);
|
||||
stats.setDropPeriod(MapleDataTool.getIntConvert("dropItemPeriod", monsterInfoData, 0) * 10000);
|
||||
|
||||
stats.setTagColor(MapleDataTool.getIntConvert("hpTagColor", monsterInfoData, 0));
|
||||
stats.setTagBgColor(MapleDataTool.getIntConvert("hpTagBgcolor", monsterInfoData, 0));
|
||||
|
||||
for (MapleData idata : monsterData) {
|
||||
if (!idata.getName().equals("info")) {
|
||||
int delay = 0;
|
||||
for (MapleData pic : idata.getChildren()) {
|
||||
delay += MapleDataTool.getIntConvert("delay", pic, 0);
|
||||
}
|
||||
stats.setAnimationTime(idata.getName(), delay);
|
||||
}
|
||||
}
|
||||
MapleData reviveInfo = monsterInfoData.getChildByPath("revive");
|
||||
if (reviveInfo != null) {
|
||||
List<Integer> revives = new LinkedList<>();
|
||||
for (MapleData data_ : reviveInfo) {
|
||||
revives.add(MapleDataTool.getInt(data_));
|
||||
}
|
||||
stats.setRevives(revives);
|
||||
}
|
||||
decodeElementalString(stats, MapleDataTool.getString("elemAttr", monsterInfoData, ""));
|
||||
MapleData monsterSkillData = monsterInfoData.getChildByPath("skill");
|
||||
if (monsterSkillData != null) {
|
||||
int i = 0;
|
||||
List<Pair<Integer, Integer>> skills = new ArrayList<>();
|
||||
while (monsterSkillData.getChildByPath(Integer.toString(i)) != null) {
|
||||
skills.add(new Pair<>(MapleDataTool.getInt(i + "/skill", monsterSkillData, 0), MapleDataTool.getInt(i + "/level", monsterSkillData, 0)));
|
||||
i++;
|
||||
}
|
||||
stats.setSkills(skills);
|
||||
}
|
||||
MapleData banishData = monsterInfoData.getChildByPath("ban");
|
||||
if (banishData != null) {
|
||||
stats.setBanishInfo(new BanishInfo(MapleDataTool.getString("banMsg", banishData), MapleDataTool.getInt("banMap/0/field", banishData, -1), MapleDataTool.getString("banMap/0/portal", banishData, "sp")));
|
||||
}
|
||||
|
||||
monsterStats.put(mid, stats);
|
||||
} catch(NullPointerException npe) {
|
||||
//System.out.println("[SEVERE] " + mFile.getName() + " failed to load. Issue: " + npe.getMessage() + "\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("Done parsing mob stats!");
|
||||
return monsterStats;
|
||||
}
|
||||
|
||||
private static int getMonsterId(String fileName) {
|
||||
return Integer.parseInt(fileName.substring(0, 7));
|
||||
}
|
||||
|
||||
private static void decodeElementalString(MapleMonsterStats stats, String elemAttr) {
|
||||
for (int i = 0; i < elemAttr.length(); i += 2) {
|
||||
stats.setEffectiveness(Element.getFromChar(elemAttr.charAt(i)), ElementalEffectiveness.getByNumber(Integer.valueOf(String.valueOf(elemAttr.charAt(i + 1)))));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
216
src/main/java/tools/mapletools/NoItemIdFetcher.java
Normal file
216
src/main/java/tools/mapletools/NoItemIdFetcher.java
Normal file
@@ -0,0 +1,216 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import provider.wz.WZFiles;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application finds inexistent itemids within the drop data from
|
||||
* the Maplestory database specified in the URL below. This program
|
||||
* assumes all itemids uses 7 digits.
|
||||
* <p>
|
||||
* A file is generated listing all the inexistent ids.
|
||||
*/
|
||||
public class NoItemIdFetcher {
|
||||
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("no_item_id_report.txt");
|
||||
private static final Connection con = SimpleDatabaseConnection.getConnection();
|
||||
|
||||
private static final Set<Integer> existingIds = new HashSet<>();
|
||||
private static final Set<Integer> nonExistingIds = new HashSet<>();
|
||||
|
||||
private static PrintWriter printWriter = null;
|
||||
private static BufferedReader bufferedReader = null;
|
||||
private static byte status = 0;
|
||||
private static int itemId = -1;
|
||||
|
||||
private static String getName(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("name");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[100];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d);
|
||||
}
|
||||
|
||||
private static void forwardCursor(int st) {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
simpleToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void simpleToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateToken(String token) {
|
||||
String d;
|
||||
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
if (status == 1) { //getting ItemId
|
||||
d = getName(token);
|
||||
itemId = Integer.parseInt(d.substring(1, 8));
|
||||
|
||||
existingIds.add(itemId);
|
||||
forwardCursor(status);
|
||||
}
|
||||
|
||||
status += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void readItemDataFile(File file) {
|
||||
// This will reference one line at a time
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
status = 0;
|
||||
try {
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
translateToken(line);
|
||||
}
|
||||
} catch (NumberFormatException npe) {
|
||||
// second criteria, itemid is on the name of the file
|
||||
|
||||
try {
|
||||
itemId = Integer.parseInt(file.getName().substring(0, 7));
|
||||
existingIds.add(itemId);
|
||||
} catch (NumberFormatException npe2) {
|
||||
}
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
} catch (FileNotFoundException ex) {
|
||||
System.out.println("Unable to open file '" + file.getName() + "'");
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Error reading file '" + file.getName() + "'");
|
||||
}
|
||||
}
|
||||
|
||||
private static void readEquipDataDirectory(String dirPath) {
|
||||
File[] folders = new File(dirPath).listFiles();
|
||||
//If this pathname does not denote a directory, then listFiles() returns null.
|
||||
|
||||
for (File folder : folders) { // enter all subfolders
|
||||
if (folder.isDirectory()) {
|
||||
System.out.println("Reading '" + dirPath + "/" + folder.getName() + "'...");
|
||||
|
||||
try {
|
||||
File[] files = folder.listFiles();
|
||||
|
||||
for (File file : files) { // enter all XML files under subfolders
|
||||
if (file.isFile()) {
|
||||
itemId = Integer.parseInt(file.getName().substring(0, 8));
|
||||
existingIds.add(itemId);
|
||||
}
|
||||
}
|
||||
} catch (NumberFormatException nfe) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void readItemDataDirectory(String dirPath) {
|
||||
File[] folders = new File(dirPath).listFiles();
|
||||
//If this pathname does not denote a directory, then listFiles() returns null.
|
||||
|
||||
for (File folder : folders) { // enter all subfolders
|
||||
if (folder.isDirectory()) {
|
||||
System.out.println("Reading '" + dirPath + "/" + folder.getName() + "'...");
|
||||
|
||||
File[] files = folder.listFiles();
|
||||
|
||||
for (File file : files) { // enter all XML files under subfolders
|
||||
if (file.isFile()) {
|
||||
readItemDataFile(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void evaluateDropsFromTable(String table) throws SQLException {
|
||||
PreparedStatement ps = con.prepareStatement("SELECT DISTINCT itemid FROM " + table + ";");
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
while (rs.next()) {
|
||||
if (!existingIds.contains(rs.getInt(1))) {
|
||||
nonExistingIds.add(rs.getInt(1));
|
||||
}
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
}
|
||||
|
||||
private static void evaluateDropsFromDb() {
|
||||
try {
|
||||
System.out.println("Evaluating item data on DB...");
|
||||
|
||||
evaluateDropsFromTable("drop_data");
|
||||
evaluateDropsFromTable("reactordrops");
|
||||
|
||||
if (!nonExistingIds.isEmpty()) {
|
||||
List<Integer> list = new ArrayList<>(nonExistingIds);
|
||||
Collections.sort(list);
|
||||
|
||||
for (Integer i : list) {
|
||||
printWriter.println(i);
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("Inexistent itemid count: " + nonExistingIds.size());
|
||||
System.out.println("Total itemid count: " + existingIds.size());
|
||||
|
||||
con.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
|
||||
|
||||
existingIds.add(0); // meso itemid
|
||||
readEquipDataDirectory(WZFiles.CHARACTER.getFilePath());
|
||||
readItemDataDirectory(WZFiles.ITEM.getFilePath());
|
||||
|
||||
evaluateDropsFromDb();
|
||||
|
||||
printWriter.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
473
src/main/java/tools/mapletools/NoItemNameFetcher.java
Normal file
473
src/main/java/tools/mapletools/NoItemNameFetcher.java
Normal file
@@ -0,0 +1,473 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import provider.*;
|
||||
import provider.wz.WZFiles;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application finds itemids with inexistent name and description from
|
||||
* within the server-side XMLs, then identify them on a report file along
|
||||
* with a XML excerpt to be appended on the String.wz xml nodes. This program
|
||||
* assumes all equipids are depicted using 8 digits and item using 7 digits.
|
||||
* <p>
|
||||
* Estimated parse time: 2 minutes
|
||||
*/
|
||||
public class NoItemNameFetcher {
|
||||
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("no_item_name_result.txt");
|
||||
private static final File OUTPUT_XML_FILE = ToolConstants.getOutputFile("no_item_name_xml.txt");
|
||||
|
||||
private static final Map<Integer, String> itemsWzPath = new HashMap<>();
|
||||
private static final Map<Integer, EquipType> equipTypes = new HashMap<>();
|
||||
private static final Map<Integer, ItemType> itemsWithNoNameProperty = new HashMap<>();
|
||||
private static final Set<Integer> equipsWithNoCashProperty = new HashSet<>();
|
||||
private static final Map<Integer, String> nameContentCache = new HashMap<>();
|
||||
private static final Map<Integer, String> descContentCache = new HashMap<>();
|
||||
|
||||
private static PrintWriter printWriter = null;
|
||||
private static ItemType curType = ItemType.UNDEF;
|
||||
|
||||
private enum ItemType {
|
||||
UNDEF, CASH, CONSUME, EQP, ETC, INS, PET
|
||||
}
|
||||
|
||||
private enum EquipType {
|
||||
UNDEF, ACCESSORY, CAP, CAPE, COAT, FACE, GLOVE, HAIR, LONGCOAT, PANTS, PETEQUIP, RING, SHIELD, SHOES, TAMING, WEAPON
|
||||
}
|
||||
|
||||
private static void processStringSubdirectoryData(MapleData subdirData, String subdirPath) {
|
||||
for (MapleData md : subdirData.getChildren()) {
|
||||
try {
|
||||
MapleData nameData = md.getChildByPath("name");
|
||||
MapleData descData = md.getChildByPath("desc");
|
||||
|
||||
int itemId = Integer.parseInt(md.getName());
|
||||
if (nameData != null && descData != null) {
|
||||
itemsWithNoNameProperty.remove(itemId);
|
||||
} else {
|
||||
if (nameData != null) {
|
||||
nameContentCache.put(itemId, MapleDataTool.getString("name", md));
|
||||
} else if (descData != null) {
|
||||
descContentCache.put(itemId, MapleDataTool.getString("desc", md));
|
||||
}
|
||||
|
||||
System.out.println("Found itemid on String.wz with no full property: " + subdirPath + subdirData.getName() + "/" + md.getName());
|
||||
}
|
||||
} catch (NumberFormatException nfe) {
|
||||
System.out.println("Error reading string image: " + subdirPath + subdirData.getName() + "/" + md.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void readStringSubdirectoryData(MapleData subdirData, int depth, String subdirPath) {
|
||||
if (depth > 0) {
|
||||
for (MapleData mDir : subdirData.getChildren()) {
|
||||
readStringSubdirectoryData(mDir, depth - 1, subdirPath + mDir.getName() + "/");
|
||||
}
|
||||
} else {
|
||||
processStringSubdirectoryData(subdirData, subdirPath);
|
||||
}
|
||||
}
|
||||
|
||||
private static void readStringSubdirectoryData(MapleData subdirData, int depth) {
|
||||
readStringSubdirectoryData(subdirData, depth, "");
|
||||
}
|
||||
|
||||
private static void readStringWZData() {
|
||||
System.out.println("Parsing String.wz...");
|
||||
MapleDataProvider stringData = MapleDataProviderFactory.getDataProvider(WZFiles.STRING);
|
||||
|
||||
MapleData cashStringData = stringData.getData("Cash.img");
|
||||
readStringSubdirectoryData(cashStringData, 0);
|
||||
|
||||
MapleData consumeStringData = stringData.getData("Consume.img");
|
||||
readStringSubdirectoryData(consumeStringData, 0);
|
||||
|
||||
MapleData eqpStringData = stringData.getData("Eqp.img");
|
||||
readStringSubdirectoryData(eqpStringData, 2);
|
||||
|
||||
MapleData etcStringData = stringData.getData("Etc.img");
|
||||
readStringSubdirectoryData(etcStringData, 1);
|
||||
|
||||
MapleData insStringData = stringData.getData("Ins.img");
|
||||
readStringSubdirectoryData(insStringData, 0);
|
||||
|
||||
MapleData petStringData = stringData.getData("Pet.img");
|
||||
readStringSubdirectoryData(petStringData, 0);
|
||||
}
|
||||
|
||||
private static boolean isTamingMob(int itemId) {
|
||||
int itemType = itemId / 1000;
|
||||
return itemType == 1902 || itemType == 1912;
|
||||
}
|
||||
|
||||
private static boolean isAccessory(int itemId) {
|
||||
return itemId >= 1110000 && itemId < 1140000;
|
||||
}
|
||||
|
||||
private static ItemType getItemTypeFromDirectoryName(String dirName) {
|
||||
return switch (dirName) {
|
||||
case "Cash" -> ItemType.CASH;
|
||||
case "Consume" -> ItemType.CONSUME;
|
||||
case "Etc" -> ItemType.ETC;
|
||||
case "Install" -> ItemType.INS;
|
||||
case "Pet" -> ItemType.PET;
|
||||
default -> ItemType.UNDEF;
|
||||
};
|
||||
}
|
||||
|
||||
private static EquipType getEquipTypeFromDirectoryName(String dirName) {
|
||||
return switch (dirName) {
|
||||
case "Accessory" -> EquipType.ACCESSORY;
|
||||
case "Cap" -> EquipType.CAP;
|
||||
case "Cape" -> EquipType.CAPE;
|
||||
case "Coat" -> EquipType.COAT;
|
||||
case "Face" -> EquipType.FACE;
|
||||
case "Glove" -> EquipType.GLOVE;
|
||||
case "Hair" -> EquipType.HAIR;
|
||||
case "Longcoat" -> EquipType.LONGCOAT;
|
||||
case "Pants" -> EquipType.PANTS;
|
||||
case "PetEquip" -> EquipType.PETEQUIP;
|
||||
case "Ring" -> EquipType.RING;
|
||||
case "Shield" -> EquipType.SHIELD;
|
||||
case "Shoes" -> EquipType.SHOES;
|
||||
case "TamingMob" -> EquipType.TAMING;
|
||||
case "Weapon" -> EquipType.WEAPON;
|
||||
default -> EquipType.UNDEF;
|
||||
};
|
||||
}
|
||||
|
||||
private static String getStringDirectoryNameFromEquipType(EquipType eType) {
|
||||
return switch (eType) {
|
||||
case ACCESSORY -> "Accessory";
|
||||
case CAP -> "Cap";
|
||||
case CAPE -> "Cape";
|
||||
case COAT -> "Coat";
|
||||
case FACE -> "Face";
|
||||
case GLOVE -> "Glove";
|
||||
case HAIR -> "Hair";
|
||||
case LONGCOAT -> "Longcoat";
|
||||
case PANTS -> "Pants";
|
||||
case PETEQUIP -> "PetEquip";
|
||||
case RING -> "Ring";
|
||||
case SHIELD -> "Shield";
|
||||
case SHOES -> "Shoes";
|
||||
case TAMING -> "Taming";
|
||||
case WEAPON -> "Weapon";
|
||||
default -> "Undefined";
|
||||
};
|
||||
}
|
||||
|
||||
private static void readEquipNodeData(MapleDataProvider data, MapleDataDirectoryEntry mDir, String wzFileName, String dirName) {
|
||||
EquipType eqType = getEquipTypeFromDirectoryName(dirName);
|
||||
|
||||
for (MapleDataFileEntry mFile : mDir.getFiles()) {
|
||||
String fileName = mFile.getName();
|
||||
|
||||
try {
|
||||
int itemId = Integer.parseInt(fileName.substring(0, 8));
|
||||
itemsWithNoNameProperty.put(itemId, curType);
|
||||
equipTypes.put(itemId, eqType);
|
||||
|
||||
itemsWzPath.put(itemId, wzFileName + "/" + dirName + "/" + fileName);
|
||||
|
||||
if (!isAccessory(itemId) && !isTamingMob(itemId)) {
|
||||
try {
|
||||
MapleData fileData = data.getData(dirName + "/" + fileName);
|
||||
MapleData mdinfo = fileData.getChildByPath("info");
|
||||
if (mdinfo.getChildByPath("cash") == null) {
|
||||
equipsWithNoCashProperty.add(itemId);
|
||||
}
|
||||
} catch (NullPointerException npe) {
|
||||
System.out.println("[SEVERE] " + mFile.getName() + " failed to load. Issue: " + npe.getMessage() + "\n\n");
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void readEquipWZData() {
|
||||
String wzFileName = "Character.wz";
|
||||
|
||||
MapleDataProvider data = MapleDataProviderFactory.getDataProvider(WZFiles.CHARACTER);
|
||||
MapleDataDirectoryEntry root = data.getRoot();
|
||||
|
||||
System.out.println("Parsing " + wzFileName + "...");
|
||||
for (MapleDataDirectoryEntry mDir : root.getSubdirectories()) {
|
||||
String dirName = mDir.getName();
|
||||
if (dirName.contentEquals("Dragon")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
readEquipNodeData(data, mDir, wzFileName, dirName);
|
||||
}
|
||||
}
|
||||
|
||||
private static void readItemWZData() {
|
||||
String wzFileName = "Item.wz";
|
||||
|
||||
MapleDataProvider data = MapleDataProviderFactory.getDataProvider(WZFiles.ITEM);
|
||||
MapleDataDirectoryEntry root = data.getRoot();
|
||||
|
||||
System.out.println("Parsing " + wzFileName + "...");
|
||||
for (MapleDataDirectoryEntry mDir : root.getSubdirectories()) {
|
||||
String dirName = mDir.getName();
|
||||
if (dirName.contentEquals("Special")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
curType = getItemTypeFromDirectoryName(dirName);
|
||||
if (!dirName.contentEquals("Pet")) {
|
||||
for (MapleDataFileEntry mFile : mDir.getFiles()) {
|
||||
String fileName = mFile.getName();
|
||||
|
||||
MapleData fileData = data.getData(dirName + "/" + fileName);
|
||||
for (MapleData mData : fileData.getChildren()) {
|
||||
try {
|
||||
int itemId = Integer.parseInt(mData.getName());
|
||||
itemsWithNoNameProperty.put(itemId, curType);
|
||||
itemsWzPath.put(itemId, wzFileName + "/" + dirName + "/" + fileName);
|
||||
} catch (Exception e) {
|
||||
System.out.println("EXCEPTION on '" + mData.getName() + "' " + wzFileName + "/" + dirName + "/" + fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
readEquipNodeData(data, mDir, wzFileName, dirName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void printReportFileHeader() {
|
||||
printWriter.println(" # Report File autogenerated from the MapleInvalidItemWithNoNameFetcher feature by Ronan Lana.");
|
||||
printWriter.println(" # Generated data takes into account several data info from the server-side WZ.xmls.");
|
||||
printWriter.println();
|
||||
}
|
||||
|
||||
private static void printReportFileResults() {
|
||||
if (!itemsWithNoNameProperty.isEmpty()) {
|
||||
printWriter.println("Itemids with missing 'name' property: ");
|
||||
|
||||
List<Integer> itemids = new ArrayList<>(itemsWithNoNameProperty.keySet());
|
||||
Collections.sort(itemids);
|
||||
|
||||
for (Integer itemid : itemids) {
|
||||
printWriter.println(" " + itemid + " " + itemsWzPath.get(itemid));
|
||||
}
|
||||
printWriter.println();
|
||||
}
|
||||
|
||||
if (!equipsWithNoCashProperty.isEmpty()) {
|
||||
printWriter.println("Equipids with missing 'cash' property: ");
|
||||
|
||||
List<Integer> itemids = new ArrayList<>(equipsWithNoCashProperty);
|
||||
Collections.sort(itemids);
|
||||
|
||||
for (Integer itemid : itemids) {
|
||||
printWriter.println(" " + itemid + " " + itemsWzPath.get(itemid));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, List<Integer>> filterMissingItemNames() {
|
||||
List<Integer> cashList = new ArrayList<>(20);
|
||||
List<Integer> consList = new ArrayList<>(20);
|
||||
List<Integer> eqpList = new ArrayList<>(20);
|
||||
List<Integer> etcList = new ArrayList<>(20);
|
||||
List<Integer> insList = new ArrayList<>(20);
|
||||
List<Integer> petList = new ArrayList<>(20);
|
||||
|
||||
for (Map.Entry<Integer, ItemType> ids : itemsWithNoNameProperty.entrySet()) {
|
||||
switch (ids.getValue()) {
|
||||
case CASH -> cashList.add(ids.getKey());
|
||||
case CONSUME -> consList.add(ids.getKey());
|
||||
case EQP -> eqpList.add(ids.getKey());
|
||||
case ETC -> etcList.add(ids.getKey());
|
||||
case INS -> insList.add(ids.getKey());
|
||||
case PET -> petList.add(ids.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, List<Integer>> nameTags = new HashMap<>();
|
||||
nameTags.put("Cash.img", cashList);
|
||||
nameTags.put("Consume.img", consList);
|
||||
nameTags.put("Eqp.img", eqpList);
|
||||
nameTags.put("Etc.img", etcList);
|
||||
nameTags.put("Ins.img", insList);
|
||||
nameTags.put("Pet.img", petList);
|
||||
|
||||
return nameTags;
|
||||
}
|
||||
|
||||
private static void printOutputFileHeader() {
|
||||
printWriter.println(" # XML File autogenerated from the MapleInvalidItemWithNoNameFetcher feature by Ronan Lana.");
|
||||
printWriter.println(" # Generated data takes into account several data info from the server-side WZ.xmls.");
|
||||
printWriter.println();
|
||||
}
|
||||
|
||||
private static String getMissingEquipName(int itemid) {
|
||||
String s = nameContentCache.get(itemid);
|
||||
if (s == null) {
|
||||
s = "MISSING NAME " + itemid;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private static String getMissingEquipDesc(int itemid) {
|
||||
String s = descContentCache.get(itemid);
|
||||
if (s == null && itemid >= 2000000) { // thanks Halcyon for noticing "missing info" on equips
|
||||
s = "MISSING INFO " + itemid;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private static void writeMissingEquipInfo(Integer itemid) {
|
||||
printWriter.println(" <imgdir name=\"" + itemid + "\">");
|
||||
|
||||
String s;
|
||||
s = getMissingEquipName(itemid);
|
||||
printWriter.println(" <string name=\"name\" value=\"" + s + "\"/>");
|
||||
|
||||
s = getMissingEquipDesc(itemid);
|
||||
printWriter.println(" <string name=\"desc\" value=\"" + s + "\"/>");
|
||||
printWriter.println(" </imgdir>");
|
||||
}
|
||||
|
||||
private static void writeEquipSubdirectoryHeader(EquipType eType) {
|
||||
printWriter.println(" <imgdir name=\"" + getStringDirectoryNameFromEquipType(eType) + "\">");
|
||||
}
|
||||
|
||||
private static void writeEquipSubdirectoryFooter() {
|
||||
printWriter.println(" </imgdir>");
|
||||
}
|
||||
|
||||
private static void writeEquipXMLHeader() {
|
||||
printWriter.println(" <imgdir name=\"Eqp\">");
|
||||
}
|
||||
|
||||
private static void writeEquipXMLFooter() {
|
||||
printWriter.println(" </imgdir>");
|
||||
}
|
||||
|
||||
private static void writeMissingItemInfo(Integer itemid) {
|
||||
printWriter.println(" <imgdir name=\"" + itemid + "\">");
|
||||
printWriter.println(" <string name=\"name\" value=\"MISSING NAME\"/>");
|
||||
printWriter.println(" <string name=\"desc\" value=\"MISSING INFO\"/>");
|
||||
printWriter.println(" </imgdir>");
|
||||
}
|
||||
|
||||
private static void writeXMLHeader(String fileName) {
|
||||
printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
|
||||
printWriter.println("<imgdir name=\"" + fileName + "\">");
|
||||
}
|
||||
|
||||
private static void writeXMLFooter() {
|
||||
printWriter.println("</imgdir>");
|
||||
}
|
||||
|
||||
private static void writeMissingEquipWZNode(EquipType eType, List<Integer> missingNames) {
|
||||
if (!missingNames.isEmpty()) {
|
||||
Collections.sort(missingNames);
|
||||
writeEquipSubdirectoryHeader(eType);
|
||||
|
||||
for (Integer equipid : missingNames) {
|
||||
writeMissingEquipInfo(equipid);
|
||||
}
|
||||
|
||||
writeEquipSubdirectoryFooter();
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeMissingStringWZNode(String nodePath, List<Integer> missingNames, boolean isEquip) {
|
||||
if (!missingNames.isEmpty()) {
|
||||
if (!isEquip) {
|
||||
Collections.sort(missingNames);
|
||||
|
||||
printWriter.println(nodePath + ":");
|
||||
printWriter.println();
|
||||
|
||||
writeXMLHeader(nodePath);
|
||||
|
||||
for (Integer i : missingNames) {
|
||||
writeMissingItemInfo(i);
|
||||
}
|
||||
|
||||
writeXMLFooter();
|
||||
|
||||
printWriter.println();
|
||||
} else {
|
||||
int arraySize = EquipType.values().length;
|
||||
|
||||
List<Integer>[] equips = new List[arraySize];
|
||||
for (int i = 0; i < arraySize; i++) {
|
||||
equips[i] = new ArrayList<>(42);
|
||||
}
|
||||
|
||||
for (Integer itemid : missingNames) {
|
||||
equips[equipTypes.get(itemid).ordinal()].add(itemid);
|
||||
}
|
||||
|
||||
printWriter.println(nodePath + ":");
|
||||
printWriter.println();
|
||||
|
||||
writeXMLHeader(nodePath);
|
||||
writeEquipXMLHeader();
|
||||
|
||||
for (EquipType eType : EquipType.values()) {
|
||||
writeMissingEquipWZNode(eType, equips[eType.ordinal()]);
|
||||
}
|
||||
|
||||
writeEquipXMLFooter();
|
||||
writeXMLFooter();
|
||||
|
||||
printWriter.println();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeMissingStringWZNames(Map<String, List<Integer>> missingNames) throws Exception {
|
||||
System.out.println("Writing remaining 'String.wz' names...");
|
||||
|
||||
printWriter = new PrintWriter(OUTPUT_XML_FILE, StandardCharsets.UTF_8);
|
||||
printOutputFileHeader();
|
||||
|
||||
String[] nodePaths = {"Cash.img", "Consume.img", "Eqp.img", "Etc.img", "Ins.img", "Pet.img"};
|
||||
for (int i = 0; i < nodePaths.length; i++) {
|
||||
writeMissingStringWZNode(nodePaths[i], missingNames.get(nodePaths[i]), i == 2);
|
||||
}
|
||||
|
||||
printWriter.close();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
curType = ItemType.EQP;
|
||||
readEquipWZData();
|
||||
|
||||
curType = ItemType.UNDEF;
|
||||
readItemWZData();
|
||||
readStringWZData(); // calculates the diff and effectively holds all items with no name property on the WZ
|
||||
|
||||
System.out.println("Reporting results...");
|
||||
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
|
||||
printReportFileHeader();
|
||||
printReportFileResults();
|
||||
printWriter.close();
|
||||
|
||||
Map<String, List<Integer>> missingNames = filterMissingItemNames();
|
||||
writeMissingStringWZNames(missingNames);
|
||||
|
||||
System.out.println("Done!");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
268
src/main/java/tools/mapletools/QuestItemCountFetcher.java
Normal file
268
src/main/java/tools/mapletools/QuestItemCountFetcher.java
Normal file
@@ -0,0 +1,268 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import provider.wz.WZFiles;
|
||||
import tools.Pair;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application parses the Quest.wz file inputted and generates a report showing
|
||||
* all cases where a quest requires an item, but doesn't take them, which may happen
|
||||
* because the node representing the item doesn't have a "count" clause.
|
||||
* <p>
|
||||
* Running it should generate a report file under "output" folder with the search results.
|
||||
*/
|
||||
public class QuestItemCountFetcher {
|
||||
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("quest_item_count_report.txt");
|
||||
private static final String ACT_NAME = WZFiles.QUEST.getFilePath() + "/Act.img.xml";
|
||||
private static final String CHECK_NAME = WZFiles.QUEST.getFilePath() + "/Check.img.xml";
|
||||
private static final int INITIAL_STRING_LENGTH = 50;
|
||||
|
||||
private static final Map<Integer, Map<Integer, Integer>> checkItems = new HashMap<>();
|
||||
private static final Map<Integer, Map<Integer, Integer>> actItems = new HashMap<>();
|
||||
|
||||
private static PrintWriter printWriter = null;
|
||||
private static BufferedReader bufferedReader = null;
|
||||
private static byte status = 0;
|
||||
private static int questId = -1;
|
||||
private static int isCompleteState = 0;
|
||||
private static int curItemId;
|
||||
private static int curItemCount;
|
||||
|
||||
private static String getName(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("name");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static String getValue(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("value");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static void forwardCursor(int st) {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
simpleToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void simpleToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void readItemLabel(String token) {
|
||||
String name = getName(token);
|
||||
String value = getValue(token);
|
||||
|
||||
switch (name) {
|
||||
case "id" -> curItemId = Integer.parseInt(value);
|
||||
case "count" -> curItemCount = Integer.parseInt(value);
|
||||
}
|
||||
}
|
||||
|
||||
private static void commitQuestItemPair(Map<Integer, Map<Integer, Integer>> map) {
|
||||
Map<Integer, Integer> list = map.get(questId);
|
||||
if (list == null) {
|
||||
list = new LinkedHashMap<>();
|
||||
map.put(questId, list);
|
||||
}
|
||||
|
||||
list.put(curItemId, curItemCount);
|
||||
}
|
||||
|
||||
private static void translateTokenAct(String token) {
|
||||
String d;
|
||||
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
|
||||
if (status == 4) {
|
||||
if (curItemCount == Integer.MAX_VALUE && isCompleteState == 1) {
|
||||
commitQuestItemPair(actItems);
|
||||
}
|
||||
}
|
||||
} else if (token.contains("imgdir")) {
|
||||
if (status == 1) { //getting QuestId
|
||||
d = getName(token);
|
||||
questId = Integer.parseInt(d);
|
||||
} else if (status == 2) { //start/complete
|
||||
d = getName(token);
|
||||
isCompleteState = Integer.parseInt(d);
|
||||
} else if (status == 3) {
|
||||
if (!token.contains("item")) {
|
||||
forwardCursor(status);
|
||||
}
|
||||
} else if (status == 4) {
|
||||
curItemId = Integer.MAX_VALUE;
|
||||
curItemCount = Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
status += 1;
|
||||
} else {
|
||||
if (status == 5) {
|
||||
readItemLabel(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateTokenCheck(String token) {
|
||||
String d;
|
||||
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
|
||||
if (status == 4) {
|
||||
Map<Integer, Integer> missedItems = actItems.get(questId);
|
||||
|
||||
if (missedItems != null && missedItems.containsKey(curItemId) && isCompleteState == 1) {
|
||||
commitQuestItemPair(checkItems);
|
||||
}
|
||||
}
|
||||
} else if (token.contains("imgdir")) {
|
||||
if (status == 1) { //getting QuestId
|
||||
d = getName(token);
|
||||
questId = Integer.parseInt(d);
|
||||
} else if (status == 2) { //start/complete
|
||||
d = getName(token);
|
||||
isCompleteState = Integer.parseInt(d);
|
||||
} else if (status == 3) {
|
||||
if (!token.contains("item")) {
|
||||
forwardCursor(status);
|
||||
}
|
||||
} else if (status == 4) {
|
||||
curItemId = Integer.MAX_VALUE;
|
||||
curItemCount = Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
status += 1;
|
||||
} else {
|
||||
if (status == 5) {
|
||||
readItemLabel(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void readQuestItemCountData() throws IOException {
|
||||
String line;
|
||||
|
||||
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(ACT_NAME), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
translateTokenAct(line);
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
|
||||
fileReader = new InputStreamReader(new FileInputStream(CHECK_NAME), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
translateTokenCheck(line);
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
}
|
||||
|
||||
private static void printReportFileHeader() {
|
||||
printWriter.println(" # Report File autogenerated from the MapleQuestItemCountFetcher feature by Ronan Lana.");
|
||||
printWriter.println(" # Generated data takes into account several data info from the server-side WZ.xmls.");
|
||||
printWriter.println();
|
||||
}
|
||||
|
||||
private static void printReportFileResults() {
|
||||
List<Pair<Integer, Pair<Integer, Integer>>> reports = new ArrayList<>();
|
||||
List<Pair<Integer, Integer>> notChecked = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<Integer, Map<Integer, Integer>> actItem : actItems.entrySet()) {
|
||||
int questid = actItem.getKey();
|
||||
|
||||
for (Map.Entry<Integer, Integer> actData : actItem.getValue().entrySet()) {
|
||||
int itemid = actData.getKey();
|
||||
|
||||
Map<Integer, Integer> checkData = checkItems.get(questid);
|
||||
if (checkData != null) {
|
||||
Integer count = checkData.get(itemid);
|
||||
if (count != null) {
|
||||
reports.add(new Pair<>(questid, new Pair<>(itemid, -count)));
|
||||
}
|
||||
} else {
|
||||
notChecked.add(new Pair<>(questid, itemid));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Pair<Integer, Pair<Integer, Integer>> r : reports) {
|
||||
printWriter.println("Questid " + r.left + " : Itemid " + r.right.left + " should have qty " + r.right.right);
|
||||
}
|
||||
|
||||
for (Pair<Integer, Integer> r : notChecked) {
|
||||
printWriter.println("Questid " + r.left + " : Itemid " + r.right + " is unchecked");
|
||||
}
|
||||
}
|
||||
|
||||
private static void reportQuestItemCountData() {
|
||||
// This will reference one line at a time
|
||||
|
||||
try {
|
||||
System.out.println("Reading WZs...");
|
||||
readQuestItemCountData();
|
||||
|
||||
System.out.println("Reporting results...");
|
||||
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
|
||||
|
||||
printReportFileHeader();
|
||||
printReportFileResults();
|
||||
|
||||
printWriter.close();
|
||||
System.out.println("Done!");
|
||||
} catch (FileNotFoundException ex) {
|
||||
System.out.println("Unable to open quest file.");
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Error reading quest file.");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
reportQuestItemCountData();
|
||||
}
|
||||
}
|
||||
529
src/main/java/tools/mapletools/QuestItemFetcher.java
Normal file
529
src/main/java/tools/mapletools/QuestItemFetcher.java
Normal file
@@ -0,0 +1,529 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import provider.wz.WZFiles;
|
||||
import server.MapleItemInformationProvider;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.Pair;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application haves 2 objectives: fetch missing drop data relevant to quests,
|
||||
* and update the questid from items that are labeled as "Quest Item" on the DB.
|
||||
* <p>
|
||||
* Running it should generate a report file under "output" folder with the search results.
|
||||
* <p>
|
||||
* Estimated parse time: 1 minute
|
||||
*/
|
||||
public class QuestItemFetcher {
|
||||
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("quest_report.txt");
|
||||
private static final int INITIAL_STRING_LENGTH = 50;
|
||||
private static final int INITIAL_LENGTH = 200;
|
||||
private static final boolean DISPLAY_EXTRA_INFO = true; // display items with zero quantity over the quest act WZ
|
||||
|
||||
private static final Connection con = SimpleDatabaseConnection.getConnection();
|
||||
private static final Map<Integer, Set<Integer>> startQuestItems = new HashMap<>(INITIAL_LENGTH);
|
||||
private static final Map<Integer, Set<Integer>> completeQuestItems = new HashMap<>(INITIAL_LENGTH);
|
||||
private static final Map<Integer, Set<Integer>> zeroedStartQuestItems = new HashMap<>();
|
||||
private static final Map<Integer, Set<Integer>> zeroedCompleteQuestItems = new HashMap<>();
|
||||
private static final Map<Integer, int[]> mixedQuestidItems = new HashMap<>();
|
||||
private static final Set<Integer> limitedQuestids = new HashSet<>();
|
||||
|
||||
private static MapleItemInformationProvider ii;
|
||||
private static PrintWriter printWriter = null;
|
||||
private static BufferedReader bufferedReader = null;
|
||||
private static byte status = 0;
|
||||
private static int questId = -1;
|
||||
private static int isCompleteState = 0;
|
||||
private static int currentItemid = 0;
|
||||
private static int currentCount = 0;
|
||||
|
||||
private static String getName(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("name");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
if (j < i) {
|
||||
return "0"; //node value containing 'name' in it's scope, cheap fix since we don't deal with strings anyway
|
||||
}
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static String getValue(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("value");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static void forwardCursor(int st) {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
simpleToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void simpleToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void inspectQuestItemList(int st) {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
readItemToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void processCurrentItem() {
|
||||
try {
|
||||
if (ii.isQuestItem(currentItemid)) {
|
||||
if (currentCount != 0) {
|
||||
if (isCompleteState == 1) {
|
||||
if (currentCount < 0) {
|
||||
Set<Integer> qi = completeQuestItems.get(questId);
|
||||
if (qi == null) {
|
||||
Set<Integer> newSet = new HashSet<>();
|
||||
newSet.add(currentItemid);
|
||||
|
||||
completeQuestItems.put(questId, newSet);
|
||||
} else {
|
||||
qi.add(currentItemid);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (currentCount > 0) {
|
||||
Set<Integer> qi = startQuestItems.get(questId);
|
||||
if (qi == null) {
|
||||
Set<Integer> newSet = new HashSet<>();
|
||||
newSet.add(currentItemid);
|
||||
|
||||
startQuestItems.put(questId, newSet);
|
||||
} else {
|
||||
qi.add(currentItemid);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isCompleteState == 1) {
|
||||
Set<Integer> qi = zeroedCompleteQuestItems.get(questId);
|
||||
if (qi == null) {
|
||||
Set<Integer> newSet = new HashSet<>();
|
||||
newSet.add(currentItemid);
|
||||
|
||||
zeroedCompleteQuestItems.put(questId, newSet);
|
||||
} else {
|
||||
qi.add(currentItemid);
|
||||
}
|
||||
} else {
|
||||
Set<Integer> qi = zeroedStartQuestItems.get(questId);
|
||||
if (qi == null) {
|
||||
Set<Integer> newSet = new HashSet<>();
|
||||
newSet.add(currentItemid);
|
||||
|
||||
zeroedStartQuestItems.put(questId, newSet);
|
||||
} else {
|
||||
qi.add(currentItemid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
private static void readItemToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
|
||||
processCurrentItem();
|
||||
|
||||
currentItemid = 0;
|
||||
currentCount = 0;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
} else {
|
||||
String d = getName(token);
|
||||
|
||||
if (d.equals("id")) {
|
||||
currentItemid = Integer.parseInt(getValue(token));
|
||||
} else if (d.equals("count")) {
|
||||
currentCount = Integer.parseInt(getValue(token));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateActToken(String token) {
|
||||
String d;
|
||||
int temp;
|
||||
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
if (status == 1) { //getting QuestId
|
||||
d = getName(token);
|
||||
questId = Integer.parseInt(d);
|
||||
} else if (status == 2) { //start/complete
|
||||
d = getName(token);
|
||||
isCompleteState = Integer.parseInt(d);
|
||||
} else if (status == 3) {
|
||||
d = getName(token);
|
||||
|
||||
if (d.contains("item")) {
|
||||
temp = status;
|
||||
inspectQuestItemList(temp);
|
||||
} else {
|
||||
forwardCursor(status);
|
||||
}
|
||||
}
|
||||
|
||||
status += 1;
|
||||
} else {
|
||||
if (status == 3) {
|
||||
d = getName(token);
|
||||
|
||||
if (d.equals("end")) {
|
||||
limitedQuestids.add(questId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateCheckToken(String token) {
|
||||
String d;
|
||||
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
if (status == 1) { //getting QuestId
|
||||
d = getName(token);
|
||||
questId = Integer.parseInt(d);
|
||||
} else if (status == 2) { //start/complete
|
||||
d = getName(token);
|
||||
isCompleteState = Integer.parseInt(d);
|
||||
} else if (status == 3) {
|
||||
forwardCursor(status);
|
||||
}
|
||||
|
||||
status += 1;
|
||||
} else {
|
||||
if (status == 3) {
|
||||
d = getName(token);
|
||||
|
||||
if (d.equals("end")) {
|
||||
limitedQuestids.add(questId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void calculateQuestItemDiff() {
|
||||
// This will remove started quest items from the "to complete" item set.
|
||||
|
||||
for (Map.Entry<Integer, Set<Integer>> qd : startQuestItems.entrySet()) {
|
||||
for (Integer qi : qd.getValue()) {
|
||||
Set<Integer> questSet = completeQuestItems.get(qd.getKey());
|
||||
|
||||
if (questSet != null) {
|
||||
if (questSet.remove(qi)) {
|
||||
if (completeQuestItems.isEmpty()) {
|
||||
completeQuestItems.remove(qd.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Pair<Integer, Integer>> getPairsQuestItem() { // quest items not gained at WZ's quest start
|
||||
List<Pair<Integer, Integer>> list = new ArrayList<>(INITIAL_LENGTH);
|
||||
|
||||
for (Map.Entry<Integer, Set<Integer>> qd : completeQuestItems.entrySet()) {
|
||||
for (Integer qi : qd.getValue()) {
|
||||
list.add(new Pair<>(qi, qd.getKey()));
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static String getTableName(boolean dropdata) {
|
||||
return dropdata ? "drop_data" : "reactordrops";
|
||||
}
|
||||
|
||||
private static void filterQuestDropsOnTable(Pair<Integer, Integer> iq, List<Pair<Integer, Integer>> itemsWithQuest, boolean dropdata) throws SQLException {
|
||||
PreparedStatement ps = con.prepareStatement("SELECT questid FROM " + getTableName(dropdata) + " WHERE itemid = ?;");
|
||||
ps.setInt(1, iq.getLeft());
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
if (rs.isBeforeFirst()) {
|
||||
while (rs.next()) {
|
||||
int curQuest = rs.getInt(1);
|
||||
if (curQuest != iq.getRight()) {
|
||||
Set<Integer> sqSet = startQuestItems.get(curQuest);
|
||||
if (sqSet != null && sqSet.contains(iq.getLeft())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int[] mixed = new int[3];
|
||||
mixed[0] = iq.getLeft();
|
||||
mixed[1] = curQuest;
|
||||
mixed[2] = iq.getRight();
|
||||
|
||||
mixedQuestidItems.put(iq.getLeft(), mixed);
|
||||
}
|
||||
}
|
||||
|
||||
itemsWithQuest.remove(iq);
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
}
|
||||
|
||||
private static void filterQuestDropsOnDB(List<Pair<Integer, Integer>> itemsWithQuest) throws SQLException {
|
||||
List<Pair<Integer, Integer>> copyItemsWithQuest = new ArrayList<>(itemsWithQuest);
|
||||
try {
|
||||
for (Pair<Integer, Integer> iq : copyItemsWithQuest) {
|
||||
filterQuestDropsOnTable(iq, itemsWithQuest, true);
|
||||
filterQuestDropsOnTable(iq, itemsWithQuest, false);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void filterDirectorySearchMatchingData(String path, List<Pair<Integer, Integer>> itemsWithQuest) {
|
||||
Iterator<File> iter = FileUtils.iterateFiles(new File(path), new String[]{"sql", "js", "txt", "java"}, true);
|
||||
|
||||
while (iter.hasNext()) {
|
||||
File file = iter.next();
|
||||
fileSearchMatchingData(file, itemsWithQuest);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean foundMatchingDataOnFile(String fileContent, String searchStr) {
|
||||
return fileContent.contains(searchStr);
|
||||
}
|
||||
|
||||
private static void fileSearchMatchingData(File file, List<Pair<Integer, Integer>> itemsWithQuest) {
|
||||
try {
|
||||
String fileContent = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
|
||||
|
||||
List<Pair<Integer, Integer>> copyItemsWithQuest = new ArrayList<>(itemsWithQuest);
|
||||
for (Pair<Integer, Integer> iq : copyItemsWithQuest) {
|
||||
if (foundMatchingDataOnFile(fileContent, String.valueOf(iq.getLeft()))) {
|
||||
itemsWithQuest.remove(iq);
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
System.out.println("Failed to read file: " + file.getAbsolutePath());
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void printReportFileHeader() {
|
||||
printWriter.println(" # Report File autogenerated from the MapleQuestItemFetcher feature by Ronan Lana.");
|
||||
printWriter.println(" # Generated data takes into account several data info from the underlying DB, server source files and the server-side WZ.xmls.");
|
||||
printWriter.println();
|
||||
}
|
||||
|
||||
private static List<Map.Entry<Integer, Integer>> getSortedMapEntries0(Map<Integer, Integer> map) {
|
||||
List<Map.Entry<Integer, Integer>> list = new ArrayList<>(map.size());
|
||||
list.addAll(map.entrySet());
|
||||
|
||||
list.sort((o1, o2) -> o1.getKey() - o2.getKey());
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static List<Map.Entry<Integer, int[]>> getSortedMapEntries1(Map<Integer, int[]> map) {
|
||||
List<Map.Entry<Integer, int[]>> list = new ArrayList<>(map.size());
|
||||
list.addAll(map.entrySet());
|
||||
|
||||
list.sort((o1, o2) -> o1.getKey() - o2.getKey());
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static List<Pair<Integer, List<Integer>>> getSortedMapEntries2(Map<Integer, Set<Integer>> map) {
|
||||
List<Pair<Integer, List<Integer>>> list = new ArrayList<>(map.size());
|
||||
for (Map.Entry<Integer, Set<Integer>> e : map.entrySet()) {
|
||||
List<Integer> il = new ArrayList<>(2);
|
||||
il.addAll(e.getValue());
|
||||
|
||||
il.sort((o1, o2) -> o1 - o2);
|
||||
|
||||
list.add(new Pair<>(e.getKey(), il));
|
||||
}
|
||||
|
||||
list.sort((o1, o2) -> o1.getLeft() - o2.getLeft());
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static String getExpiredStringLabel(int questid) {
|
||||
return (!limitedQuestids.contains(questid) ? "" : " EXPIRED");
|
||||
}
|
||||
|
||||
private static void reportQuestItemData() {
|
||||
// This will reference one line at a time
|
||||
String line = null;
|
||||
String fileName = null;
|
||||
|
||||
try {
|
||||
System.out.println("Reading WZs...");
|
||||
|
||||
fileName = WZFiles.QUEST.getFilePath() + "/Check.img.xml";
|
||||
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(fileName), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
translateCheckToken(line); // fetch expired quests through here as well
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
|
||||
fileName = WZFiles.QUEST.getFilePath() + "/Act.img.xml";
|
||||
fileReader = new InputStreamReader(new FileInputStream(fileName), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
translateActToken(line);
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
|
||||
System.out.println("Calculating table diffs...");
|
||||
calculateQuestItemDiff();
|
||||
|
||||
System.out.println("Filtering drops on DB...");
|
||||
List<Pair<Integer, Integer>> itemsWithQuest = getPairsQuestItem();
|
||||
|
||||
filterQuestDropsOnDB(itemsWithQuest);
|
||||
con.close();
|
||||
|
||||
System.out.println("Filtering drops on project files...");
|
||||
// finally, filter whether this item is mentioned on the source code or not.
|
||||
filterDirectorySearchMatchingData("scripts", itemsWithQuest);
|
||||
filterDirectorySearchMatchingData("sql", itemsWithQuest);
|
||||
filterDirectorySearchMatchingData("src", itemsWithQuest);
|
||||
|
||||
System.out.println("Reporting results...");
|
||||
// report suspects of missing quest drop data, as well as those drop data that may have incorrect questids.
|
||||
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
|
||||
|
||||
printReportFileHeader();
|
||||
|
||||
if (!mixedQuestidItems.isEmpty()) {
|
||||
printWriter.println("INCORRECT QUESTIDS ON DB");
|
||||
for (Map.Entry<Integer, int[]> emqi : getSortedMapEntries1(mixedQuestidItems)) {
|
||||
int[] mqi = emqi.getValue();
|
||||
printWriter.println(mqi[0] + " : " + mqi[1] + " -> " + mqi[2] + getExpiredStringLabel(mqi[2]));
|
||||
}
|
||||
printWriter.println("\n\n\n\n\n");
|
||||
}
|
||||
|
||||
if (!itemsWithQuest.isEmpty()) {
|
||||
Map<Integer, Integer> mapIwq = new HashMap<>(itemsWithQuest.size());
|
||||
for (Pair<Integer, Integer> iwq : itemsWithQuest) {
|
||||
mapIwq.put(iwq.getLeft(), iwq.getRight());
|
||||
}
|
||||
|
||||
printWriter.println("ITEMS WITH NO QUEST DROP DATA ON DB");
|
||||
for (Map.Entry<Integer, Integer> iwq : getSortedMapEntries0(mapIwq)) {
|
||||
printWriter.println(iwq.getKey() + " - " + iwq.getValue() + getExpiredStringLabel(iwq.getValue()));
|
||||
}
|
||||
printWriter.println("\n\n\n\n\n");
|
||||
}
|
||||
|
||||
if (DISPLAY_EXTRA_INFO) {
|
||||
if (!zeroedStartQuestItems.isEmpty()) {
|
||||
printWriter.println("START QUEST ITEMS WITH ZERO QUANTITY");
|
||||
for (Pair<Integer, List<Integer>> iwq : getSortedMapEntries2(zeroedStartQuestItems)) {
|
||||
printWriter.println(iwq.getLeft() + getExpiredStringLabel(iwq.getLeft()) + ":");
|
||||
for (Integer i : iwq.getRight()) {
|
||||
printWriter.println(" " + i);
|
||||
}
|
||||
printWriter.println();
|
||||
}
|
||||
printWriter.println("\n\n\n\n\n");
|
||||
}
|
||||
|
||||
if (!zeroedCompleteQuestItems.isEmpty()) {
|
||||
printWriter.println("COMPLETE QUEST ITEMS WITH ZERO QUANTITY");
|
||||
for (Pair<Integer, List<Integer>> iwq : getSortedMapEntries2(zeroedCompleteQuestItems)) {
|
||||
printWriter.println(iwq.getLeft() + getExpiredStringLabel(iwq.getLeft()) + ":");
|
||||
for (Integer i : iwq.getRight()) {
|
||||
printWriter.println(" " + i);
|
||||
}
|
||||
printWriter.println();
|
||||
}
|
||||
printWriter.println("\n\n\n\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
printWriter.close();
|
||||
System.out.println("Done!");
|
||||
} catch (FileNotFoundException ex) {
|
||||
System.out.println("Unable to open file '" + fileName + "'");
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Error reading file '" + fileName + "'");
|
||||
} catch (SQLException e) {
|
||||
System.out.println("Warning: Could not establish connection to database to report quest data.");
|
||||
System.out.println(e.getMessage());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
DatabaseConnection.initializeConnectionPool(); // MapleItemInformationProvider loads some unrelated db data
|
||||
ii = MapleItemInformationProvider.getInstance();
|
||||
|
||||
reportQuestItemData();
|
||||
}
|
||||
}
|
||||
|
||||
263
src/main/java/tools/mapletools/QuestMesoFetcher.java
Normal file
263
src/main/java/tools/mapletools/QuestMesoFetcher.java
Normal file
@@ -0,0 +1,263 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import provider.wz.WZFiles;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application parses the Quest.wz file inputted and generates a report showing
|
||||
* all cases where a quest takes a meso fee to complete a quest, but it doesn't
|
||||
* properly checks the player for the needed amount before completing it.
|
||||
* <p>
|
||||
* Running it should generate a report file under "output" folder with the search results.
|
||||
*/
|
||||
public class QuestMesoFetcher {
|
||||
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("quest_meso_report.txt");
|
||||
private static final boolean PRINT_FEES = true; // print missing values as additional info report
|
||||
private static final int INITIAL_STRING_LENGTH = 50;
|
||||
|
||||
private static final Map<Integer, Integer> checkedMesoQuests = new HashMap<>();
|
||||
private static final Map<Integer, Integer> appliedMesoQuests = new HashMap<>();
|
||||
private static final Set<Integer> checkedEndscriptQuests = new HashSet<>();
|
||||
|
||||
private static PrintWriter printWriter = null;
|
||||
private static BufferedReader bufferedReader = null;
|
||||
private static byte status = 0;
|
||||
private static int questId = -1;
|
||||
private static int isCompleteState = 0;
|
||||
private static int currentMeso = 0;
|
||||
|
||||
private static String getName(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("name");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static String getValue(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("value");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static void forwardCursor(int st) {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
simpleToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void simpleToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateTokenAct(String token) {
|
||||
String d;
|
||||
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
if (status == 1) { //getting QuestId
|
||||
d = getName(token);
|
||||
questId = Integer.parseInt(d);
|
||||
} else if (status == 2) { //start/complete
|
||||
d = getName(token);
|
||||
isCompleteState = Integer.parseInt(d);
|
||||
} else if (status == 3) {
|
||||
forwardCursor(status);
|
||||
}
|
||||
|
||||
status += 1;
|
||||
} else {
|
||||
if (token.contains("money")) {
|
||||
if (isCompleteState != 0) {
|
||||
d = getValue(token);
|
||||
|
||||
currentMeso = -1 * Integer.parseInt(d);
|
||||
|
||||
if (currentMeso > 0) {
|
||||
appliedMesoQuests.put(questId, currentMeso);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateTokenCheck(String token) {
|
||||
String d;
|
||||
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
if (status == 1) { //getting QuestId
|
||||
d = getName(token);
|
||||
questId = Integer.parseInt(d);
|
||||
} else if (status == 2) { //start/complete
|
||||
d = getName(token);
|
||||
isCompleteState = Integer.parseInt(d);
|
||||
} else if (status == 3) {
|
||||
forwardCursor(status);
|
||||
}
|
||||
|
||||
status += 1;
|
||||
} else {
|
||||
if (token.contains("endmeso")) {
|
||||
d = getValue(token);
|
||||
currentMeso = Integer.parseInt(d);
|
||||
|
||||
checkedMesoQuests.put(questId, currentMeso);
|
||||
} else if (token.contains("endscript")) {
|
||||
checkedEndscriptQuests.add(questId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void readQuestMesoData() throws IOException {
|
||||
String line;
|
||||
|
||||
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(WZFiles.QUEST.getFilePath() + "/Act.img.xml"), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
translateTokenAct(line);
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
|
||||
fileReader = new InputStreamReader(new FileInputStream(WZFiles.QUEST.getFilePath() + "/Check.img.xml"), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
translateTokenCheck(line);
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
}
|
||||
|
||||
private static void printReportFileHeader() {
|
||||
printWriter.println(" # Report File autogenerated from the MapleQuestMesoFetcher feature by Ronan Lana.");
|
||||
printWriter.println(" # Generated data takes into account several data info from the server-side WZ.xmls.");
|
||||
printWriter.println();
|
||||
}
|
||||
|
||||
private static void printReportFileResults(Map<Integer, Integer> target, Map<Integer, Integer> base, boolean testingCheck) {
|
||||
List<Integer> result = new ArrayList<>();
|
||||
List<Integer> error = new ArrayList<>();
|
||||
|
||||
Map<Integer, Integer> questFee = new HashMap<>();
|
||||
|
||||
for (Map.Entry<Integer, Integer> e : base.entrySet()) {
|
||||
Integer v = target.get(e.getKey());
|
||||
|
||||
if (v == null) {
|
||||
if (testingCheck || !checkedEndscriptQuests.contains(e.getKey())) {
|
||||
result.add(e.getKey());
|
||||
questFee.put(e.getKey(), e.getValue());
|
||||
}
|
||||
} else if (v.intValue() != e.getValue().intValue()) {
|
||||
error.add(e.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
if (!result.isEmpty() || !error.isEmpty()) {
|
||||
printWriter.println("MISMATCH INFORMATION ON '" + (testingCheck ? "check" : "act") + "':");
|
||||
if (!result.isEmpty()) {
|
||||
result.sort((o1, o2) -> o1 - o2);
|
||||
|
||||
printWriter.println("# MISSING");
|
||||
|
||||
if (!PRINT_FEES) {
|
||||
for (Integer i : result) {
|
||||
printWriter.println(i);
|
||||
}
|
||||
} else {
|
||||
for (Integer i : result) {
|
||||
printWriter.println(i + " " + questFee.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
printWriter.println();
|
||||
}
|
||||
|
||||
if (!error.isEmpty() && testingCheck) {
|
||||
error.sort((o1, o2) -> o1 - o2);
|
||||
|
||||
printWriter.println("# WRONG VALUE");
|
||||
|
||||
for (Integer i : error) {
|
||||
printWriter.println(i);
|
||||
}
|
||||
|
||||
printWriter.println();
|
||||
}
|
||||
|
||||
printWriter.println("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
private static void reportQuestMesoData() {
|
||||
// This will reference one line at a time
|
||||
|
||||
try {
|
||||
System.out.println("Reading WZs...");
|
||||
readQuestMesoData();
|
||||
|
||||
System.out.println("Reporting results...");
|
||||
// report missing meso checks on quest completes
|
||||
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
|
||||
|
||||
printReportFileHeader();
|
||||
|
||||
printReportFileResults(checkedMesoQuests, appliedMesoQuests, true);
|
||||
printReportFileResults(appliedMesoQuests, checkedMesoQuests, false);
|
||||
|
||||
printWriter.close();
|
||||
System.out.println("Done!");
|
||||
} catch (FileNotFoundException ex) {
|
||||
System.out.println("Unable to open quest file.");
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Error reading quest file.");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
reportQuestMesoData();
|
||||
}
|
||||
}
|
||||
|
||||
362
src/main/java/tools/mapletools/QuestlineFetcher.java
Normal file
362
src/main/java/tools/mapletools/QuestlineFetcher.java
Normal file
@@ -0,0 +1,362 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import provider.wz.WZFiles;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application parses the Quest.wz file inputted and generates a report showing
|
||||
* all cases where quest script files have not been found for quests that requires a
|
||||
* script file.
|
||||
* As an extension, it highlights missing script files for questlines that hand over
|
||||
* skills as rewards.
|
||||
* <p>
|
||||
* Running it should generate a report file under "output" folder with the search results.
|
||||
*/
|
||||
public class QuestlineFetcher {
|
||||
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("questline_report.txt");
|
||||
private static final String ACT_NAME = WZFiles.QUEST.getFilePath() + "/Act.img.xml";
|
||||
private static final String CHECK_NAME = WZFiles.QUEST.getFilePath() + "/Check.img.xml";
|
||||
private static final int INITIAL_STRING_LENGTH = 50;
|
||||
|
||||
private static final Stack<Integer> skillObtainableQuests = new Stack<>();
|
||||
private static final Set<Integer> scriptedQuestFiles = new HashSet<>();
|
||||
private static final Set<Integer> expiredQuests = new HashSet<>();
|
||||
private static final Map<Integer, List<Integer>> questDependencies = new HashMap<>();
|
||||
private static final Set<Integer> nonScriptedQuests = new HashSet<>();
|
||||
private static final Set<Integer> skillObtainableNonScriptedQuests = new HashSet<>();
|
||||
|
||||
private static PrintWriter printWriter = null;
|
||||
private static InputStreamReader fileReader = null;
|
||||
private static BufferedReader bufferedReader = null;
|
||||
private static byte status = 0;
|
||||
private static int questId = -1;
|
||||
private static int isCompleteState = 0;
|
||||
private static boolean isScriptedQuest;
|
||||
private static boolean isExpiredQuest;
|
||||
private static List<Integer> questDependencyList;
|
||||
private static int curQuestId;
|
||||
private static int curQuestState;
|
||||
|
||||
private static String getName(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("name");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static String getValue(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("value");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static void forwardCursor(int st) {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
simpleToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void simpleToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateTokenCheck(String token) {
|
||||
String d;
|
||||
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
|
||||
if (status == 1) {
|
||||
evaluateCurrentQuest();
|
||||
} else if (status == 4) {
|
||||
evaluateCurrentQuestDependency();
|
||||
}
|
||||
} else if (token.contains("imgdir")) {
|
||||
if (status == 1) { //getting QuestId
|
||||
d = getName(token);
|
||||
questId = Integer.parseInt(d);
|
||||
|
||||
isScriptedQuest = false;
|
||||
isExpiredQuest = false;
|
||||
questDependencyList = new LinkedList<>();
|
||||
} else if (status == 2) { //start/complete
|
||||
d = getName(token);
|
||||
isCompleteState = Integer.parseInt(d);
|
||||
} else if (status == 3) {
|
||||
if (isCompleteState == 1 || !token.contains("quest")) {
|
||||
forwardCursor(status);
|
||||
}
|
||||
}
|
||||
|
||||
status += 1;
|
||||
} else {
|
||||
if (status == 3) {
|
||||
d = getName(token);
|
||||
|
||||
if (d.contains("script")) {
|
||||
isScriptedQuest = true;
|
||||
} else if (d.contains("end")) {
|
||||
isExpiredQuest = true;
|
||||
}
|
||||
} else if (status == 5) {
|
||||
readQuestLabel(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateTokenAct(String token) {
|
||||
String d;
|
||||
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
if (status == 1) { //getting QuestId
|
||||
d = getName(token);
|
||||
questId = Integer.parseInt(d);
|
||||
} else if (status == 2) { //start/complete
|
||||
d = getName(token);
|
||||
isCompleteState = Integer.parseInt(d);
|
||||
} else if (status == 3) {
|
||||
if (isCompleteState == 1 && token.contains("skill")) {
|
||||
skillObtainableQuests.add(questId);
|
||||
}
|
||||
|
||||
forwardCursor(status);
|
||||
}
|
||||
|
||||
status += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void readQuestLabel(String token) {
|
||||
String name = getName(token);
|
||||
String value = getValue(token);
|
||||
|
||||
switch (name) {
|
||||
case "id" -> curQuestId = Integer.parseInt(value);
|
||||
case "state" -> curQuestState = Integer.parseInt(value);
|
||||
}
|
||||
}
|
||||
|
||||
private static void evaluateCurrentQuestDependency() {
|
||||
if (curQuestState == 2) {
|
||||
questDependencyList.add(curQuestId);
|
||||
}
|
||||
}
|
||||
|
||||
private static void evaluateCurrentQuest() {
|
||||
if (isScriptedQuest && !scriptedQuestFiles.contains(questId)) {
|
||||
nonScriptedQuests.add(questId);
|
||||
}
|
||||
if (isExpiredQuest) {
|
||||
expiredQuests.add(questId);
|
||||
}
|
||||
|
||||
questDependencies.put(questId, questDependencyList);
|
||||
}
|
||||
|
||||
private static void instantiateQuestScriptFiles(String directoryName) {
|
||||
File directory = new File(directoryName);
|
||||
|
||||
// get all the files from a directory
|
||||
File[] fList = directory.listFiles();
|
||||
for (File file : fList) {
|
||||
if (file.isFile()) {
|
||||
String fname = file.getName();
|
||||
|
||||
try {
|
||||
Integer questid = Integer.parseInt(fname.substring(0, fname.indexOf('.')));
|
||||
scriptedQuestFiles.add(questid);
|
||||
} catch (NumberFormatException nfe) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void readQuestsWithMissingScripts() throws IOException {
|
||||
String line;
|
||||
|
||||
fileReader = new InputStreamReader(new FileInputStream(CHECK_NAME), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
translateTokenCheck(line);
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
}
|
||||
|
||||
private static void readQuestsWithSkillReward() throws IOException {
|
||||
String line;
|
||||
|
||||
fileReader = new InputStreamReader(new FileInputStream(ACT_NAME), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
translateTokenAct(line);
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
}
|
||||
|
||||
private static void calculateSkillRelatedMissingQuestScripts() {
|
||||
Stack<Integer> frontierQuests = skillObtainableQuests;
|
||||
Set<Integer> solvedQuests = new HashSet<>();
|
||||
|
||||
while (!frontierQuests.isEmpty()) {
|
||||
Integer questid = frontierQuests.pop();
|
||||
solvedQuests.add(questid);
|
||||
|
||||
if (nonScriptedQuests.contains(questid)) {
|
||||
skillObtainableNonScriptedQuests.add(questid);
|
||||
nonScriptedQuests.remove(questid);
|
||||
}
|
||||
|
||||
List<Integer> questDependency = questDependencies.get(questid);
|
||||
for (Integer i : questDependency) {
|
||||
if (!solvedQuests.contains(i)) {
|
||||
frontierQuests.add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void printReportFileHeader() {
|
||||
printWriter.println(" # Report File autogenerated from the MapleQuestlineFetcher feature by Ronan Lana.");
|
||||
printWriter.println(" # Generated data takes into account several data info from the server-side WZ.xmls.");
|
||||
printWriter.println();
|
||||
}
|
||||
|
||||
private static List<Integer> getSortedListEntries(Set<Integer> set) {
|
||||
List<Integer> list = new ArrayList<>(set);
|
||||
Collections.sort(list);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static void printReportFileResults() {
|
||||
if (!skillObtainableNonScriptedQuests.isEmpty()) {
|
||||
printWriter.println("SKILL-RELATED NON-SCRIPTED QUESTS");
|
||||
for (Integer nsq : getSortedListEntries(skillObtainableNonScriptedQuests)) {
|
||||
printWriter.println(" " + nsq + (expiredQuests.contains(nsq) ? " EXPIRED" : ""));
|
||||
}
|
||||
|
||||
printWriter.println();
|
||||
}
|
||||
|
||||
printWriter.println("\nCOMMON NON-SCRIPTED QUESTS");
|
||||
for (Integer nsq : getSortedListEntries(nonScriptedQuests)) {
|
||||
printWriter.println(" " + nsq + (expiredQuests.contains(nsq) ? " EXPIRED" : ""));
|
||||
}
|
||||
}
|
||||
|
||||
private static void reportQuestlineData() {
|
||||
// This will reference one line at a time
|
||||
|
||||
try {
|
||||
System.out.println("Reading quest scripts...");
|
||||
instantiateQuestScriptFiles(ToolConstants.SCRIPTS_PATH + "/quest");
|
||||
|
||||
System.out.println("Reading WZs...");
|
||||
readQuestsWithSkillReward();
|
||||
readQuestsWithMissingScripts();
|
||||
|
||||
System.out.println("Calculating skill related quests...");
|
||||
calculateSkillRelatedMissingQuestScripts();
|
||||
|
||||
System.out.println("Reporting results...");
|
||||
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
|
||||
|
||||
printReportFileHeader();
|
||||
printReportFileResults();
|
||||
|
||||
printWriter.close();
|
||||
System.out.println("Done!");
|
||||
} catch (FileNotFoundException ex) {
|
||||
System.out.println("Unable to open quest file.");
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Error reading quest file.");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
private static List<Pair<Integer, List<Integer>>> getSortedMapEntries(Map<Integer, List<Integer>> map) {
|
||||
List<Pair<Integer, List<Integer>>> list = new ArrayList<>(map.size());
|
||||
for(Map.Entry<Integer, List<Integer>> e : map.entrySet()) {
|
||||
List<Integer> il = new ArrayList<>(2);
|
||||
for(Integer i : e.getValue()) {
|
||||
il.add(i);
|
||||
}
|
||||
|
||||
Collections.sort(il, new Comparator<Integer>() {
|
||||
@Override
|
||||
public int compare(Integer o1, Integer o2) {
|
||||
return o1 - o2;
|
||||
}
|
||||
});
|
||||
|
||||
list.add(new Pair<>(e.getKey(), il));
|
||||
}
|
||||
|
||||
Collections.sort(list, new Comparator<Pair<Integer, List<Integer>>>() {
|
||||
@Override
|
||||
public int compare(Pair<Integer, List<Integer>> o1, Pair<Integer, List<Integer>> o2) {
|
||||
return o1.getLeft() - o2.getLeft();
|
||||
}
|
||||
});
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static void DumpQuestlineData() {
|
||||
for(Pair<Integer, List<Integer>> questDependency : getSortedMapEntries(questDependencies)) {
|
||||
if(!questDependency.right.isEmpty()) {
|
||||
System.out.println(questDependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
public static void main(String[] args) {
|
||||
reportQuestlineData();
|
||||
}
|
||||
}
|
||||
|
||||
112
src/main/java/tools/mapletools/ReactorDropFetcher.java
Normal file
112
src/main/java/tools/mapletools/ReactorDropFetcher.java
Normal file
@@ -0,0 +1,112 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application reports in reactor ids that have drops on the SQL table but are
|
||||
* not yet coded.
|
||||
*/
|
||||
public class ReactorDropFetcher {
|
||||
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("reactor_drop_report.txt");
|
||||
private static final String REACTOR_SCRIPT_PATH = ToolConstants.SCRIPTS_PATH + "/reactor";
|
||||
private static final Connection con = SimpleDatabaseConnection.getConnection();
|
||||
|
||||
private static PrintWriter printWriter = null;
|
||||
private static final Set<Integer> reactors = new HashSet<>();
|
||||
|
||||
private static void printReportFileHeader() {
|
||||
printWriter.println(" # Report File autogenerated from the MapleReactorDropFetcher feature by Ronan Lana.");
|
||||
printWriter.println(" # Generated data takes into account several data info from the underlying DB and the server-side files.");
|
||||
printWriter.println();
|
||||
}
|
||||
|
||||
private static int getReactorIdFromFilename(String name) {
|
||||
try {
|
||||
return Integer.parseInt(name.substring(0, name.indexOf('.')));
|
||||
} catch (Exception e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void removeScriptedReactorids(String directoryName) {
|
||||
File directory = new File(directoryName);
|
||||
|
||||
// get all the files from a directory
|
||||
File[] fList = directory.listFiles();
|
||||
for (File file : fList) {
|
||||
if (file.isFile()) {
|
||||
reactors.remove(getReactorIdFromFilename(file.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadReactoridsOnDB() throws SQLException {
|
||||
PreparedStatement ps = con.prepareStatement("SELECT DISTINCT reactorid FROM reactordrops;");
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
while (rs.next()) {
|
||||
reactors.add(rs.getInt("reactorid"));
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
}
|
||||
|
||||
private static List<Integer> getSortedReactorids() {
|
||||
List<Integer> sortedReactors = new ArrayList<>(reactors);
|
||||
Collections.sort(sortedReactors);
|
||||
|
||||
return sortedReactors;
|
||||
}
|
||||
|
||||
private static void fetchMissingReactorDrops() throws SQLException {
|
||||
loadReactoridsOnDB();
|
||||
removeScriptedReactorids(REACTOR_SCRIPT_PATH);
|
||||
}
|
||||
|
||||
private static void reportMissingReactorDrops() throws SQLException {
|
||||
if (!reactors.isEmpty()) {
|
||||
printWriter.println("MISSING REACTOR DROP SCRIPTS");
|
||||
for (Integer reactorid : getSortedReactorids()) {
|
||||
printWriter.println(" " + reactorid);
|
||||
}
|
||||
printWriter.println("\n");
|
||||
}
|
||||
}
|
||||
|
||||
private static void reportMissingReactors() {
|
||||
try {
|
||||
System.out.println("Fetching reactors from DB...");
|
||||
fetchMissingReactorDrops();
|
||||
|
||||
con.close();
|
||||
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
|
||||
|
||||
// report suspects of missing quest drop data, as well as those drop data that may have incorrect questids.
|
||||
System.out.println("Reporting results...");
|
||||
printReportFileHeader();
|
||||
reportMissingReactorDrops();
|
||||
|
||||
printWriter.close();
|
||||
System.out.println("Done!");
|
||||
} catch (SQLException e) {
|
||||
System.out.println("Warning: Could not establish connection to database to report quest data.");
|
||||
System.out.println(e.getMessage());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
reportMissingReactors();
|
||||
}
|
||||
}
|
||||
30
src/main/java/tools/mapletools/SimpleDatabaseConnection.java
Normal file
30
src/main/java/tools/mapletools/SimpleDatabaseConnection.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.core.config.Configurator;
|
||||
import tools.DatabaseConnection;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
final class SimpleDatabaseConnection {
|
||||
private SimpleDatabaseConnection() {}
|
||||
|
||||
static Connection getConnection() {
|
||||
muffleLogging();
|
||||
DatabaseConnection.initializeConnectionPool();
|
||||
|
||||
try {
|
||||
return DatabaseConnection.getConnection();
|
||||
} catch (SQLException e) {
|
||||
throw new IllegalStateException("Failed to get database connection", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void muffleLogging() {
|
||||
final Level minimumVisibleLevel = Level.WARN;
|
||||
Configurator.setLevel(LogManager.getLogger(com.zaxxer.hikari.HikariDataSource.class).getName(), minimumVisibleLevel);
|
||||
Configurator.setRootLevel(minimumVisibleLevel);
|
||||
}
|
||||
}
|
||||
336
src/main/java/tools/mapletools/SkillMakerFetcher.java
Normal file
336
src/main/java/tools/mapletools/SkillMakerFetcher.java
Normal file
@@ -0,0 +1,336 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import provider.wz.WZFiles;
|
||||
import server.MapleItemInformationProvider;
|
||||
import tools.DatabaseConnection;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* The objective of this program is to uncover all maker data from the
|
||||
* ItemMaker.wz.xml files and generate a SQL file with every data info
|
||||
* for the Maker DB tables.
|
||||
*/
|
||||
|
||||
public class SkillMakerFetcher {
|
||||
private static final File INPUT_FILE = new File(WZFiles.ETC.getFile(), "ItemMake.img.xml");
|
||||
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("maker-data.sql");
|
||||
private static final int INITIAL_STRING_LENGTH = 50;
|
||||
|
||||
private static PrintWriter printWriter = null;
|
||||
private static BufferedReader bufferedReader = null;
|
||||
private static byte status = 0;
|
||||
private static byte state = 0;
|
||||
|
||||
// maker data fields
|
||||
private static int id = -1;
|
||||
private static int itemid = -1;
|
||||
private static int reqLevel = -1;
|
||||
private static int reqMakerLevel = -1;
|
||||
private static int reqItem = -1;
|
||||
private static int reqMeso = -1;
|
||||
private static int reqEquip = -1;
|
||||
private static int catalyst = -1;
|
||||
private static int quantity = -1;
|
||||
private static int tuc = -1;
|
||||
|
||||
private static int recipePos = -1;
|
||||
private static int recipeProb = -1;
|
||||
private static int recipeCount = -1;
|
||||
private static int recipeItem = -1;
|
||||
|
||||
static List<int[]> recipeList = null;
|
||||
static List<int[]> randomList = null;
|
||||
static List<MakerItemEntry> makerList = new ArrayList<>(100);
|
||||
|
||||
private static void resetMakerDataFields() {
|
||||
reqLevel = 0;
|
||||
reqMakerLevel = 0;
|
||||
reqItem = 0;
|
||||
reqMeso = 0;
|
||||
reqEquip = 0;
|
||||
catalyst = 0;
|
||||
quantity = 0;
|
||||
tuc = 0;
|
||||
|
||||
recipePos = 0;
|
||||
recipeProb = 0;
|
||||
recipeCount = 0;
|
||||
recipeItem = 0;
|
||||
|
||||
recipeList = null;
|
||||
randomList = null;
|
||||
}
|
||||
|
||||
private static String getName(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("name");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
String s = d.trim();
|
||||
s.replaceFirst("^0+(?!$)", "");
|
||||
|
||||
return (s);
|
||||
}
|
||||
|
||||
private static String getValue(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("value");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
String s = d.trim();
|
||||
s.replaceFirst("^0+(?!$)", "");
|
||||
|
||||
return (s);
|
||||
}
|
||||
|
||||
private static int[] generateRecipeItem() {
|
||||
int[] pair = new int[2];
|
||||
pair[0] = recipeItem;
|
||||
pair[1] = recipeCount;
|
||||
|
||||
return pair;
|
||||
}
|
||||
|
||||
private static int[] generateRandomItem() {
|
||||
int[] tuple = new int[3];
|
||||
tuple[0] = recipeItem;
|
||||
tuple[1] = recipeCount;
|
||||
tuple[2] = recipeProb;
|
||||
|
||||
return tuple;
|
||||
}
|
||||
|
||||
private static void simpleToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void forwardCursor(int st) {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
simpleToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateToken(String token) {
|
||||
String d;
|
||||
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
|
||||
if (status == 2) { //close item maker data
|
||||
generateUpdatedItemFee(); // for equipments, this will try to update reqMeso to be conformant with the client.
|
||||
makerList.add(new MakerItemEntry(id, itemid, reqLevel, reqMakerLevel, reqItem, reqMeso, reqEquip, catalyst, quantity, tuc, recipeCount, recipeItem, recipeList, randomList));
|
||||
resetMakerDataFields();
|
||||
} else if (status == 4) { //close recipe/random item
|
||||
if (state == 0) {
|
||||
recipeList.add(generateRecipeItem());
|
||||
} else if (state == 1) {
|
||||
randomList.add(generateRandomItem());
|
||||
}
|
||||
}
|
||||
} else if (token.contains("imgdir")) {
|
||||
if (status == 1) { //getting id
|
||||
d = getName(token);
|
||||
id = Integer.parseInt(d);
|
||||
System.out.println("Parsing maker id " + id);
|
||||
} else if (status == 2) { //getting target item id
|
||||
d = getName(token);
|
||||
itemid = Integer.parseInt(d);
|
||||
} else if (status == 3) {
|
||||
d = getName(token);
|
||||
|
||||
switch (d) {
|
||||
case "recipe" -> {
|
||||
recipeList = new LinkedList<>();
|
||||
state = 0;
|
||||
}
|
||||
case "randomReward" -> {
|
||||
randomList = new LinkedList<>();
|
||||
state = 1;
|
||||
}
|
||||
default -> forwardCursor(3); // unused content, read until end of block
|
||||
}
|
||||
} else if (status == 4) { // inside recipe/random
|
||||
d = getName(token);
|
||||
recipePos = Integer.parseInt(d);
|
||||
}
|
||||
|
||||
status += 1;
|
||||
} else {
|
||||
if (status == 3) {
|
||||
d = getName(token);
|
||||
|
||||
switch (d) {
|
||||
case "itemNum" -> quantity = Integer.parseInt(getValue(token));
|
||||
case "meso" -> reqMeso = Integer.parseInt(getValue(token));
|
||||
case "reqItem" -> reqItem = Integer.parseInt(getValue(token));
|
||||
case "reqLevel" -> reqLevel = Integer.parseInt(getValue(token));
|
||||
case "reqSkillLevel" -> reqMakerLevel = Integer.parseInt(getValue(token));
|
||||
case "tuc" -> tuc = Integer.parseInt(getValue(token));
|
||||
case "catalyst" -> catalyst = Integer.parseInt(getValue(token));
|
||||
case "reqEquip" -> reqEquip = Integer.parseInt(getValue(token));
|
||||
default -> {
|
||||
System.out.println("Unhandled case: '" + d + "'");
|
||||
state = 2;
|
||||
}
|
||||
}
|
||||
} else if (status == 5) { // inside recipe/random item
|
||||
d = getName(token);
|
||||
if (d.equals("item")) {
|
||||
recipeItem = Integer.parseInt(getValue(token));
|
||||
} else {
|
||||
if (state == 0) {
|
||||
recipeCount = Integer.parseInt(getValue(token));
|
||||
} else {
|
||||
if (d.equals("itemNum")) {
|
||||
recipeCount = Integer.parseInt(getValue(token));
|
||||
} else {
|
||||
recipeProb = Integer.parseInt(getValue(token));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void generateUpdatedItemFee() {
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
float adjPrice = reqMeso;
|
||||
|
||||
if (itemid < 2000000) {
|
||||
Map<String, Integer> stats = ii.getEquipStats(itemid);
|
||||
if (stats != null) {
|
||||
int val = itemid / 100000;
|
||||
|
||||
if (val == 13 || val == 14) { // is weapon-type
|
||||
adjPrice /= 10;
|
||||
adjPrice += reqMeso;
|
||||
|
||||
adjPrice /= 1000;
|
||||
reqMeso = 1000 * (int) Math.floor(adjPrice);
|
||||
} else {
|
||||
adjPrice /= ((stats.get("reqLevel") >= 108) ? 10 : 11);
|
||||
adjPrice += reqMeso;
|
||||
|
||||
adjPrice /= 1000;
|
||||
reqMeso = 1000 * (int) Math.ceil(adjPrice);
|
||||
}
|
||||
} else {
|
||||
System.out.println("null stats for itemid " + itemid);
|
||||
}
|
||||
} else {
|
||||
adjPrice /= 10;
|
||||
adjPrice += reqMeso;
|
||||
|
||||
adjPrice /= 1000;
|
||||
reqMeso = 1000 * (int) Math.ceil(adjPrice);
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteMakerTableFile() {
|
||||
printWriter.println(" # SQL File autogenerated from the MapleSkillMakerFetcher feature by Ronan Lana.");
|
||||
printWriter.println(" # Generated data is conformant with the ItemMake.img.xml file used to compile this.");
|
||||
printWriter.println();
|
||||
|
||||
StringBuilder sb_create = new StringBuilder("INSERT IGNORE INTO `makercreatedata` (`id`, `itemid`, `req_level`, `req_maker_level`, `req_meso`, `req_item`, `req_equip`, `catalyst`, `quantity`, `tuc`) VALUES\r\n");
|
||||
StringBuilder sb_recipe = new StringBuilder("INSERT IGNORE INTO `makerrecipedata` (`itemid`, `req_item`, `count`) VALUES\r\n");
|
||||
StringBuilder sb_reward = new StringBuilder("INSERT IGNORE INTO `makerrewarddata` (`itemid`, `rewardid`, `quantity`, `prob`) VALUES\r\n");
|
||||
|
||||
for (MakerItemEntry it : makerList) {
|
||||
sb_create.append(" (" + it.id + ", " + it.itemid + ", " + it.reqLevel + ", " + it.reqMakerLevel + ", " + it.reqMeso + ", " + it.reqItem + ", " + it.reqEquip + ", " + it.catalyst + ", " + it.quantity + ", " + it.tuc + "),\r\n");
|
||||
|
||||
if (it.recipeList != null) {
|
||||
for (int[] rit : it.recipeList) {
|
||||
sb_recipe.append(" (" + it.itemid + ", " + rit[0] + ", " + rit[1] + "),\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (it.randomList != null) {
|
||||
for (int[] rit : it.randomList) {
|
||||
sb_reward.append(" (" + it.itemid + ", " + rit[0] + ", " + rit[1] + ", " + rit[2] + "),\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sb_create.setLength(sb_create.length() - 3);
|
||||
sb_create.append(";\r\n");
|
||||
|
||||
sb_recipe.setLength(sb_recipe.length() - 3);
|
||||
sb_recipe.append(";\r\n");
|
||||
|
||||
sb_reward.setLength(sb_reward.length() - 3);
|
||||
sb_reward.append(";");
|
||||
|
||||
printWriter.println(sb_create);
|
||||
printWriter.println(sb_recipe);
|
||||
printWriter.println(sb_reward);
|
||||
}
|
||||
|
||||
private static void writeMakerTableData() {
|
||||
// This will reference one line at a time
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
|
||||
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(INPUT_FILE), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
resetMakerDataFields();
|
||||
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
translateToken(line);
|
||||
}
|
||||
|
||||
WriteMakerTableFile();
|
||||
|
||||
printWriter.close();
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
} catch (FileNotFoundException ex) {
|
||||
System.out.println("Unable to open file '" + INPUT_FILE + "'");
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Error reading file '" + INPUT_FILE + "'");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
DatabaseConnection.initializeConnectionPool(); // Using MapleItemInformationProvider which loads som unrelated things from the db
|
||||
writeMakerTableData();
|
||||
}
|
||||
}
|
||||
|
||||
182
src/main/java/tools/mapletools/SkillMakerReagentIndexer.java
Normal file
182
src/main/java/tools/mapletools/SkillMakerReagentIndexer.java
Normal file
@@ -0,0 +1,182 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import provider.wz.WZFiles;
|
||||
import tools.Pair;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* The main objective of this program is to index relevant reagent data
|
||||
* from the Item.wz folder and generate a SQL table with them, to be used
|
||||
* by the server source.
|
||||
*/
|
||||
public class SkillMakerReagentIndexer {
|
||||
private static final File INPUT_FILE = new File(WZFiles.ITEM.getFile(), "Etc/0425.img.xml");
|
||||
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("maker-reagent-data.sql");
|
||||
private static final int INITIAL_STRING_LENGTH = 50;
|
||||
private static final List<Pair<Integer, Pair<String, Integer>>> reagentList = new ArrayList<>();
|
||||
|
||||
private static PrintWriter printWriter = null;
|
||||
private static BufferedReader bufferedReader = null;
|
||||
private static byte status = 0;
|
||||
private static int id = -1;
|
||||
|
||||
private static String getName(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("name");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static String getValue(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("value");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static void simpleToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void forwardCursor(int st) {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
simpleToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateToken(String token) {
|
||||
String d;
|
||||
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
if (status == 1) { //getting id
|
||||
d = getName(token);
|
||||
id = Integer.parseInt(d);
|
||||
System.out.println("Parsing maker reagent id " + id);
|
||||
} else if (status == 2) {
|
||||
d = getName(token);
|
||||
if (!d.equals("info")) {
|
||||
System.out.println("not info");
|
||||
forwardCursor(status);
|
||||
}
|
||||
}
|
||||
|
||||
status += 1;
|
||||
} else {
|
||||
if (status == 3) {
|
||||
if (token.contains("int")) {
|
||||
d = getName(token);
|
||||
|
||||
if (d.contains("inc") || d.contains("rand")) {
|
||||
Integer v = Integer.valueOf(getValue(token));
|
||||
Pair<String, Integer> reagBuff = new Pair<>(d, v);
|
||||
|
||||
Pair<Integer, Pair<String, Integer>> reagItem = new Pair<>(id, reagBuff);
|
||||
reagentList.add(reagItem);
|
||||
}
|
||||
} else {
|
||||
if (token.contains("canvas")) {
|
||||
forwardCursor(status + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void SortReagentList() {
|
||||
reagentList.sort((p1, p2) -> p1.getLeft().compareTo(p2.getLeft()));
|
||||
}
|
||||
|
||||
private static void WriteMakerReagentTableFile() {
|
||||
printWriter.println(" # SQL File autogenerated from the MapleSkillMakerReagentIndexer feature by Ronan Lana.");
|
||||
printWriter.println(" # Generated data is conformant with the Item.wz folder used to compile this.");
|
||||
printWriter.println();
|
||||
|
||||
printWriter.println("CREATE TABLE IF NOT EXISTS `makerreagentdata` (");
|
||||
printWriter.println(" `itemid` int(11) NOT NULL,");
|
||||
printWriter.println(" `stat` varchar(20) NOT NULL,");
|
||||
printWriter.println(" `value` smallint(6) NOT NULL,");
|
||||
printWriter.println(" PRIMARY KEY (`itemid`)");
|
||||
printWriter.println(");");
|
||||
printWriter.println();
|
||||
|
||||
StringBuilder sb = new StringBuilder("INSERT IGNORE INTO `makerreagentdata` (`itemid`, `stat`, `value`) VALUES\r\n");
|
||||
|
||||
for (Pair<Integer, Pair<String, Integer>> it : reagentList) {
|
||||
sb.append(" (" + it.left + ", \"" + it.right.left + "\", " + it.right.right + "),\r\n");
|
||||
}
|
||||
|
||||
sb.setLength(sb.length() - 3);
|
||||
sb.append(";");
|
||||
|
||||
printWriter.println(sb);
|
||||
}
|
||||
|
||||
private static void writeMakerReagentTableData() {
|
||||
// This will reference one line at a time
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(INPUT_FILE), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
translateToken(line);
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
|
||||
SortReagentList();
|
||||
|
||||
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
|
||||
WriteMakerReagentTableFile();
|
||||
printWriter.close();
|
||||
} catch (FileNotFoundException ex) {
|
||||
System.out.println("Unable to open file '" + OUTPUT_FILE + "'");
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Error reading file '" + OUTPUT_FILE + "'");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
writeMakerReagentTableData();
|
||||
}
|
||||
}
|
||||
128
src/main/java/tools/mapletools/SkillbookChanceFetcher.java
Normal file
128
src/main/java/tools/mapletools/SkillbookChanceFetcher.java
Normal file
@@ -0,0 +1,128 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import server.life.MapleMonsterStats;
|
||||
import tools.Pair;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application traces missing meso drop data on the underlying DB (that must be
|
||||
* defined on the DatabaseConnection file of this project) and generates a
|
||||
* SQL file that proposes missing drop entries for the drop_data table.
|
||||
* <p>
|
||||
* The meso range is calculated accordingly with the target mob stats, such as level
|
||||
* and if it's a boss or not, similarly as how it has been done for the actual meso
|
||||
* drops.
|
||||
*/
|
||||
public class SkillbookChanceFetcher {
|
||||
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("skillbook_drop_data.sql");
|
||||
private static final Map<Pair<Integer, Integer>, Integer> skillbookChances = new HashMap<>();
|
||||
|
||||
private static PrintWriter printWriter;
|
||||
private static Map<Integer, MapleMonsterStats> mobStats;
|
||||
|
||||
private static List<Map.Entry<Pair<Integer, Integer>, Integer>> sortedSkillbookChances() {
|
||||
List<Map.Entry<Pair<Integer, Integer>, Integer>> skillbookChancesList = new ArrayList<>(skillbookChances.entrySet());
|
||||
|
||||
skillbookChancesList.sort((o1, o2) -> {
|
||||
if (o1.getKey().getLeft().equals(o2.getKey().getLeft())) {
|
||||
return o1.getKey().getRight() < o2.getKey().getRight() ? -1 : (o1.getKey().getRight().equals(o2.getKey().getRight()) ? 0 : 1);
|
||||
}
|
||||
|
||||
return (o1.getKey().getLeft() < o2.getKey().getLeft()) ? -1 : 1;
|
||||
});
|
||||
|
||||
return skillbookChancesList;
|
||||
}
|
||||
|
||||
private static boolean isLegendSkillUpgradeBook(int itemid) {
|
||||
int itemidBranch = itemid / 10000;
|
||||
return (itemidBranch == 228 && itemid >= 2280013 || itemidBranch == 229 && itemid >= 2290126); // drop rate of Legends are higher
|
||||
}
|
||||
|
||||
private static void fetchSkillbookDropChances() {
|
||||
Connection con = SimpleDatabaseConnection.getConnection();
|
||||
|
||||
try {
|
||||
PreparedStatement ps = con.prepareStatement("SELECT dropperid, itemid FROM drop_data WHERE itemid >= 2280000 AND itemid < 2300000");
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
while (rs.next()) {
|
||||
int mobid = rs.getInt("dropperid");
|
||||
int itemid = rs.getInt("itemid");
|
||||
|
||||
int expectedChance = 250;
|
||||
|
||||
if (mobStats.get(mobid) != null) {
|
||||
int level = mobStats.get(mobid).getLevel();
|
||||
expectedChance *= Math.max(2, (level - 80) / 15);
|
||||
|
||||
if (mobStats.get(mobid).isBoss()) {
|
||||
expectedChance *= 20;
|
||||
} else {
|
||||
expectedChance *= 1;
|
||||
}
|
||||
} else {
|
||||
expectedChance = 1287;
|
||||
}
|
||||
|
||||
if (isLegendSkillUpgradeBook(itemid)) { // drop rate of Legends seems to be higher than explorers, in retrospect from values in DB
|
||||
expectedChance *= 3;
|
||||
}
|
||||
|
||||
skillbookChances.put(new Pair<>(mobid, itemid), expectedChance);
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
con.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void printSkillbookChanceUpdateSqlHeader() {
|
||||
printWriter.println(" # SQL File autogenerated from the MapleSkillbookChanceFetcher feature by Ronan Lana.");
|
||||
printWriter.println(" # Generated data takes into account mob stats such as level and boss for the chance rates.");
|
||||
printWriter.println();
|
||||
|
||||
printWriter.println(" REPLACE INTO drop_data (`dropperid`, `itemid`, `minimum_quantity`, `maximum_quantity`, `questid`, `chance`) VALUES");
|
||||
}
|
||||
|
||||
private static void generateSkillbookChanceUpdateFile() {
|
||||
try {
|
||||
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
|
||||
|
||||
printSkillbookChanceUpdateSqlHeader();
|
||||
|
||||
List<Map.Entry<Pair<Integer, Integer>, Integer>> skillbookChancesList = sortedSkillbookChances();
|
||||
for (Map.Entry<Pair<Integer, Integer>, Integer> e : skillbookChancesList) {
|
||||
printWriter.println("(" + e.getKey().getLeft() + ", " + e.getKey().getRight() + ", 1, 1, 0, " + e.getValue() + "),");
|
||||
}
|
||||
|
||||
printWriter.close();
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// load mob stats from WZ
|
||||
mobStats = MonsterStatFetcher.getAllMonsterStats();
|
||||
|
||||
fetchSkillbookDropChances();
|
||||
generateSkillbookChanceUpdateFile();
|
||||
}
|
||||
}
|
||||
157
src/main/java/tools/mapletools/SkillbookStackUpdate.java
Normal file
157
src/main/java/tools/mapletools/SkillbookStackUpdate.java
Normal file
@@ -0,0 +1,157 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import provider.wz.WZFiles;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application parses skillbook XMLs, filling up stack amount of those
|
||||
* items to 100 (eliminating limitations on held skillbooks, now using
|
||||
* default stack quantity expected from USE items).
|
||||
* <p>
|
||||
* Estimated parse time: 10 seconds
|
||||
*/
|
||||
public class SkillbookStackUpdate {
|
||||
private static final File INPUT_DIRECTORY = new File(WZFiles.ITEM.getFile(), "Consume");
|
||||
private static final File OUTPUT_DIRECTORY = ToolConstants.getOutputFile("skillbook-update");
|
||||
private static final int INITIAL_STRING_LENGTH = 50;
|
||||
|
||||
private static PrintWriter printWriter = null;
|
||||
private static BufferedReader bufferedReader = null;
|
||||
private static int status = 0;
|
||||
|
||||
private static boolean isSkillMasteryBook(int itemid) {
|
||||
return itemid >= 2280000 && itemid < 2300000;
|
||||
}
|
||||
|
||||
private static String getName(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("name");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
if (j < i) {
|
||||
return "0"; //node value containing 'name' in it's scope, cheap fix since we don't deal with strings anyway
|
||||
}
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static String getValue(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("value");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static void forwardCursor(int st) {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
simpleToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void simpleToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
}
|
||||
printWriter.println(token);
|
||||
}
|
||||
|
||||
private static void translateItemToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
|
||||
if (status == 2) { //itemid
|
||||
int itemid = Integer.parseInt(getName(token));
|
||||
|
||||
if (!isSkillMasteryBook(itemid)) {
|
||||
printWriter.println(token);
|
||||
forwardCursor(status);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (status == 3) {
|
||||
if (getName(token).contentEquals("slotMax")) {
|
||||
printWriter.println(" <int name=\"slotMax\" value=\"100\"/>");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printWriter.println(token);
|
||||
}
|
||||
|
||||
private static void parseItemFile(File file, File outputFile) {
|
||||
setupDirectories(outputFile);
|
||||
// This will reference one line at a time
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
printWriter = new PrintWriter(outputFile);
|
||||
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
translateItemToken(line);
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
printWriter.close();
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Error reading file '" + file.getName() + "'");
|
||||
ex.printStackTrace();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void setupDirectories(File file) {
|
||||
if (!file.getParentFile().exists()) {
|
||||
file.getParentFile().mkdirs();
|
||||
}
|
||||
}
|
||||
|
||||
private static void parseItemDirectory(File inputDirectory, File outputDirectory) {
|
||||
for (File f : inputDirectory.listFiles()) {
|
||||
parseItemFile(f, new File(outputDirectory, f.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Reading item files...");
|
||||
parseItemDirectory(INPUT_DIRECTORY, OUTPUT_DIRECTORY);
|
||||
System.out.println("Done!");
|
||||
}
|
||||
|
||||
}
|
||||
18
src/main/java/tools/mapletools/ToolConstants.java
Normal file
18
src/main/java/tools/mapletools/ToolConstants.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
class ToolConstants {
|
||||
static final File INPUT_DIRECTORY = new File("tools/input");
|
||||
static final File OUTPUT_DIRECTORY = new File("tools/output");
|
||||
static final String SCRIPTS_PATH = "scripts";
|
||||
static final String HANDBOOK_PATH = "handbook";
|
||||
|
||||
static File getInputFile(String fileName) {
|
||||
return new File(INPUT_DIRECTORY, fileName);
|
||||
}
|
||||
|
||||
static File getOutputFile(String fileName) {
|
||||
return new File(OUTPUT_DIRECTORY, fileName);
|
||||
}
|
||||
}
|
||||
256
src/main/java/tools/mapletools/WorldmapChecker.java
Normal file
256
src/main/java/tools/mapletools/WorldmapChecker.java
Normal file
@@ -0,0 +1,256 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import provider.wz.WZFiles;
|
||||
import tools.Pair;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* This application parses the Map.wz file inputted and reports areas (mapids) that are supposed to be referenced
|
||||
* throughout the map tree (area map -> continent map -> world map) but are currently missing.
|
||||
*/
|
||||
public class WorldmapChecker {
|
||||
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("worldmap_report.txt");
|
||||
private static final int INITIAL_STRING_LENGTH = 50;
|
||||
private static final Map<String, Set<Integer>> worldMapids = new HashMap<>();
|
||||
private static final Map<String, String> parentWorldmaps = new HashMap<>();
|
||||
private static final Set<String> rootWorldmaps = new HashSet<>();
|
||||
|
||||
private static PrintWriter printWriter = null;
|
||||
private static BufferedReader bufferedReader = null;
|
||||
private static Set<Integer> currentWorldMapids;
|
||||
private static String currentParent;
|
||||
private static byte status = 0;
|
||||
private static boolean isInfo;
|
||||
|
||||
private static String getName(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("name");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static String getValue(String token) {
|
||||
int i, j;
|
||||
char[] dest;
|
||||
String d;
|
||||
|
||||
i = token.lastIndexOf("value");
|
||||
i = token.indexOf("\"", i) + 1; //lower bound of the string
|
||||
j = token.indexOf("\"", i); //upper bound
|
||||
|
||||
dest = new char[INITIAL_STRING_LENGTH];
|
||||
token.getChars(i, j, dest, 0);
|
||||
|
||||
d = new String(dest);
|
||||
return (d.trim());
|
||||
}
|
||||
|
||||
private static void forwardCursor(int st) {
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
while (status >= st && (line = bufferedReader.readLine()) != null) {
|
||||
simpleToken(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void simpleToken(String token) {
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void translateToken(String token) {
|
||||
String d;
|
||||
|
||||
if (token.contains("/imgdir")) {
|
||||
status -= 1;
|
||||
} else if (token.contains("imgdir")) {
|
||||
status += 1;
|
||||
|
||||
if (status == 2) {
|
||||
d = getName(token);
|
||||
|
||||
switch (d) {
|
||||
case "MapList" -> isInfo = false;
|
||||
case "info" -> isInfo = true;
|
||||
default -> forwardCursor(status);
|
||||
}
|
||||
} else if (status == 4) {
|
||||
d = getName(token);
|
||||
|
||||
if (!d.contentEquals("mapNo")) {
|
||||
forwardCursor(status);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (status == 4) {
|
||||
currentWorldMapids.add(Integer.valueOf(getValue(token)));
|
||||
} else if (status == 2 && isInfo) {
|
||||
try {
|
||||
d = getName(token);
|
||||
if (d.contentEquals("parentMap")) {
|
||||
currentParent = (getValue(token) + ".img.xml");
|
||||
} else {
|
||||
forwardCursor(status);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("failed '" + token + "'");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void parseWorldmapFile(File worldmapFile) throws IOException {
|
||||
String line;
|
||||
|
||||
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(worldmapFile), StandardCharsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
currentParent = "";
|
||||
status = 0;
|
||||
|
||||
currentWorldMapids = new HashSet<>();
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
translateToken(line);
|
||||
}
|
||||
|
||||
String worldmapName = worldmapFile.getName();
|
||||
worldMapids.put(worldmapName, currentWorldMapids);
|
||||
|
||||
if (!currentParent.isEmpty()) {
|
||||
parentWorldmaps.put(worldmapName, currentParent);
|
||||
} else {
|
||||
rootWorldmaps.add(worldmapName);
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
}
|
||||
|
||||
private static void parseWorldmapDirectory() {
|
||||
File folder = new File(WZFiles.MAP.getFilePath(), "WorldMap");
|
||||
System.out.println("Parsing directory '" + folder.getPath() + "'");
|
||||
for (File file : folder.listFiles()) {
|
||||
if (file.isFile()) {
|
||||
try {
|
||||
parseWorldmapFile(file);
|
||||
} catch (FileNotFoundException ex) {
|
||||
System.out.println("Unable to open worldmap file " + file.getAbsolutePath() + ".");
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Error reading worldmap file " + file.getAbsolutePath() + ".");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void printReportFileHeader() {
|
||||
printWriter.println(" # Report File autogenerated from the MapleWorldmapChecker feature by Ronan Lana.");
|
||||
printWriter.println(" # Generated data takes into account several data info from the server-side WZ.xmls.");
|
||||
printWriter.println();
|
||||
}
|
||||
|
||||
private static void printReportFileResults(List<Pair<String, List<Pair<Integer, String>>>> results) {
|
||||
printWriter.println("Missing mapid references in top hierarchy:\n");
|
||||
for (Pair<String, List<Pair<Integer, String>>> res : results) {
|
||||
printWriter.println("'" + res.getLeft() + "':");
|
||||
|
||||
for (Pair<Integer, String> i : res.getRight()) {
|
||||
printWriter.println(" " + i);
|
||||
}
|
||||
|
||||
printWriter.println("\n");
|
||||
}
|
||||
}
|
||||
|
||||
private static void verifyWorldmapTreeMapids() {
|
||||
try {
|
||||
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
|
||||
printReportFileHeader();
|
||||
|
||||
if (rootWorldmaps.size() > 1) {
|
||||
printWriter.println("[WARNING] Detected several root worldmaps: " + rootWorldmaps + "\n");
|
||||
}
|
||||
|
||||
Set<String> worldmaps = new HashSet<>(parentWorldmaps.keySet());
|
||||
worldmaps.addAll(rootWorldmaps);
|
||||
|
||||
Map<String, Set<Integer>> tempMapids = new HashMap<>(worldMapids.size());
|
||||
for (Map.Entry<String, Set<Integer>> e : worldMapids.entrySet()) {
|
||||
tempMapids.put(e.getKey(), new HashSet<>(e.getValue()));
|
||||
}
|
||||
|
||||
Map<String, List<Pair<Integer, String>>> unreferencedMapids = new HashMap<>();
|
||||
|
||||
for (String s : worldmaps) {
|
||||
List<Pair<Integer, String>> currentUnreferencedMapids = new ArrayList<>();
|
||||
|
||||
for (Integer i : tempMapids.get(s)) {
|
||||
String parent = parentWorldmaps.get(s);
|
||||
|
||||
while (parent != null) {
|
||||
Set<Integer> mapids = worldMapids.get(parent);
|
||||
if (!mapids.contains(i)) {
|
||||
currentUnreferencedMapids.add(new Pair<>(i, parent));
|
||||
break;
|
||||
} else {
|
||||
tempMapids.get(parent).remove(i);
|
||||
}
|
||||
|
||||
parent = parentWorldmaps.get(parent);
|
||||
}
|
||||
}
|
||||
|
||||
if (!currentUnreferencedMapids.isEmpty()) {
|
||||
unreferencedMapids.put(s, currentUnreferencedMapids);
|
||||
}
|
||||
}
|
||||
|
||||
if (!unreferencedMapids.isEmpty()) {
|
||||
List<Pair<String, List<Pair<Integer, String>>>> unreferencedEntries = new ArrayList<>(20);
|
||||
for (Map.Entry<String, List<Pair<Integer, String>>> e : unreferencedMapids.entrySet()) {
|
||||
List<Pair<Integer, String>> list = new ArrayList<>(e.getValue());
|
||||
list.sort((o1, o2) -> o1.getLeft().compareTo(o2.getLeft()));
|
||||
|
||||
unreferencedEntries.add(new Pair<>(e.getKey(), list));
|
||||
}
|
||||
|
||||
unreferencedEntries.sort((o1, o2) -> o1.getLeft().compareTo(o2.getLeft()));
|
||||
|
||||
printReportFileResults(unreferencedEntries);
|
||||
}
|
||||
|
||||
printWriter.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
parseWorldmapDirectory();
|
||||
verifyWorldmapTreeMapids();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user