diff --git a/src/main/java/net/server/Server.java b/src/main/java/net/server/Server.java index 3051ee6781..2a3f4c4921 100644 --- a/src/main/java/net/server/Server.java +++ b/src/main/java/net/server/Server.java @@ -853,10 +853,12 @@ public class Server { throw new IllegalStateException(sqle); } + // Run slow operations asynchronously to make startup faster final List> futures = new ArrayList<>(); futures.add(initExecutor.submit(() -> SkillFactory.loadAllSkills())); futures.add(initExecutor.submit(() -> CashItemFactory.loadAllCashItems())); futures.add(initExecutor.submit(() -> MapleQuest.loadAllQuests())); + futures.add(initExecutor.submit(() -> MapleSkillbookInformationProvider.loadAllSkillbookInformation())); ThreadManager.getInstance().start(); initializeTimelyTasks(); // aggregated method for timely tasks thanks to lxconan @@ -890,6 +892,7 @@ public class Server { log.info("Families loaded in {} seconds", familyLoadTime); } + // Wait on all async tasks to complete for (Future future : futures) { try { future.get(); @@ -917,7 +920,6 @@ public class Server { Duration initDuration = Duration.between(beforeInit, Instant.now()); log.info("Cosmic is now online after {} ms.", initDuration.toMillis()); - MapleSkillbookInformationProvider.getInstance(); OpcodeConstants.generateOpcodeNames(); CommandsExecutor.getInstance(); diff --git a/src/main/java/scripting/npc/NPCConversationManager.java b/src/main/java/scripting/npc/NPCConversationManager.java index 4c21dfc5c5..3788e5a2b7 100644 --- a/src/main/java/scripting/npc/NPCConversationManager.java +++ b/src/main/java/scripting/npc/NPCConversationManager.java @@ -570,7 +570,7 @@ public class NPCConversationManager extends AbstractPlayerInteraction { public Object[] getAvailableSkillBooks() { List ret = MapleItemInformationProvider.getInstance().usableSkillBooks(this.getPlayer()); - ret.addAll(MapleSkillbookInformationProvider.getInstance().getTeachableSkills(this.getPlayer())); + ret.addAll(MapleSkillbookInformationProvider.getTeachableSkills(this.getPlayer())); return ret.toArray(); } @@ -580,7 +580,7 @@ public class NPCConversationManager extends AbstractPlayerInteraction { } public String getSkillBookInfo(int itemid) { - SkillBookEntry sbe = MapleSkillbookInformationProvider.getInstance().getSkillbookAvailability(itemid); + SkillBookEntry sbe = MapleSkillbookInformationProvider.getSkillbookAvailability(itemid); switch (sbe) { case UNAVAILABLE: return ""; diff --git a/src/main/java/server/MapleSkillbookInformationProvider.java b/src/main/java/server/MapleSkillbookInformationProvider.java index 444f3563dc..e7909d7d1b 100644 --- a/src/main/java/server/MapleSkillbookInformationProvider.java +++ b/src/main/java/server/MapleSkillbookInformationProvider.java @@ -40,15 +40,12 @@ import java.util.regex.Pattern; * * @author RonanLana */ + +/** + * Only used in 1 script that gives players information about where skillbooks can be found + */ public class MapleSkillbookInformationProvider { - - private final static MapleSkillbookInformationProvider instance = new MapleSkillbookInformationProvider(); - - public static MapleSkillbookInformationProvider getInstance() { - return instance; - } - - protected static Map foundSkillbooks = new HashMap<>(); + private static volatile Map foundSkillbooks = new HashMap<>(); public enum SkillBookEntry { UNAVAILABLE, @@ -59,13 +56,15 @@ public class MapleSkillbookInformationProvider { SCRIPT } - private static String rootDirectory = "."; - - private static int skillbookMinItemid = 2280000; - private static int skillbookMaxItemid = 2300000; // exclusively - - static { - loadSkillbooks(); + private static final int SKILLBOOK_MIN_ITEMID = 2280000; + private static final int SKILLBOOK_MAX_ITEMID = 2300000; // exclusively + + public static void loadAllSkillbookInformation() { + Map loadedSkillbooks = new HashMap<>(); + loadedSkillbooks.putAll(fetchSkillbooksFromQuests()); + loadedSkillbooks.putAll(fetchSkillbooksFromReactors()); + loadedSkillbooks.putAll(fetchSkillbooksFromScripts()); + MapleSkillbookInformationProvider.foundSkillbooks = loadedSkillbooks; } private static boolean is4thJobSkill(int itemid) { @@ -73,7 +72,7 @@ public class MapleSkillbookInformationProvider { } private static boolean isSkillBook(int itemid) { - return itemid >= skillbookMinItemid && itemid < skillbookMaxItemid; + return itemid >= SKILLBOOK_MIN_ITEMID && itemid < SKILLBOOK_MAX_ITEMID; } private static boolean isQuestBook(int itemid) { @@ -86,9 +85,9 @@ public class MapleSkillbookInformationProvider { MapleData startReqItemData = questStartData.getChildByPath("item"); if (startReqItemData != null) { for (MapleData itemData : startReqItemData.getChildren()) { - int itemid = MapleDataTool.getInt("id", itemData, 0); - if (isQuestBook(itemid)) { - return itemid; + int itemId = MapleDataTool.getInt("id", itemData, 0); + if (isQuestBook(itemId)) { + return itemId; } } } @@ -115,40 +114,41 @@ public class MapleSkillbookInformationProvider { return -1; } - private static void fetchSkillbooksFromQuests() { + private static Map fetchSkillbooksFromQuests() { MapleDataProvider questDataProvider = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "Quest.wz")); MapleData actData = questDataProvider.getData("Act.img"); MapleData checkData = questDataProvider.getData("Check.img"); - + + final Map loadedSkillbooks = new HashMap<>(); for (MapleData questData : actData.getChildren()) { for (MapleData questStatusData : questData.getChildren()) { for (MapleData questNodeData : questStatusData.getChildren()) { String actNodeName = questNodeData.getName(); if (actNodeName.contentEquals("item")) { for (MapleData questItemData : questNodeData.getChildren()) { - int itemid = MapleDataTool.getInt("id", questItemData, 0); - int itemcount = MapleDataTool.getInt("count", questItemData, 0); + int itemId = MapleDataTool.getInt("id", questItemData, 0); + int itemCount = MapleDataTool.getInt("count", questItemData, 0); - if (isSkillBook(itemid) && itemcount > 0) { + if (isSkillBook(itemId) && itemCount > 0) { int questbook = fetchQuestbook(checkData, questData.getName()); if (questbook < 0) { - foundSkillbooks.put(itemid, SkillBookEntry.QUEST); + loadedSkillbooks.put(itemId, SkillBookEntry.QUEST); } else { - foundSkillbooks.put(itemid, SkillBookEntry.QUEST_BOOK); + loadedSkillbooks.put(itemId, SkillBookEntry.QUEST_BOOK); } } } } else if (actNodeName.contentEquals("skill")) { for (MapleData questSkillData : questNodeData.getChildren()) { - int skillid = MapleDataTool.getInt("id", questSkillData, 0); - if (is4thJobSkill(skillid)) { + int skillId = MapleDataTool.getInt("id", questSkillData, 0); + if (is4thJobSkill(skillId)) { // negative itemids are skill rewards int questbook = fetchQuestbook(checkData, questData.getName()); if (questbook < 0) { - foundSkillbooks.put(-skillid, SkillBookEntry.QUEST_REWARD); + loadedSkillbooks.put(-skillId, SkillBookEntry.QUEST_REWARD); } else { - foundSkillbooks.put(-skillid, SkillBookEntry.QUEST_BOOK); + loadedSkillbooks.put(-skillId, SkillBookEntry.QUEST_BOOK); } } } @@ -156,24 +156,30 @@ public class MapleSkillbookInformationProvider { } } } + + return loadedSkillbooks; } - private static void fetchSkillbooksFromReactors() { + private static Map fetchSkillbooksFromReactors() { + Map loadedSkillbooks = new HashMap<>(); + try (Connection con = DatabaseConnection.getConnection(); PreparedStatement ps = con.prepareStatement("SELECT itemid FROM reactordrops WHERE itemid >= ? AND itemid < ?;")) { - ps.setInt(1, skillbookMinItemid); - ps.setInt(2, skillbookMaxItemid); + ps.setInt(1, SKILLBOOK_MIN_ITEMID); + ps.setInt(2, SKILLBOOK_MAX_ITEMID); try (ResultSet rs = ps.executeQuery()) { if (rs.isBeforeFirst()) { while (rs.next()) { - foundSkillbooks.put(rs.getInt("itemid"), SkillBookEntry.REACTOR); + loadedSkillbooks.put(rs.getInt("itemid"), SkillBookEntry.REACTOR); } } } } catch (SQLException sqle) { sqle.printStackTrace(); } + + return loadedSkillbooks; } private static void listFiles(String directoryName, ArrayList files) { @@ -197,28 +203,20 @@ public class MapleSkillbookInformationProvider { return files; } - private static void filterScriptDirectorySearchMatchingData(String path) { - for (File file : listFilesFromDirectoryRecursively(rootDirectory + "/" + path)) { - if (file.getName().endsWith(".js")) { - fileSearchMatchingData(file); - } - } - } - - private static Set foundMatchingDataOnFile(String fileContent) { - Set matches = new HashSet<>(4); + private static Set findMatchingSkillbookIdsOnFile(String fileContent) { + Set skillbookIds = new HashSet<>(4); Matcher searchM = Pattern.compile("22(8|9)[0-9]{4}").matcher(fileContent); int idx = 0; while (searchM.find(idx)) { idx = searchM.end(); - matches.add(Integer.valueOf(fileContent.substring(searchM.start(), idx))); + skillbookIds.add(Integer.valueOf(fileContent.substring(searchM.start(), idx))); } - return matches; + return skillbookIds; } - static String readFileToString(File file, String encoding) throws IOException { + private static String readFileToString(File file, String encoding) throws IOException { Scanner scanner = new Scanner(file, encoding); String text = ""; try { @@ -232,36 +230,42 @@ public class MapleSkillbookInformationProvider { return text; } - private static void fileSearchMatchingData(File file) { + private static Map fileSearchMatchingData(File file) { + Map scriptFileSkillbooks = new HashMap<>(); + try { String fileContent = readFileToString(file, "UTF-8"); - Set books = foundMatchingDataOnFile(fileContent); - for (Integer i : books) { - foundSkillbooks.put(i, SkillBookEntry.SCRIPT); + Set skillbookIds = findMatchingSkillbookIdsOnFile(fileContent); + for (Integer skillbookId : skillbookIds) { + scriptFileSkillbooks.put(skillbookId, SkillBookEntry.SCRIPT); } } catch (IOException ioe) { System.out.println("Failed to read " + file.getName() + "."); ioe.printStackTrace(); } + + return scriptFileSkillbooks; } - private static void fetchSkillbooksFromScripts() { - filterScriptDirectorySearchMatchingData("scripts"); + private static Map fetchSkillbooksFromScripts() { + Map scriptSkillbooks = new HashMap<>(); + + for (File file : listFilesFromDirectoryRecursively("./scripts")) { + if (file.getName().endsWith(".js")) { + scriptSkillbooks.putAll(fileSearchMatchingData(file)); + } + } + + return scriptSkillbooks; } - private static void loadSkillbooks() { - fetchSkillbooksFromQuests(); - fetchSkillbooksFromReactors(); - fetchSkillbooksFromScripts(); - } - - public SkillBookEntry getSkillbookAvailability(int itemid) { - SkillBookEntry sbe = foundSkillbooks.get(itemid); + public static SkillBookEntry getSkillbookAvailability(int itemId) { + SkillBookEntry sbe = foundSkillbooks.get(itemId); return sbe != null ? sbe : SkillBookEntry.UNAVAILABLE; } - public List getTeachableSkills(MapleCharacter chr) { + public static List getTeachableSkills(MapleCharacter chr) { List list = new ArrayList<>(); for (Integer book : foundSkillbooks.keySet()) {