Merge pull request #20 from P0nk/faster-startup

Improve the startup sequence
This commit is contained in:
Ponk
2021-04-16 20:45:15 +02:00
committed by GitHub
11 changed files with 399 additions and 370 deletions
+81 -83
View File
@@ -182,97 +182,95 @@ public class MapleFamily {
} }
} }
public static void loadAllFamilies() { public static void loadAllFamilies(Connection con) {
try(Connection con = DatabaseConnection.getConnection()) { List<Pair<Pair<Integer, Integer>, MapleFamilyEntry>> unmatchedJuniors = new ArrayList<>(200); // <<world, seniorid> familyEntry>
List<Pair<Pair<Integer, Integer>, MapleFamilyEntry>> unmatchedJuniors = new ArrayList<>(200); // <<world, seniorid> familyEntry> try (PreparedStatement psEntries = con.prepareStatement("SELECT * FROM family_character")) {
try(PreparedStatement psEntries = con.prepareStatement("SELECT * FROM family_character")) { ResultSet rsEntries = psEntries.executeQuery();
ResultSet rsEntries = psEntries.executeQuery(); while (rsEntries.next()) { // can be optimized
while(rsEntries.next()) { // can be optimized int cid = rsEntries.getInt("cid");
int cid = rsEntries.getInt("cid"); String name = null;
String name = null; int level = -1;
int level = -1; int jobID = -1;
int jobID = -1; int world = -1;
int world = -1; try (PreparedStatement ps = con.prepareStatement("SELECT world, name, level, job FROM characters WHERE id = ?")) {
try(PreparedStatement ps = con.prepareStatement("SELECT world, name, level, job FROM characters WHERE id = ?")) { ps.setInt(1, cid);
ps.setInt(1, cid); ResultSet rs = ps.executeQuery();
ResultSet rs = ps.executeQuery(); if (rs.next()) {
if(rs.next()) { world = rs.getInt("world");
world = rs.getInt("world"); name = rs.getString("name");
name = rs.getString("name"); level = rs.getInt("level");
level = rs.getInt("level"); jobID = rs.getInt("job");
jobID = rs.getInt("job");
} else {
FilePrinter.printError(FilePrinter.FAMILY_ERROR, "Could not load character information of " + cid + " in loadAllFamilies(). (RECORD DOES NOT EXIST)");
continue;
}
} catch(SQLException e) {
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not load character information of " + cid + " in loadAllFamilies(). (SQL ERROR)");
continue;
}
int familyid = rsEntries.getInt("familyid");
int seniorid = rsEntries.getInt("seniorid");
int reputation = rsEntries.getInt("reputation");
int todaysRep = rsEntries.getInt("todaysrep");
int totalRep = rsEntries.getInt("totalreputation");
int repsToSenior = rsEntries.getInt("reptosenior");
String precepts = rsEntries.getString("precepts");
//Timestamp lastResetTime = rsEntries.getTimestamp("lastresettime"); //taken care of by FamilyDailyResetTask
World wserv = Server.getInstance().getWorld(world);
if (wserv == null) {
continue;
}
MapleFamily family = wserv.getFamily(familyid);
if(family == null) {
family = new MapleFamily(familyid, world);
Server.getInstance().getWorld(world).addFamily(familyid, family);
}
MapleFamilyEntry familyEntry = new MapleFamilyEntry(family, cid, name, level, MapleJob.getById(jobID));
family.addEntry(familyEntry);
if(seniorid <= 0) {
family.setLeader(familyEntry);
family.setMessage(precepts, false);
}
MapleFamilyEntry senior = family.getEntryByID(seniorid);
if(senior != null) {
familyEntry.setSenior(family.getEntryByID(seniorid), false);
} else { } else {
if(seniorid > 0) unmatchedJuniors.add(new Pair<>(new Pair<>(world, seniorid), familyEntry)); FilePrinter.printError(FilePrinter.FAMILY_ERROR, "Could not load character information of " + cid + " in loadAllFamilies(). (RECORD DOES NOT EXIST)");
} continue;
familyEntry.setReputation(reputation);
familyEntry.setTodaysRep(todaysRep);
familyEntry.setTotalReputation(totalRep);
familyEntry.setRepsToSenior(repsToSenior);
//load used entitlements
try (PreparedStatement ps = con.prepareStatement("SELECT entitlementid FROM family_entitlement WHERE charid = ?")) {
ps.setInt(1, familyEntry.getChrId());
ResultSet rs = ps.executeQuery();
while(rs.next()) {
familyEntry.setEntitlementUsed(rs.getInt("entitlementid"));
}
} }
} catch (SQLException e) {
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not load character information of " + cid + " in loadAllFamilies(). (SQL ERROR)");
continue;
} }
} catch(SQLException e) { int familyid = rsEntries.getInt("familyid");
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not get family_character entries."); int seniorid = rsEntries.getInt("seniorid");
e.printStackTrace(); int reputation = rsEntries.getInt("reputation");
} int todaysRep = rsEntries.getInt("todaysrep");
// link missing ones (out of order) int totalRep = rsEntries.getInt("totalreputation");
for(Pair<Pair<Integer, Integer>, MapleFamilyEntry> unmatchedJunior : unmatchedJuniors) { int repsToSenior = rsEntries.getInt("reptosenior");
int world = unmatchedJunior.getLeft().getLeft(); String precepts = rsEntries.getString("precepts");
int seniorid = unmatchedJunior.getLeft().getRight(); //Timestamp lastResetTime = rsEntries.getTimestamp("lastresettime"); //taken care of by FamilyDailyResetTask
MapleFamilyEntry junior = unmatchedJunior.getRight(); World wserv = Server.getInstance().getWorld(world);
MapleFamilyEntry senior = Server.getInstance().getWorld(world).getFamily(junior.getFamily().getID()).getEntryByID(seniorid); if (wserv == null) {
if(senior != null) { continue;
junior.setSenior(senior, false); }
MapleFamily family = wserv.getFamily(familyid);
if (family == null) {
family = new MapleFamily(familyid, world);
Server.getInstance().getWorld(world).addFamily(familyid, family);
}
MapleFamilyEntry familyEntry = new MapleFamilyEntry(family, cid, name, level, MapleJob.getById(jobID));
family.addEntry(familyEntry);
if (seniorid <= 0) {
family.setLeader(familyEntry);
family.setMessage(precepts, false);
}
MapleFamilyEntry senior = family.getEntryByID(seniorid);
if (senior != null) {
familyEntry.setSenior(family.getEntryByID(seniorid), false);
} else { } else {
FilePrinter.printError(FilePrinter.FAMILY_ERROR, "Missing senior for character " + junior.getName() + " in world " + world); if (seniorid > 0) {
unmatchedJuniors.add(new Pair<>(new Pair<>(world, seniorid), familyEntry));
}
}
familyEntry.setReputation(reputation);
familyEntry.setTodaysRep(todaysRep);
familyEntry.setTotalReputation(totalRep);
familyEntry.setRepsToSenior(repsToSenior);
//load used entitlements
try (PreparedStatement ps = con.prepareStatement("SELECT entitlementid FROM family_entitlement WHERE charid = ?")) {
ps.setInt(1, familyEntry.getChrId());
ResultSet rs = ps.executeQuery();
while (rs.next()) {
familyEntry.setEntitlementUsed(rs.getInt("entitlementid"));
}
} }
} }
} catch(SQLException e) { } catch (SQLException e) {
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not get DB connection."); FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not get family_character entries.");
e.printStackTrace(); e.printStackTrace();
} }
for(World world : Server.getInstance().getWorlds()) { // link missing ones (out of order)
for(MapleFamily family : world.getFamilies()) { for (Pair<Pair<Integer, Integer>, MapleFamilyEntry> unmatchedJunior : unmatchedJuniors) {
int world = unmatchedJunior.getLeft().getLeft();
int seniorid = unmatchedJunior.getLeft().getRight();
MapleFamilyEntry junior = unmatchedJunior.getRight();
MapleFamilyEntry senior = Server.getInstance().getWorld(world).getFamily(junior.getFamily().getID()).getEntryByID(seniorid);
if (senior != null) {
junior.setSenior(senior, false);
} else {
FilePrinter.printError(FilePrinter.FAMILY_ERROR, "Missing senior for character " + junior.getName() + " in world " + world);
}
}
for (World world : Server.getInstance().getWorlds()) {
for (MapleFamily family : world.getFamilies()) {
family.getLeader().doFullCount(); family.getLeader().doFullCount();
} }
} }
+8 -9
View File
@@ -31,33 +31,32 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
public class SkillFactory { public class SkillFactory {
private static Map<Integer, Skill> skills = new HashMap<>(); private static volatile Map<Integer, Skill> skills = new HashMap<>();
private static MapleDataProvider datasource = MapleDataProviderFactory.getDataProvider(MapleDataProviderFactory.fileInWZPath("Skill.wz")); private static final MapleDataProvider datasource = MapleDataProviderFactory.getDataProvider(MapleDataProviderFactory.fileInWZPath("Skill.wz"));
public static Skill getSkill(int id) { public static Skill getSkill(int id) {
if (!skills.isEmpty()) { return skills.get(id);
return skills.get(id);
}
return null;
} }
public static void loadAllSkills() { public static void loadAllSkills() {
final Map<Integer, Skill> loadedSkills = new HashMap<>();
final MapleDataDirectoryEntry root = datasource.getRoot(); final MapleDataDirectoryEntry root = datasource.getRoot();
int skillid;
for (MapleDataFileEntry topDir : root.getFiles()) { // Loop thru jobs for (MapleDataFileEntry topDir : root.getFiles()) { // Loop thru jobs
if (topDir.getName().length() <= 8) { if (topDir.getName().length() <= 8) {
for (MapleData data : datasource.getData(topDir.getName())) { // Loop thru each jobs for (MapleData data : datasource.getData(topDir.getName())) { // Loop thru each jobs
if (data.getName().equals("skill")) { if (data.getName().equals("skill")) {
for (MapleData data2 : data) { // Loop thru each jobs for (MapleData data2 : data) { // Loop thru each jobs
if (data2 != null) { if (data2 != null) {
skillid = Integer.parseInt(data2.getName()); int skillId = Integer.parseInt(data2.getName());
skills.put(skillid, loadFromData(skillid, data2)); loadedSkills.put(skillId, loadFromData(skillId, data2));
} }
} }
} }
} }
} }
} }
skills = loadedSkills;
} }
private static Skill loadFromData(int id, MapleData data) { private static Skill loadFromData(int id, MapleData data) {
@@ -19,6 +19,8 @@
*/ */
package client.inventory.manipulator; package client.inventory.manipulator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tools.DatabaseConnection; import tools.DatabaseConnection;
import java.sql.Connection; import java.sql.Connection;
@@ -32,7 +34,7 @@ import java.util.Set;
* @author RonanLana * @author RonanLana
*/ */
public class MapleCashidGenerator { public class MapleCashidGenerator {
private static final Logger log = LoggerFactory.getLogger(MapleCashidGenerator.class);
private final static Set<Integer> existentCashids = new HashSet<>(10000); private final static Set<Integer> existentCashids = new HashSet<>(10000);
private static Integer runningCashid = 0; private static Integer runningCashid = 0;
@@ -49,13 +51,9 @@ public class MapleCashidGenerator {
} }
} }
public static synchronized void loadExistentCashIdsFromDb() { public static synchronized void loadExistentCashIdsFromDb(Connection con) throws SQLException {
try (Connection con = DatabaseConnection.getConnection()) { loadExistentCashIdsFromQuery(con, "SELECT id FROM rings");
loadExistentCashIdsFromQuery(con, "SELECT id FROM rings"); loadExistentCashIdsFromQuery(con, "SELECT petid FROM pets");
loadExistentCashIdsFromQuery(con, "SELECT petid FROM pets");
} catch (SQLException ex) {
ex.printStackTrace();
}
runningCashid = 0; runningCashid = 0;
do { do {
@@ -67,7 +65,11 @@ public class MapleCashidGenerator {
runningCashid++; runningCashid++;
if (runningCashid >= 777000000) { if (runningCashid >= 777000000) {
existentCashids.clear(); existentCashids.clear();
loadExistentCashIdsFromDb(); try (Connection con = DatabaseConnection.getConnection()) {
loadExistentCashIdsFromDb(con);
} catch (SQLException e) {
log.warn("Failed to reset overflowing cash ids", e);
}
} }
} }
@@ -339,22 +339,18 @@ public class NewYearCardRecord {
} }
} }
} }
public static void startPendingNewYearCardRequests() { public static void startPendingNewYearCardRequests(Connection con) throws SQLException {
try (Connection con = DatabaseConnection.getConnection()) { try (PreparedStatement ps = con.prepareStatement("SELECT * FROM newyear WHERE timereceived = 0 AND senderdiscard = 0")) {
try (PreparedStatement ps = con.prepareStatement("SELECT * FROM newyear WHERE timereceived = 0 AND senderdiscard = 0")) { try (ResultSet rs = ps.executeQuery()) {
try (ResultSet rs = ps.executeQuery()) { while (rs.next()) {
while (rs.next()) { NewYearCardRecord newyear = new NewYearCardRecord(rs.getInt("senderid"), rs.getString("sendername"), rs.getInt("receiverid"), rs.getString("receivername"), rs.getString("message"));
NewYearCardRecord newyear = new NewYearCardRecord(rs.getInt("senderid"), rs.getString("sendername"), rs.getInt("receiverid"), rs.getString("receivername"), rs.getString("message")); newyear.setExtraNewYearCardRecord(rs.getInt("id"), rs.getBoolean("senderdiscard"), rs.getBoolean("receiverdiscard"), rs.getBoolean("received"), rs.getLong("timesent"), rs.getLong("timereceived"));
newyear.setExtraNewYearCardRecord(rs.getInt("id"), rs.getBoolean("senderdiscard"), rs.getBoolean("receiverdiscard"), rs.getBoolean("received"), rs.getLong("timesent"), rs.getLong("timereceived"));
Server.getInstance().setNewYearCard(newyear);
Server.getInstance().setNewYearCard(newyear); newyear.startNewYearCardTask();
newyear.startNewYearCardTask();
}
} }
} }
} catch(SQLException sqle) {
sqle.printStackTrace();
} }
} }
} }
+142 -120
View File
@@ -80,8 +80,13 @@ import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.time.Duration;
import java.time.Instant;
import java.util.*; import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
@@ -134,7 +139,7 @@ public class Server {
private final AtomicLong currentTime = new AtomicLong(0); private final AtomicLong currentTime = new AtomicLong(0);
private long serverCurrentTime = 0; private long serverCurrentTime = 0;
private boolean availableDeveloperRoom = false; private volatile boolean availableDeveloperRoom = false;
private boolean online = false; private boolean online = false;
public static long uptime = System.currentTimeMillis(); public static long uptime = System.currentTimeMillis();
@@ -625,7 +630,7 @@ public class Server {
} }
} }
public void updateActiveCoupons() throws SQLException { public void updateActiveCoupons(Connection con) throws SQLException {
synchronized (activeCoupons) { synchronized (activeCoupons) {
activeCoupons.clear(); activeCoupons.clear();
Calendar c = Calendar.getInstance(); Calendar c = Calendar.getInstance();
@@ -633,23 +638,19 @@ public class Server {
int weekDay = c.get(Calendar.DAY_OF_WEEK); int weekDay = c.get(Calendar.DAY_OF_WEEK);
int hourDay = c.get(Calendar.HOUR_OF_DAY); int hourDay = c.get(Calendar.HOUR_OF_DAY);
try (Connection con = DatabaseConnection.getConnection()) { int weekdayMask = (1 << weekDay);
PreparedStatement ps = con.prepareStatement("SELECT couponid FROM nxcoupons WHERE (activeday & ?) = ? AND starthour <= ? AND endhour > ?");
ps.setInt(1, weekdayMask);
ps.setInt(2, weekdayMask);
ps.setInt(3, hourDay);
ps.setInt(4, hourDay);
int weekdayMask = (1 << weekDay); try (ResultSet rs = ps.executeQuery()) {
PreparedStatement ps = con.prepareStatement("SELECT couponid FROM nxcoupons WHERE (activeday & ?) = ? AND starthour <= ? AND endhour > ?"); while (rs.next()) {
ps.setInt(1, weekdayMask); activeCoupons.add(rs.getInt("couponid"));
ps.setInt(2, weekdayMask);
ps.setInt(3, hourDay);
ps.setInt(4, hourDay);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
activeCoupons.add(rs.getInt("couponid"));
}
} }
} catch (SQLException ex) {
ex.printStackTrace();
} }
} }
} }
@@ -703,7 +704,7 @@ public class Server {
} }
private void installWorldPlayerRanking(int worldid) { private void installWorldPlayerRanking(int worldid) {
List<Pair<Integer, List<Pair<String, Integer>>>> ranking = updatePlayerRankingFromDB(worldid); List<Pair<Integer, List<Pair<String, Integer>>>> ranking = loadPlayerRankingFromDB(worldid);
if (!ranking.isEmpty()) { if (!ranking.isEmpty()) {
wldWLock.lock(); wldWLock.lock();
try { try {
@@ -735,7 +736,7 @@ public class Server {
wldWLock.unlock(); wldWLock.unlock();
} }
} else { } else {
List<Pair<Integer, List<Pair<String, Integer>>>> ranking = updatePlayerRankingFromDB(-1 * (this.getWorldsSize() - 2)); // update ranking list List<Pair<Integer, List<Pair<String, Integer>>>> ranking = loadPlayerRankingFromDB(-1 * (this.getWorldsSize() - 2)); // update ranking list
wldWLock.lock(); wldWLock.lock();
try { try {
@@ -747,40 +748,47 @@ public class Server {
} }
public void updateWorldPlayerRanking() { public void updateWorldPlayerRanking() {
List<Pair<Integer, List<Pair<String, Integer>>>> rankUpdates = updatePlayerRankingFromDB(-1 * (this.getWorldsSize() - 1)); List<Pair<Integer, List<Pair<String, Integer>>>> rankUpdates = loadPlayerRankingFromDB(-1 * (this.getWorldsSize() - 1));
if (!rankUpdates.isEmpty()) { if (rankUpdates.isEmpty()) {
wldWLock.lock(); return;
try {
if (!YamlConfig.config.server.USE_WHOLE_SERVER_RANKING) {
for (int i = playerRanking.size(); i <= rankUpdates.get(rankUpdates.size() - 1).getLeft(); i++) {
playerRanking.add(new ArrayList<>(0));
}
for (Pair<Integer, List<Pair<String, Integer>>> wranks : rankUpdates) {
playerRanking.set(wranks.getLeft(), wranks.getRight());
}
} else {
playerRanking.set(0, rankUpdates.get(0).getRight());
}
} finally {
wldWLock.unlock();
}
} }
wldWLock.lock();
try {
if (!YamlConfig.config.server.USE_WHOLE_SERVER_RANKING) {
for (int i = playerRanking.size(); i <= rankUpdates.get(rankUpdates.size() - 1).getLeft(); i++) {
playerRanking.add(new ArrayList<>(0));
}
for (Pair<Integer, List<Pair<String, Integer>>> wranks : rankUpdates) {
playerRanking.set(wranks.getLeft(), wranks.getRight());
}
} else {
playerRanking.set(0, rankUpdates.get(0).getRight());
}
} finally {
wldWLock.unlock();
}
} }
private void initWorldPlayerRanking() { private void initWorldPlayerRanking() {
if (YamlConfig.config.server.USE_WHOLE_SERVER_RANKING) { if (YamlConfig.config.server.USE_WHOLE_SERVER_RANKING) {
playerRanking.add(new ArrayList<>(0)); wldWLock.lock();
try {
playerRanking.add(new ArrayList<>(0));
} finally {
wldWLock.unlock();
}
} }
updateWorldPlayerRanking(); updateWorldPlayerRanking();
} }
private static List<Pair<Integer, List<Pair<String, Integer>>>> updatePlayerRankingFromDB(int worldid) { private static List<Pair<Integer, List<Pair<String, Integer>>>> loadPlayerRankingFromDB(int worldid) {
List<Pair<Integer, List<Pair<String, Integer>>>> rankSystem = new ArrayList<>(); List<Pair<Integer, List<Pair<String, Integer>>>> rankSystem = new ArrayList<>();
List<Pair<String, Integer>> rankUpdate = new ArrayList<>(0);
try (Connection con = DatabaseConnection.getConnection()) { try (Connection con = DatabaseConnection.getConnection()) {
String worldQuery; String worldQuery;
if (!YamlConfig.config.server.USE_WHOLE_SERVER_RANKING) { if (!YamlConfig.config.server.USE_WHOLE_SERVER_RANKING) {
if (worldid >= 0) { if (worldid >= 0) {
@@ -792,6 +800,7 @@ public class Server {
worldQuery = (" AND `characters`.`world` >= 0 AND `characters`.`world` <= " + Math.abs(worldid)); worldQuery = (" AND `characters`.`world` >= 0 AND `characters`.`world` <= " + Math.abs(worldid));
} }
List<Pair<String, Integer>> rankUpdate = new ArrayList<>(0);
try (PreparedStatement ps = con.prepareStatement("SELECT `characters`.`name`, `characters`.`level`, `characters`.`world` FROM `characters` LEFT JOIN accounts ON accounts.id = characters.accountid WHERE `characters`.`gm` < 2 AND `accounts`.`banned` = '0'" + worldQuery + " ORDER BY " + (!YamlConfig.config.server.USE_WHOLE_SERVER_RANKING ? "world, " : "") + "level DESC, exp DESC, lastExpGainTime ASC LIMIT 50"); try (PreparedStatement ps = con.prepareStatement("SELECT `characters`.`name`, `characters`.`level`, `characters`.`world` FROM `characters` LEFT JOIN accounts ON accounts.id = characters.accountid WHERE `characters`.`gm` < 2 AND `accounts`.`banned` = '0'" + worldQuery + " ORDER BY " + (!YamlConfig.config.server.USE_WHOLE_SERVER_RANKING ? "world, " : "") + "level DESC, exp DESC, lastExpGainTime ASC LIMIT 50");
ResultSet rs = ps.executeQuery()) { ResultSet rs = ps.executeQuery()) {
@@ -824,6 +833,7 @@ public class Server {
} }
public void init() { public void init() {
Instant beforeInit = Instant.now();
log.info("Cosmic v{} starting up.", ServerConstants.VERSION); log.info("Cosmic v{} starting up.", ServerConstants.VERSION);
if (YamlConfig.config.server.SHUTDOWNHOOK) { if (YamlConfig.config.server.SHUTDOWNHOOK) {
@@ -834,50 +844,39 @@ public class Server {
throw new IllegalStateException("Failed to initiate a connection to the database"); throw new IllegalStateException("Failed to initiate a connection to the database");
} }
final ExecutorService initExecutor = Executors.newFixedThreadPool(10);
// Run slow operations asynchronously to make startup faster
final List<Future<?>> 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()));
futures.add(initExecutor.submit(() -> MaplePlayerNPCFactory.loadFactoryMetadata()));
TimeZone.setDefault(TimeZone.getTimeZone(YamlConfig.config.server.TIMEZONE)); TimeZone.setDefault(TimeZone.getTimeZone(YamlConfig.config.server.TIMEZONE));
try (Connection con = DatabaseConnection.getConnection()) { try (Connection con = DatabaseConnection.getConnection()) {
try (PreparedStatement ps = con.prepareStatement("UPDATE accounts SET loggedin = 0")) { setAllLoggedOut(con);
ps.executeUpdate(); setAllMerchantsInactive(con);
}
try (PreparedStatement ps = con.prepareStatement("UPDATE characters SET HasMerchant = 0")) {
ps.executeUpdate();
}
cleanNxcodeCoupons(con); cleanNxcodeCoupons(con);
loadCouponRates(con); loadCouponRates(con);
updateActiveCoupons(); updateActiveCoupons(con);
} catch (SQLException sqle) { NewYearCardRecord.startPendingNewYearCardRequests(con);
sqle.printStackTrace(); MapleCashidGenerator.loadExistentCashIdsFromDb(con);
} applyAllNameChanges(con); // -- name changes can be missed by INSTANT_NAME_CHANGE --
applyAllWorldTransfers(con);
applyAllNameChanges(); // -- name changes can be missed by INSTANT_NAME_CHANGE -- if (YamlConfig.config.server.USE_FAMILY_SYSTEM) {
applyAllWorldTransfers(); MapleFamily.loadAllFamilies(con);
//MaplePet.clearMissingPetsFromDb(); // thanks Optimist for noticing this taking too long to run }
MapleCashidGenerator.loadExistentCashIdsFromDb(); } catch (SQLException sqle) {
log.error("Failed to run all startup-bound database tasks", sqle);
throw new IllegalStateException(sqle);
}
ThreadManager.getInstance().start(); ThreadManager.getInstance().start();
initializeTimelyTasks(); // aggregated method for timely tasks thanks to lxconan initializeTimelyTasks(); // aggregated method for timely tasks thanks to lxconan
long timeToTake = System.currentTimeMillis();
SkillFactory.loadAllSkills();
final double skillLoadTime = (System.currentTimeMillis() - timeToTake) / 1000.0;
log.info("Skills loaded in {} seconds", skillLoadTime);
timeToTake = System.currentTimeMillis();
CashItemFactory.getSpecialCashItems();
final double itemLoadTime = (System.currentTimeMillis() - timeToTake) / 1000.0;
log.info("Items loaded in {} seconds", itemLoadTime);
timeToTake = System.currentTimeMillis();
MapleQuest.loadAllQuest();
final double questLoadTime = (System.currentTimeMillis() - timeToTake) / 1000.0;
log.info("Quest loaded in {} seconds", questLoadTime);
NewYearCardRecord.startPendingNewYearCardRequests();
if (YamlConfig.config.server.USE_THREAD_TRACKER) { if (YamlConfig.config.server.USE_THREAD_TRACKER) {
ThreadTracker.getInstance().registerThreadTrackerTask(); ThreadTracker.getInstance().registerThreadTrackerTask();
} }
@@ -890,7 +889,6 @@ public class Server {
} }
initWorldPlayerRanking(); initWorldPlayerRanking();
MaplePlayerNPCFactory.loadFactoryMetadata();
loadPlayerNpcMapStepFromDb(); loadPlayerNpcMapStepFromDb();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace();//For those who get errors e.printStackTrace();//For those who get errors
@@ -898,11 +896,14 @@ public class Server {
System.exit(0); System.exit(0);
} }
if (YamlConfig.config.server.USE_FAMILY_SYSTEM) { // Wait on all async tasks to complete
timeToTake = System.currentTimeMillis(); for (Future<?> future : futures) {
MapleFamily.loadAllFamilies(); try {
final double familyLoadTime = (System.currentTimeMillis() - timeToTake) / 1000.0; future.get();
log.info("Families loaded in {} seconds", familyLoadTime); } catch (Exception e) {
log.error("Failed to run all startup-bound loading tasks", e);
throw new IllegalStateException(e);
}
} }
IoBuffer.setUseDirectBuffer(false); // join IO operations performed by lxconan IoBuffer.setUseDirectBuffer(false); // join IO operations performed by lxconan
@@ -919,10 +920,10 @@ public class Server {
log.info("Listening on port 8484"); log.info("Listening on port 8484");
log.info("Cosmic is now online.");
online = true; online = true;
Duration initDuration = Duration.between(beforeInit, Instant.now());
log.info("Cosmic is now online after {} ms.", initDuration.toMillis());
MapleSkillbookInformationProvider.getInstance();
OpcodeConstants.generateOpcodeNames(); OpcodeConstants.generateOpcodeNames();
CommandsExecutor.getInstance(); CommandsExecutor.getInstance();
@@ -931,6 +932,18 @@ public class Server {
} }
} }
private static void setAllLoggedOut(Connection con) throws SQLException {
try (PreparedStatement ps = con.prepareStatement("UPDATE accounts SET loggedin = 0")) {
ps.executeUpdate();
}
}
private static void setAllMerchantsInactive(Connection con) throws SQLException {
try (PreparedStatement ps = con.prepareStatement("UPDATE characters SET HasMerchant = 0")) {
ps.executeUpdate();
}
}
private void initializeTimelyTasks() { private void initializeTimelyTasks() {
TimerManager tMan = TimerManager.getInstance(); TimerManager tMan = TimerManager.getInstance();
tMan.start(); tMan.start();
@@ -1570,39 +1583,42 @@ public class Server {
} }
} }
private static void applyAllNameChanges() { private static void applyAllNameChanges(Connection con) throws SQLException {
try (Connection con = DatabaseConnection.getConnection(); try (PreparedStatement ps = con.prepareStatement("SELECT * FROM namechanges WHERE completionTime IS NULL");
PreparedStatement ps = con.prepareStatement("SELECT * FROM namechanges WHERE completionTime IS NULL");
ResultSet rs = ps.executeQuery()) { ResultSet rs = ps.executeQuery()) {
List<Pair<String, String>> changedNames = new LinkedList<>(); //logging only List<Pair<String, String>> changedNames = new LinkedList<>(); //logging only
while (rs.next()) {
con.setAutoCommit(false); con.setAutoCommit(false);
int nameChangeId = rs.getInt("id"); try {
int characterId = rs.getInt("characterId"); while (rs.next()) {
String oldName = rs.getString("old"); int nameChangeId = rs.getInt("id");
String newName = rs.getString("new"); int characterId = rs.getInt("characterId");
boolean success = MapleCharacter.doNameChange(con, characterId, oldName, newName, nameChangeId); String oldName = rs.getString("old");
if (!success) { String newName = rs.getString("new");
con.rollback(); //discard changes boolean success = MapleCharacter.doNameChange(con, characterId, oldName, newName, nameChangeId);
} else { if (!success) {
changedNames.add(new Pair<>(oldName, newName)); con.rollback(); //discard changes
} else {
con.commit();
changedNames.add(new Pair<>(oldName, newName));
}
} }
} finally {
con.setAutoCommit(true); con.setAutoCommit(true);
} }
//log //log
for (Pair<String, String> namePair : changedNames) { for (Pair<String, String> namePair : changedNames) {
FilePrinter.print(FilePrinter.CHANGE_CHARACTER_NAME, "Name change applied : from \"" + namePair.getLeft() + "\" to \"" + namePair.getRight() + "\" at " + Calendar.getInstance().getTime().toString()); log.info("Name change applied - from: \"{}\" to \"{}\" at {}", namePair.getLeft(), namePair.getRight(), Instant.now());
} }
} catch (SQLException e) { } catch (SQLException e) {
e.printStackTrace(); log.warn("Failed to retrieve list of pending name changes", e);
FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e, "Failed to retrieve list of pending name changes."); throw e;
} }
} }
private static void applyAllWorldTransfers() { private static void applyAllWorldTransfers(Connection con) throws SQLException {
try (Connection con = DatabaseConnection.getConnection(); try (PreparedStatement ps = con.prepareStatement("SELECT * FROM worldtransfers WHERE completionTime IS NULL",
PreparedStatement ps = con.prepareStatement("SELECT * FROM worldtransfers WHERE completionTime IS NULL", ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
ResultSet rs = ps.executeQuery()) { ResultSet rs = ps.executeQuery()) {
List<Integer> removedTransfers = new LinkedList<>(); List<Integer> removedTransfers = new LinkedList<>();
while (rs.next()) { while (rs.next()) {
@@ -1625,33 +1641,39 @@ public class Server {
} }
rs.beforeFirst(); rs.beforeFirst();
List<Pair<Integer, Pair<Integer, Integer>>> worldTransfers = new LinkedList<>(); //logging only <charid, <oldWorld, newWorld>> List<Pair<Integer, Pair<Integer, Integer>>> worldTransfers = new LinkedList<>(); //logging only <charid, <oldWorld, newWorld>>
while (rs.next()) {
con.setAutoCommit(false); con.setAutoCommit(false);
int nameChangeId = rs.getInt("id"); try {
if (removedTransfers.contains(nameChangeId)) { while (rs.next()) {
continue; int nameChangeId = rs.getInt("id");
} if (removedTransfers.contains(nameChangeId)) {
int characterId = rs.getInt("characterId"); continue;
int oldWorld = rs.getInt("from"); }
int newWorld = rs.getInt("to"); int characterId = rs.getInt("characterId");
boolean success = MapleCharacter.doWorldTransfer(con, characterId, oldWorld, newWorld, nameChangeId); int oldWorld = rs.getInt("from");
if (!success) { int newWorld = rs.getInt("to");
con.rollback(); boolean success = MapleCharacter.doWorldTransfer(con, characterId, oldWorld, newWorld, nameChangeId);
} else { if (!success) {
worldTransfers.add(new Pair<>(characterId, new Pair<>(oldWorld, newWorld))); con.rollback();
} else {
con.commit();
worldTransfers.add(new Pair<>(characterId, new Pair<>(oldWorld, newWorld)));
}
} }
} finally {
con.setAutoCommit(true); con.setAutoCommit(true);
} }
//log //log
for (Pair<Integer, Pair<Integer, Integer>> worldTransferPair : worldTransfers) { for (Pair<Integer, Pair<Integer, Integer>> worldTransferPair : worldTransfers) {
int charId = worldTransferPair.getLeft(); int charId = worldTransferPair.getLeft();
int oldWorld = worldTransferPair.getRight().getLeft(); int oldWorld = worldTransferPair.getRight().getLeft();
int newWorld = worldTransferPair.getRight().getRight(); int newWorld = worldTransferPair.getRight().getRight();
FilePrinter.print(FilePrinter.WORLD_TRANSFER, "World transfer applied : Character ID " + charId + " from World " + oldWorld + " to World " + newWorld + " at " + Calendar.getInstance().getTime().toString()); log.info("World transfer applied - character id {} from world {} to world {} at {}", charId, oldWorld, newWorld, Instant.now());
} }
} catch (SQLException e) { } catch (SQLException e) {
e.printStackTrace(); log.warn("Failed to retrieve list of pending world transfers", e);
FilePrinter.printError(FilePrinter.WORLD_TRANSFER, e, "Failed to retrieve list of pending world transfers."); throw e;
} }
} }
+11 -3
View File
@@ -19,8 +19,12 @@
*/ */
package net.server.task; package net.server.task;
import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import net.server.Server; import net.server.Server;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tools.DatabaseConnection;
import tools.FilePrinter; import tools.FilePrinter;
/** /**
@@ -28,13 +32,17 @@ import tools.FilePrinter;
* @info Thread responsible for maintaining coupons EXP & DROP effects active * @info Thread responsible for maintaining coupons EXP & DROP effects active
*/ */
public class CouponTask implements Runnable { public class CouponTask implements Runnable {
private static final Logger log = LoggerFactory.getLogger(CouponTask.class);
@Override @Override
public void run() { public void run() {
try { try {
Server.getInstance().updateActiveCoupons(); try (Connection con = DatabaseConnection.getConnection()) {
Server.getInstance().updateActiveCoupons(con);
}
Server.getInstance().commitActiveCoupons(); Server.getInstance().commitActiveCoupons();
} catch(SQLException sqle) { } catch (SQLException sqle) {
FilePrinter.printError(FilePrinter.EXCEPTION_CAUGHT, "Unexpected SQL error: " + sqle.getMessage()); log.error("Error updating coupon effects", sqle);
} }
} }
} }
@@ -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 "";
+24 -19
View File
@@ -40,7 +40,6 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.*; import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
/* /*
@@ -144,15 +143,16 @@ public class CashShop {
} }
public static class CashItemFactory { public static class CashItemFactory {
private static volatile Map<Integer, CashItem> items = new HashMap<>();
private static volatile List<Integer> randomitemsns = new ArrayList<>();
private static volatile Map<Integer, List<Integer>> packages = new HashMap<>();
private static volatile List<SpecialCashItem> specialcashitems = new ArrayList<>();
private static final Map<Integer, CashItem> items = new HashMap<>(); public static void loadAllCashItems() {
private static final Map<Integer, List<Integer>> packages = new HashMap<>();
private static final List<SpecialCashItem> specialcashitems = new ArrayList<>();
private static final List<Integer> randomitemsns = new ArrayList<>();
static {
MapleDataProvider etc = MapleDataProviderFactory.getDataProvider(new File("wz/Etc.wz")); MapleDataProvider etc = MapleDataProviderFactory.getDataProvider(new File("wz/Etc.wz"));
Map<Integer, CashItem> loadedItems = new HashMap<>();
List<Integer> onSaleItems = new ArrayList<>();
for (MapleData item : etc.getData("Commodity.img").getChildren()) { for (MapleData item : etc.getData("Commodity.img").getChildren()) {
int sn = MapleDataTool.getIntConvert("SN", item); int sn = MapleDataTool.getIntConvert("SN", item);
int itemId = MapleDataTool.getIntConvert("ItemId", item); int itemId = MapleDataTool.getIntConvert("ItemId", item);
@@ -160,9 +160,16 @@ public class CashShop {
long period = MapleDataTool.getIntConvert("Period", item, 1); long period = MapleDataTool.getIntConvert("Period", item, 1);
short count = (short) MapleDataTool.getIntConvert("Count", item, 1); short count = (short) MapleDataTool.getIntConvert("Count", item, 1);
boolean onSale = MapleDataTool.getIntConvert("OnSale", item, 0) == 1; boolean onSale = MapleDataTool.getIntConvert("OnSale", item, 0) == 1;
items.put(sn, new CashItem(sn, itemId, price, period, count, onSale)); loadedItems.put(sn, new CashItem(sn, itemId, price, period, count, onSale));
}
if (onSale) {
onSaleItems.add(sn);
}
}
CashItemFactory.items = loadedItems;
CashItemFactory.randomitemsns = onSaleItems;
Map<Integer, List<Integer>> loadedPackages = new HashMap<>();
for (MapleData cashPackage : etc.getData("CashPackage.img").getChildren()) { for (MapleData cashPackage : etc.getData("CashPackage.img").getChildren()) {
List<Integer> cPackage = new ArrayList<>(); List<Integer> cPackage = new ArrayList<>();
@@ -170,24 +177,21 @@ public class CashShop {
cPackage.add(Integer.parseInt(item.getData().toString())); cPackage.add(Integer.parseInt(item.getData().toString()));
} }
packages.put(Integer.parseInt(cashPackage.getName()), cPackage); loadedPackages.put(Integer.parseInt(cashPackage.getName()), cPackage);
}
for (Entry<Integer, CashItem> e : items.entrySet()) {
if (e.getValue().isOnSale()) {
randomitemsns.add(e.getKey());
}
} }
CashItemFactory.packages = loadedPackages;
List<SpecialCashItem> loadedSpecialItems = new ArrayList<>();
try (Connection con = DatabaseConnection.getConnection(); try (Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM specialcashitems"); PreparedStatement ps = con.prepareStatement("SELECT * FROM specialcashitems");
ResultSet rs = ps.executeQuery()) { ResultSet rs = ps.executeQuery()) {
while (rs.next()) { while (rs.next()) {
specialcashitems.add(new SpecialCashItem(rs.getInt("sn"), rs.getInt("modifier"), rs.getByte("info"))); loadedSpecialItems.add(new SpecialCashItem(rs.getInt("sn"), rs.getInt("modifier"), rs.getByte("info")));
} }
} catch (SQLException ex) { } catch (SQLException ex) {
ex.printStackTrace(); ex.printStackTrace();
} }
CashItemFactory.specialcashitems = loadedSpecialItems;
} }
public static CashItem getRandomCashItem() { public static CashItem getRandomCashItem() {
@@ -222,16 +226,17 @@ public class CashShop {
} }
public static void reloadSpecialCashItems() {//Yay? public static void reloadSpecialCashItems() {//Yay?
specialcashitems.clear(); List<SpecialCashItem> loadedSpecialItems = new ArrayList<>();
try (Connection con = DatabaseConnection.getConnection(); try (Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM specialcashitems"); PreparedStatement ps = con.prepareStatement("SELECT * FROM specialcashitems");
ResultSet rs = ps.executeQuery()) { ResultSet rs = ps.executeQuery()) {
while (rs.next()) { while (rs.next()) {
specialcashitems.add(new SpecialCashItem(rs.getInt("sn"), rs.getInt("modifier"), rs.getByte("info"))); loadedSpecialItems.add(new SpecialCashItem(rs.getInt("sn"), rs.getInt("modifier"), rs.getByte("info")));
} }
} catch (SQLException ex) { } catch (SQLException ex) {
ex.printStackTrace(); ex.printStackTrace();
} }
CashItemFactory.specialcashitems = loadedSpecialItems;
} }
} }
@@ -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;
private static int skillbookMaxItemid = 2300000; // exclusively public static void loadAllSkillbookInformation() {
Map<Integer, SkillBookEntry> loadedSkillbooks = new HashMap<>();
static { loadedSkillbooks.putAll(fetchSkillbooksFromQuests());
loadSkillbooks(); loadedSkillbooks.putAll(fetchSkillbooksFromReactors());
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,40 +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);
} }
} }
} }
@@ -156,24 +156,30 @@ public class MapleSkillbookInformationProvider {
} }
} }
} }
return loadedSkillbooks;
} }
private static void fetchSkillbooksFromReactors() { 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));
}
}
return scriptSkillbooks;
} }
private static void loadSkillbooks() { public static SkillBookEntry getSkillbookAvailability(int itemId) {
fetchSkillbooksFromQuests(); SkillBookEntry sbe = foundSkillbooks.get(itemId);
fetchSkillbooksFromReactors();
fetchSkillbooksFromScripts();
}
public SkillBookEntry getSkillbookAvailability(int 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()) {
@@ -19,17 +19,18 @@
*/ */
package server.life; package server.life;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.LinkedList;
import net.server.Server; import net.server.Server;
import provider.MapleData; import provider.MapleData;
import provider.MapleDataProvider; import provider.MapleDataProvider;
import provider.MapleDataProviderFactory; import provider.MapleDataProviderFactory;
import provider.MapleDataTool; import provider.MapleDataTool;
import java.io.File;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/** /**
* *
* @author RonanLana * @author RonanLana
@@ -41,11 +42,11 @@ public class MaplePlayerNPCFactory {
private static final Map<Integer, List<MaplePlayerNPC>> dnpcMaps = new HashMap<>(); private static final Map<Integer, List<MaplePlayerNPC>> dnpcMaps = new HashMap<>();
private static Integer runningDeveloperOid = 2147483000; // 647 slots, long enough private static Integer runningDeveloperOid = 2147483000; // 647 slots, long enough
public static boolean isExistentScriptid(int scriptid) { public synchronized static boolean isExistentScriptid(int scriptid) {
return npcData.getData(scriptid + ".img") != null; return npcData.getData(scriptid + ".img") != null;
} }
public static void loadDeveloperRoomMetadata(MapleDataProvider npc) { private static void loadDeveloperRoomMetadata(MapleDataProvider npc) {
MapleData thisData = npc.getData("9977777.img"); MapleData thisData = npc.getData("9977777.img");
if(thisData != null) { if(thisData != null) {
MapleDataProvider map = MapleDataProviderFactory.getDataProvider(new File("wz/Map.wz")); MapleDataProvider map = MapleDataProviderFactory.getDataProvider(new File("wz/Map.wz"));
@@ -65,7 +66,7 @@ public class MaplePlayerNPCFactory {
} }
} }
public static void loadFactoryMetadata() { public synchronized static void loadFactoryMetadata() {
MapleDataProvider npc = npcData; MapleDataProvider npc = npcData;
loadDeveloperRoomMetadata(npc); loadDeveloperRoomMetadata(npc);
@@ -134,7 +135,7 @@ public class MaplePlayerNPCFactory {
} }
} }
public static List<MaplePlayerNPC> getDeveloperNpcsFromMapid(int mapid) { public synchronized static List<MaplePlayerNPC> getDeveloperNpcsFromMapid(int mapid) {
return dnpcMaps.get(mapid); return dnpcMaps.get(mapid);
} }
} }
+34 -40
View File
@@ -21,20 +21,10 @@
*/ */
package server.quest; package server.quest;
import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import client.MapleCharacter; import client.MapleCharacter;
import client.MapleQuestStatus; import client.MapleQuestStatus;
import client.MapleQuestStatus.Status; import client.MapleQuestStatus.Status;
import config.YamlConfig; import config.YamlConfig;
import java.util.Collection;
import java.util.EnumMap;
import java.util.Map.Entry;
import java.util.Set;
import provider.MapleData; import provider.MapleData;
import provider.MapleDataProvider; import provider.MapleDataProvider;
import provider.MapleDataProviderFactory; import provider.MapleDataProviderFactory;
@@ -44,6 +34,10 @@ import server.quest.requirements.*;
import tools.MaplePacketCreator; import tools.MaplePacketCreator;
import tools.StringUtil; import tools.StringUtil;
import java.io.File;
import java.util.*;
import java.util.Map.Entry;
/** /**
* *
* @author Matze * @author Matze
@@ -51,8 +45,8 @@ import tools.StringUtil;
*/ */
public class MapleQuest { public class MapleQuest {
private static Map<Integer, MapleQuest> quests = new HashMap<>(); private static volatile Map<Integer, MapleQuest> quests = new HashMap<>();
private static Map<Integer, Integer> infoNumberQuests = new HashMap<>(); private static volatile Map<Integer, Integer> infoNumberQuests = new HashMap<>();
private static Map<Short, Integer> medals = new HashMap<>(); private static Map<Short, Integer> medals = new HashMap<>();
private static final Set<Short> exploitableQuests = new HashSet<>(); private static final Set<Short> exploitableQuests = new HashSet<>();
@@ -459,9 +453,7 @@ public class MapleQuest {
} }
public static void clearCache(int quest) { public static void clearCache(int quest) {
if(quests.containsKey(quest)){ quests.remove(quest);
quests.remove(quest);
}
} }
public static void clearCache() { public static void clearCache() {
@@ -656,29 +648,31 @@ public class MapleQuest {
return ret; return ret;
} }
public static void loadAllQuest() { public static void loadAllQuests() {
try { final Map<Integer, MapleQuest> loadedQuests = new HashMap<>();
for(MapleData quest : questInfo.getChildren()) { final Map<Integer, Integer> loadedInfoNumberQuests = new HashMap<>();
int questID = Integer.parseInt(quest.getName());
for (MapleData quest : questInfo.getChildren()) {
MapleQuest q = new MapleQuest(questID); int questID = Integer.parseInt(quest.getName());
quests.put(questID, q);
MapleQuest q = new MapleQuest(questID);
int infoNumber; loadedQuests.put(questID, q);
infoNumber = q.getInfoNumber(Status.STARTED); int infoNumber;
if (infoNumber > 0) {
infoNumberQuests.put(infoNumber, questID); infoNumber = q.getInfoNumber(Status.STARTED);
} if (infoNumber > 0) {
loadedInfoNumberQuests.put(infoNumber, questID);
infoNumber = q.getInfoNumber(Status.COMPLETED); }
if (infoNumber > 0) {
infoNumberQuests.put(infoNumber, questID); infoNumber = q.getInfoNumber(Status.COMPLETED);
} if (infoNumber > 0) {
} loadedInfoNumberQuests.put(infoNumber, questID);
} catch (Exception ex) { }
ex.printStackTrace(); }
}
} MapleQuest.quests = loadedQuests;
MapleQuest.infoNumberQuests = loadedInfoNumberQuests;
}
} }