Dynamic World/Channel deployment + Channel scheduler update
Added "8-slot SETUP expand" item on the CashShop. Solved a concurrency issue on the fameGainByQuest method. Refactored many resource freeing modules throughout the source code. Implemented dynamic deployment of worlds and channels on the server system. Only creation of channels and worlds are available on this feature. Added a dedicated worker for schedules requested on EventManager. Fixed a potential cause for deadlocks on the channel schedulers' system. Refactored many schedules used by the EventManager and Channel, futher improving overall scheduling performance on the server.
This commit is contained in:
@@ -2198,12 +2198,15 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
|
||||
public void disableDoorSpawn() {
|
||||
canDoor = false;
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
|
||||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
canDoor = true;
|
||||
}
|
||||
}, 5000);
|
||||
};
|
||||
|
||||
client.getChannelServer().registerOverallAction(mapid, r, 5000);
|
||||
}
|
||||
|
||||
public void disbandGuild() {
|
||||
@@ -8195,13 +8198,17 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
}
|
||||
|
||||
private void fameGainByQuest() {
|
||||
int delta = quest_fame / ServerConstants.FAME_GAIN_BY_QUEST;
|
||||
if(delta > 0) {
|
||||
gainFame(delta);
|
||||
client.announce(MaplePacketCreator.getShowFameGain(delta));
|
||||
synchronized (quests) {
|
||||
quest_fame += 1;
|
||||
|
||||
int delta = quest_fame / ServerConstants.FAME_GAIN_BY_QUEST;
|
||||
if(delta > 0) {
|
||||
gainFame(delta);
|
||||
client.announce(MaplePacketCreator.getShowFameGain(delta));
|
||||
}
|
||||
|
||||
quest_fame %= ServerConstants.FAME_GAIN_BY_QUEST;
|
||||
}
|
||||
|
||||
quest_fame %= ServerConstants.FAME_GAIN_BY_QUEST;
|
||||
}
|
||||
|
||||
public void updateQuest(MapleQuestStatus quest) {
|
||||
@@ -8218,7 +8225,6 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
MapleQuest mquest = quest.getQuest();
|
||||
short questid = mquest.getId();
|
||||
if(!mquest.isSameDayRepeatable() && !MapleQuest.isExploitableQuest(questid)) {
|
||||
quest_fame += 1;
|
||||
if(ServerConstants.FAME_GAIN_BY_QUEST > 0)
|
||||
fameGainByQuest();
|
||||
}
|
||||
@@ -8388,8 +8394,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setObjectId(int id) {
|
||||
}
|
||||
public void setObjectId(int id) {}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
@@ -2784,7 +2784,7 @@ public class Commands {
|
||||
|
||||
Server server = Server.getInstance();
|
||||
byte worldb = Byte.parseByte(sub[1]);
|
||||
if (worldb <= (server.getWorlds().size() - 1)) {
|
||||
if (worldb <= (server.getWorldsSize() - 1)) {
|
||||
try {
|
||||
String[] socket = server.getIP(worldb, c.getChannel()).split(":");
|
||||
c.getWorldServer().removePlayer(player);
|
||||
@@ -2799,7 +2799,7 @@ public class Commands {
|
||||
}
|
||||
|
||||
} else {
|
||||
player.message("Invalid world; highest number available: " + (server.getWorlds().size() - 1));
|
||||
player.message("Invalid world; highest number available: " + (server.getWorldsSize() - 1));
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -2855,6 +2855,82 @@ public class Commands {
|
||||
}
|
||||
break;
|
||||
|
||||
case "addchannel":
|
||||
if (sub.length < 2) {
|
||||
player.dropMessage(5, "Syntax: @addchannel <worldid>");
|
||||
break;
|
||||
}
|
||||
|
||||
int worldid = Integer.parseInt(sub[1]);
|
||||
|
||||
int chid = Server.getInstance().addChannel(worldid);
|
||||
if(chid >= 0) {
|
||||
player.dropMessage(5, "NEW Channel " + chid + " successfully deployed on world " + worldid + ".");
|
||||
} else {
|
||||
if(chid == -3) {
|
||||
player.dropMessage(5, "Invalid worldid detected. Channel creation aborted.");
|
||||
} else if(chid == -2) {
|
||||
player.dropMessage(5, "Reached channel limit on worldid " + worldid + ". Channel creation aborted.");
|
||||
} else if(chid == -1) {
|
||||
player.dropMessage(5, "Error detected when loading the 'world.ini' file. Channel creation aborted.");
|
||||
} else {
|
||||
player.dropMessage(5, "NEW Channel failed to be deployed. Check if the needed port is already in use or other limitations are taking place.");
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "addworld":
|
||||
int wid = Server.getInstance().addWorld();
|
||||
|
||||
if(wid >= 0) {
|
||||
player.dropMessage(5, "NEW World " + wid + " successfully deployed.");
|
||||
} else {
|
||||
if(wid == -2) {
|
||||
player.dropMessage(5, "Error detected when loading the 'world.ini' file. World creation aborted.");
|
||||
} else {
|
||||
player.dropMessage(5, "NEW World failed to be deployed. Check if needed ports are already in use or maximum world count has been reached.");
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
/*
|
||||
case "removechannel":
|
||||
if (sub.length < 2) {
|
||||
player.dropMessage(5, "Syntax: @removechannel <worldid>");
|
||||
break;
|
||||
}
|
||||
|
||||
int worldId = Integer.parseInt(sub[1]);
|
||||
if(Server.getInstance().removeChannel(worldId)) {
|
||||
player.dropMessage(5, "Successfully removed a channel on World " + worldId + ". Current channel count: " + Server.getInstance().getWorld(worldId).getChannelsSize() + ".");
|
||||
} else {
|
||||
player.dropMessage(5, "Failed to remove last Channel on world " + worldId + ". Check if either that world exists or there are people currently playing there.");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "removeworld":
|
||||
int rwid = Server.getInstance().getWorldsSize() - 1;
|
||||
if(rwid <= 0) {
|
||||
player.dropMessage(5, "Unable to remove world 0.");
|
||||
break;
|
||||
}
|
||||
|
||||
if(Server.getInstance().removeWorld()) {
|
||||
player.dropMessage(5, "Successfully removed a world. Current world count: " + Server.getInstance().getWorldsSize() + ".");
|
||||
} else {
|
||||
if(rwid < 0) {
|
||||
player.dropMessage(5, "No registered worlds to remove.");
|
||||
} else {
|
||||
player.dropMessage(5, "Failed to remove world " + rwid + ". Check if there are people currently playing there.");
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
*/
|
||||
|
||||
case "shutdown":
|
||||
case "shutdownnow":
|
||||
int time = 60000;
|
||||
|
||||
@@ -33,7 +33,7 @@ public class MapleInventoryProof extends MapleInventory {
|
||||
|
||||
public void cloneContents(MapleInventory inv) {
|
||||
inv.lockInventory();
|
||||
lock.lock();
|
||||
lock.lock();
|
||||
try {
|
||||
inventory.clear();
|
||||
this.setSlotLimit(inv.getSlotLimit());
|
||||
|
||||
@@ -17,6 +17,8 @@ public class ServerConstants {
|
||||
public static short VERSION = 83;
|
||||
|
||||
//Login Configuration
|
||||
public static final int WLDLIST_SIZE = 21; //Max possible worlds on the server.
|
||||
public static final int CHANNEL_SIZE = 20; //Max possible channels per world (which is 20, based on the channel list on login phase).
|
||||
public static final int CHANNEL_LOAD = 100; //Max players per channel (limit actually used to calculate the World server capacity).
|
||||
|
||||
public static final long RESPAWN_INTERVAL = 10 * 1000; //10 seconds, 10000.
|
||||
@@ -120,7 +122,7 @@ public class ServerConstants {
|
||||
public static final int ITEM_EXPIRE_TIME = 3 * 60 * 1000; //Time before items start disappearing. Recommended to be set up to 3 minutes.
|
||||
public static final int KITE_EXPIRE_TIME = 60 * 60 * 1000; //Time before kites (cash item) disappears.
|
||||
public static final int ITEM_MONITOR_TIME = 5 * 60 * 1000; //Interval between item monitoring tasks on maps, which checks for dangling (null) item objects on the map item history.
|
||||
public static final int LOCK_MONITOR_TIME = 30 * 1000; //Waiting time for a lock to be released. If it reaches timeout, a critical server deadlock has made present.
|
||||
public static final int LOCK_MONITOR_TIME = 3 * 60 * 1000; //Waiting time for a lock to be released. If it reaches timeout, a critical server deadlock has made present.
|
||||
public static final int ITEM_EXPIRE_CHECK = 10 * 1000; //Interval between item expiring tasks on maps, which checks and makes disappear expired items.
|
||||
public static final int ITEM_LIMIT_ON_MAP = 200; //Max number of items allowed on a map.
|
||||
public static final int MAP_VISITED_SIZE = 5; //Max length for last mapids visited by a player. This is used to recover and update drops on these maps accordingly with player actions.
|
||||
|
||||
@@ -31,8 +31,8 @@ import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@@ -104,17 +104,24 @@ public class Server {
|
||||
private List<Pair<Integer, String>> worldRecommendedList = new LinkedList<>();
|
||||
private final Map<Integer, MapleGuild> guilds = new HashMap<>(100);
|
||||
private final Map<MapleClient, Long> inLoginState = new HashMap<>(100);
|
||||
private final Lock srvLock = new MonitoredReentrantLock(MonitoredLockType.SERVER);
|
||||
private final Lock disLock = new MonitoredReentrantLock(MonitoredLockType.SERVER_DISEASES);
|
||||
private final ReentrantReadWriteLock lgnLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.SERVER_LOGIN, true);
|
||||
private final ReadLock lgnRLock = lgnLock.readLock();
|
||||
private final WriteLock lgnWLock = lgnLock.writeLock();
|
||||
|
||||
private final PlayerBuffStorage buffStorage = new PlayerBuffStorage();
|
||||
private final Map<Integer, MapleAlliance> alliances = new HashMap<>(100);
|
||||
private final Map<Integer, NewYearCardRecord> newyears = new HashMap<>();
|
||||
private final List<MapleClient> processDiseaseAnnouncePlayers = new LinkedList<>();
|
||||
private final List<MapleClient> registeredDiseaseAnnouncePlayers = new LinkedList<>();
|
||||
|
||||
private final Lock srvLock = new MonitoredReentrantLock(MonitoredLockType.SERVER);
|
||||
private final Lock disLock = new MonitoredReentrantLock(MonitoredLockType.SERVER_DISEASES);
|
||||
|
||||
private final ReentrantReadWriteLock wldLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.SERVER_WORLDS, true);
|
||||
private final ReadLock wldRLock = wldLock.readLock();
|
||||
private final WriteLock wldWLock = wldLock.writeLock();
|
||||
|
||||
private final ReentrantReadWriteLock lgnLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.SERVER_LOGIN, true);
|
||||
private final ReadLock lgnRLock = lgnLock.readLock();
|
||||
private final WriteLock lgnWLock = lgnLock.writeLock();
|
||||
|
||||
private final AtomicLong currentTime = new AtomicLong(0);
|
||||
private long serverCurrentTime = 0;
|
||||
|
||||
@@ -175,13 +182,17 @@ public class Server {
|
||||
|
||||
private void loadPlayerNpcMapStepFromDb() {
|
||||
try {
|
||||
List<World> wlist = this.getWorlds();
|
||||
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT * FROM playernpcs_field");
|
||||
|
||||
ResultSet rs = ps.executeQuery();
|
||||
while(rs.next()) {
|
||||
int world = rs.getInt("world"), map = rs.getInt("map"), step = rs.getInt("step"), podium = rs.getInt("podium");
|
||||
worlds.get(world).setPlayerNpcMapData(map, step, podium);
|
||||
|
||||
World w = wlist.get(world);
|
||||
if(w != null) w.setPlayerNpcMapData(map, step, podium);
|
||||
}
|
||||
|
||||
rs.close();
|
||||
@@ -192,40 +203,264 @@ public class Server {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public void removeChannel(int worldid, int channel) { //lol don't!
|
||||
channels.remove(channel);
|
||||
|
||||
World world = worlds.get(worldid);
|
||||
if (world != null) {
|
||||
world.removeChannel(channel);
|
||||
public World getWorld(int id) {
|
||||
wldRLock.lock();
|
||||
try {
|
||||
return worlds.get(id);
|
||||
} finally {
|
||||
wldRLock.unlock();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
public List<World> getWorlds() {
|
||||
wldRLock.lock();
|
||||
try {
|
||||
return Collections.unmodifiableList(worlds);
|
||||
} finally {
|
||||
wldRLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public int getWorldsSize() {
|
||||
wldRLock.lock();
|
||||
try {
|
||||
return worlds.size();
|
||||
} finally {
|
||||
wldRLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public Channel getChannel(int world, int channel) {
|
||||
return worlds.get(world).getChannel(channel);
|
||||
return this.getWorld(world).getChannel(channel);
|
||||
}
|
||||
|
||||
public List<Channel> getChannelsFromWorld(int world) {
|
||||
return worlds.get(world).getChannels();
|
||||
return this.getWorld(world).getChannels();
|
||||
}
|
||||
|
||||
|
||||
public List<Channel> getAllChannels() {
|
||||
List<Channel> channelz = new ArrayList<>();
|
||||
for (World world : worlds) {
|
||||
for (World world : this.getWorlds()) {
|
||||
for (Channel ch : world.getChannels()) {
|
||||
channelz.add(ch);
|
||||
}
|
||||
}
|
||||
return channelz;
|
||||
}
|
||||
|
||||
public String getIP(int world, int channel) {
|
||||
return channels.get(world).get(channel);
|
||||
|
||||
public Set<Integer> getOpenChannels(int world) {
|
||||
wldRLock.lock();
|
||||
try {
|
||||
return new HashSet<>(channels.get(world).keySet());
|
||||
} finally {
|
||||
wldRLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private long getTimeLeftForNextHour() {
|
||||
public String getIP(int world, int channel) {
|
||||
wldRLock.lock();
|
||||
try {
|
||||
return channels.get(world).get(channel);
|
||||
} finally {
|
||||
wldRLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void dumpData() {
|
||||
wldWLock.lock();
|
||||
try {
|
||||
System.out.println(worlds);
|
||||
System.out.println(channels);
|
||||
System.out.println(worldRecommendedList);
|
||||
System.out.println();
|
||||
System.out.println("---------------------");
|
||||
} finally {
|
||||
wldWLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public int addChannel(int worldid) {
|
||||
wldWLock.lock();
|
||||
try {
|
||||
if(worldid >= worlds.size()) return -3;
|
||||
|
||||
Map<Integer, String> worldChannels = channels.get(worldid);
|
||||
if(worldChannels == null) return -3;
|
||||
|
||||
int channelid = worldChannels.size();
|
||||
if(channelid >= ServerConstants.CHANNEL_SIZE) return -2;
|
||||
|
||||
Properties p = loadWorldINI();
|
||||
if(p == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
channelid++;
|
||||
World world = this.getWorld(worldid);
|
||||
Channel channel = new Channel(worldid, channelid, getCurrentTime());
|
||||
|
||||
channel.setServerMessage(p.getProperty("whyamirecommended" + worldid));
|
||||
|
||||
world.addChannel(channel);
|
||||
worldChannels.put(channelid, channel.getIP());
|
||||
|
||||
return channelid;
|
||||
} finally {
|
||||
wldWLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public int addWorld() {
|
||||
Properties p = loadWorldINI();
|
||||
if(p == null) return -2;
|
||||
|
||||
int newWorld = initWorld(p);
|
||||
if(newWorld > -1) {
|
||||
Set<Integer> accounts;
|
||||
|
||||
lgnRLock.lock();
|
||||
try {
|
||||
accounts = new HashSet<>(accountChars.keySet());
|
||||
} finally {
|
||||
lgnRLock.unlock();
|
||||
}
|
||||
|
||||
for(Integer accId : accounts) {
|
||||
loadAccountCharactersView(accId, 0, newWorld);
|
||||
}
|
||||
}
|
||||
|
||||
return newWorld;
|
||||
}
|
||||
|
||||
private int initWorld(Properties p) {
|
||||
wldWLock.lock();
|
||||
try {
|
||||
int i = worlds.size();
|
||||
|
||||
if(i >= ServerConstants.WLDLIST_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
System.out.println("Starting world " + i);
|
||||
World world = new World(i,
|
||||
Integer.parseInt(p.getProperty("flag" + i)),
|
||||
p.getProperty("eventmessage" + i),
|
||||
ServerConstants.EXP_RATE,
|
||||
ServerConstants.DROP_RATE,
|
||||
ServerConstants.MESO_RATE,
|
||||
ServerConstants.QUEST_RATE);
|
||||
|
||||
worldRecommendedList.add(new Pair<>(i, p.getProperty("whyamirecommended" + i)));
|
||||
worlds.add(world);
|
||||
|
||||
Map<Integer, String> channelInfo = new HashMap<>();
|
||||
long bootTime = System.currentTimeMillis();
|
||||
for (int j = 1; j <= Integer.parseInt(p.getProperty("channels" + i)); j++) {
|
||||
int channelid = j;
|
||||
Channel channel = new Channel(i, channelid, bootTime);
|
||||
|
||||
world.addChannel(channel);
|
||||
channelInfo.put(channelid, channel.getIP());
|
||||
}
|
||||
|
||||
channels.add(i, channelInfo);
|
||||
|
||||
world.setServerMessage(p.getProperty("servermessage" + i));
|
||||
System.out.println("Finished loading world " + i + "\r\n");
|
||||
|
||||
return i;
|
||||
} finally {
|
||||
wldWLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removeChannel(int worldid) { //lol don't!
|
||||
wldWLock.lock();
|
||||
try {
|
||||
if(worldid >= worlds.size()) return false;
|
||||
|
||||
World world = worlds.get(worldid);
|
||||
if (world != null) {
|
||||
int channel = world.removeChannel();
|
||||
|
||||
Map<Integer, String> m = channels.get(worldid);
|
||||
if(m != null) m.remove(channel);
|
||||
|
||||
return channel > -1;
|
||||
}
|
||||
} finally {
|
||||
wldWLock.unlock();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean removeWorld() { //lol don't!
|
||||
World w;
|
||||
int worldid;
|
||||
|
||||
wldRLock.lock();
|
||||
try {
|
||||
worldid = worlds.size() - 1;
|
||||
if(worldid < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
w = worlds.get(worldid);
|
||||
} finally {
|
||||
wldRLock.unlock();
|
||||
}
|
||||
|
||||
if(w == null || w.getPlayerStorage().getSize() > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wldWLock.lock();
|
||||
try {
|
||||
if(worldid == worlds.size() - 1) {
|
||||
w.shutdown();
|
||||
|
||||
worlds.remove(worldid);
|
||||
channels.remove(worldid);
|
||||
worldRecommendedList.remove(worldid);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} finally {
|
||||
wldWLock.unlock();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void resetServerWorlds() {
|
||||
wldWLock.lock();
|
||||
try {
|
||||
worlds.clear();
|
||||
worlds = null;
|
||||
channels.clear();
|
||||
channels = null;
|
||||
worldRecommendedList.clear();
|
||||
worldRecommendedList = null;
|
||||
} finally {
|
||||
wldWLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public static Properties loadWorldINI() {
|
||||
Properties p = new Properties();
|
||||
try {
|
||||
p.load(new FileInputStream("world.ini"));
|
||||
return p;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("[SEVERE] Could not find/open 'world.ini'.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static long getTimeLeftForNextHour() {
|
||||
Calendar nextHour = Calendar.getInstance();
|
||||
nextHour.add(Calendar.HOUR, 1);
|
||||
nextHour.set(Calendar.MINUTE, 0);
|
||||
@@ -368,12 +603,8 @@ public class Server {
|
||||
}
|
||||
|
||||
public void init() {
|
||||
Properties p = new Properties();
|
||||
try {
|
||||
p.load(new FileInputStream("world.ini"));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("[SEVERE] Could not find/open 'world.ini'.");
|
||||
Properties p = loadWorldINI();
|
||||
if(p == null) {
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
@@ -437,27 +668,7 @@ public class Server {
|
||||
Integer worldCount = Math.min(GameConstants.WORLD_NAMES.length, Integer.parseInt(p.getProperty("worlds")));
|
||||
|
||||
for (int i = 0; i < worldCount; i++) {
|
||||
System.out.println("Starting world " + i);
|
||||
World world = new World(i,
|
||||
Integer.parseInt(p.getProperty("flag" + i)),
|
||||
p.getProperty("eventmessage" + i),
|
||||
ServerConstants.EXP_RATE,
|
||||
ServerConstants.DROP_RATE,
|
||||
ServerConstants.MESO_RATE,
|
||||
ServerConstants.QUEST_RATE);
|
||||
|
||||
worldRecommendedList.add(new Pair<>(i, p.getProperty("whyamirecommended" + i)));
|
||||
worlds.add(world);
|
||||
channels.add(new HashMap<Integer, String>());
|
||||
long bootTime = System.currentTimeMillis();
|
||||
for (int j = 0; j < Integer.parseInt(p.getProperty("channels" + i)); j++) {
|
||||
int channelid = j + 1;
|
||||
Channel channel = new Channel(i, channelid, bootTime);
|
||||
world.addChannel(channel);
|
||||
channels.get(i).put(channelid, channel.getIP());
|
||||
}
|
||||
world.setServerMessage(p.getProperty("servermessage" + i));
|
||||
System.out.println("Finished loading world " + i + "\r\n");
|
||||
initWorld(p);
|
||||
}
|
||||
|
||||
MaplePlayerNPCFactory.loadFactoryMetadata();
|
||||
@@ -594,22 +805,7 @@ public class Server {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Set<Integer> getChannelServer(int world) {
|
||||
return new HashSet<>(channels.get(world).keySet());
|
||||
}
|
||||
|
||||
public byte getHighestChannelId() {
|
||||
byte highest = 0;
|
||||
for (Iterator<Integer> it = channels.get(0).keySet().iterator(); it.hasNext();) {
|
||||
Integer channel = it.next();
|
||||
if (channel != null && channel.intValue() > highest) {
|
||||
highest = channel.byteValue();
|
||||
}
|
||||
}
|
||||
return highest;
|
||||
}
|
||||
|
||||
|
||||
public int createGuild(int leaderId, String name) {
|
||||
return MapleGuild.createGuild(leaderId, name);
|
||||
}
|
||||
@@ -661,15 +857,7 @@ public class Server {
|
||||
return g;
|
||||
}
|
||||
}
|
||||
|
||||
public void clearGuilds() {//remake
|
||||
synchronized (guilds) {
|
||||
guilds.clear();
|
||||
}
|
||||
//for (List<Channel> world : worlds.values()) {
|
||||
//reloadGuildCharacters();
|
||||
}
|
||||
|
||||
|
||||
public void setGuildMemberOnline(MapleCharacter mc, boolean bOnline, int channel) {
|
||||
MapleGuild g = getGuild(mc.getGuildId(), mc.getWorld(), mc);
|
||||
g.setOnline(mc.getId(), bOnline, channel);
|
||||
@@ -854,14 +1042,6 @@ public class Server {
|
||||
return activeFly.contains(accountid);
|
||||
}
|
||||
|
||||
public World getWorld(int id) {
|
||||
return worlds.get(id);
|
||||
}
|
||||
|
||||
public List<World> getWorlds() {
|
||||
return worlds;
|
||||
}
|
||||
|
||||
public int getCharacterWorld(Integer chrid) {
|
||||
lgnRLock.lock();
|
||||
try {
|
||||
@@ -893,11 +1073,11 @@ public class Server {
|
||||
|
||||
public void updateCharacterEntry(MapleCharacter chr) {
|
||||
MapleCharacter chrView = chr.generateCharacterEntry();
|
||||
World wserv = worlds.get(chrView.getWorld());
|
||||
|
||||
lgnWLock.lock();
|
||||
try {
|
||||
wserv.registerAccountCharacterView(chrView.getAccountID(), chrView);
|
||||
World wserv = this.getWorld(chrView.getWorld());
|
||||
if(wserv != null) wserv.registerAccountCharacterView(chrView.getAccountID(), chrView);
|
||||
} finally {
|
||||
lgnWLock.unlock();
|
||||
}
|
||||
@@ -914,8 +1094,9 @@ public class Server {
|
||||
worldChars.put(chrid, world);
|
||||
|
||||
MapleCharacter chrView = chr.generateCharacterEntry();
|
||||
World wserv = worlds.get(chrView.getWorld());
|
||||
wserv.registerAccountCharacterView(chrView.getAccountID(), chrView);
|
||||
|
||||
World wserv = this.getWorld(chrView.getWorld());
|
||||
if(wserv != null) wserv.registerAccountCharacterView(chrView.getAccountID(), chrView);
|
||||
} finally {
|
||||
lgnWLock.unlock();
|
||||
}
|
||||
@@ -929,8 +1110,8 @@ public class Server {
|
||||
|
||||
Integer world = worldChars.remove(chrid);
|
||||
if(world != null) {
|
||||
World wserv = worlds.get(world);
|
||||
wserv.unregisterAccountCharacterView(accountid, chrid);
|
||||
World wserv = this.getWorld(world);
|
||||
if(wserv != null) wserv.unregisterAccountCharacterView(accountid, chrid);
|
||||
}
|
||||
} finally {
|
||||
lgnWLock.unlock();
|
||||
@@ -942,15 +1123,16 @@ public class Server {
|
||||
try {
|
||||
Integer chrid = chr.getId(), accountid = chr.getAccountID(), world = worldChars.get(chr.getId());
|
||||
if(world != null) {
|
||||
World wserv = worlds.get(world);
|
||||
wserv.unregisterAccountCharacterView(accountid, chrid);
|
||||
World wserv = this.getWorld(world);
|
||||
if(wserv != null) wserv.unregisterAccountCharacterView(accountid, chrid);
|
||||
}
|
||||
|
||||
worldChars.put(chrid, toWorld);
|
||||
|
||||
MapleCharacter chrView = chr.generateCharacterEntry();
|
||||
World wserv = worlds.get(toWorld);
|
||||
wserv.registerAccountCharacterView(chrView.getAccountID(), chrView);
|
||||
|
||||
World wserv = this.getWorld(toWorld);
|
||||
if(wserv != null) wserv.registerAccountCharacterView(chrView.getAccountID(), chrView);
|
||||
} finally {
|
||||
lgnWLock.unlock();
|
||||
}
|
||||
@@ -968,7 +1150,7 @@ public class Server {
|
||||
*/
|
||||
|
||||
public Pair<Pair<Integer, List<MapleCharacter>>, List<Pair<Integer, List<MapleCharacter>>>> loadAccountCharlist(Integer accountId) {
|
||||
List<World> wlist = worlds;
|
||||
List<World> wlist = this.getWorlds();
|
||||
List<Pair<Integer, List<MapleCharacter>>> accChars = new ArrayList<>(wlist.size() + 1);
|
||||
int chrTotal = 0;
|
||||
List<MapleCharacter> lastwchars = null;
|
||||
@@ -995,14 +1177,14 @@ public class Server {
|
||||
return new Pair<>(new Pair<>(chrTotal, lastwchars), accChars);
|
||||
}
|
||||
|
||||
private static List<List<MapleCharacter>> loadAccountCharactersViewFromDb(MapleClient c, int wlen) {
|
||||
private static List<List<MapleCharacter>> loadAccountCharactersViewFromDb(int accId, int wlen) {
|
||||
List<List<MapleCharacter>> wchars = new ArrayList<>(wlen);
|
||||
for(int i = 0; i < wlen; i++) wchars.add(i, new LinkedList<MapleCharacter>());
|
||||
|
||||
List<MapleCharacter> chars = new LinkedList<>();
|
||||
int curWorld = 0;
|
||||
try {
|
||||
List<Pair<Item, Integer>> accEquips = ItemFactory.loadEquippedItems(c.getAccID(), true, true);
|
||||
List<Pair<Item, Integer>> accEquips = ItemFactory.loadEquippedItems(accId, true, true);
|
||||
Map<Integer, List<Item>> accPlayerEquips = new HashMap<>();
|
||||
|
||||
for(Pair<Item, Integer> ae : accEquips) {
|
||||
@@ -1017,7 +1199,7 @@ public class Server {
|
||||
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
try (PreparedStatement ps = con.prepareStatement("SELECT * FROM characters WHERE accountid = ? ORDER BY world, id")) {
|
||||
ps.setInt(1, c.getAccID());
|
||||
ps.setInt(1, accId);
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
int cworld = rs.getByte("world");
|
||||
@@ -1047,7 +1229,6 @@ public class Server {
|
||||
|
||||
public void loadAccountCharacters(MapleClient c) {
|
||||
Integer accId = c.getAccID();
|
||||
int gmLevel = 0;
|
||||
boolean firstAccountLogin;
|
||||
|
||||
lgnRLock.lock();
|
||||
@@ -1069,9 +1250,14 @@ public class Server {
|
||||
lgnRLock.unlock();
|
||||
}
|
||||
|
||||
int gmLevel = 0;
|
||||
for(Integer aw : accWorlds) {
|
||||
for(MapleCharacter chr : worlds.get(aw).getAllCharactersView()) {
|
||||
if(gmLevel < chr.gmLevel()) gmLevel = chr.gmLevel();
|
||||
World wserv = this.getWorld(aw);
|
||||
|
||||
if (wserv != null) {
|
||||
for(MapleCharacter chr : wserv.getAllCharactersView()) {
|
||||
if(gmLevel < chr.gmLevel()) gmLevel = chr.gmLevel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1079,13 +1265,23 @@ public class Server {
|
||||
return;
|
||||
}
|
||||
|
||||
List<List<MapleCharacter>> accChars = loadAccountCharactersViewFromDb(c, worlds.size());
|
||||
int gmLevel = loadAccountCharactersView(c.getAccID(), 0, 0);
|
||||
c.setGMLevel(gmLevel);
|
||||
}
|
||||
|
||||
private int loadAccountCharactersView(Integer accId, int gmLevel, int fromWorldid) { // returns the maximum gmLevel found
|
||||
List<World> wlist = this.getWorlds();
|
||||
List<List<MapleCharacter>> accChars = loadAccountCharactersViewFromDb(accId, wlist.size());
|
||||
|
||||
lgnWLock.lock();
|
||||
try {
|
||||
Set<Integer> chars = new HashSet<>(5);
|
||||
for(int wid = 0; wid < worlds.size(); wid++) {
|
||||
World w = worlds.get(wid);
|
||||
Set<Integer> chars = accountChars.get(accId);
|
||||
if(chars == null) {
|
||||
chars = new HashSet<>(5);
|
||||
}
|
||||
|
||||
for(int wid = fromWorldid; wid < wlist.size(); wid++) {
|
||||
World w = wlist.get(wid);
|
||||
List<MapleCharacter> wchars = accChars.get(wid);
|
||||
w.loadAccountCharactersView(accId, wchars);
|
||||
|
||||
@@ -1103,7 +1299,7 @@ public class Server {
|
||||
lgnWLock.unlock();
|
||||
}
|
||||
|
||||
c.setGMLevel(gmLevel);
|
||||
return gmLevel;
|
||||
}
|
||||
|
||||
private static String getRemoteIp(InetSocketAddress isa) {
|
||||
@@ -1198,6 +1394,7 @@ public class Server {
|
||||
for (World w : getWorlds()) {
|
||||
w.shutdown();
|
||||
}
|
||||
|
||||
/*for (World w : getWorlds()) {
|
||||
while (w.getPlayerStorage().getAllCharacters().size() > 0) {
|
||||
try {
|
||||
@@ -1232,12 +1429,8 @@ public class Server {
|
||||
}
|
||||
}
|
||||
}
|
||||
worlds.clear();
|
||||
worlds = null;
|
||||
channels.clear();
|
||||
channels = null;
|
||||
worldRecommendedList.clear();
|
||||
worldRecommendedList = null;
|
||||
|
||||
resetServerWorlds();
|
||||
|
||||
System.out.println("Worlds + Channels are offline.");
|
||||
acceptor.unbind();
|
||||
|
||||
@@ -41,10 +41,12 @@ public enum MonitoredLockType {
|
||||
BUFF_STORAGE,
|
||||
PLAYER_STORAGE,
|
||||
SERVER,
|
||||
SERVER_LOGIN,
|
||||
SERVER_DISEASES,
|
||||
SERVER_LOGIN,
|
||||
SERVER_WORLDS,
|
||||
MERCHANT,
|
||||
CHANNEL,
|
||||
CHANNEL_EVENTS,
|
||||
CHANNEL_FACEEXPRS,
|
||||
CHANNEL_FACESCHDL,
|
||||
CHANNEL_MOBACTION,
|
||||
@@ -60,6 +62,7 @@ public enum MonitoredLockType {
|
||||
WORLD_OWL,
|
||||
WORLD_PETS,
|
||||
WORLD_CHARS,
|
||||
WORLD_CHANNELS,
|
||||
WORLD_MOUNTS,
|
||||
WORLD_PSHOPS,
|
||||
WORLD_MERCHS,
|
||||
@@ -69,6 +72,7 @@ public enum MonitoredLockType {
|
||||
EIM_SCRIPT,
|
||||
EM_LOBBY,
|
||||
EM_QUEUE,
|
||||
EM_SCHDL,
|
||||
CASHSHOP,
|
||||
VISITOR_PSHOP,
|
||||
STORAGE,
|
||||
|
||||
@@ -146,4 +146,16 @@ public class MonitoredReadLock extends ReentrantReadWriteLock.ReadLock {
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
state.lock();
|
||||
try {
|
||||
if(timeoutSchedule != null) {
|
||||
timeoutSchedule.cancel(false);
|
||||
timeoutSchedule = null;
|
||||
}
|
||||
} finally {
|
||||
state.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,4 +150,16 @@ public class MonitoredReentrantLock extends ReentrantLock {
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
state.lock();
|
||||
try {
|
||||
if(timeoutSchedule != null) {
|
||||
timeoutSchedule.cancel(false);
|
||||
timeoutSchedule = null;
|
||||
}
|
||||
} finally {
|
||||
state.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,4 +145,16 @@ public class MonitoredWriteLock extends ReentrantReadWriteLock.WriteLock {
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
state.lock();
|
||||
try {
|
||||
if(timeoutSchedule != null) {
|
||||
timeoutSchedule.cancel(false);
|
||||
timeoutSchedule = null;
|
||||
}
|
||||
} finally {
|
||||
state.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
|
||||
@@ -92,6 +91,7 @@ public final class Channel {
|
||||
private MobClearSkillScheduler mobClearSkillSchedulers[] = new MobClearSkillScheduler[4];
|
||||
private MobMistScheduler mobMistSchedulers[] = new MobMistScheduler[4];
|
||||
private FaceExpressionScheduler faceExpressionSchedulers[] = new FaceExpressionScheduler[4];
|
||||
private EventScheduler eventSchedulers[] = new EventScheduler[4];
|
||||
private OverallScheduler channelSchedulers[] = new OverallScheduler[4];
|
||||
private Map<Integer, MapleHiredMerchant> hiredMerchants = new HashMap<>();
|
||||
private final Map<Integer, Integer> storedVars = new HashMap<>();
|
||||
@@ -100,6 +100,9 @@ public final class Channel {
|
||||
private MapleEvent event;
|
||||
private boolean finishedShutdown = false;
|
||||
private int usedDojo = 0;
|
||||
|
||||
private ScheduledFuture<?> respawnTask;
|
||||
|
||||
private int[] dojoStage;
|
||||
private long[] dojoFinishTime;
|
||||
private ScheduledFuture<?>[] dojoTask;
|
||||
@@ -123,9 +126,9 @@ public final class Channel {
|
||||
private ReadLock merchRlock = merchantLock.readLock();
|
||||
private WriteLock merchWlock = merchantLock.writeLock();
|
||||
|
||||
private Lock faceLock[] = new MonitoredReentrantLock[4];
|
||||
private MonitoredReentrantLock faceLock[] = new MonitoredReentrantLock[4];
|
||||
|
||||
private Lock lock = new MonitoredReentrantLock(MonitoredLockType.CHANNEL, true);
|
||||
private MonitoredReentrantLock lock = new MonitoredReentrantLock(MonitoredLockType.CHANNEL, true);
|
||||
|
||||
public Channel(final int world, final int channel, long startTime) {
|
||||
this.world = world;
|
||||
@@ -141,7 +144,7 @@ public final class Channel {
|
||||
IoBuffer.setUseDirectBuffer(false);
|
||||
IoBuffer.setAllocator(new SimpleBufferAllocator());
|
||||
acceptor = new NioSocketAcceptor();
|
||||
TimerManager.getInstance().register(new respawnMaps(), ServerConstants.RESPAWN_INTERVAL);
|
||||
respawnTask = TimerManager.getInstance().register(new respawnMaps(), ServerConstants.RESPAWN_INTERVAL);
|
||||
acceptor.setHandler(new MapleServerHandler(world, channel));
|
||||
acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30);
|
||||
acceptor.getFilterChain().addLast("codec", (IoFilter) new ProtocolCodecFilter(new MapleCodecFactory()));
|
||||
@@ -169,6 +172,7 @@ public final class Channel {
|
||||
mobClearSkillSchedulers[i] = new MobClearSkillScheduler();
|
||||
mobMistSchedulers[i] = new MobMistScheduler();
|
||||
faceExpressionSchedulers[i] = new FaceExpressionScheduler(faceLock[i]);
|
||||
eventSchedulers[i] = new EventScheduler();
|
||||
channelSchedulers[i] = new OverallScheduler();
|
||||
}
|
||||
|
||||
@@ -191,6 +195,20 @@ public final class Channel {
|
||||
|
||||
closeAllMerchants();
|
||||
players.disconnectAll();
|
||||
|
||||
if(respawnTask != null) {
|
||||
respawnTask.cancel(false);
|
||||
respawnTask = null;
|
||||
}
|
||||
|
||||
mapFactory.dispose();
|
||||
mapFactory = null;
|
||||
|
||||
eventSM.cancel();
|
||||
eventSM = null;
|
||||
|
||||
closeChannelSchedules();
|
||||
|
||||
acceptor.unbind();
|
||||
|
||||
finishedShutdown = true;
|
||||
@@ -200,8 +218,58 @@ public final class Channel {
|
||||
System.err.println("Error while shutting down Channel " + channel + " on World " + world + "\r\n" + e);
|
||||
}
|
||||
}
|
||||
|
||||
private void closeChannelSchedules() {
|
||||
for(int i = 0; i < 20; i++) {
|
||||
if(dojoTask[i] != null) {
|
||||
dojoTask[i].cancel(false);
|
||||
dojoTask[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void closeAllMerchants() {
|
||||
for(int i = 0; i < 4; i++) {
|
||||
if(mobStatusSchedulers[i] != null) {
|
||||
mobStatusSchedulers[i].dispose();
|
||||
mobStatusSchedulers[i] = null;
|
||||
}
|
||||
|
||||
if(mobAnimationSchedulers[i] != null) {
|
||||
mobAnimationSchedulers[i].dispose();
|
||||
mobAnimationSchedulers[i] = null;
|
||||
}
|
||||
|
||||
if(mobClearSkillSchedulers[i] != null) {
|
||||
mobClearSkillSchedulers[i].dispose();
|
||||
mobClearSkillSchedulers[i] = null;
|
||||
}
|
||||
|
||||
if(mobMistSchedulers[i] != null) {
|
||||
mobMistSchedulers[i].dispose();
|
||||
mobMistSchedulers[i] = null;
|
||||
}
|
||||
|
||||
if(faceExpressionSchedulers[i] != null) {
|
||||
faceExpressionSchedulers[i].dispose();
|
||||
faceExpressionSchedulers[i] = null;
|
||||
}
|
||||
|
||||
if(eventSchedulers[i] != null) {
|
||||
eventSchedulers[i].dispose();
|
||||
eventSchedulers[i] = null;
|
||||
}
|
||||
|
||||
if(channelSchedulers[i] != null) {
|
||||
channelSchedulers[i].dispose();
|
||||
channelSchedulers[i] = null;
|
||||
}
|
||||
|
||||
faceLock[i].dispose();
|
||||
}
|
||||
|
||||
lock.dispose();
|
||||
}
|
||||
|
||||
private void closeAllMerchants() {
|
||||
merchWlock.lock();
|
||||
try {
|
||||
final Iterator<MapleHiredMerchant> hmit = hiredMerchants.values().iterator();
|
||||
@@ -442,7 +510,7 @@ public final class Channel {
|
||||
}
|
||||
}
|
||||
|
||||
private int getDojoSlot(int dojoMapId) {
|
||||
private static int getDojoSlot(int dojoMapId) {
|
||||
return (dojoMapId % 100) + ((dojoMapId / 10000 == 92502) ? 5 : 0);
|
||||
}
|
||||
|
||||
@@ -456,7 +524,7 @@ public final class Channel {
|
||||
resetDojo(dojoMapId, 0);
|
||||
}
|
||||
|
||||
public void resetDojo(int dojoMapId, int thisStg) {
|
||||
private void resetDojo(int dojoMapId, int thisStg) {
|
||||
int slot = getDojoSlot(dojoMapId);
|
||||
this.dojoStage[slot] = thisStg;
|
||||
|
||||
@@ -880,6 +948,10 @@ public final class Channel {
|
||||
mobMistSchedulers[getChannelSchedulerIndex(mapid)].registerMistCancelAction(runAction, delay);
|
||||
}
|
||||
|
||||
public void registerEventAction(int mapid, Runnable runAction, long delay) {
|
||||
eventSchedulers[getChannelSchedulerIndex(mapid)].registerDelayedAction(runAction, delay);
|
||||
}
|
||||
|
||||
public void registerOverallAction(int mapid, Runnable runAction, long delay) {
|
||||
channelSchedulers[getChannelSchedulerIndex(mapid)].registerDelayedAction(runAction, delay);
|
||||
}
|
||||
@@ -902,13 +974,16 @@ public final class Channel {
|
||||
|
||||
faceLock[lockid].lock();
|
||||
try {
|
||||
if(chr.isLoggedinWorld()) {
|
||||
faceExpressionSchedulers[lockid].registerFaceExpression(chr.getId(), cancelAction);
|
||||
map.broadcastMessage(chr, MaplePacketCreator.facialExpression(chr, emote), false);
|
||||
if(!chr.isLoggedinWorld()) {
|
||||
return;
|
||||
}
|
||||
|
||||
faceExpressionSchedulers[lockid].registerFaceExpression(chr.getId(), cancelAction);
|
||||
} finally {
|
||||
faceLock[lockid].unlock();
|
||||
}
|
||||
|
||||
map.broadcastMessage(chr, MaplePacketCreator.facialExpression(chr, emote), false);
|
||||
}
|
||||
|
||||
public void unregisterFaceExpression(int mapid, MapleCharacter chr) {
|
||||
|
||||
@@ -32,6 +32,8 @@ import tools.MaplePacketCreator;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
public final class GiveFameHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
MapleCharacter target = (MapleCharacter) c.getPlayer().getMap().getMapObject(slea.readInt());
|
||||
int mode = slea.readByte();
|
||||
|
||||
@@ -28,10 +28,12 @@ import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import server.TimerManager;
|
||||
import tools.Pair;
|
||||
|
||||
import net.server.Server;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.MonitoredReentrantLock;
|
||||
import server.TimerManager;
|
||||
import tools.Pair;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -40,11 +42,11 @@ import net.server.audit.locks.MonitoredReentrantLock;
|
||||
public abstract class BaseScheduler {
|
||||
private int idleProcs = 0;
|
||||
private List<SchedulerListener> listeners = new LinkedList<>();
|
||||
private final List<Lock> externalLocks;
|
||||
private final List<Lock> externalLocks = new LinkedList<>();
|
||||
private Map<Object, Pair<Runnable, Long>> registeredEntries = new HashMap<>();
|
||||
|
||||
private ScheduledFuture<?> schedulerTask = null;
|
||||
private Lock schedulerLock;
|
||||
private MonitoredReentrantLock schedulerLock;
|
||||
private Runnable monitorTask = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -54,13 +56,15 @@ public abstract class BaseScheduler {
|
||||
|
||||
protected BaseScheduler(MonitoredLockType lockType) {
|
||||
schedulerLock = new MonitoredReentrantLock(lockType, true);
|
||||
externalLocks = new LinkedList<>();
|
||||
}
|
||||
|
||||
// NOTE: practice EXTREME caution when adding external locks to the scheduler system, if you don't know what you're doing DON'T USE THIS.
|
||||
protected BaseScheduler(MonitoredLockType lockType, List<Lock> extLocks) {
|
||||
schedulerLock = new MonitoredReentrantLock(lockType, true);
|
||||
externalLocks = extLocks;
|
||||
|
||||
for(Lock lock : extLocks) {
|
||||
externalLocks.add(lock);
|
||||
}
|
||||
}
|
||||
|
||||
protected void addListener(SchedulerListener listener) {
|
||||
@@ -112,13 +116,13 @@ public abstract class BaseScheduler {
|
||||
unlockScheduler();
|
||||
}
|
||||
|
||||
long timeNow = System.currentTimeMillis();
|
||||
long timeNow = Server.getInstance().getCurrentTime();
|
||||
toRemove = new LinkedList<>();
|
||||
for(Entry<Object, Pair<Runnable, Long>> rmd : registeredEntriesCopy.entrySet()) {
|
||||
Pair<Runnable, Long> r = rmd.getValue();
|
||||
|
||||
if(r.getRight() < timeNow) {
|
||||
r.getLeft().run(); // runs the cancel action
|
||||
r.getLeft().run(); // runs the scheduled action
|
||||
toRemove.add(rmd.getKey());
|
||||
}
|
||||
}
|
||||
@@ -145,21 +149,29 @@ public abstract class BaseScheduler {
|
||||
schedulerTask = TimerManager.getInstance().register(monitorTask, ServerConstants.MOB_STATUS_MONITOR_PROC, ServerConstants.MOB_STATUS_MONITOR_PROC);
|
||||
}
|
||||
|
||||
registeredEntries.put(key, new Pair<>(removalAction, System.currentTimeMillis() + duration));
|
||||
registeredEntries.put(key, new Pair<>(removalAction, Server.getInstance().getCurrentTime() + duration));
|
||||
} finally {
|
||||
unlockScheduler();
|
||||
}
|
||||
}
|
||||
|
||||
protected void interruptEntry(Object key) {
|
||||
Runnable toRun = null;
|
||||
|
||||
lockScheduler();
|
||||
try {
|
||||
Pair<Runnable, Long> rm = registeredEntries.remove(key);
|
||||
if(rm != null) rm.getLeft().run();
|
||||
if(rm != null) {
|
||||
toRun = rm.getLeft();
|
||||
}
|
||||
} finally {
|
||||
unlockScheduler();
|
||||
}
|
||||
|
||||
if(toRun != null) {
|
||||
toRun.run();
|
||||
}
|
||||
|
||||
dispatchRemovedEntries(Collections.singletonList(key), false);
|
||||
}
|
||||
|
||||
@@ -168,4 +180,22 @@ public abstract class BaseScheduler {
|
||||
listener.removedScheduledEntries(toRemove, fromUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
lockScheduler();
|
||||
try {
|
||||
if(schedulerTask != null) {
|
||||
schedulerTask.cancel(false);
|
||||
schedulerTask = null;
|
||||
}
|
||||
|
||||
listeners.clear();
|
||||
externalLocks.clear();
|
||||
registeredEntries.clear();
|
||||
} finally {
|
||||
unlockScheduler();
|
||||
}
|
||||
|
||||
schedulerLock.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
36
src/net/server/channel/worker/EventScheduler.java
Normal file
36
src/net/server/channel/worker/EventScheduler.java
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft 2016 - 2018 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.server.channel.worker;
|
||||
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ronan
|
||||
*/
|
||||
public class EventScheduler extends BaseScheduler {
|
||||
public EventScheduler() {
|
||||
super(MonitoredLockType.CHANNEL_EVENTS);
|
||||
}
|
||||
|
||||
public void registerDelayedAction(Runnable runAction, long delay) {
|
||||
registerEntry(runAction, runAction, delay);
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,6 @@ import net.server.audit.locks.MonitoredLockType;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import net.server.audit.locks.MonitoredReentrantLock;
|
||||
|
||||
/**
|
||||
@@ -33,7 +32,7 @@ import net.server.audit.locks.MonitoredReentrantLock;
|
||||
*/
|
||||
public class MobAnimationScheduler extends BaseScheduler {
|
||||
Set<Integer> onAnimationMobs = new HashSet<>(1000);
|
||||
private Lock animationLock = new MonitoredReentrantLock(MonitoredLockType.CHANNEL_MOBANIMAT, true);
|
||||
private MonitoredReentrantLock animationLock = new MonitoredReentrantLock(MonitoredLockType.CHANNEL_MOBANIMAT, true);
|
||||
|
||||
private static Runnable r = new Runnable() {
|
||||
@Override
|
||||
@@ -73,4 +72,10 @@ public class MobAnimationScheduler extends BaseScheduler {
|
||||
animationLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
animationLock.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ import java.util.HashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.MonitoredReentrantLock;
|
||||
|
||||
@@ -35,7 +34,7 @@ import net.server.audit.locks.MonitoredReentrantLock;
|
||||
*/
|
||||
public class MobStatusScheduler extends BaseScheduler {
|
||||
private Map<MonsterStatusEffect, MobStatusOvertimeEntry> registeredMobStatusOvertime = new HashMap<>();
|
||||
private Lock overtimeStatusLock = new MonitoredReentrantLock(MonitoredLockType.CHANNEL_OVTSTATUS, true);
|
||||
private MonitoredReentrantLock overtimeStatusLock = new MonitoredReentrantLock(MonitoredLockType.CHANNEL_OVTSTATUS, true);
|
||||
|
||||
private class MobStatusOvertimeEntry {
|
||||
private int procCount;
|
||||
@@ -48,11 +47,11 @@ public class MobStatusScheduler extends BaseScheduler {
|
||||
r = run;
|
||||
}
|
||||
|
||||
protected void update() {
|
||||
protected void update(List<Runnable> toRun) {
|
||||
procCount++;
|
||||
if(procCount >= procLimit) {
|
||||
procCount = 0;
|
||||
r.run();
|
||||
toRun.add(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,6 +62,8 @@ public class MobStatusScheduler extends BaseScheduler {
|
||||
super.addListener(new SchedulerListener() {
|
||||
@Override
|
||||
public void removedScheduledEntries(List<Object> toRemove, boolean update) {
|
||||
List<Runnable> toRun = new ArrayList<>();
|
||||
|
||||
overtimeStatusLock.lock();
|
||||
try {
|
||||
for(Object mseo : toRemove) {
|
||||
@@ -74,12 +75,16 @@ public class MobStatusScheduler extends BaseScheduler {
|
||||
// it's probably ok to use one thread for both management & overtime actions
|
||||
List<MobStatusOvertimeEntry> mdoeList = new ArrayList<>(registeredMobStatusOvertime.values());
|
||||
for(MobStatusOvertimeEntry mdoe : mdoeList) {
|
||||
mdoe.update();
|
||||
mdoe.update(toRun);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
overtimeStatusLock.unlock();
|
||||
}
|
||||
|
||||
for(Runnable r : toRun) {
|
||||
r.run();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -102,4 +107,10 @@ public class MobStatusScheduler extends BaseScheduler {
|
||||
public void interruptMobStatus(MonsterStatusEffect mse) {
|
||||
interruptEntry(mse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
overtimeStatusLock.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ public class MapleGuild {
|
||||
if (!bDirty) {
|
||||
return;
|
||||
}
|
||||
Set<Integer> chs = Server.getInstance().getChannelServer(world);
|
||||
Set<Integer> chs = Server.getInstance().getOpenChannels(world);
|
||||
if (notifications.keySet().size() != chs.size()) {
|
||||
notifications.clear();
|
||||
for (Integer ch : chs) {
|
||||
@@ -284,7 +284,7 @@ public class MapleGuild {
|
||||
buildNotifications();
|
||||
}
|
||||
try {
|
||||
for (Integer b : Server.getInstance().getChannelServer(world)) {
|
||||
for (Integer b : Server.getInstance().getOpenChannels(world)) {
|
||||
if (notifications.get(b).size() > 0) {
|
||||
if (bcop == BCOp.DISBAND) {
|
||||
Server.getInstance().getWorld(world).setGuildAndRank(notifications.get(b), 0, 5, exceptionId);
|
||||
|
||||
@@ -31,7 +31,7 @@ public final class ViewAllCharRegisterPicHandler extends AbstractMaplePacketHand
|
||||
return;
|
||||
}
|
||||
|
||||
int channel = Randomizer.rand(1, server.getWorld(c.getWorld()).getChannels().size());
|
||||
int channel = Randomizer.rand(1, server.getWorld(c.getWorld()).getChannelsSize());
|
||||
c.setChannel(channel);
|
||||
|
||||
String mac = slea.readMapleAsciiString();
|
||||
|
||||
@@ -58,7 +58,7 @@ public final class ViewAllCharSelectedHandler extends AbstractMaplePacketHandler
|
||||
}
|
||||
|
||||
try {
|
||||
int channel = Randomizer.rand(1, c.getWorldServer().getChannels().size());
|
||||
int channel = Randomizer.rand(1, c.getWorldServer().getChannelsSize());
|
||||
c.setChannel(channel);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
@@ -27,7 +27,7 @@ public class ViewAllCharSelectedWithPicHandler extends AbstractMaplePacketHandle
|
||||
}
|
||||
|
||||
c.setWorld(server.getCharacterWorld(charId));
|
||||
int channel = Randomizer.rand(1, c.getWorldServer().getChannels().size());
|
||||
int channel = Randomizer.rand(1, c.getWorldServer().getChannelsSize());
|
||||
c.setChannel(channel);
|
||||
|
||||
String macs = slea.readMapleAsciiString();
|
||||
|
||||
@@ -90,7 +90,7 @@ public class RankingWorker implements Runnable {
|
||||
resetMoveRank(false);
|
||||
}
|
||||
|
||||
for(int j = 0; j < Server.getInstance().getWorlds().size(); j++) {
|
||||
for(int j = 0; j < Server.getInstance().getWorldsSize(); j++) {
|
||||
updateRanking(-1, j); //overall ranking
|
||||
for (int i = 0; i <= MapleJob.getMax(); i++) {
|
||||
updateRanking(i, j);
|
||||
|
||||
@@ -31,7 +31,6 @@ import java.util.Map.Entry;
|
||||
import java.util.Map;
|
||||
import java.util.Comparator;
|
||||
import net.server.audit.locks.MonitoredReentrantLock;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
|
||||
public class MapleParty {
|
||||
@@ -44,7 +43,7 @@ public class MapleParty {
|
||||
private Map<Integer, Integer> histMembers = new HashMap<>();
|
||||
private int nextEntry = 0;
|
||||
|
||||
private Lock lock = new MonitoredReentrantLock(MonitoredLockType.PARTY, true);
|
||||
private MonitoredReentrantLock lock = new MonitoredReentrantLock(MonitoredLockType.PARTY, true);
|
||||
|
||||
public MapleParty(int id, MaplePartyCharacter chrfor) {
|
||||
this.leaderId = chrfor.getId();
|
||||
@@ -215,6 +214,10 @@ public class MapleParty {
|
||||
if(newLeadr != null) world.updateParty(this.getId(), PartyOperation.CHANGE_LEADER, newLeadr);
|
||||
}
|
||||
|
||||
public void disposeLocks() {
|
||||
lock.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
|
||||
@@ -47,11 +47,11 @@ import java.util.Map.Entry;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import net.server.audit.locks.MonitoredReentrantLock;
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import scripting.event.EventInstanceManager;
|
||||
import server.TimerManager;
|
||||
@@ -76,6 +76,7 @@ import tools.DatabaseConnection;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Pair;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -97,8 +98,12 @@ public class World {
|
||||
private Map<Integer, MapleGuildSummary> gsStore = new HashMap<>();
|
||||
private PlayerStorage players = new PlayerStorage();
|
||||
|
||||
private final ReentrantReadWriteLock chnLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_CHANNELS, true);
|
||||
private final ReentrantReadWriteLock.ReadLock chnRLock = chnLock.readLock();
|
||||
private final ReentrantReadWriteLock.WriteLock chnWLock = chnLock.writeLock();
|
||||
|
||||
private Map<Integer, SortedMap<Integer, MapleCharacter>> accountChars = new HashMap<>();
|
||||
private Lock accountCharsLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_CHARS, true);
|
||||
private MonitoredReentrantLock accountCharsLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_CHARS, true);
|
||||
|
||||
private Set<Integer> queuedGuilds = new HashSet<>();
|
||||
private Map<Integer, Pair<Pair<Boolean, Boolean>, Pair<Integer, Integer>>> queuedMarriages = new HashMap<>();
|
||||
@@ -107,31 +112,31 @@ public class World {
|
||||
private Map<Integer, Integer> partyChars = new HashMap<>();
|
||||
private Map<Integer, MapleParty> parties = new HashMap<>();
|
||||
private AtomicInteger runningPartyId = new AtomicInteger();
|
||||
private Lock partyLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_PARTY, true);
|
||||
private MonitoredReentrantLock partyLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_PARTY, true);
|
||||
|
||||
private Map<Integer, Integer> owlSearched = new LinkedHashMap<>();
|
||||
private Lock owlLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_OWL);
|
||||
private MonitoredReentrantLock owlLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_OWL);
|
||||
|
||||
private Lock activePetsLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_PETS, true);
|
||||
private MonitoredReentrantLock activePetsLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_PETS, true);
|
||||
private Map<Integer, Byte> activePets = new LinkedHashMap<>();
|
||||
private ScheduledFuture<?> petsSchedule;
|
||||
private long petUpdate;
|
||||
|
||||
private Lock activeMountsLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_MOUNTS, true);
|
||||
private MonitoredReentrantLock activeMountsLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_MOUNTS, true);
|
||||
private Map<Integer, Byte> activeMounts = new LinkedHashMap<>();
|
||||
private ScheduledFuture<?> mountsSchedule;
|
||||
private long mountUpdate;
|
||||
|
||||
private Lock activePlayerShopsLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_PSHOPS, true);
|
||||
private MonitoredReentrantLock activePlayerShopsLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_PSHOPS, true);
|
||||
private Map<Integer, MaplePlayerShop> activePlayerShops = new LinkedHashMap<>();
|
||||
|
||||
private Lock activeMerchantsLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_MERCHS, true);
|
||||
private MonitoredReentrantLock activeMerchantsLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_MERCHS, true);
|
||||
private Map<Integer, Pair<MapleHiredMerchant, Byte>> activeMerchants = new LinkedHashMap<>();
|
||||
private long merchantUpdate;
|
||||
|
||||
private Map<Runnable, Long> registeredTimedMapObjects = new LinkedHashMap<>();
|
||||
private ScheduledFuture<?> timedMapObjectsSchedule;
|
||||
private Lock timedMapObjectLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_MAPOBJS, true);
|
||||
private MonitoredReentrantLock timedMapObjectLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_MAPOBJS, true);
|
||||
|
||||
private ScheduledFuture<?> charactersSchedule;
|
||||
private ScheduledFuture<?> marriagesSchedule;
|
||||
@@ -159,20 +164,75 @@ public class World {
|
||||
|
||||
}
|
||||
|
||||
public int getChannelsSize() {
|
||||
chnRLock.lock();
|
||||
try {
|
||||
return channels.size();
|
||||
} finally {
|
||||
chnRLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public List<Channel> getChannels() {
|
||||
return channels;
|
||||
chnRLock.lock();
|
||||
try {
|
||||
return new ArrayList<>(channels);
|
||||
} finally {
|
||||
chnRLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public Channel getChannel(int channel) {
|
||||
return channels.get(channel - 1);
|
||||
chnRLock.lock();
|
||||
try {
|
||||
return channels.get(channel - 1);
|
||||
} finally {
|
||||
chnRLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void addChannel(Channel channel) {
|
||||
channels.add(channel);
|
||||
chnWLock.lock();
|
||||
try {
|
||||
channels.add(channel);
|
||||
} finally {
|
||||
chnWLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void removeChannel(int channel) {
|
||||
channels.remove(channel);
|
||||
public int removeChannel() {
|
||||
Channel ch;
|
||||
int chIdx;
|
||||
|
||||
chnRLock.lock();
|
||||
try {
|
||||
chIdx = channels.size() - 1;
|
||||
if(chIdx < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ch = channels.get(chIdx);
|
||||
} finally {
|
||||
chnRLock.unlock();
|
||||
}
|
||||
|
||||
if(ch == null || ch.getPlayerStorage().getSize() > 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
chnWLock.lock();
|
||||
try {
|
||||
if(chIdx == channels.size() - 1) {
|
||||
channels.remove(chIdx);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} finally {
|
||||
chnWLock.unlock();
|
||||
}
|
||||
|
||||
ch.shutdown();
|
||||
return ch.getId();
|
||||
}
|
||||
|
||||
public void setFlag(byte b) {
|
||||
@@ -342,11 +402,11 @@ public class World {
|
||||
}
|
||||
|
||||
public void removePlayer(MapleCharacter chr) {
|
||||
if(!channels.get(chr.getClient().getChannel() - 1).removePlayer(chr)) {
|
||||
if(!getChannel(chr.getClient().getChannel()).removePlayer(chr)) {
|
||||
if(!chr.getClient().getChannelServer().removePlayer(chr)) {
|
||||
// oy the player is not where it should be, find this mf
|
||||
|
||||
for(Channel ch : channels) {
|
||||
for(Channel ch : getChannels()) {
|
||||
if(ch.removePlayer(chr)) {
|
||||
break;
|
||||
}
|
||||
@@ -394,7 +454,7 @@ public class World {
|
||||
}
|
||||
|
||||
public int getWorldCapacityStatus() {
|
||||
int worldCap = channels.size() * ServerConstants.CHANNEL_LOAD;
|
||||
int worldCap = getChannelsSize() * ServerConstants.CHANNEL_LOAD;
|
||||
int num = players.getSize();
|
||||
|
||||
int status;
|
||||
@@ -555,7 +615,7 @@ public class World {
|
||||
}
|
||||
|
||||
public Pair<Integer, Integer> getWeddingCoupleForGuest(int guestId, Boolean cathedral) {
|
||||
for(Channel ch : channels) {
|
||||
for(Channel ch : getChannels()) {
|
||||
Pair<Integer, Integer> p = ch.getWeddingCoupleForGuest(guestId, cathedral);
|
||||
if(p != null) {
|
||||
return p;
|
||||
@@ -580,7 +640,7 @@ public class World {
|
||||
int selectedPos = Integer.MAX_VALUE;
|
||||
|
||||
for(Integer pw : possibleWeddings) {
|
||||
for(Channel ch : channels) {
|
||||
for(Channel ch : getChannels()) {
|
||||
int pos = ch.getWeddingReservationStatus(pw, cathedral);
|
||||
if(pos != -1) {
|
||||
if(pos < selectedPos) {
|
||||
@@ -1420,7 +1480,7 @@ public class World {
|
||||
}
|
||||
|
||||
public void setServerMessage(String msg) {
|
||||
for (Channel ch : channels) {
|
||||
for (Channel ch : getChannels()) {
|
||||
ch.setServerMessage(msg);
|
||||
}
|
||||
}
|
||||
@@ -1588,6 +1648,29 @@ public class World {
|
||||
}
|
||||
}
|
||||
|
||||
private void disposeLocks() {
|
||||
List<MapleParty> pList;
|
||||
partyLock.lock();
|
||||
try {
|
||||
pList = new ArrayList<>(parties.values());
|
||||
} finally {
|
||||
partyLock.unlock();
|
||||
}
|
||||
|
||||
for(MapleParty p : pList) {
|
||||
p.disposeLocks();
|
||||
}
|
||||
|
||||
accountCharsLock.dispose();
|
||||
partyLock.dispose();
|
||||
owlLock.dispose();
|
||||
activePetsLock.dispose();
|
||||
activeMountsLock.dispose();
|
||||
activePlayerShopsLock.dispose();
|
||||
activeMerchantsLock.dispose();
|
||||
timedMapObjectLock.dispose();
|
||||
}
|
||||
|
||||
public final void shutdown() {
|
||||
for (Channel ch : getChannels()) {
|
||||
ch.shutdown();
|
||||
@@ -1608,6 +1691,17 @@ public class World {
|
||||
timedMapObjectsSchedule = null;
|
||||
}
|
||||
|
||||
if(charactersSchedule != null) {
|
||||
charactersSchedule.cancel(false);
|
||||
charactersSchedule = null;
|
||||
}
|
||||
|
||||
if(marriagesSchedule != null) {
|
||||
marriagesSchedule.cancel(false);
|
||||
marriagesSchedule = null;
|
||||
}
|
||||
|
||||
players.disconnectAll();
|
||||
disposeLocks();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.Iterator;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
|
||||
@@ -93,8 +92,8 @@ public class EventInstanceManager {
|
||||
private final ReadLock rL = lock.readLock();
|
||||
private final WriteLock wL = lock.writeLock();
|
||||
|
||||
private final Lock pL = new MonitoredReentrantLock(MonitoredLockType.EIM_PARTY, true);
|
||||
private final Lock sL = new MonitoredReentrantLock(MonitoredLockType.EIM_SCRIPT, true);
|
||||
private final MonitoredReentrantLock pL = new MonitoredReentrantLock(MonitoredLockType.EIM_PARTY, true);
|
||||
private final MonitoredReentrantLock sL = new MonitoredReentrantLock(MonitoredLockType.EIM_SCRIPT, true);
|
||||
|
||||
private ScheduledFuture<?> event_schedule = null;
|
||||
private boolean disposed = false;
|
||||
@@ -638,13 +637,6 @@ public class EventInstanceManager {
|
||||
return (kc == null) ? 0 : kc;
|
||||
}
|
||||
|
||||
public void cancelSchedule() {
|
||||
if(event_schedule != null) {
|
||||
event_schedule.cancel(false);
|
||||
event_schedule = null;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void dispose() {
|
||||
if(disposed) return;
|
||||
|
||||
@@ -673,9 +665,14 @@ public class EventInstanceManager {
|
||||
wL.unlock();
|
||||
}
|
||||
|
||||
cancelSchedule();
|
||||
if(event_schedule != null) {
|
||||
event_schedule.cancel(false);
|
||||
event_schedule = null;
|
||||
}
|
||||
|
||||
killCount.clear();
|
||||
mapIds.clear();
|
||||
props.clear();
|
||||
|
||||
disposeExpedition();
|
||||
|
||||
@@ -686,14 +683,24 @@ public class EventInstanceManager {
|
||||
} finally {
|
||||
sL.unlock();
|
||||
}
|
||||
|
||||
disposeLocks();
|
||||
}
|
||||
|
||||
private void disposeLocks() {
|
||||
pL.dispose();
|
||||
sL.dispose();
|
||||
}
|
||||
|
||||
public MapleMapFactory getMapFactory() {
|
||||
return mapFactory;
|
||||
}
|
||||
|
||||
public void schedule(final String methodName, long delay) {
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
List<MapleCharacter> chrList = this.getPlayerList();
|
||||
int mapid = !chrList.isEmpty() ? chrList.get(0).getMapId() : 0;
|
||||
|
||||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
@@ -708,7 +715,9 @@ public class EventInstanceManager {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}, delay);
|
||||
};
|
||||
|
||||
getEm().getChannelServer().registerEventAction(mapid, r, delay);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
|
||||
@@ -26,7 +26,6 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -42,7 +41,7 @@ import net.server.channel.Channel;
|
||||
import net.server.guild.MapleGuild;
|
||||
import net.server.world.MapleParty;
|
||||
import net.server.world.MaplePartyCharacter;
|
||||
import server.TimerManager;
|
||||
import scripting.event.worker.EventScriptScheduler;
|
||||
import server.expeditions.MapleExpedition;
|
||||
import server.maps.MapleMap;
|
||||
import server.life.MapleMonster;
|
||||
@@ -53,7 +52,6 @@ import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.MonitoredReentrantLock;
|
||||
|
||||
@@ -67,6 +65,7 @@ public class EventManager {
|
||||
private Channel cserv;
|
||||
private World wserv;
|
||||
private Server server;
|
||||
private EventScriptScheduler ess = new EventScriptScheduler();
|
||||
private Map<String, EventInstanceManager> instances = new HashMap<String, EventInstanceManager>();
|
||||
private Map<String, Integer> instanceLocks = new HashMap<String, Integer>();
|
||||
private final Queue<Integer> queuedGuilds = new LinkedList<>();
|
||||
@@ -76,8 +75,8 @@ public class EventManager {
|
||||
private Integer readyId = 0;
|
||||
private Properties props = new Properties();
|
||||
private String name;
|
||||
private Lock lobbyLock = new MonitoredReentrantLock(MonitoredLockType.EM_LOBBY);
|
||||
private Lock queueLock = new MonitoredReentrantLock(MonitoredLockType.EM_QUEUE);
|
||||
private MonitoredReentrantLock lobbyLock = new MonitoredReentrantLock(MonitoredLockType.EM_LOBBY);
|
||||
private MonitoredReentrantLock queueLock = new MonitoredReentrantLock(MonitoredLockType.EM_QUEUE);
|
||||
|
||||
private static final int maxLobbys = 8; // an event manager holds up to this amount of concurrent lobbys
|
||||
|
||||
@@ -92,12 +91,47 @@ public class EventManager {
|
||||
for(int i = 0; i < maxLobbys; i++) this.openedLobbys.add(false);
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
public void cancel() { // make sure to only call this when there are NO PLAYERS ONLINE to mess around with the event manager!
|
||||
ess.dispose();
|
||||
|
||||
try {
|
||||
iv.invokeFunction("cancelSchedule", (Object) null);
|
||||
} catch (ScriptException | NoSuchMethodException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
synchronized(instances) {
|
||||
for(EventInstanceManager eim : instances.values()) {
|
||||
eim.dispose();
|
||||
}
|
||||
instances.clear();
|
||||
}
|
||||
|
||||
List<EventInstanceManager> readyEims;
|
||||
queueLock.lock();
|
||||
try {
|
||||
readyEims = new ArrayList<>(readyInstances);
|
||||
readyInstances.clear();
|
||||
} finally {
|
||||
queueLock.unlock();
|
||||
}
|
||||
|
||||
for(EventInstanceManager eim : readyEims) {
|
||||
eim.dispose();
|
||||
}
|
||||
|
||||
props.clear();
|
||||
cserv = null;
|
||||
wserv = null;
|
||||
server = null;
|
||||
iv = null;
|
||||
|
||||
disposeLocks();
|
||||
}
|
||||
|
||||
private void disposeLocks() {
|
||||
lobbyLock.dispose();
|
||||
queueLock.dispose();
|
||||
}
|
||||
|
||||
private static List<Integer> convertToIntegerArray(List<Double> list) {
|
||||
@@ -123,12 +157,12 @@ public class EventManager {
|
||||
}
|
||||
}
|
||||
|
||||
public ScheduledFuture<?> schedule(String methodName, long delay) {
|
||||
public EventScheduledFuture schedule(String methodName, long delay) {
|
||||
return schedule(methodName, null, delay);
|
||||
}
|
||||
|
||||
public ScheduledFuture<?> schedule(final String methodName, final EventInstanceManager eim, long delay) {
|
||||
return TimerManager.getInstance().schedule(new Runnable() {
|
||||
public EventScheduledFuture schedule(final String methodName, final EventInstanceManager eim, long delay) {
|
||||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
@@ -137,11 +171,16 @@ public class EventManager {
|
||||
Logger.getLogger(EventManager.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}, delay);
|
||||
};
|
||||
|
||||
ess.registerEntry(r, delay);
|
||||
|
||||
// hate to do that, but those schedules can still be cancelled, so well... Let GC do it's job
|
||||
return new EventScheduledFuture(r, ess);
|
||||
}
|
||||
|
||||
public ScheduledFuture<?> scheduleAtTimestamp(final String methodName, long timestamp) {
|
||||
return TimerManager.getInstance().scheduleAtTimestamp(new Runnable() {
|
||||
public EventScheduledFuture scheduleAtTimestamp(final String methodName, long timestamp) {
|
||||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
@@ -150,7 +189,10 @@ public class EventManager {
|
||||
Logger.getLogger(EventManager.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}, timestamp);
|
||||
};
|
||||
|
||||
ess.registerEntry(r, timestamp - server.getCurrentTime());
|
||||
return new EventScheduledFuture(r, ess);
|
||||
}
|
||||
|
||||
public World getWorldServer() {
|
||||
@@ -187,7 +229,7 @@ public class EventManager {
|
||||
}
|
||||
|
||||
public void disposeInstance(final String name) {
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
ess.registerEntry(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
freeLobbyInstance(name);
|
||||
|
||||
40
src/scripting/event/EventScheduledFuture.java
Normal file
40
src/scripting/event/EventScheduledFuture.java
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft 2016 - 2018 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package scripting.event;
|
||||
|
||||
import scripting.event.worker.EventScriptScheduler;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ronan
|
||||
*/
|
||||
public class EventScheduledFuture {
|
||||
Runnable r;
|
||||
EventScriptScheduler ess;
|
||||
|
||||
public EventScheduledFuture(Runnable r, EventScriptScheduler ess) {
|
||||
this.r = r;
|
||||
this.ess = ess;
|
||||
}
|
||||
|
||||
public void cancel(boolean dummy) { // will always implement "non-interrupt if running" regardless of boolean value
|
||||
ess.cancelEntry(r);
|
||||
}
|
||||
}
|
||||
169
src/scripting/event/worker/EventScriptScheduler.java
Normal file
169
src/scripting/event/worker/EventScriptScheduler.java
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft 2016 - 2018 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package scripting.event.worker;
|
||||
|
||||
import constants.ServerConstants;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import server.TimerManager;
|
||||
import net.server.Server;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.MonitoredReentrantLock;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ronan
|
||||
*/
|
||||
public class EventScriptScheduler {
|
||||
private boolean disposed = false;
|
||||
private int idleProcs = 0;
|
||||
private Map<Runnable, Long> registeredEntries = new HashMap<>();
|
||||
|
||||
private ScheduledFuture<?> schedulerTask = null;
|
||||
private MonitoredReentrantLock schedulerLock;
|
||||
private Runnable monitorTask = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
runBaseSchedule();
|
||||
}
|
||||
};
|
||||
|
||||
public EventScriptScheduler() {
|
||||
schedulerLock = new MonitoredReentrantLock(MonitoredLockType.EM_SCHDL, true);
|
||||
}
|
||||
|
||||
private void runBaseSchedule() {
|
||||
List<Runnable> toRemove;
|
||||
Map<Runnable, Long> registeredEntriesCopy;
|
||||
|
||||
schedulerLock.lock();
|
||||
try {
|
||||
if(registeredEntries.isEmpty()) {
|
||||
idleProcs++;
|
||||
|
||||
if(idleProcs >= ServerConstants.MOB_STATUS_MONITOR_LIFE) {
|
||||
if(schedulerTask != null) {
|
||||
schedulerTask.cancel(false);
|
||||
schedulerTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
idleProcs = 0;
|
||||
registeredEntriesCopy = new HashMap<>(registeredEntries);
|
||||
} finally {
|
||||
schedulerLock.unlock();
|
||||
}
|
||||
|
||||
long timeNow = Server.getInstance().getCurrentTime();
|
||||
toRemove = new LinkedList<>();
|
||||
for(Entry<Runnable, Long> rmd : registeredEntriesCopy.entrySet()) {
|
||||
if(rmd.getValue() < timeNow) {
|
||||
Runnable r = rmd.getKey();
|
||||
|
||||
r.run(); // runs the scheduled action
|
||||
toRemove.add(r);
|
||||
}
|
||||
}
|
||||
|
||||
if(!toRemove.isEmpty()) {
|
||||
schedulerLock.lock();
|
||||
try {
|
||||
for(Runnable r : toRemove) {
|
||||
registeredEntries.remove(r);
|
||||
}
|
||||
} finally {
|
||||
schedulerLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void registerEntry(final Runnable scheduledAction, final long duration) {
|
||||
|
||||
Thread t = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
schedulerLock.lock();
|
||||
try {
|
||||
idleProcs = 0;
|
||||
if(schedulerTask == null) {
|
||||
if(disposed) return;
|
||||
|
||||
schedulerTask = TimerManager.getInstance().register(monitorTask, ServerConstants.MOB_STATUS_MONITOR_PROC, ServerConstants.MOB_STATUS_MONITOR_PROC);
|
||||
}
|
||||
|
||||
registeredEntries.put(scheduledAction, Server.getInstance().getCurrentTime() + duration);
|
||||
} finally {
|
||||
schedulerLock.unlock();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
t.start();
|
||||
}
|
||||
|
||||
public void cancelEntry(final Runnable scheduledAction) {
|
||||
|
||||
Thread t = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
schedulerLock.lock();
|
||||
try {
|
||||
registeredEntries.remove(scheduledAction);
|
||||
} finally {
|
||||
schedulerLock.unlock();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
t.start();
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
|
||||
Thread t = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
schedulerLock.lock();
|
||||
try {
|
||||
if(schedulerTask != null) {
|
||||
schedulerTask.cancel(false);
|
||||
schedulerTask = null;
|
||||
}
|
||||
|
||||
registeredEntries.clear();
|
||||
disposed = true;
|
||||
} finally {
|
||||
schedulerLock.unlock();
|
||||
}
|
||||
|
||||
schedulerLock.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
@@ -119,7 +119,7 @@ public class MapleStorage {
|
||||
return slots;
|
||||
}
|
||||
|
||||
public boolean gainSlots(int slots) {
|
||||
public synchronized boolean gainSlots(int slots) {
|
||||
slots += this.slots;
|
||||
|
||||
if (slots <= 48) {
|
||||
@@ -129,11 +129,7 @@ public class MapleStorage {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setSlots(byte set) {
|
||||
this.slots = set;
|
||||
}
|
||||
|
||||
|
||||
public void saveToDB(Connection con) {
|
||||
try {
|
||||
try (PreparedStatement ps = con.prepareStatement("UPDATE storages SET slots = ?, meso = ? WHERE storageid = ?")) {
|
||||
|
||||
@@ -54,7 +54,6 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import net.server.audit.locks.MonitoredReentrantLock;
|
||||
import net.server.Server;
|
||||
import net.server.channel.Channel;
|
||||
@@ -95,10 +94,10 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
private int parentMobOid = 0;
|
||||
private final HashMap<Integer, AtomicInteger> takenDamage = new HashMap<>();
|
||||
|
||||
private Lock externalLock = new MonitoredReentrantLock(MonitoredLockType.MOB_EXT);
|
||||
private Lock monsterLock = new MonitoredReentrantLock(MonitoredLockType.MOB, true);
|
||||
private Lock statiLock = new MonitoredReentrantLock(MonitoredLockType.MOB_STATI);
|
||||
private Lock animationLock = new MonitoredReentrantLock(MonitoredLockType.MOB_ANI);
|
||||
private MonitoredReentrantLock externalLock = new MonitoredReentrantLock(MonitoredLockType.MOB_EXT);
|
||||
private MonitoredReentrantLock monsterLock = new MonitoredReentrantLock(MonitoredLockType.MOB, true);
|
||||
private MonitoredReentrantLock statiLock = new MonitoredReentrantLock(MonitoredLockType.MOB_STATI);
|
||||
private MonitoredReentrantLock animationLock = new MonitoredReentrantLock(MonitoredLockType.MOB_ANI);
|
||||
|
||||
public MapleMonster(int id, MapleMonsterStats stats) {
|
||||
super(id);
|
||||
@@ -1476,4 +1475,11 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
public final void changeDifficulty(final int difficulty, boolean pqMob) {
|
||||
changeLevelByDifficulty(difficulty, pqMob);
|
||||
}
|
||||
|
||||
public final void disposeLocks() {
|
||||
externalLock.dispose();
|
||||
monsterLock.dispose();
|
||||
statiLock.dispose();
|
||||
animationLock.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,7 +248,7 @@ public class MaplePlayerNPC extends AbstractMapleMapObject {
|
||||
}
|
||||
|
||||
private static void getRunningWorldRanks(Connection con) throws SQLException {
|
||||
int numWorlds = Server.getInstance().getWorlds().size();
|
||||
int numWorlds = Server.getInstance().getWorldsSize();
|
||||
for(int i = 0; i < numWorlds; i++) {
|
||||
runningWorldRank.add(new AtomicInteger(1));
|
||||
}
|
||||
@@ -625,7 +625,7 @@ public class MaplePlayerNPC extends AbstractMapleMapObject {
|
||||
PreparedStatement ps = con.prepareStatement("SELECT DISTINCT world, map FROM playernpcs");
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
int wsize = Server.getInstance().getWorlds().size();
|
||||
int wsize = Server.getInstance().getWorldsSize();
|
||||
while(rs.next()) {
|
||||
int world = rs.getInt("world"), map = rs.getInt("map");
|
||||
if(world >= wsize) continue;
|
||||
|
||||
@@ -2103,6 +2103,10 @@ public class MapleMap {
|
||||
}
|
||||
}
|
||||
|
||||
private void registerMapSchedule(Runnable r, long delay) {
|
||||
this.getChannelServer().registerOverallAction(mapid, r, delay);
|
||||
}
|
||||
|
||||
private void activateItemReactors(final MapleMapItem drop, final MapleClient c) {
|
||||
final Item item = drop.getItem();
|
||||
|
||||
@@ -2113,7 +2117,7 @@ public class MapleMap {
|
||||
if (react.getReactItem(react.getEventState()).getLeft() == item.getItemId() && react.getReactItem(react.getEventState()).getRight() == item.getQuantity()) {
|
||||
|
||||
if (react.getArea().contains(drop.getPosition())) {
|
||||
TimerManager.getInstance().schedule(new ActivateItemReactor(drop, react, c), 5000);
|
||||
registerMapSchedule(new ActivateItemReactor(drop, react, c), 5000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2151,7 +2155,7 @@ public class MapleMap {
|
||||
if (reactArea.contains(drop.getPosition())) {
|
||||
MapleClient owner = drop.getOwnerClient();
|
||||
if(owner != null) {
|
||||
TimerManager.getInstance().schedule(new ActivateItemReactor(drop, react, owner), 5000);
|
||||
registerMapSchedule(new ActivateItemReactor(drop, react, owner), 5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2173,13 +2177,16 @@ public class MapleMap {
|
||||
}
|
||||
mapEffect = new MapleMapEffect(msg, itemId);
|
||||
broadcastMessage(mapEffect.makeStartData());
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
|
||||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
broadcastMessage(mapEffect.makeDestroyData());
|
||||
mapEffect = null;
|
||||
}
|
||||
}, time);
|
||||
};
|
||||
|
||||
registerMapSchedule(r, time);
|
||||
}
|
||||
|
||||
public MapleCharacter getAnyCharacterFromParty(int partyid) {
|
||||
@@ -3807,4 +3814,37 @@ public class MapleMap {
|
||||
spawnMonsterOnGroundBelow(m, targetPoint);
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
for(MapleMonster mm : this.getMonsters()) {
|
||||
mm.disposeLocks();
|
||||
}
|
||||
|
||||
clearMapObjects();
|
||||
|
||||
event = null;
|
||||
footholds = null;
|
||||
portals.clear();
|
||||
mapEffect = null;
|
||||
|
||||
if(mapMonitor != null) {
|
||||
mapMonitor.cancel(false);
|
||||
mapMonitor = null;
|
||||
}
|
||||
|
||||
chrWLock.lock();
|
||||
try {
|
||||
if(itemMonitor != null) {
|
||||
itemMonitor.cancel(false);
|
||||
itemMonitor = null;
|
||||
}
|
||||
|
||||
if(expireItemsTask != null) {
|
||||
expireItemsTask.cancel(false);
|
||||
expireItemsTask = null;
|
||||
}
|
||||
} finally {
|
||||
chrWLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,7 +437,10 @@ public class MapleMapFactory {
|
||||
mapsRLock.unlock();
|
||||
}
|
||||
|
||||
for(MapleMap map: mapValues) map.setEventInstance(null);
|
||||
for(MapleMap map: mapValues) {
|
||||
map.dispose();
|
||||
}
|
||||
|
||||
this.event = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ import tools.MaplePacketCreator;
|
||||
*/
|
||||
public class MapleTVEffect {
|
||||
|
||||
private final static boolean ACTIVE[] = new boolean[Server.getInstance().getWorlds().size()];
|
||||
private final static boolean ACTIVE[] = new boolean[Server.getInstance().getWorldsSize()];
|
||||
|
||||
public static synchronized boolean broadcastMapleTVIfNotActive(MapleCharacter player, MapleCharacter victim, List<String> messages, int tvType){
|
||||
int w = player.getWorld();
|
||||
|
||||
Reference in New Issue
Block a user