Merge branch 'master' into feat/postgresql-database
# Conflicts: # config.yaml # src/main/java/config/ServerConfig.java
This commit is contained in:
93
src/main/java/server/ExpLogger.java
Normal file
93
src/main/java/server/ExpLogger.java
Normal file
@@ -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<ExpLogRecord> 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<ExpLogRecord> 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.scheduleWithFixedDelay(saveExpLoggerToDBRunnable, EXP_LOGGER_THREAD_SLEEP_DURATION_SECONDS, 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -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<Byte, List<Integer>> 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<Short, Integer> 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<Short, Integer> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Integer, List<PlayerNPC>> 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<Short, Integer> 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<PlayerNPC> 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<Short, Integer> 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<PlayerNPC> 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<PlayerNPC> getDeveloperNpcsFromMapid(int mapid) {
|
||||
return dnpcMaps.get(mapid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,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;
|
||||
@@ -251,13 +254,6 @@ public class MapFactory {
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
List<PlayerNPC> dnpcs = PlayerNPCFactory.getDeveloperNpcsFromMapid(mapid);
|
||||
if (dnpcs != null) {
|
||||
for (PlayerNPC dnpc : dnpcs) {
|
||||
map.addPlayerNPCMapObject(dnpc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadLifeFromWz(map, mapData);
|
||||
|
||||
Reference in New Issue
Block a user