Load skillbook source information async on startup

This commit is contained in:
P0nk
2021-04-13 08:14:11 +02:00
parent 055ee01ae9
commit dd7c2a697a
3 changed files with 71 additions and 65 deletions

View File

@@ -853,10 +853,12 @@ public class Server {
throw new IllegalStateException(sqle); throw new IllegalStateException(sqle);
} }
// Run slow operations asynchronously to make startup faster
final List<Future<?>> futures = new ArrayList<>(); final List<Future<?>> futures = new ArrayList<>();
futures.add(initExecutor.submit(() -> SkillFactory.loadAllSkills())); futures.add(initExecutor.submit(() -> SkillFactory.loadAllSkills()));
futures.add(initExecutor.submit(() -> CashItemFactory.loadAllCashItems())); futures.add(initExecutor.submit(() -> CashItemFactory.loadAllCashItems()));
futures.add(initExecutor.submit(() -> MapleQuest.loadAllQuests())); futures.add(initExecutor.submit(() -> MapleQuest.loadAllQuests()));
futures.add(initExecutor.submit(() -> MapleSkillbookInformationProvider.loadAllSkillbookInformation()));
ThreadManager.getInstance().start(); ThreadManager.getInstance().start();
initializeTimelyTasks(); // aggregated method for timely tasks thanks to lxconan initializeTimelyTasks(); // aggregated method for timely tasks thanks to lxconan
@@ -890,6 +892,7 @@ public class Server {
log.info("Families loaded in {} seconds", familyLoadTime); log.info("Families loaded in {} seconds", familyLoadTime);
} }
// Wait on all async tasks to complete
for (Future<?> future : futures) { for (Future<?> future : futures) {
try { try {
future.get(); future.get();
@@ -917,7 +920,6 @@ public class Server {
Duration initDuration = Duration.between(beforeInit, Instant.now()); Duration initDuration = Duration.between(beforeInit, Instant.now());
log.info("Cosmic is now online after {} ms.", initDuration.toMillis()); log.info("Cosmic is now online after {} ms.", initDuration.toMillis());
MapleSkillbookInformationProvider.getInstance();
OpcodeConstants.generateOpcodeNames(); OpcodeConstants.generateOpcodeNames();
CommandsExecutor.getInstance(); CommandsExecutor.getInstance();

View File

@@ -570,7 +570,7 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
public Object[] getAvailableSkillBooks() { public Object[] getAvailableSkillBooks() {
List<Integer> ret = MapleItemInformationProvider.getInstance().usableSkillBooks(this.getPlayer()); List<Integer> ret = MapleItemInformationProvider.getInstance().usableSkillBooks(this.getPlayer());
ret.addAll(MapleSkillbookInformationProvider.getInstance().getTeachableSkills(this.getPlayer())); ret.addAll(MapleSkillbookInformationProvider.getTeachableSkills(this.getPlayer()));
return ret.toArray(); return ret.toArray();
} }
@@ -580,7 +580,7 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
} }
public String getSkillBookInfo(int itemid) { public String getSkillBookInfo(int itemid) {
SkillBookEntry sbe = MapleSkillbookInformationProvider.getInstance().getSkillbookAvailability(itemid); SkillBookEntry sbe = MapleSkillbookInformationProvider.getSkillbookAvailability(itemid);
switch (sbe) { switch (sbe) {
case UNAVAILABLE: case UNAVAILABLE:
return ""; return "";

View File

@@ -40,15 +40,12 @@ import java.util.regex.Pattern;
* *
* @author RonanLana * @author RonanLana
*/ */
/**
* Only used in 1 script that gives players information about where skillbooks can be found
*/
public class MapleSkillbookInformationProvider { public class MapleSkillbookInformationProvider {
private static volatile Map<Integer, SkillBookEntry> foundSkillbooks = new HashMap<>();
private final static MapleSkillbookInformationProvider instance = new MapleSkillbookInformationProvider();
public static MapleSkillbookInformationProvider getInstance() {
return instance;
}
protected static Map<Integer, SkillBookEntry> foundSkillbooks = new HashMap<>();
public enum SkillBookEntry { public enum SkillBookEntry {
UNAVAILABLE, UNAVAILABLE,
@@ -59,13 +56,15 @@ public class MapleSkillbookInformationProvider {
SCRIPT SCRIPT
} }
private static String rootDirectory = "."; private static final int SKILLBOOK_MIN_ITEMID = 2280000;
private static final int SKILLBOOK_MAX_ITEMID = 2300000; // exclusively
private static int skillbookMinItemid = 2280000; public static void loadAllSkillbookInformation() {
private static int skillbookMaxItemid = 2300000; // exclusively Map<Integer, SkillBookEntry> loadedSkillbooks = new HashMap<>();
loadedSkillbooks.putAll(fetchSkillbooksFromQuests());
static { loadedSkillbooks.putAll(fetchSkillbooksFromReactors());
loadSkillbooks(); loadedSkillbooks.putAll(fetchSkillbooksFromScripts());
MapleSkillbookInformationProvider.foundSkillbooks = loadedSkillbooks;
} }
private static boolean is4thJobSkill(int itemid) { private static boolean is4thJobSkill(int itemid) {
@@ -73,7 +72,7 @@ public class MapleSkillbookInformationProvider {
} }
private static boolean isSkillBook(int itemid) { 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) { private static boolean isQuestBook(int itemid) {
@@ -86,9 +85,9 @@ public class MapleSkillbookInformationProvider {
MapleData startReqItemData = questStartData.getChildByPath("item"); MapleData startReqItemData = questStartData.getChildByPath("item");
if (startReqItemData != null) { if (startReqItemData != null) {
for (MapleData itemData : startReqItemData.getChildren()) { for (MapleData itemData : startReqItemData.getChildren()) {
int itemid = MapleDataTool.getInt("id", itemData, 0); int itemId = MapleDataTool.getInt("id", itemData, 0);
if (isQuestBook(itemid)) { if (isQuestBook(itemId)) {
return itemid; return itemId;
} }
} }
} }
@@ -115,41 +114,41 @@ public class MapleSkillbookInformationProvider {
return -1; return -1;
} }
private static void fetchSkillbooksFromQuests() { private static Map<Integer, SkillBookEntry> fetchSkillbooksFromQuests() {
MapleDataProvider questDataProvider = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "Quest.wz")); MapleDataProvider questDataProvider = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "Quest.wz"));
MapleData actData = questDataProvider.getData("Act.img"); MapleData actData = questDataProvider.getData("Act.img");
MapleData checkData = questDataProvider.getData("Check.img"); MapleData checkData = questDataProvider.getData("Check.img");
final Map<Integer, SkillBookEntry> loadedSkillbooks = new HashMap<>();
for (MapleData questData : actData.getChildren()) { for (MapleData questData : actData.getChildren()) {
for (MapleData questStatusData : questData.getChildren()) { for (MapleData questStatusData : questData.getChildren()) {
for (MapleData questNodeData : questStatusData.getChildren()) { for (MapleData questNodeData : questStatusData.getChildren()) {
String actNodeName = questNodeData.getName(); String actNodeName = questNodeData.getName();
if (actNodeName.contentEquals("item")) { if (actNodeName.contentEquals("item")) {
for (MapleData questItemData : questNodeData.getChildren()) { for (MapleData questItemData : questNodeData.getChildren()) {
int itemid = MapleDataTool.getInt("id", questItemData, 0); int itemId = MapleDataTool.getInt("id", questItemData, 0);
int itemcount = MapleDataTool.getInt("count", 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()); int questbook = fetchQuestbook(checkData, questData.getName());
if (questbook < 0) { if (questbook < 0) {
foundSkillbooks.put(itemid, SkillBookEntry.QUEST); loadedSkillbooks.put(itemId, SkillBookEntry.QUEST);
} else { } else {
foundSkillbooks.put(itemid, SkillBookEntry.QUEST_BOOK); loadedSkillbooks.put(itemId, SkillBookEntry.QUEST_BOOK);
} }
} }
} }
} else if (actNodeName.contentEquals("skill")) { } else if (actNodeName.contentEquals("skill")) {
for (MapleData questSkillData : questNodeData.getChildren()) { for (MapleData questSkillData : questNodeData.getChildren()) {
int skillid = MapleDataTool.getInt("id", questSkillData, 0); int skillId = MapleDataTool.getInt("id", questSkillData, 0);
if (is4thJobSkill(skillid)) { if (is4thJobSkill(skillId)) {
// negative itemids are skill rewards // negative itemids are skill rewards
int questbook = fetchQuestbook(checkData, questData.getName()); int questbook = fetchQuestbook(checkData, questData.getName());
if (questbook < 0) { if (questbook < 0) {
foundSkillbooks.put(-skillid, SkillBookEntry.QUEST_REWARD); loadedSkillbooks.put(-skillId, SkillBookEntry.QUEST_REWARD);
} else { } else {
foundSkillbooks.put(-skillid, SkillBookEntry.QUEST_BOOK); loadedSkillbooks.put(-skillId, SkillBookEntry.QUEST_BOOK);
}
} }
} }
} }
@@ -158,22 +157,29 @@ public class MapleSkillbookInformationProvider {
} }
} }
private static void fetchSkillbooksFromReactors() { return loadedSkillbooks;
}
private static Map<Integer, SkillBookEntry> fetchSkillbooksFromReactors() {
Map<Integer, SkillBookEntry> loadedSkillbooks = new HashMap<>();
try (Connection con = DatabaseConnection.getConnection(); try (Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT itemid FROM reactordrops WHERE itemid >= ? AND itemid < ?;")) { PreparedStatement ps = con.prepareStatement("SELECT itemid FROM reactordrops WHERE itemid >= ? AND itemid < ?;")) {
ps.setInt(1, skillbookMinItemid); ps.setInt(1, SKILLBOOK_MIN_ITEMID);
ps.setInt(2, skillbookMaxItemid); ps.setInt(2, SKILLBOOK_MAX_ITEMID);
try (ResultSet rs = ps.executeQuery()) { try (ResultSet rs = ps.executeQuery()) {
if (rs.isBeforeFirst()) { if (rs.isBeforeFirst()) {
while (rs.next()) { while (rs.next()) {
foundSkillbooks.put(rs.getInt("itemid"), SkillBookEntry.REACTOR); loadedSkillbooks.put(rs.getInt("itemid"), SkillBookEntry.REACTOR);
} }
} }
} }
} catch (SQLException sqle) { } catch (SQLException sqle) {
sqle.printStackTrace(); sqle.printStackTrace();
} }
return loadedSkillbooks;
} }
private static void listFiles(String directoryName, ArrayList<File> files) { private static void listFiles(String directoryName, ArrayList<File> files) {
@@ -197,28 +203,20 @@ public class MapleSkillbookInformationProvider {
return files; return files;
} }
private static void filterScriptDirectorySearchMatchingData(String path) { private static Set<Integer> findMatchingSkillbookIdsOnFile(String fileContent) {
for (File file : listFilesFromDirectoryRecursively(rootDirectory + "/" + path)) { Set<Integer> skillbookIds = new HashSet<>(4);
if (file.getName().endsWith(".js")) {
fileSearchMatchingData(file);
}
}
}
private static Set<Integer> foundMatchingDataOnFile(String fileContent) {
Set<Integer> matches = new HashSet<>(4);
Matcher searchM = Pattern.compile("22(8|9)[0-9]{4}").matcher(fileContent); Matcher searchM = Pattern.compile("22(8|9)[0-9]{4}").matcher(fileContent);
int idx = 0; int idx = 0;
while (searchM.find(idx)) { while (searchM.find(idx)) {
idx = searchM.end(); 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); Scanner scanner = new Scanner(file, encoding);
String text = ""; String text = "";
try { try {
@@ -232,36 +230,42 @@ public class MapleSkillbookInformationProvider {
return text; return text;
} }
private static void fileSearchMatchingData(File file) { private static Map<Integer, SkillBookEntry> fileSearchMatchingData(File file) {
Map<Integer, SkillBookEntry> scriptFileSkillbooks = new HashMap<>();
try { try {
String fileContent = readFileToString(file, "UTF-8"); String fileContent = readFileToString(file, "UTF-8");
Set<Integer> books = foundMatchingDataOnFile(fileContent); Set<Integer> skillbookIds = findMatchingSkillbookIdsOnFile(fileContent);
for (Integer i : books) { for (Integer skillbookId : skillbookIds) {
foundSkillbooks.put(i, SkillBookEntry.SCRIPT); scriptFileSkillbooks.put(skillbookId, SkillBookEntry.SCRIPT);
} }
} catch (IOException ioe) { } catch (IOException ioe) {
System.out.println("Failed to read " + file.getName() + "."); System.out.println("Failed to read " + file.getName() + ".");
ioe.printStackTrace(); ioe.printStackTrace();
} }
return scriptFileSkillbooks;
} }
private static void fetchSkillbooksFromScripts() { private static Map<Integer, SkillBookEntry> fetchSkillbooksFromScripts() {
filterScriptDirectorySearchMatchingData("scripts"); Map<Integer, SkillBookEntry> scriptSkillbooks = new HashMap<>();
for (File file : listFilesFromDirectoryRecursively("./scripts")) {
if (file.getName().endsWith(".js")) {
scriptSkillbooks.putAll(fileSearchMatchingData(file));
}
} }
private static void loadSkillbooks() { return scriptSkillbooks;
fetchSkillbooksFromQuests();
fetchSkillbooksFromReactors();
fetchSkillbooksFromScripts();
} }
public SkillBookEntry getSkillbookAvailability(int itemid) { public static SkillBookEntry getSkillbookAvailability(int itemId) {
SkillBookEntry sbe = foundSkillbooks.get(itemid); SkillBookEntry sbe = foundSkillbooks.get(itemId);
return sbe != null ? sbe : SkillBookEntry.UNAVAILABLE; return sbe != null ? sbe : SkillBookEntry.UNAVAILABLE;
} }
public List<Integer> getTeachableSkills(MapleCharacter chr) { public static List<Integer> getTeachableSkills(MapleCharacter chr) {
List<Integer> list = new ArrayList<>(); List<Integer> list = new ArrayList<>();
for (Integer book : foundSkillbooks.keySet()) { for (Integer book : foundSkillbooks.keySet()) {