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:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user