From e320bafa8b792480a2bbba9043982cb55facf60f Mon Sep 17 00:00:00 2001 From: leevccc <94272011+leevccc@users.noreply.github.com> Date: Fri, 8 Sep 2023 04:59:56 +0800 Subject: [PATCH 1/8] fix: error flag after use karma setFalg() function is designed to take arguments of type short. Forcing the short type flag to be converted to the byte type causes some errors here. For example, the equipment merge system will make the UNTRADEABLE flag become 520, combined with the Scissors of Karma 16, and finally become 536, this value is beyond the byte range, and the mandatory variable type will cause the flag error, the specific problem I encountered is that the MERGE_UNTRADEABLE flag is lost after using the Scissors of Karma. --- .../java/client/inventory/manipulator/KarmaManipulator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/client/inventory/manipulator/KarmaManipulator.java b/src/main/java/client/inventory/manipulator/KarmaManipulator.java index 68cc77b5c5..660738ae25 100644 --- a/src/main/java/client/inventory/manipulator/KarmaManipulator.java +++ b/src/main/java/client/inventory/manipulator/KarmaManipulator.java @@ -43,7 +43,7 @@ public class KarmaManipulator { flag ^= karmaFlag; flag |= ItemConstants.UNTRADEABLE; - item.setFlag((byte) flag); + item.setFlag(flag); } } @@ -53,6 +53,6 @@ public class KarmaManipulator { flag |= karmaFlag; flag &= (0xFFFFFFFF ^ ItemConstants.UNTRADEABLE); - item.setFlag((byte) flag); + item.setFlag(flag); } } From 60a44252ea7c2f8ce89844a57c3b90a590b09452 Mon Sep 17 00:00:00 2001 From: spiderpig60 Date: Sun, 10 Sep 2023 01:53:53 +0300 Subject: [PATCH 2/8] added logs for character exp gain --- config.yaml | 1 + database/sql/1-db_database.sql | 11 +++++ src/main/java/client/Character.java | 60 ++++++++++++++++++++++++++ src/main/java/config/ServerConfig.java | 1 + 4 files changed, 73 insertions(+) diff --git a/config.yaml b/config.yaml index c34d9848f0..48d43f3aa8 100644 --- a/config.yaml +++ b/config.yaml @@ -234,6 +234,7 @@ server: USE_STARTING_AP_4: false #Use early-GMS 4/4/4/4 starting stats. To overcome AP shortage, this gives 4AP/5AP at 1st/2nd job advancements. USE_AUTOBAN: false #Commands the server to detect infractors automatically. USE_AUTOBAN_LOG: true #Log autoban related messages. Still logs even with USE_AUTOBAN disabled. + USE_EXP_GAIN_LOG: true #Logs characters exp gains; logs world rate & coupon exp, total gained exp, and current exp, level can be calculated from "ExpTable". USE_AUTOSAVE: true #Enables server autosaving feature (saves characters to DB each 1 hour). USE_SERVER_AUTOASSIGNER: false #HeavenMS-builtin autoassigner, uses algorithm based on distributing AP accordingly with required secondary stat on equipments. USE_REFRESH_RANK_MOVE: true diff --git a/database/sql/1-db_database.sql b/database/sql/1-db_database.sql index c24fc76912..639ca90b80 100644 --- a/database/sql/1-db_database.sql +++ b/database/sql/1-db_database.sql @@ -21484,6 +21484,17 @@ CREATE TABLE IF NOT EXISTS `worldtransfers` ( INDEX (characterid) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; +CREATE TABLE IF NOT EXISTS `characterexplogs` ( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `world_exp_rate` INT, + `exp_coupon` INT, + `gained_exp` BIGINT, + `current_exp` INT, + `exp_gain_time` DATETIME, + `charid` int(11) NOT NULL, + PRIMARY KEY (`id`), + FOREIGN KEY (`charid`) REFERENCES `characters`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; ALTER TABLE `dueyitems` ADD CONSTRAINT `dueyitems_ibfk_1` FOREIGN KEY (`PackageId`) REFERENCES `dueypackages` (`PackageId`) ON DELETE CASCADE; diff --git a/src/main/java/client/Character.java b/src/main/java/client/Character.java index 289abd8396..53ea8df391 100644 --- a/src/main/java/client/Character.java +++ b/src/main/java/client/Character.java @@ -150,6 +150,8 @@ public class Character extends AbstractCharacterObject { private final AtomicInteger gachaexp = new AtomicInteger(); private final AtomicInteger meso = new AtomicInteger(); private final AtomicInteger chair = new AtomicInteger(-1); + private long totalExpGained = 0; + private static final LinkedList> expGainLogQueue = new LinkedList<>(); private int merchantmeso; private BuddyList buddylist; private EventInstanceManager eventInstance = null; @@ -323,6 +325,45 @@ public class Character extends AbstractCharacterObject { setPosition(new Point(0, 0)); } + static { + if (YamlConfig.config.server.USE_EXP_GAIN_LOG) { + SaveExpLogToDBThread expThread = new SaveExpLogToDBThread(); + expThread.setPriority(Thread.MIN_PRIORITY); // Set to the lowest priority + expThread.start(); + } + } + + // Nested static class for saving exp logs to the database + private static class SaveExpLogToDBThread extends Thread { + @Override + public void run() { + while(true) { + try { + if (expGainLogQueue.isEmpty()) { + synchronized (expGainLogQueue) { + expGainLogQueue.wait(); + } + } + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("INSERT INTO characterexplogs (world_exp_rate, exp_coupon, gained_exp, current_exp, exp_gain_time, charid) VALUES (?, ?, ?, ?, ?, ?)")) { + while (!expGainLogQueue.isEmpty()) { + Map psMap = expGainLogQueue.poll(); + ps.setInt(1, (int) psMap.get("world_exp_rate")); + ps.setInt(2, (int) psMap.get("exp_coupon")); + ps.setLong(3,(long) psMap.get("gained_exp")); + ps.setInt(4, (int) psMap.get("current_exp")); + ps.setTimestamp(5, (Timestamp) psMap.get("exp_gain_time")); + ps.setInt(6, (int) psMap.get("charid")); + ps.executeUpdate(); + } + } + } catch (SQLException | InterruptedException e) { + e.printStackTrace(); + } + } + } + } + private static Job getJobStyleInternal(int jobid, byte opt) { int jobtype = jobid / 100; @@ -3092,6 +3133,7 @@ public class Character extends AbstractCharacterObject { leftover = nextExp - Integer.MAX_VALUE; } updateSingleStat(Stat.EXP, exp.addAndGet((int) total)); + totalExpGained += total; if (show) { announceExpGain(gain, equip, party, inChat, white); } @@ -3107,11 +3149,29 @@ public class Character extends AbstractCharacterObject { if (leftover > 0) { gainExpInternal(leftover, equip, party, false, inChat, white); } else { + saveExpLogToDB(); lastExpGainTime = System.currentTimeMillis(); } } } + private void saveExpLogToDB() { + if (YamlConfig.config.server.USE_EXP_GAIN_LOG) { + Map psMap = new HashMap<>(); + psMap.put("world_exp_rate", getWorldServer().getExpRate()); + psMap.put("exp_coupon", expCoupon); + psMap.put("gained_exp", totalExpGained); + psMap.put("current_exp", exp.get()); + psMap.put("charid", id); + psMap.put("exp_gain_time", new Timestamp(System.currentTimeMillis())); + expGainLogQueue.add(psMap); + synchronized (expGainLogQueue){ + expGainLogQueue.notifyAll(); + } + totalExpGained = 0; + } + } + private Pair applyFame(int delta) { petLock.lock(); try { diff --git a/src/main/java/config/ServerConfig.java b/src/main/java/config/ServerConfig.java index 499ac738ef..d7050da6a6 100644 --- a/src/main/java/config/ServerConfig.java +++ b/src/main/java/config/ServerConfig.java @@ -82,6 +82,7 @@ public class ServerConfig { public boolean USE_STARTING_AP_4; public boolean USE_AUTOBAN; public boolean USE_AUTOBAN_LOG; + public boolean USE_EXP_GAIN_LOG; public boolean USE_AUTOSAVE; public boolean USE_SERVER_AUTOASSIGNER; public boolean USE_REFRESH_RANK_MOVE; From 90b44c3a8bb1d13f32cd21e7d4682371685845cf Mon Sep 17 00:00:00 2001 From: spiderpig60 Date: Sun, 10 Sep 2023 01:53:53 +0300 Subject: [PATCH 3/8] added timed thread, added batch, cleaned code changed starting value to false --- config.yaml | 1 + database/sql/1-db_database.sql | 11 +++ src/main/java/client/Character.java | 19 +++++- src/main/java/config/ServerConfig.java | 1 + src/main/java/server/ExpLogger.java | 93 ++++++++++++++++++++++++++ 5 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 src/main/java/server/ExpLogger.java diff --git a/config.yaml b/config.yaml index c34d9848f0..0f350fd551 100644 --- a/config.yaml +++ b/config.yaml @@ -234,6 +234,7 @@ server: USE_STARTING_AP_4: false #Use early-GMS 4/4/4/4 starting stats. To overcome AP shortage, this gives 4AP/5AP at 1st/2nd job advancements. USE_AUTOBAN: false #Commands the server to detect infractors automatically. USE_AUTOBAN_LOG: true #Log autoban related messages. Still logs even with USE_AUTOBAN disabled. + USE_EXP_GAIN_LOG: false #Logs characters exp gains; logs world rate & coupon exp, total gained exp, and current exp, level can be calculated from "ExpTable". USE_AUTOSAVE: true #Enables server autosaving feature (saves characters to DB each 1 hour). USE_SERVER_AUTOASSIGNER: false #HeavenMS-builtin autoassigner, uses algorithm based on distributing AP accordingly with required secondary stat on equipments. USE_REFRESH_RANK_MOVE: true diff --git a/database/sql/1-db_database.sql b/database/sql/1-db_database.sql index c24fc76912..639ca90b80 100644 --- a/database/sql/1-db_database.sql +++ b/database/sql/1-db_database.sql @@ -21484,6 +21484,17 @@ CREATE TABLE IF NOT EXISTS `worldtransfers` ( INDEX (characterid) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; +CREATE TABLE IF NOT EXISTS `characterexplogs` ( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `world_exp_rate` INT, + `exp_coupon` INT, + `gained_exp` BIGINT, + `current_exp` INT, + `exp_gain_time` DATETIME, + `charid` int(11) NOT NULL, + PRIMARY KEY (`id`), + FOREIGN KEY (`charid`) REFERENCES `characters`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; ALTER TABLE `dueyitems` ADD CONSTRAINT `dueyitems_ibfk_1` FOREIGN KEY (`PackageId`) REFERENCES `dueypackages` (`PackageId`) ON DELETE CASCADE; diff --git a/src/main/java/client/Character.java b/src/main/java/client/Character.java index 289abd8396..6fa96bae61 100644 --- a/src/main/java/client/Character.java +++ b/src/main/java/client/Character.java @@ -59,6 +59,7 @@ import scripting.AbstractPlayerInteraction; import scripting.event.EventInstanceManager; import scripting.item.ItemScriptManager; import server.*; +import server.ExpLogger.ExpLogRecord; import server.ItemInformationProvider.ScriptedItem; import server.events.Events; import server.events.RescueGaga; @@ -150,6 +151,7 @@ public class Character extends AbstractCharacterObject { private final AtomicInteger gachaexp = new AtomicInteger(); private final AtomicInteger meso = new AtomicInteger(); private final AtomicInteger chair = new AtomicInteger(-1); + private long totalExpGained = 0; private int merchantmeso; private BuddyList buddylist; private EventInstanceManager eventInstance = null; @@ -234,7 +236,7 @@ public class Character extends AbstractCharacterObject { private final List viptrockmaps = new ArrayList<>(); private Map events = new LinkedHashMap<>(); private PartyQuest partyQuest = null; - private final List> npcUpdateQuests = new LinkedList<>(); +private final List> npcUpdateQuests = new LinkedList<>(); private Dragon dragon = null; private Ring marriageRing; private int marriageItemid = -1; @@ -3092,6 +3094,7 @@ public class Character extends AbstractCharacterObject { leftover = nextExp - Integer.MAX_VALUE; } updateSingleStat(Stat.EXP, exp.addAndGet((int) total)); + totalExpGained += total; if (show) { announceExpGain(gain, equip, party, inChat, white); } @@ -3108,6 +3111,20 @@ public class Character extends AbstractCharacterObject { gainExpInternal(leftover, equip, party, false, inChat, white); } else { lastExpGainTime = System.currentTimeMillis(); + + if (YamlConfig.config.server.USE_EXP_GAIN_LOG) { + ExpLogRecord expLogRecord = new ExpLogger.ExpLogRecord( + getWorldServer().getExpRate(), + expCoupon, + totalExpGained, + exp.get(), + new Timestamp(lastExpGainTime), + id + ); + ExpLogger.putExpLogRecord(expLogRecord); + } + + totalExpGained = 0; } } } diff --git a/src/main/java/config/ServerConfig.java b/src/main/java/config/ServerConfig.java index 499ac738ef..d7050da6a6 100644 --- a/src/main/java/config/ServerConfig.java +++ b/src/main/java/config/ServerConfig.java @@ -82,6 +82,7 @@ public class ServerConfig { public boolean USE_STARTING_AP_4; public boolean USE_AUTOBAN; public boolean USE_AUTOBAN_LOG; + public boolean USE_EXP_GAIN_LOG; public boolean USE_AUTOSAVE; public boolean USE_SERVER_AUTOASSIGNER; public boolean USE_REFRESH_RANK_MOVE; diff --git a/src/main/java/server/ExpLogger.java b/src/main/java/server/ExpLogger.java new file mode 100644 index 0000000000..ef5591bf77 --- /dev/null +++ b/src/main/java/server/ExpLogger.java @@ -0,0 +1,93 @@ +package server; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadFactory; +import java.sql.Timestamp; +import static java.util.concurrent.TimeUnit.*; + +import config.YamlConfig; +import tools.DatabaseConnection; + +public class ExpLogger { + private static final LinkedBlockingQueue expLoggerQueue = new LinkedBlockingQueue<>(); + private static final short EXP_LOGGER_THREAD_SLEEP_DURATION_SECONDS = 60; + private static final short EXP_LOGGER_THREAD_SHUTDOWN_WAIT_DURATION_MINUTES = 5; + + public record ExpLogRecord(int worldExpRate, int expCoupon, long gainedExp, int currentExp,Timestamp expGainTime, int charid) {} + + public static void putExpLogRecord(ExpLogRecord expLogRecord) { + try { + expLoggerQueue.put(expLogRecord); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + static private ScheduledExecutorService schdExctr = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setPriority(Thread.MIN_PRIORITY); + return t; + } + }); + + private static Runnable saveExpLoggerToDBRunnable = new Runnable() { + @Override + public void run() { + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("INSERT INTO characterexplogs (world_exp_rate, exp_coupon, gained_exp, current_exp, exp_gain_time, charid) VALUES (?, ?, ?, ?, ?, ?)")) { + + List drainedExpLogs = new ArrayList<>(); + expLoggerQueue.drainTo(drainedExpLogs); + for (ExpLogRecord expLogRecord : drainedExpLogs) { + ps.setInt(1, expLogRecord.worldExpRate); + ps.setInt(2, expLogRecord.expCoupon); + ps.setLong(3, expLogRecord.gainedExp); + ps.setInt(4, expLogRecord.currentExp); + ps.setTimestamp(5, expLogRecord.expGainTime); + ps.setInt(6, expLogRecord.charid); + ps.addBatch(); + } + ps.executeBatch(); + } catch (SQLException sqle) { + sqle.printStackTrace(); + } + } + }; + + + private static void startExpLogger() { + schdExctr.schedule(saveExpLoggerToDBRunnable, EXP_LOGGER_THREAD_SLEEP_DURATION_SECONDS, SECONDS); + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + stopExpLogger(); + })); + } + + private static boolean stopExpLogger() { + schdExctr.shutdown(); + try { + schdExctr.awaitTermination(EXP_LOGGER_THREAD_SHUTDOWN_WAIT_DURATION_MINUTES, MINUTES); + Thread runThreadBeforeShutdown = new Thread(saveExpLoggerToDBRunnable); + runThreadBeforeShutdown.setPriority(Thread.MIN_PRIORITY); + runThreadBeforeShutdown.start(); + return true; + } catch (InterruptedException e) { + e.printStackTrace(); + return false; + } + } + + static { + if (YamlConfig.config.server.USE_EXP_GAIN_LOG) { + startExpLogger(); + } + } +} \ No newline at end of file From c145a536880675bfce5d53c2761bcae73fd545ad Mon Sep 17 00:00:00 2001 From: ormizj <81110074+ormizj@users.noreply.github.com> Date: Fri, 15 Sep 2023 01:04:22 +0300 Subject: [PATCH 4/8] fixed wrong method call --- src/main/java/server/ExpLogger.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/server/ExpLogger.java b/src/main/java/server/ExpLogger.java index ef5591bf77..c9016bdf69 100644 --- a/src/main/java/server/ExpLogger.java +++ b/src/main/java/server/ExpLogger.java @@ -65,7 +65,7 @@ public class ExpLogger { private static void startExpLogger() { - schdExctr.schedule(saveExpLoggerToDBRunnable, EXP_LOGGER_THREAD_SLEEP_DURATION_SECONDS, SECONDS); + schdExctr.scheduleWithFixedDelay(saveExpLoggerToDBRunnable, EXP_LOGGER_THREAD_SLEEP_DURATION_SECONDS, EXP_LOGGER_THREAD_SLEEP_DURATION_SECONDS, SECONDS); Runtime.getRuntime().addShutdownHook(new Thread(() -> { stopExpLogger(); })); @@ -90,4 +90,4 @@ public class ExpLogger { startExpLogger(); } } -} \ No newline at end of file +} From a49c1703ae1f43b72a3093ffee8a769402d80e5d Mon Sep 17 00:00:00 2001 From: P0nk Date: Mon, 18 Sep 2023 23:26:37 +0200 Subject: [PATCH 5/8] Remove developer player npc --- .../java/server/life/PlayerNPCFactory.java | 107 ------------------ src/main/java/server/maps/MapFactory.java | 12 +- 2 files changed, 4 insertions(+), 115 deletions(-) diff --git a/src/main/java/server/life/PlayerNPCFactory.java b/src/main/java/server/life/PlayerNPCFactory.java index a0a8de7f59..bacf994a1b 100644 --- a/src/main/java/server/life/PlayerNPCFactory.java +++ b/src/main/java/server/life/PlayerNPCFactory.java @@ -19,124 +19,17 @@ */ package server.life; -import constants.id.ItemId; -import constants.id.MapId; -import constants.id.NpcId; -import net.server.Server; -import provider.Data; import provider.DataProvider; import provider.DataProviderFactory; -import provider.DataTool; import provider.wz.WZFiles; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - /** * @author RonanLana */ public class PlayerNPCFactory { private static final DataProvider npcData = DataProviderFactory.getDataProvider(WZFiles.NPC); - private static final Map> dnpcMaps = new HashMap<>(); - private static Integer runningDeveloperOid = 2147483000; // 647 slots, long enough - public synchronized static boolean isExistentScriptid(int scriptid) { return npcData.getData(scriptid + ".img") != null; } - - private static void loadDeveloperRoomMetadata(DataProvider npc) { - Data thisData = npc.getData(NpcId.CUSTOM_DEV + ".img"); - if (thisData != null) { - DataProvider map = DataProviderFactory.getDataProvider(WZFiles.MAP); - - thisData = map.getData("Map/Map7/" + MapId.DEVELOPERS_HQ + ".img"); - if (thisData != null) { - DataProvider sound = DataProviderFactory.getDataProvider(WZFiles.SOUND); - - thisData = sound.getData("Field.img"); - if (thisData != null) { - Data md = thisData.getChildByPath("anthem/brazil"); - if (md != null) { - Server.getInstance().setAvailableDeveloperRoom(); - } - } - } - } - } - - public synchronized static void loadFactoryMetadata() { - DataProvider npc = npcData; - loadDeveloperRoomMetadata(npc); - - DataProvider etc = DataProviderFactory.getDataProvider(WZFiles.ETC); - Data dnpcData = etc.getData("DeveloperNpc.img"); - if (dnpcData != null) { - for (Data data : dnpcData.getChildren()) { - int scriptId = Integer.parseInt(data.getName()); - - String name = DataTool.getString("name", data, ""); - int face = DataTool.getIntConvert("face", data, 20000); - int hair = DataTool.getIntConvert("hair", data, 30000); - int gender = DataTool.getIntConvert("gender", data, 0); - byte skin = (byte) DataTool.getIntConvert("skin", data, 0); - int dir = DataTool.getIntConvert("dir", data, 0); - int mapid = DataTool.getIntConvert("map", data, 0); - int FH = DataTool.getIntConvert("fh", data, 0); - int RX0 = DataTool.getIntConvert("rx0", data, 0); - int RX1 = DataTool.getIntConvert("rx1", data, 0); - int CX = DataTool.getIntConvert("cx", data, 0); - int CY = DataTool.getIntConvert("cy", data, 0); - - Map equips = new HashMap<>(); - for (Data edata : data.getChildByPath("equips").getChildren()) { - short equippos = (short) DataTool.getIntConvert("pos", edata); - int equipid = DataTool.getIntConvert("itemid", edata); - - equips.put(equippos, equipid); - } - - List dnpcSet = dnpcMaps.get(mapid); - if (dnpcSet == null) { - dnpcSet = new LinkedList<>(); - dnpcMaps.put(mapid, dnpcSet); - } - - dnpcSet.add(new PlayerNPC(name, scriptId, face, hair, gender, skin, equips, dir, FH, RX0, RX1, CX, CY, runningDeveloperOid)); - runningDeveloperOid++; - } - } else { - Data thisData = npc.getData(NpcId.CUSTOM_DEV + ".img"); - - if (thisData != null) { - byte[] encData = {0x52, 0x6F, 0x6E, 0x61, 0x6E}; - String name = new String(encData); - int face = 20104, hair = 30215, gender = 0, skin = 0, dir = 0, mapid = MapId.DEVELOPERS_HQ; - int FH = 4, RX0 = -143, RX1 = -243, CX = -193, CY = 117, scriptId = NpcId.CUSTOM_DEV; - - Map equips = new HashMap<>(); - equips.put((short) -1, ItemId.GREEN_HEADBAND); - equips.put((short) -11, ItemId.TIMELESS_NIBLEHEIM); - equips.put((short) -8, ItemId.BLUE_KORBEN); - equips.put((short) -6, ItemId.MITHRIL_PLATINE_PANTS); - equips.put((short) -7, ItemId.BLUE_CARZEN_BOOTS); - equips.put((short) -5, ItemId.MITHRIL_PLATINE); - - List dnpcSet = dnpcMaps.get(mapid); - if (dnpcSet == null) { - dnpcSet = new LinkedList<>(); - dnpcMaps.put(mapid, dnpcSet); - } - - dnpcSet.add(new PlayerNPC(name, scriptId, face, hair, gender, (byte) skin, equips, dir, FH, RX0, RX1, CX, CY, runningDeveloperOid)); - runningDeveloperOid++; - } - } - } - - public synchronized static List getDeveloperNpcsFromMapid(int mapid) { - return dnpcMaps.get(mapid); - } } diff --git a/src/main/java/server/maps/MapFactory.java b/src/main/java/server/maps/MapFactory.java index 1c7a85cd0c..a1c3f14c21 100644 --- a/src/main/java/server/maps/MapFactory.java +++ b/src/main/java/server/maps/MapFactory.java @@ -28,7 +28,10 @@ import provider.DataProviderFactory; import provider.DataTool; import provider.wz.WZFiles; import scripting.event.EventInstanceManager; -import server.life.*; +import server.life.AbstractLoadedLife; +import server.life.LifeFactory; +import server.life.Monster; +import server.life.PlayerNPC; import server.partyquest.GuardianSpawnPoint; import tools.DatabaseConnection; import tools.StringUtil; @@ -249,13 +252,6 @@ public class MapFactory { } catch (SQLException e) { e.printStackTrace(); } - - List dnpcs = PlayerNPCFactory.getDeveloperNpcsFromMapid(mapid); - if (dnpcs != null) { - for (PlayerNPC dnpc : dnpcs) { - map.addPlayerNPCMapObject(dnpc); - } - } } loadLifeFromWz(map, mapData); From 0aee9d7c3f360bb48a6d399462b4f1ed3ca1ffb4 Mon Sep 17 00:00:00 2001 From: P0nk Date: Mon, 18 Sep 2023 23:45:56 +0200 Subject: [PATCH 6/8] Fix unable to create playernpc The initialization of the running world counters depended on worlds already having been initialized, so I made that dependency more explicit. --- src/main/java/net/server/Server.java | 15 ++++++------ src/main/java/server/life/PlayerNPC.java | 31 +++++++++--------------- 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/src/main/java/net/server/Server.java b/src/main/java/net/server/Server.java index 5b854bebcd..679d136643 100644 --- a/src/main/java/net/server/Server.java +++ b/src/main/java/net/server/Server.java @@ -57,7 +57,7 @@ import server.SkillbookInformationProvider; import server.ThreadManager; import server.TimerManager; import server.expeditions.ExpeditionBossLog; -import server.life.PlayerNPCFactory; +import server.life.PlayerNPC; import server.quest.Quest; import service.NoteService; import tools.DatabaseConnection; @@ -849,15 +849,15 @@ public class Server { final ExecutorService initExecutor = Executors.newFixedThreadPool(10); // 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(() -> Quest.loadAllQuests())); - futures.add(initExecutor.submit(() -> SkillbookInformationProvider.loadAllSkillbookInformation())); - futures.add(initExecutor.submit(() -> PlayerNPCFactory.loadFactoryMetadata())); + futures.add(initExecutor.submit(SkillFactory::loadAllSkills)); + futures.add(initExecutor.submit(CashItemFactory::loadAllCashItems)); + futures.add(initExecutor.submit(Quest::loadAllQuests)); + futures.add(initExecutor.submit(SkillbookInformationProvider::loadAllSkillbookInformation)); initExecutor.shutdown(); TimeZone.setDefault(TimeZone.getTimeZone(YamlConfig.config.server.TIMEZONE)); + final int worldCount = Math.min(GameConstants.WORLD_NAMES.length, YamlConfig.config.server.WORLDS); try (Connection con = DatabaseConnection.getConnection()) { setAllLoggedOut(con); setAllMerchantsInactive(con); @@ -868,6 +868,7 @@ public class Server { CashIdGenerator.loadExistentCashIdsFromDb(con); applyAllNameChanges(con); // -- name changes can be missed by INSTANT_NAME_CHANGE -- applyAllWorldTransfers(con); + PlayerNPC.loadRunningRankData(con, worldCount); } catch (SQLException sqle) { log.error("Failed to run all startup-bound database tasks", sqle); throw new IllegalStateException(sqle); @@ -877,8 +878,6 @@ public class Server { initializeTimelyTasks(channelDependencies); // aggregated method for timely tasks thanks to lxconan try { - int worldCount = Math.min(GameConstants.WORLD_NAMES.length, YamlConfig.config.server.WORLDS); - for (int i = 0; i < worldCount; i++) { initWorld(); } diff --git a/src/main/java/server/life/PlayerNPC.java b/src/main/java/server/life/PlayerNPC.java index c0487d0276..f5a592016d 100644 --- a/src/main/java/server/life/PlayerNPC.java +++ b/src/main/java/server/life/PlayerNPC.java @@ -53,6 +53,8 @@ import java.util.concurrent.atomic.AtomicInteger; * @author XoticStory * @author Ronan */ +// TODO: remove dependency on custom Npc.wz. All NPCs with id 9901910 and above are custom additions for player npcs. +// In summary: NPCs 9901910-9906599 and 9977777 are custom additions to HeavenMS that should be removed. public class PlayerNPC extends AbstractMapObject { private static final Logger log = LoggerFactory.getLogger(PlayerNPC.class); private static final Map> availablePlayerNpcScriptIds = new HashMap<>(); @@ -67,10 +69,6 @@ public class PlayerNPC extends AbstractMapObject { private int dir, FH, RX0, RX1, CY; private int worldRank, overallRank, worldJobRank, overallJobRank; - static { - getRunningMetadata(); - } - public PlayerNPC(String name, int scriptId, int face, int hair, int gender, byte skin, Map equips, int dir, int FH, int RX0, int RX1, int CX, int CY, int oid) { this.equips = equips; this.scriptId = scriptId; @@ -128,6 +126,12 @@ public class PlayerNPC extends AbstractMapObject { } } + public static void loadRunningRankData(Connection con, int worlds) throws SQLException { + getRunningOverallRanks(con); + getRunningWorldRanks(con, worlds); + getRunningWorldJobRanks(con); + } + public Map getEquips() { return equips; } @@ -213,16 +217,6 @@ public class PlayerNPC extends AbstractMapObject { client.sendPacket(PacketCreator.removePlayerNPC(this.getObjectId())); } - private static void getRunningMetadata() { - try (Connection con = DatabaseConnection.getConnection()) { - getRunningOverallRanks(con); - getRunningWorldRanks(con); - getRunningWorldJobRanks(con); - } catch (SQLException e) { - e.printStackTrace(); - } - } - private static void getRunningOverallRanks(Connection con) throws SQLException { try (PreparedStatement ps = con.prepareStatement("SELECT max(overallrank) FROM playernpcs"); ResultSet rs = ps.executeQuery()) { @@ -235,9 +229,8 @@ public class PlayerNPC extends AbstractMapObject { } } - private static void getRunningWorldRanks(Connection con) throws SQLException { - int numWorlds = Server.getInstance().getWorldsSize(); - for (int i = 0; i < numWorlds; i++) { + private static void getRunningWorldRanks(Connection con, int worlds) throws SQLException { + for (int i = 0; i < worlds; i++) { runningWorldRank.add(new AtomicInteger(1)); } @@ -246,7 +239,7 @@ public class PlayerNPC extends AbstractMapObject { while (rs.next()) { int wid = rs.getInt(1); - if (wid < numWorlds) { + if (wid < worlds) { runningWorldRank.get(wid).set(rs.getInt(2) + 1); } } @@ -649,4 +642,4 @@ public class PlayerNPC extends AbstractMapObject { e.printStackTrace(); } } -} \ No newline at end of file +} From c3404d296a6d948b05feaff20652add05b1cae7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=B0=8F=E5=AE=9D?= <18984316437@189.cn> Date: Fri, 13 Oct 2023 21:16:40 +0800 Subject: [PATCH 7/8] Fix MTS sql syntax error and month error --- .../server/channel/handlers/MTSHandler.java | 44 ++++--------------- src/main/java/server/MTSItemInfo.java | 11 +++-- 2 files changed, 17 insertions(+), 38 deletions(-) diff --git a/src/main/java/net/server/channel/handlers/MTSHandler.java b/src/main/java/net/server/channel/handlers/MTSHandler.java index ca00cc842f..abffbe9ad1 100644 --- a/src/main/java/net/server/channel/handlers/MTSHandler.java +++ b/src/main/java/net/server/channel/handlers/MTSHandler.java @@ -45,6 +45,8 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Calendar; import java.util.List; @@ -122,40 +124,12 @@ public final class MTSHandler extends AbstractPacketHandler { return; } } - Calendar calendar = Calendar.getInstance(); - int year; - int month; - int day; - int oldmax = calendar.getActualMaximum(Calendar.DAY_OF_MONTH); - int oldday = calendar.get(Calendar.DAY_OF_MONTH) + 7; - if (oldmax < oldday) { - if (calendar.get(Calendar.MONTH) + 2 > 12) { - year = calendar.get(Calendar.YEAR) + 1; - month = 1; - calendar.set(year, month, 1); - day = oldday - oldmax; - } else { - month = calendar.get(Calendar.MONTH) + 2; - year = calendar.get(Calendar.YEAR); - calendar.set(year, month, 1); - day = oldday - oldmax; - } - } else { - day = calendar.get(Calendar.DAY_OF_MONTH) + 7; - month = calendar.get(Calendar.MONTH); - year = calendar.get(Calendar.YEAR); - } - String date = year + "-"; - if (month < 10) { - date += "0" + month + "-"; - } else { - date += month + "-"; - } - if (day < 10) { - date += "0" + day; - } else { - date += day + ""; - } + + LocalDate now = LocalDate.now(); + LocalDate sellEnd = now.plusDays(7); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + String date = sellEnd.format(formatter); + if (!i.getInventoryType().equals(InventoryType.EQUIP)) { Item item = i; try (PreparedStatement pse = con.prepareStatement("INSERT INTO mts_items (tab, type, itemid, quantity, expiration, giftFrom, seller, price, owner, sellername, sell_ends) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")) { @@ -761,7 +735,7 @@ public final class MTSHandler extends AbstractPacketHandler { } } } - try (PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM mts_items WHERE tab = ? " + (type != 0 ? "AND type = ?" : "") + "AND transfer = 0")) { + try (PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM mts_items WHERE tab = ? " + (type != 0 ? "AND type = ?" : "") + " AND transfer = 0")) { ps.setInt(1, tab); if (type != 0) { ps.setInt(2, type); diff --git a/src/main/java/server/MTSItemInfo.java b/src/main/java/server/MTSItemInfo.java index 3779d69925..c091861167 100644 --- a/src/main/java/server/MTSItemInfo.java +++ b/src/main/java/server/MTSItemInfo.java @@ -23,6 +23,8 @@ package server; import client.inventory.Item; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.util.Calendar; /** @@ -38,13 +40,16 @@ public class MTSItemInfo { private int day = 1; public MTSItemInfo(Item item, int price, int id, int cid, String seller, String date) { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + LocalDate sellEnd = LocalDate.parse(date, formatter); + this.item = item; this.price = price; this.seller = seller; this.id = id; - this.year = Integer.parseInt(date.substring(0, 4)); - this.month = Integer.parseInt(date.substring(5, 7)); - this.day = Integer.parseInt(date.substring(8, 10)); + this.year = sellEnd.getYear(); + this.month = sellEnd.getMonthValue(); + this.day = sellEnd.getDayOfMonth(); } public Item getItem() { From d6147d51916292f728b6deff907dae2f457d4876 Mon Sep 17 00:00:00 2001 From: remsus Date: Wed, 8 Nov 2023 11:26:55 +0100 Subject: [PATCH 8/8] Fix preparation of invList (CASH was skipped) in prepareInventoryItemList (AbstractPlayerInteraction) --- src/main/java/scripting/AbstractPlayerInteraction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/scripting/AbstractPlayerInteraction.java b/src/main/java/scripting/AbstractPlayerInteraction.java index f388e70f81..cbeb18476a 100644 --- a/src/main/java/scripting/AbstractPlayerInteraction.java +++ b/src/main/java/scripting/AbstractPlayerInteraction.java @@ -291,7 +291,7 @@ public class AbstractPlayerInteraction { int size = Math.min(itemids.size(), quantity.size()); List>> invList = new ArrayList<>(6); - for (int i = InventoryType.UNDEFINED.getType(); i < InventoryType.CASH.getType(); i++) { + for (int i = InventoryType.UNDEFINED.getType(); i <= InventoryType.CASH.getType(); i++) { invList.add(new LinkedList<>()); }