Channel Services + Mob Movement patch + Portal map scripts

Refactored several schedulers within the channel class, now running within their own service modules.
Fixed a case where mob movements would get mistakably processed for other than the target mob during a map transition, leading to weird movements on the mob in the entered area.
Added usage of foreign key for petid's.
Implemented functionality for "Hair Membership" coupons.
Fixed skill Body Pressure not applying the chance to neutralise on touch.
Fixed quest related to NPC Shaman Rock not completing due to unmatched progress.
Fixed an issue with updating title progress "Touch the Sky".
This commit is contained in:
ronancpl
2019-10-12 17:13:03 -03:00
parent b93c9ce1d6
commit 80cd240ab8
65 changed files with 1195 additions and 779 deletions

View File

@@ -142,7 +142,6 @@ import client.processor.action.PetAutopotProcessor;
import constants.game.ExpTable;
import constants.game.GameConstants;
import constants.inventory.ItemConstants;
import constants.net.ServerConstants;
import constants.skills.Aran;
import constants.skills.Beginner;
import constants.skills.Bishop;
@@ -172,6 +171,9 @@ import constants.skills.Shadower;
import constants.skills.Sniper;
import constants.skills.Warrior;
import constants.skills.ThunderBreaker;
import net.server.channel.services.ServiceType;
import net.server.channel.services.task.BaseService;
import net.server.channel.services.task.FaceExpressionService;
import org.apache.mina.util.ConcurrentHashSet;
public class MapleCharacter extends AbstractMapleCharacterObject {
@@ -320,7 +322,6 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
private boolean loggedIn = false;
private boolean useCS; //chaos scroll upon crafting item.
private long npcCd;
private long petLootCd;
private long lastHpDec = 0;
private int newWarpMap = -1;
private boolean canWarpMap = true; //only one "warp" must be used per call, and this will define the right one.
@@ -398,8 +399,6 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
quests = new LinkedHashMap<>();
setPosition(new Point(0, 0));
petLootCd = Server.getInstance().getCurrentTime();
}
private static MapleJob getJobStyleInternal(int jobid, byte opt) {
@@ -439,7 +438,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
public static MapleCharacter getDefault(MapleClient c) {
MapleCharacter ret = new MapleCharacter();
ret.client = c;
ret.gmLevel = 0;
ret.setGMLevel(0);
ret.hp = 50;
ret.setMaxHp(50);
ret.mp = 5;
@@ -569,14 +568,6 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
client.getSession().setAttribute(MapleClient.CLIENT_TRANSITION);
}
public long getPetLootCd() {
return petLootCd;
}
public void setPetLootCd(long cd) {
petLootCd = cd;
}
public boolean getCS() {
return useCS;
}
@@ -822,7 +813,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
mainstat = localstr;
secondarystat = localdex;
}
return (int) (((weapon.getMaxDamageMultiplier() * mainstat + secondarystat) / 100.0) * watk);
return (int) Math.ceil(((weapon.getMaxDamageMultiplier() * mainstat + secondarystat) / 100.0) * watk);
}
public int calculateMaxBaseDamage(int watk) {
@@ -838,7 +829,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
int attack = (int) Math.min(Math.floor((2 * getLevel() + 31) / 3), 31);
maxbasedamage = (int) (localstr * weapMulti + localdex) * attack / 100;
maxbasedamage = (int) Math.ceil((localstr * weapMulti + localdex) * attack / 100.0);
} else {
maxbasedamage = 1;
}
@@ -2346,6 +2337,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
ps.setInt(1, cid);
ps.executeUpdate();
}
try (PreparedStatement ps = con.prepareStatement("DELETE FROM family_character WHERE cid = ?")) {
ps.setInt(1, cid);
ps.executeUpdate();
}
try (PreparedStatement ps = con.prepareStatement("DELETE FROM famelog WHERE characterid_to = ?")) {
ps.setInt(1, cid);
ps.executeUpdate();
@@ -2383,7 +2378,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
int petid = rs.getInt("petid");
if(petid > -1) {
if(!rs.wasNull()) {
try (PreparedStatement ps2 = con.prepareStatement("DELETE FROM pets WHERE petid = ?")) {
ps2.setInt(1, petid);
ps2.executeUpdate();
@@ -2850,7 +2845,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
long timeNow = Server.getInstance().getCurrentTime();
if(timeNow - lastExpression > 2000) {
lastExpression = timeNow;
client.getChannelServer().registerFaceExpression(map, this, emote);
FaceExpressionService service = (FaceExpressionService) client.getChannelServer().getServiceAccess(ServiceType.FACE_EXPRESSION);
service.registerFaceExpression(map, this, emote);
}
}
@@ -5614,6 +5611,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
public void setGMLevel(int level) {
this.gmLevel = Math.min(level, 6);
this.gmLevel = Math.max(level, 0);
whiteChat = gmLevel >= 4; // thanks ozanrijen for suggesting default white chat
}
public void closePartySearchInteractions() {
@@ -6975,8 +6974,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
ret.gachaexp.set(rs.getInt("gachaexp"));
ret.mapid = rs.getInt("map");
ret.initialSpawnPoint = rs.getInt("spawnpoint");
ret.gmLevel = rs.getInt("gm");
ret.setGMLevel(rs.getInt("gm"));
ret.world = rs.getByte("world");
ret.rank = rs.getInt("rank");
ret.rankMove = rs.getInt("rankMove");
@@ -7029,7 +7027,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
ret.inventory[MapleInventoryType.EQUIPPED.ordinal()] = this.getInventory(MapleInventoryType.EQUIPPED);
ret.gmLevel = this.gmLevel();
ret.setGMLevel(this.gmLevel());
ret.world = this.getWorld();
ret.rank = this.getRank();
ret.rankMove = this.getRankMove();
@@ -7091,7 +7089,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
ret.loadCharSkillPoints(rs.getString("sp").split(","));
ret.meso.set(rs.getInt("meso"));
ret.merchantmeso = rs.getInt("MerchantMesos");
ret.gmLevel = rs.getInt("gm");
ret.setGMLevel(rs.getInt("gm"));
ret.skinColor = MapleSkinColor.getById(rs.getInt("skincolor"));
ret.gender = rs.getInt("gender");
ret.job = MapleJob.getById(rs.getInt("job"));
@@ -7187,11 +7185,14 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
PreparedStatement ps2, ps3;
ResultSet rs2, rs3;
ps3 = con.prepareStatement("SELECT petid FROM inventoryitems WHERE characterid = ? AND petid > -1");
ps3 = con.prepareStatement("SELECT petid FROM inventoryitems WHERE characterid = ? AND petid IS NOT NULL");
ps3.setInt(1, charid);
rs3 = ps3.executeQuery();
while(rs3.next()) {
int petId = rs3.getInt("petid");
if (rs3.wasNull()) {
petId = -1;
}
ps2 = con.prepareStatement("SELECT itemid FROM petignores WHERE petid = ?");
ps2.setInt(1, petId);
@@ -7371,7 +7372,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
ps.setInt(1, charid);
rs = ps.executeQuery();
while (rs.next()) {
ret.skills.put(SkillFactory.getSkill(rs.getInt("skillid")), new SkillEntry(rs.getByte("skilllevel"), rs.getInt("masterlevel"), rs.getLong("expiration")));
Skill pSkill = SkillFactory.getSkill(rs.getInt("skillid"));
if(pSkill != null) // edit reported by shavit, thanks Zein for noticing an NPE here
{
ret.skills.put(pSkill, new SkillEntry(rs.getByte("skilllevel"), rs.getInt("masterlevel"), rs.getLong("expiration")));
}
}
rs.close();
ps.close();

View File

@@ -126,7 +126,11 @@ public class Item implements Comparable<Item> {
public int getPetId() {
return petid;
}
public Integer getPetIdForDb() {
return petid > -1 ? petid : null;
}
@Override
public int compareTo(Item other) {
if (this.id < other.getItemId()) {

View File

@@ -173,7 +173,12 @@ public enum ItemFactory {
if (mit.equals(MapleInventoryType.EQUIP) || mit.equals(MapleInventoryType.EQUIPPED)) {
items.add(new Pair<Item, MapleInventoryType>(loadEquipFromResultSet(rs), mit));
} else {
Item item = new Item(rs.getInt("itemid"), (byte) rs.getInt("position"), (short) rs.getInt("quantity"), rs.getInt("petid"));
int petid = rs.getInt("petid");
if (rs.wasNull()) {
petid = -1;
}
Item item = new Item(rs.getInt("itemid"), (byte) rs.getInt("position"), (short) rs.getInt("quantity"), petid);
item.setOwner(rs.getString("owner"));
item.setExpiration(rs.getLong("expiration"));
item.setGiftFrom(rs.getString("giftFrom"));
@@ -229,7 +234,7 @@ public enum ItemFactory {
ps.setInt(6, item.getPosition());
ps.setInt(7, item.getQuantity());
ps.setString(8, item.getOwner());
ps.setInt(9, item.getPetId());
ps.setObject(9, item.getPetIdForDb(), java.sql.Types.INTEGER);
ps.setInt(10, item.getFlag());
ps.setLong(11, item.getExpiration());
ps.setString(12, item.getGiftFrom());
@@ -329,7 +334,12 @@ public enum ItemFactory {
items.add(new Pair<Item, MapleInventoryType>(loadEquipFromResultSet(rs), mit));
} else {
if(bundles > 0) {
Item item = new Item(rs.getInt("itemid"), (byte) rs.getInt("position"), (short)(bundles * rs.getInt("quantity")), rs.getInt("petid"));
int petid = rs.getInt("petid");
if (rs.wasNull()) {
petid = -1;
}
Item item = new Item(rs.getInt("itemid"), (byte) rs.getInt("position"), (short)(bundles * rs.getInt("quantity")), petid);
item.setOwner(rs.getString("owner"));
item.setExpiration(rs.getLong("expiration"));
item.setGiftFrom(rs.getString("giftFrom"));
@@ -404,7 +414,7 @@ public enum ItemFactory {
ps.setInt(6, item.getPosition());
ps.setInt(7, item.getQuantity());
ps.setString(8, item.getOwner());
ps.setInt(9, item.getPetId());
ps.setObject(9, item.getPetIdForDb(), java.sql.Types.INTEGER);
ps.setInt(10, item.getFlag());
ps.setLong(11, item.getExpiration());
ps.setString(12, item.getGiftFrom());

View File

@@ -98,75 +98,11 @@ public class MaplePet extends Item {
}
}
private static void unreferenceMissingPetsFromInventoryDb() {
PreparedStatement ps = null;
Connection con = null;
try {
con = DatabaseConnection.getConnection();
ps = con.prepareStatement("UPDATE inventoryitems SET petid = -1, expiration = 0 WHERE petid != -1 AND petid NOT IN (SELECT petid FROM pets)");
ps.executeUpdate();
ps.close();
con.close();
} catch(SQLException ex) {
ex.printStackTrace();
} finally {
try {
if(ps != null && !ps.isClosed()) {
ps.close();
}
if(con != null && !con.isClosed()) {
con.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
private static void deleteMissingPetsFromDb() {
PreparedStatement ps = null;
Connection con = null;
try {
con = DatabaseConnection.getConnection();
ps = con.prepareStatement("DELETE FROM pets WHERE petid NOT IN (SELECT petid FROM inventoryitems WHERE petid != -1)");
ps.executeUpdate();
ps.close();
con.close();
} catch(SQLException ex) {
ex.printStackTrace();
} finally {
try {
if(ps != null && !ps.isClosed()) {
ps.close();
}
if(con != null && !con.isClosed()) {
con.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void clearMissingPetsFromDb() {
unreferenceMissingPetsFromInventoryDb();
deleteMissingPetsFromDb();
}
public static void deleteFromDb(MapleCharacter owner, int petid) {
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("DELETE FROM pets WHERE `petid` = ?");
ps.setInt(1, petid);
ps.executeUpdate();
ps.close();
ps = con.prepareStatement("DELETE FROM petignores WHERE `petid` = ?"); // thanks Vcoc for detecting petignores remaining after deletion
PreparedStatement ps = con.prepareStatement("DELETE FROM pets WHERE `petid` = ?"); // thanks Vcoc for detecting petignores remaining after deletion
ps.setInt(1, petid);
ps.executeUpdate();
ps.close();

View File

@@ -41,7 +41,10 @@ public class MapleCashidGenerator {
ResultSet rs = ps.executeQuery();
while (rs.next()) {
existentCashids.add(rs.getInt(1));
int id = rs.getInt(1);
if (!rs.wasNull()) {
existentCashids.add(id);
}
}
rs.close();

View File

@@ -62,7 +62,7 @@ public class SpawnPetProcessor {
long expiration = chr.getInventory(MapleInventoryType.CASH).getItem(slot).getExpiration();
MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, petid, (short) 1, false, false);
MapleInventoryManipulator.addById(c, evolveid, (short) 1, null, petId, expiration);
MaplePet.deleteFromDb(chr, petid);
c.announce(MaplePacketCreator.enableActions());
return;
}

View File

@@ -154,7 +154,6 @@ public class ServerConfig {
public int MAX_AP;
public int MAX_EVENT_LEVELS;
public long BLOCK_NPC_RACE_CONDT;
public long PET_LOOT_UPON_ATTACK;
public int TOT_MOB_QUEST_REQUIREMENT;
public int MOB_REACTOR_REFRESH_TIME;
public int PARTY_SEARCH_REENTRY_LIMIT;

View File

@@ -21,7 +21,6 @@
*/
package net.server;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.security.Security;

View File

@@ -50,7 +50,6 @@ import net.mina.MapleCodecFactory;
import net.server.PlayerStorage;
import net.server.Server;
import net.server.channel.task.*;
import net.server.world.World;
import net.server.world.MapleParty;
@@ -77,7 +76,9 @@ import server.maps.MapleMiniDungeon;
import tools.MaplePacketCreator;
import tools.Pair;
import client.MapleCharacter;
import client.status.MonsterStatusEffect;
import net.server.channel.services.ServiceType;
import net.server.channel.services.ServicesManager;
import net.server.channel.services.task.BaseService;
import server.maps.MapleMiniDungeonInfo;
public final class Channel {
@@ -89,13 +90,7 @@ public final class Channel {
private String ip, serverMessage;
private MapleMapManager mapManager;
private EventScriptManager eventSM;
private MobStatusScheduler mobStatusSchedulers[] = new MobStatusScheduler[YamlConfig.config.server.CHANNEL_LOCKS];
private MobAnimationScheduler mobAnimationSchedulers[] = new MobAnimationScheduler[YamlConfig.config.server.CHANNEL_LOCKS];
private MobClearSkillScheduler mobClearSkillSchedulers[] = new MobClearSkillScheduler[YamlConfig.config.server.CHANNEL_LOCKS];
private MobMistScheduler mobMistSchedulers[] = new MobMistScheduler[YamlConfig.config.server.CHANNEL_LOCKS];
private FaceExpressionScheduler faceExpressionSchedulers[] = new FaceExpressionScheduler[YamlConfig.config.server.CHANNEL_LOCKS];
private EventScheduler eventSchedulers[] = new EventScheduler[YamlConfig.config.server.CHANNEL_LOCKS];
private OverallScheduler channelSchedulers[] = new OverallScheduler[YamlConfig.config.server.CHANNEL_LOCKS];
private ServicesManager services = new ServicesManager();
private Map<Integer, MapleHiredMerchant> hiredMerchants = new HashMap<>();
private final Map<Integer, Integer> storedVars = new HashMap<>();
private Set<Integer> playersAway = new HashSet<>();
@@ -173,17 +168,7 @@ public final class Channel {
dojoTask[i] = null;
}
for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) {
faceLock[i] = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHANNEL_FACEEXPRS, true);
mobStatusSchedulers[i] = new MobStatusScheduler();
mobAnimationSchedulers[i] = new MobAnimationScheduler();
mobClearSkillSchedulers[i] = new MobClearSkillScheduler();
mobMistSchedulers[i] = new MobMistScheduler();
faceExpressionSchedulers[i] = new FaceExpressionScheduler(faceLock[i]);
eventSchedulers[i] = new EventScheduler();
channelSchedulers[i] = new OverallScheduler();
}
services = new ServicesManager();
System.out.println(" Channel " + getId() + ": Listening on port " + port);
} catch (Exception e) {
@@ -191,15 +176,23 @@ public final class Channel {
}
}
public void reloadEventScriptManager(){
public synchronized void reloadEventScriptManager(){
if (finishedShutdown) {
return;
}
eventSM.cancel();
eventSM = null;
eventSM = new EventScriptManager(this, getEvents());
eventSM.init();
}
public final void shutdown() {
public final synchronized void shutdown() {
try {
if (finishedShutdown) {
return;
}
System.out.println("Shutting down Channel " + channel + " on World " + world);
closeAllMerchants();
@@ -227,6 +220,10 @@ public final class Channel {
}
}
private void closeChannelServices() {
services.shutdown();
}
private void closeChannelSchedules() {
for(int i = 0; i < 20; i++) {
if(dojoTask[i] != null) {
@@ -234,44 +231,8 @@ public final class Channel {
dojoTask[i] = null;
}
}
for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; 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;
}
}
closeChannelServices();
disposeLocks();
}
@@ -315,7 +276,11 @@ public final class Channel {
public MapleMapManager getMapFactory() {
return mapManager;
}
public BaseService getServiceAccess(ServiceType sv) {
return services.getAccess(sv).getService();
}
public int getWorld() {
return world;
}
@@ -1030,84 +995,6 @@ public final class Channel {
return !usedMC.contains(getMonsterCarnivalRoom(cpq1, field));
}
private static int getChannelSchedulerIndex(int mapid) {
int section = 1000000000 / YamlConfig.config.server.CHANNEL_LOCKS;
return mapid / section;
}
public void registerMobStatus(int mapid, MonsterStatusEffect mse, Runnable cancelAction, long duration) {
registerMobStatus(mapid, mse, cancelAction, duration, null, -1);
}
public void registerMobStatus(int mapid, MonsterStatusEffect mse, Runnable cancelAction, long duration, Runnable overtimeAction, int overtimeDelay) {
mobStatusSchedulers[getChannelSchedulerIndex(mapid)].registerMobStatus(mse, cancelAction, duration, overtimeAction, overtimeDelay);
}
public void interruptMobStatus(int mapid, MonsterStatusEffect mse) {
mobStatusSchedulers[getChannelSchedulerIndex(mapid)].interruptMobStatus(mse);
}
public boolean registerMobOnAnimationEffect(int mapid, int mobHash, long delay) {
return mobAnimationSchedulers[getChannelSchedulerIndex(mapid)].registerAnimationMode(mobHash, delay);
}
public void registerMobClearSkillAction(int mapid, Runnable runAction, long delay) {
mobClearSkillSchedulers[getChannelSchedulerIndex(mapid)].registerClearSkillAction(runAction, delay);
}
public void registerMobMistCancelAction(int mapid, Runnable runAction, long delay) {
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);
}
public void forceRunOverallAction(int mapid, Runnable runAction) {
channelSchedulers[getChannelSchedulerIndex(mapid)].forceRunDelayedAction(runAction);
}
public void registerFaceExpression(final MapleMap map, final MapleCharacter chr, int emote) {
int lockid = getChannelSchedulerIndex(map.getId());
Runnable cancelAction = new Runnable() {
@Override
public void run() {
if(chr.isLoggedinWorld()) {
map.broadcastMessage(chr, MaplePacketCreator.facialExpression(chr, 0), false);
}
}
};
faceLock[lockid].lock();
try {
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) {
int lockid = getChannelSchedulerIndex(mapid);
faceLock[lockid].lock();
try {
faceExpressionSchedulers[lockid].unregisterFaceExpression(chr.getId());
} finally {
faceLock[lockid].unlock();
}
}
public void debugMarriageStatus() {
System.out.println(" ----- WORLD DATA -----");
getWorldServer().debugMarriageStatus();

View File

@@ -656,7 +656,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
// Find the base damage to base futher calculations on.
// Several skills have their own formula in this section.
long calcDmgMax = 0;
long calcDmgMax;
if(magic && ret.skill != 0) { // thanks onechord for noticing a few false positives stemming from maxdmg as 0
calcDmgMax = (long) (Math.ceil((chr.getTotalMagic() * Math.ceil(chr.getTotalMagic() / 1000.0) + chr.getTotalMagic()) / 30.0) + Math.ceil(chr.getTotalInt() / 200.0));
@@ -846,7 +846,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
}
} else if (ret.skill == Aran.BODY_PRESSURE) {
if (monster != null) {
int bodyPressureDmg = monster.getMaxHp() * SkillFactory.getSkill(Aran.BODY_PRESSURE).getEffect(ret.skilllevel).getDamage() / 100;
int bodyPressureDmg = (int) Math.ceil(monster.getMaxHp() * SkillFactory.getSkill(Aran.BODY_PRESSURE).getEffect(ret.skilllevel).getDamage() / 100.0);
if (bodyPressureDmg > calcDmgMax) {
calcDmgMax = bodyPressureDmg;
}

View File

@@ -26,10 +26,6 @@ import java.util.Iterator;
import java.util.List;
import config.YamlConfig;
import server.MapleStatEffect;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.data.input.SeekableLittleEndianAccessor;
import client.MapleBuffStat;
import client.MapleCharacter;
import client.MapleClient;
@@ -37,7 +33,6 @@ import client.MapleJob;
import client.Skill;
import client.SkillFactory;
import constants.game.GameConstants;
import constants.net.ServerConstants;
import constants.skills.Crusader;
import constants.skills.DawnWarrior;
import constants.skills.DragonKnight;
@@ -45,13 +40,16 @@ import constants.skills.Hero;
import constants.skills.NightWalker;
import constants.skills.Rogue;
import constants.skills.WindArcher;
import server.MapleStatEffect;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.data.input.SeekableLittleEndianAccessor;
public final class CloseRangeDamageHandler extends AbstractDealDamageHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
MapleCharacter chr = c.getPlayer();
//chr.setPetLootCd(currentServerTime());
/*long timeElapsed = currentServerTime() - chr.getAutobanManager().getLastSpam(8);
if(timeElapsed < 300) {

View File

@@ -21,25 +21,24 @@
*/
package net.server.channel.handlers;
import config.YamlConfig;
import server.MapleStatEffect;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
import client.MapleBuffStat;
import client.MapleCharacter;
import client.MapleClient;
import client.Skill;
import client.SkillFactory;
import config.YamlConfig;
import constants.skills.Bishop;
import constants.skills.Evan;
import constants.skills.FPArchMage;
import constants.skills.ILArchMage;
import server.MapleStatEffect;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
public final class MagicDamageHandler extends AbstractDealDamageHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
MapleCharacter chr = c.getPlayer();
//chr.setPetLootCd(currentServerTime());
/*long timeElapsed = currentServerTime() - chr.getAutobanManager().getLastSpam(8);
if(timeElapsed < 300) {

View File

@@ -55,6 +55,10 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
MapleCharacter player = c.getPlayer();
MapleMap map = player.getMap();
if (player.isChangingMaps()) { // thanks Lame for noticing mob movement shuffle (mob OID on different maps) happening on map transitions
return;
}
int objectid = slea.readInt();
short moveid = slea.readShort();
MapleMapObject mmo = map.getMapObject(objectid);

View File

@@ -59,16 +59,19 @@ public final class NPCTalkHandler extends AbstractMaplePacketHandler {
c.announce(MaplePacketCreator.enableActions());
return;
}
if(npc.getId() >= 9100100 && npc.getId() <= 9100200) {
// Custom handling for gachapon scripts to reduce the amount of scripts needed.
// Custom handling to reduce the amount of scripts needed.
if (npc.getId() >= 9100100 && npc.getId() <= 9100200) {
NPCScriptManager.getInstance().start(c, npc.getId(), "gachapon", null);
} else if (npc.getName().endsWith("Maple TV")) {
NPCScriptManager.getInstance().start(c, npc.getId(), "mapleTV", null);
} else {
boolean hasNpcScript = NPCScriptManager.getInstance().start(c, npc.getId(), oid, null);
if (!hasNpcScript) {
if (!npc.hasShop()) {
FilePrinter.printError(FilePrinter.NPC_UNCODED, "NPC " + npc.getName() + "(" + npc.getId() + ") is not coded.");
return;
} else if(c.getPlayer().getShop() != null) {
} else if (c.getPlayer().getShop() != null) {
c.announce(MaplePacketCreator.enableActions());
return;
}

View File

@@ -26,13 +26,11 @@ import java.util.Set;
import client.MapleCharacter;
import client.MapleClient;
import client.inventory.MaplePet;
import config.YamlConfig;
import net.AbstractMaplePacketHandler;
import server.maps.MapleMapItem;
import server.maps.MapleMapObject;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
import constants.net.ServerConstants;
/**
* @author TheRamon
@@ -42,10 +40,6 @@ public final class PetLootHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
MapleCharacter chr = c.getPlayer();
if(currentServerTime() - chr.getPetLootCd() < YamlConfig.config.server.PET_LOOT_UPON_ATTACK) {
c.announce(MaplePacketCreator.enableActions());
return;
}
int petIndex = chr.getPetIndex(slea.readInt());
MaplePet pet = chr.getPet(petIndex);

View File

@@ -21,13 +21,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.server.channel.handlers;
import client.inventory.manipulator.MapleInventoryManipulator;
import config.YamlConfig;
import server.MapleItemInformationProvider;
import server.MapleStatEffect;
import tools.MaplePacketCreator;
import tools.Randomizer;
import tools.data.input.SeekableLittleEndianAccessor;
import client.MapleBuffStat;
import client.MapleCharacter;
import client.MapleClient;
@@ -37,8 +30,9 @@ import client.inventory.Item;
import client.inventory.MapleInventory;
import client.inventory.MapleInventoryType;
import client.inventory.MapleWeaponType;
import client.inventory.manipulator.MapleInventoryManipulator;
import config.YamlConfig;
import constants.inventory.ItemConstants;
import constants.net.ServerConstants;
import constants.skills.Aran;
import constants.skills.Buccaneer;
import constants.skills.NightLord;
@@ -46,13 +40,18 @@ import constants.skills.NightWalker;
import constants.skills.Shadower;
import constants.skills.ThunderBreaker;
import constants.skills.WindArcher;
import server.MapleItemInformationProvider;
import server.MapleStatEffect;
import tools.MaplePacketCreator;
import tools.Randomizer;
import tools.data.input.SeekableLittleEndianAccessor;
public final class RangedAttackHandler extends AbstractDealDamageHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
MapleCharacter chr = c.getPlayer();
//chr.setPetLootCd(currentServerTime());
/*long timeElapsed = currentServerTime() - chr.getAutobanManager().getLastSpam(8);
if(timeElapsed < 300) {

View File

@@ -213,7 +213,7 @@ public final class TakeDamageHandler extends AbstractMaplePacketHandler {
map.broadcastMessage(chr, MaplePacketCreator.damageMonster(oid, bouncedamage), false, true);
attacker.aggroMonsterDamage(chr, bouncedamage);
}
MapleStatEffect bPressure = chr.getBuffEffect(MapleBuffStat.COMBO_BARRIER);
MapleStatEffect bPressure = chr.getBuffEffect(MapleBuffStat.BODY_PRESSURE);
if (bPressure != null) {
Skill skill = SkillFactory.getSkill(Aran.BODY_PRESSURE);
if (!attacker.alreadyBuffedStats().contains(MonsterStatus.NEUTRALISE)) {

View File

@@ -17,7 +17,7 @@
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.task;
package net.server.channel.services;
import config.YamlConfig;
import java.util.Collections;

View File

@@ -17,7 +17,7 @@
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.task;
package net.server.channel.services;
import java.util.List;

View File

@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2018 RonanLana
This file is part of the HeavenMS MapleStory Server, commands OdinMS-based
Copyleft (L) 2016 - 2019 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
@@ -17,24 +17,36 @@
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.task;
package net.server.channel.services;
import net.server.audit.locks.MonitoredLockType;
import net.server.channel.services.task.BaseService;
/**
*
* @author Ronan
*/
public class OverallScheduler extends BaseScheduler {
public OverallScheduler() {
super(MonitoredLockType.CHANNEL_OVERALL);
public class Service <T extends BaseService> {
private Class<T> cls;
private BaseService service;
public Service(Class<T> s) {
try {
cls = s;
service = (BaseService) cls.getConstructor().newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
public void registerDelayedAction(Runnable runAction, long delay) {
registerEntry(runAction, runAction, delay);
public T getService() {
return cls.cast(service);
}
public void forceRunDelayedAction(Runnable runAction) {
interruptEntry(runAction);
public void dispose() {
service.dispose();
service = null;
}
}

View File

@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2018 RonanLana
This file is part of the HeavenMS MapleStory Server, commands OdinMS-based
Copyleft (L) 2016 - 2019 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
@@ -17,20 +17,32 @@
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.task;
package net.server.channel.services;
import net.server.audit.locks.MonitoredLockType;
import net.server.channel.services.task.*;
/**
*
* @author Ronan
*/
public class MobMistScheduler extends BaseScheduler {
public MobMistScheduler() {
super(MonitoredLockType.CHANNEL_MOBMIST);
public enum ServiceType {
MOB_STATUS(MobStatusService.class),
MOB_ANIMATION(MobAnimationService.class),
MOB_CLEAR_SKILL(MobClearSkillService.class),
MOB_MIST(MobMistService.class),
FACE_EXPRESSION(FaceExpressionService.class),
EVENT(EventService.class),
OVERALL(OverallService.class);
private Class<? extends BaseService> s;
private ServiceType(Class<? extends BaseService> service) {
s = service;
}
public void registerMistCancelAction(Runnable runAction, long delay) {
registerEntry(runAction, runAction, delay);
public Service createService() {
return new Service(s);
}
}

View File

@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2018 RonanLana
This file is part of the HeavenMS MapleStory Server, commands OdinMS-based
Copyleft (L) 2016 - 2019 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
@@ -17,26 +17,34 @@
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.task;
import java.util.Collections;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReentrantLock;
package net.server.channel.services;
/**
*
* @author Ronan
*/
public class FaceExpressionScheduler extends BaseScheduler {
public FaceExpressionScheduler(final MonitoredReentrantLock channelFaceLock) {
super(MonitoredLockType.CHANNEL_FACESCHDL, Collections.singletonList(channelFaceLock));
public class ServicesManager {
private Service[] services;
public ServicesManager() {
ServiceType[] serviceTypes = ServiceType.values();
services = new Service[serviceTypes.length];
for (ServiceType type : serviceTypes) {
services[type.ordinal()] = type.createService();
}
}
public void registerFaceExpression(Integer characterId, Runnable runAction) {
registerEntry(characterId, runAction, 5000);
public Service getAccess(ServiceType s) {
return services[s.ordinal()];
}
public void unregisterFaceExpression(Integer characterId) {
interruptEntry(characterId);
public void shutdown() {
for (int i = 0; i < ServiceType.values().length; i++) {
services[i].dispose();
}
services = null;
}
}

View File

@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2018 RonanLana
This file is part of the HeavenMS MapleStory Server, commands OdinMS-based
Copyleft (L) 2016 - 2019 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
@@ -17,20 +17,21 @@
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.task;
package net.server.channel.services.task;
import net.server.audit.locks.MonitoredLockType;
import config.YamlConfig;
/**
*
* @author Ronan
*/
public class EventScheduler extends BaseScheduler {
public EventScheduler() {
super(MonitoredLockType.CHANNEL_EVENTS);
public abstract class BaseService {
protected static int getChannelSchedulerIndex(int mapid) {
int section = 1000000000 / YamlConfig.config.server.CHANNEL_LOCKS;
return mapid / section;
}
public void registerDelayedAction(Runnable runAction, long delay) {
registerEntry(runAction, runAction, delay);
}
public abstract void dispose();
}

View File

@@ -0,0 +1,65 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 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.services.task;
import config.YamlConfig;
import net.server.audit.locks.MonitoredLockType;
import net.server.channel.services.BaseScheduler;
/**
*
* @author Ronan
*/
public class EventService extends BaseService {
private EventScheduler eventSchedulers[] = new EventScheduler[YamlConfig.config.server.CHANNEL_LOCKS];
public EventService() {
for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) {
eventSchedulers[i] = new EventScheduler();
}
}
public void dispose() {
for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) {
if(eventSchedulers[i] != null) {
eventSchedulers[i].dispose();
eventSchedulers[i] = null;
}
}
}
public void registerEventAction(int mapid, Runnable runAction, long delay) {
eventSchedulers[getChannelSchedulerIndex(mapid)].registerDelayedAction(runAction, delay);
}
private class EventScheduler extends BaseScheduler {
public EventScheduler() {
super(MonitoredLockType.CHANNEL_EVENTS);
}
public void registerDelayedAction(Runnable runAction, long delay) {
registerEntry(runAction, runAction, delay);
}
}
}

View File

@@ -0,0 +1,128 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 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.services.task;
import client.MapleCharacter;
import config.YamlConfig;
import java.util.Collections;
import net.server.audit.LockCollector;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReentrantLock;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import net.server.channel.services.BaseScheduler;
import server.maps.MapleMap;
import tools.MaplePacketCreator;
/**
*
* @author Ronan
*/
public class FaceExpressionService extends BaseService {
private FaceExpressionScheduler faceExpressionSchedulers[] = new FaceExpressionScheduler[YamlConfig.config.server.CHANNEL_LOCKS];
private MonitoredReentrantLock faceLock[] = new MonitoredReentrantLock[YamlConfig.config.server.CHANNEL_LOCKS];
public FaceExpressionService() {
for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) {
faceLock[i] = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHANNEL_FACEEXPRS, true);
faceExpressionSchedulers[i] = new FaceExpressionScheduler(faceLock[i]);
}
}
private void emptyLocks() {
for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) {
faceLock[i] = faceLock[i].dispose();
}
}
private void disposeLocks() {
LockCollector.getInstance().registerDisposeAction(new Runnable() {
@Override
public void run() {
emptyLocks();
}
});
}
public void dispose() {
for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) {
if(faceExpressionSchedulers[i] != null) {
faceExpressionSchedulers[i].dispose();
faceExpressionSchedulers[i] = null;
}
}
disposeLocks();
}
public void registerFaceExpression(final MapleMap map, final MapleCharacter chr, int emote) {
int lockid = getChannelSchedulerIndex(map.getId());
Runnable cancelAction = new Runnable() {
@Override
public void run() {
if(chr.isLoggedinWorld()) {
map.broadcastMessage(chr, MaplePacketCreator.facialExpression(chr, 0), false);
}
}
};
faceLock[lockid].lock();
try {
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) {
int lockid = getChannelSchedulerIndex(mapid);
faceLock[lockid].lock();
try {
faceExpressionSchedulers[lockid].unregisterFaceExpression(chr.getId());
} finally {
faceLock[lockid].unlock();
}
}
private class FaceExpressionScheduler extends BaseScheduler {
public FaceExpressionScheduler(final MonitoredReentrantLock channelFaceLock) {
super(MonitoredLockType.CHANNEL_FACESCHDL, Collections.singletonList(channelFaceLock));
}
public void registerFaceExpression(Integer characterId, Runnable runAction) {
registerEntry(characterId, runAction, 5000);
}
public void unregisterFaceExpression(Integer characterId) {
interruptEntry(characterId);
}
}
}

View File

@@ -0,0 +1,125 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 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.services.task;
import config.YamlConfig;
import net.server.audit.locks.MonitoredLockType;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import net.server.audit.LockCollector;
import net.server.audit.locks.MonitoredReentrantLock;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import net.server.channel.services.BaseScheduler;
import net.server.channel.services.SchedulerListener;
/**
*
* @author Ronan
*/
public class MobAnimationService extends BaseService {
private MobAnimationScheduler mobAnimationSchedulers[] = new MobAnimationScheduler[YamlConfig.config.server.CHANNEL_LOCKS];
public MobAnimationService() {
for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) {
mobAnimationSchedulers[i] = new MobAnimationScheduler();
}
}
public void dispose() {
for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) {
if(mobAnimationSchedulers[i] != null) {
mobAnimationSchedulers[i].dispose();
mobAnimationSchedulers[i] = null;
}
}
}
public boolean registerMobOnAnimationEffect(int mapid, int mobHash, long delay) {
return mobAnimationSchedulers[getChannelSchedulerIndex(mapid)].registerAnimationMode(mobHash, delay);
}
private static Runnable r = new Runnable() {
@Override
public void run() {} // do nothing
};
private class MobAnimationScheduler extends BaseScheduler {
Set<Integer> onAnimationMobs = new HashSet<>(1000);
private MonitoredReentrantLock animationLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHANNEL_MOBANIMAT, true);
public MobAnimationScheduler() {
super(MonitoredLockType.CHANNEL_MOBACTION);
super.addListener(new SchedulerListener() {
@Override
public void removedScheduledEntries(List<Object> toRemove, boolean update) {
animationLock.lock();
try {
for(Object hashObj : toRemove) {
Integer mobHash = (Integer) hashObj;
onAnimationMobs.remove(mobHash);
}
} finally {
animationLock.unlock();
}
}
});
}
public boolean registerAnimationMode(Integer mobHash, long animationTime) {
animationLock.lock();
try {
if(onAnimationMobs.contains(mobHash)) {
return false;
}
registerEntry(mobHash, r, animationTime);
onAnimationMobs.add(mobHash);
return true;
} finally {
animationLock.unlock();
}
}
@Override
public void dispose() {
disposeLocks();
super.dispose();
}
private void disposeLocks() {
LockCollector.getInstance().registerDisposeAction(new Runnable() {
@Override
public void run() {
emptyLocks();
}
});
}
private void emptyLocks() {
animationLock = animationLock.dispose();
}
}
}

View File

@@ -0,0 +1,65 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 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.services.task;
import config.YamlConfig;
import net.server.audit.locks.MonitoredLockType;
import net.server.channel.services.BaseScheduler;
/**
*
* @author Ronan
*/
public class MobClearSkillService extends BaseService {
private MobClearSkillScheduler mobClearSkillSchedulers[] = new MobClearSkillScheduler[YamlConfig.config.server.CHANNEL_LOCKS];
public MobClearSkillService() {
for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) {
mobClearSkillSchedulers[i] = new MobClearSkillScheduler();
}
}
public void dispose() {
for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) {
if(mobClearSkillSchedulers[i] != null) {
mobClearSkillSchedulers[i].dispose();
mobClearSkillSchedulers[i] = null;
}
}
}
public void registerMobClearSkillAction(int mapid, Runnable runAction, long delay) {
mobClearSkillSchedulers[getChannelSchedulerIndex(mapid)].registerClearSkillAction(runAction, delay);
}
private class MobClearSkillScheduler extends BaseScheduler {
public MobClearSkillScheduler() {
super(MonitoredLockType.CHANNEL_MOBSKILL);
}
public void registerClearSkillAction(Runnable runAction, long delay) {
registerEntry(runAction, runAction, delay);
}
}
}

View File

@@ -0,0 +1,65 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 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.services.task;
import config.YamlConfig;
import net.server.audit.locks.MonitoredLockType;
import net.server.channel.services.BaseScheduler;
/**
*
* @author Ronan
*/
public class MobMistService extends BaseService {
private MobMistScheduler mobMistSchedulers[] = new MobMistScheduler[YamlConfig.config.server.CHANNEL_LOCKS];
public MobMistService() {
for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) {
mobMistSchedulers[i] = new MobMistScheduler();
}
}
public void dispose() {
for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) {
if(mobMistSchedulers[i] != null) {
mobMistSchedulers[i].dispose();
mobMistSchedulers[i] = null;
}
}
}
public void registerMobMistCancelAction(int mapid, Runnable runAction, long delay) {
mobMistSchedulers[getChannelSchedulerIndex(mapid)].registerMistCancelAction(runAction, delay);
}
private class MobMistScheduler extends BaseScheduler {
public MobMistScheduler() {
super(MonitoredLockType.CHANNEL_MOBMIST);
}
public void registerMistCancelAction(Runnable runAction, long delay) {
registerEntry(runAction, runAction, delay);
}
}
}

View File

@@ -0,0 +1,168 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 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.services.task;
import client.status.MonsterStatusEffect;
import config.YamlConfig;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import net.server.audit.LockCollector;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReentrantLock;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import net.server.channel.services.BaseScheduler;
import net.server.channel.services.SchedulerListener;
/**
*
* @author Ronan
*/
public class MobStatusService extends BaseService {
private MobStatusScheduler mobStatusSchedulers[] = new MobStatusScheduler[YamlConfig.config.server.CHANNEL_LOCKS];
public MobStatusService() {
for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) {
mobStatusSchedulers[i] = new MobStatusScheduler();
}
}
public void dispose() {
for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) {
if(mobStatusSchedulers[i] != null) {
mobStatusSchedulers[i].dispose();
mobStatusSchedulers[i] = null;
}
}
}
public void registerMobStatus(int mapid, MonsterStatusEffect mse, Runnable cancelAction, long duration) {
registerMobStatus(mapid, mse, cancelAction, duration, null, -1);
}
public void registerMobStatus(int mapid, MonsterStatusEffect mse, Runnable cancelAction, long duration, Runnable overtimeAction, int overtimeDelay) {
mobStatusSchedulers[getChannelSchedulerIndex(mapid)].registerMobStatus(mse, cancelAction, duration, overtimeAction, overtimeDelay);
}
public void interruptMobStatus(int mapid, MonsterStatusEffect mse) {
mobStatusSchedulers[getChannelSchedulerIndex(mapid)].interruptMobStatus(mse);
}
private class MobStatusScheduler extends BaseScheduler {
private Map<MonsterStatusEffect, MobStatusOvertimeEntry> registeredMobStatusOvertime = new HashMap<>();
private MonitoredReentrantLock overtimeStatusLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHANNEL_OVTSTATUS, true);
private class MobStatusOvertimeEntry {
private int procCount;
private int procLimit;
private Runnable r;
protected MobStatusOvertimeEntry(int delay, Runnable run) {
procCount = 0;
procLimit = (int)Math.ceil((float) delay / YamlConfig.config.server.MOB_STATUS_MONITOR_PROC);
r = run;
}
protected void update(List<Runnable> toRun) {
procCount++;
if(procCount >= procLimit) {
procCount = 0;
toRun.add(r);
}
}
}
public MobStatusScheduler() {
super(MonitoredLockType.CHANNEL_MOBSTATUS);
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) {
MonsterStatusEffect mse = (MonsterStatusEffect) mseo;
registeredMobStatusOvertime.remove(mse);
}
if(update) {
// 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(toRun);
}
}
} finally {
overtimeStatusLock.unlock();
}
for(Runnable r : toRun) {
r.run();
}
}
});
}
public void registerMobStatus(MonsterStatusEffect mse, Runnable cancelStatus, long duration, Runnable overtimeStatus, int overtimeDelay) {
if(overtimeStatus != null) {
MobStatusOvertimeEntry mdoe = new MobStatusOvertimeEntry(overtimeDelay, overtimeStatus);
overtimeStatusLock.lock();
try {
registeredMobStatusOvertime.put(mse, mdoe);
} finally {
overtimeStatusLock.unlock();
}
}
registerEntry(mse, cancelStatus, duration);
}
public void interruptMobStatus(MonsterStatusEffect mse) {
interruptEntry(mse);
}
@Override
public void dispose() {
disposeLocks();
super.dispose();
}
private void disposeLocks() {
LockCollector.getInstance().registerDisposeAction(new Runnable() {
@Override
public void run() {
emptyLocks();
}
});
}
private void emptyLocks() {
overtimeStatusLock = overtimeStatusLock.dispose();
}
}
}

View File

@@ -0,0 +1,74 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 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.services.task;
import config.YamlConfig;
import net.server.audit.locks.MonitoredLockType;
import net.server.channel.services.BaseScheduler;
/**
*
* @author Ronan
*/
public class OverallService extends BaseService { // thanks Alex for suggesting a refactor over the several channel schedulers unnecessarily populating the Channel class
private OverallScheduler channelSchedulers[] = new OverallScheduler[YamlConfig.config.server.CHANNEL_LOCKS];
public OverallService() {
for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) {
channelSchedulers[i] = new OverallScheduler();
}
}
public void dispose() {
for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) {
if(channelSchedulers[i] != null) {
channelSchedulers[i].dispose();
channelSchedulers[i] = null;
}
}
}
public void registerOverallAction(int mapid, Runnable runAction, long delay) {
channelSchedulers[getChannelSchedulerIndex(mapid)].registerDelayedAction(runAction, delay);
}
public void forceRunOverallAction(int mapid, Runnable runAction) {
channelSchedulers[getChannelSchedulerIndex(mapid)].forceRunDelayedAction(runAction);
}
public class OverallScheduler extends BaseScheduler {
public OverallScheduler() {
super(MonitoredLockType.CHANNEL_OVERALL);
}
public void registerDelayedAction(Runnable runAction, long delay) {
registerEntry(runAction, runAction, delay);
}
public void forceRunDelayedAction(Runnable runAction) {
interruptEntry(runAction);
}
}
}

View File

@@ -1,96 +0,0 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 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.task;
import net.server.audit.locks.MonitoredLockType;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import net.server.audit.LockCollector;
import net.server.audit.locks.MonitoredReentrantLock;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
/**
*
* @author Ronan
*/
public class MobAnimationScheduler extends BaseScheduler {
Set<Integer> onAnimationMobs = new HashSet<>(1000);
private MonitoredReentrantLock animationLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHANNEL_MOBANIMAT, true);
private static Runnable r = new Runnable() {
@Override
public void run() {} // do nothing
};
public MobAnimationScheduler() {
super(MonitoredLockType.CHANNEL_MOBACTION);
super.addListener(new SchedulerListener() {
@Override
public void removedScheduledEntries(List<Object> toRemove, boolean update) {
animationLock.lock();
try {
for(Object hashObj : toRemove) {
Integer mobHash = (Integer) hashObj;
onAnimationMobs.remove(mobHash);
}
} finally {
animationLock.unlock();
}
}
});
}
public boolean registerAnimationMode(Integer mobHash, long animationTime) {
animationLock.lock();
try {
if(onAnimationMobs.contains(mobHash)) {
return false;
}
registerEntry(mobHash, r, animationTime);
onAnimationMobs.add(mobHash);
return true;
} finally {
animationLock.unlock();
}
}
@Override
public void dispose() {
disposeLocks();
super.dispose();
}
private void disposeLocks() {
LockCollector.getInstance().registerDisposeAction(new Runnable() {
@Override
public void run() {
emptyLocks();
}
});
}
private void emptyLocks() {
animationLock = animationLock.dispose();
}
}

View File

@@ -1,36 +0,0 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 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.task;
import net.server.audit.locks.MonitoredLockType;
/**
*
* @author Ronan
*/
public class MobClearSkillScheduler extends BaseScheduler {
public MobClearSkillScheduler() {
super(MonitoredLockType.CHANNEL_MOBSKILL);
}
public void registerClearSkillAction(Runnable runAction, long delay) {
registerEntry(runAction, runAction, delay);
}
}

View File

@@ -1,131 +0,0 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 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.task;
import client.status.MonsterStatusEffect;
import config.YamlConfig;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import net.server.audit.LockCollector;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReentrantLock;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
/**
*
* @author Ronan
*/
public class MobStatusScheduler extends BaseScheduler {
private Map<MonsterStatusEffect, MobStatusOvertimeEntry> registeredMobStatusOvertime = new HashMap<>();
private MonitoredReentrantLock overtimeStatusLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHANNEL_OVTSTATUS, true);
private class MobStatusOvertimeEntry {
private int procCount;
private int procLimit;
private Runnable r;
protected MobStatusOvertimeEntry(int delay, Runnable run) {
procCount = 0;
procLimit = (int)Math.ceil((float) delay / YamlConfig.config.server.MOB_STATUS_MONITOR_PROC);
r = run;
}
protected void update(List<Runnable> toRun) {
procCount++;
if(procCount >= procLimit) {
procCount = 0;
toRun.add(r);
}
}
}
public MobStatusScheduler() {
super(MonitoredLockType.CHANNEL_MOBSTATUS);
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) {
MonsterStatusEffect mse = (MonsterStatusEffect) mseo;
registeredMobStatusOvertime.remove(mse);
}
if(update) {
// 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(toRun);
}
}
} finally {
overtimeStatusLock.unlock();
}
for(Runnable r : toRun) {
r.run();
}
}
});
}
public void registerMobStatus(MonsterStatusEffect mse, Runnable cancelStatus, long duration, Runnable overtimeStatus, int overtimeDelay) {
if(overtimeStatus != null) {
MobStatusOvertimeEntry mdoe = new MobStatusOvertimeEntry(overtimeDelay, overtimeStatus);
overtimeStatusLock.lock();
try {
registeredMobStatusOvertime.put(mse, mdoe);
} finally {
overtimeStatusLock.unlock();
}
}
registerEntry(mse, cancelStatus, duration);
}
public void interruptMobStatus(MonsterStatusEffect mse) {
interruptEntry(mse);
}
@Override
public void dispose() {
disposeLocks();
super.dispose();
}
private void disposeLocks() {
LockCollector.getInstance().registerDisposeAction(new Runnable() {
@Override
public void run() {
emptyLocks();
}
});
}
private void emptyLocks() {
overtimeStatusLock = overtimeStatusLock.dispose();
}
}

View File

@@ -107,7 +107,12 @@ public class MapleDataTool {
} else if (data.getType() == MapleDataType.STRING) {
return Integer.parseInt(getString(data));
} else {
return ((Integer) data.getData()).intValue();
Object numData = data.getData();
if (numData instanceof Integer) {
return (Integer) numData;
} else {
return (Short) numData;
}
}
}

View File

@@ -27,8 +27,8 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import scripting.AbstractPlayerInteraction;
import scripting.map.MapScriptManager;
import server.maps.MaplePortal;
import server.quest.MapleQuest;
import tools.DatabaseConnection;
import tools.MaplePacketCreator;
@@ -44,6 +44,11 @@ public class PortalPlayerInteraction extends AbstractPlayerInteraction {
public MaplePortal getPortal() {
return portal;
}
public void runMapScript() {
MapScriptManager msm = MapScriptManager.getInstance();
msm.runMapScript(c, "onUserEnter/" + portal.getScriptName(), false);
}
public boolean hasLevel30Character() {
PreparedStatement ps = null;

View File

@@ -21,7 +21,6 @@ package server;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.Connection;
@@ -38,6 +37,9 @@ import java.util.Scanner;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import provider.MapleData;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import tools.DatabaseConnection;
/**
@@ -45,6 +47,7 @@ import tools.DatabaseConnection;
* @author RonanLana
*/
public class MapleSkillbookInformationProvider {
private final static MapleSkillbookInformationProvider instance = new MapleSkillbookInformationProvider();
public static MapleSkillbookInformationProvider getInstance() {
@@ -86,167 +89,31 @@ public class MapleSkillbookInformationProvider {
static {
loadSkillbooks();
}
private static String getName(String token) {
int i, j;
char[] dest;
String d;
i = token.lastIndexOf("name");
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
if(j < i) { //node value containing 'name' in it's scope, cheap fix since we don't deal with strings anyway
System.out.println("[CRITICAL] Found this '" + token + "'");
return "0";
}
dest = new char[initialStringLength];
token.getChars(i, j, dest, 0);
d = new String(dest);
return(d.trim());
}
private static String getValue(String token) {
int i, j;
char[] dest;
String d;
i = token.lastIndexOf("value");
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[initialStringLength];
token.getChars(i, j, dest, 0);
d = new String(dest);
return(d.trim());
}
private static void forwardCursor(int st) {
String line = null;
try {
while(status >= st && (line = bufferedReader.readLine()) != null) {
simpleToken(line);
}
}
catch(Exception e) {
e.printStackTrace();
}
}
private static void simpleToken(String token) {
if(token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir") && !token.endsWith("/>")) { // '\>' XML node description not being accounted, issue found thanks to Robin Schulz, CanIGetaPR
status += 1;
}
}
private static void inspectQuestItemList(int st) {
String line = null;
try {
while(status >= st && (line = bufferedReader.readLine()) != null) {
readItemToken(line);
}
}
catch(Exception e) {
e.printStackTrace();
}
}
public static boolean isSkillBook(int itemid) {
return itemid >= skillbookMinItemid && itemid < skillbookMaxItemid;
}
private static void processCurrentItem() {
try {
if(isSkillBook(currentItemid)) {
if(currentCount > 0) {
foundSkillbooks.put(currentItemid, SkillBookEntry.QUEST);
}
}
} catch(Exception e) {}
}
private static void readItemToken(String token) {
if(token.contains("/imgdir")) {
status -= 1;
processCurrentItem();
currentItemid = 0;
currentCount = 0;
}
else if(token.contains("imgdir") && !token.endsWith("/>")) {
status += 1;
}
else {
String d = getName(token);
if(d.equals("id")) {
currentItemid = Integer.parseInt(getValue(token));
} else if(d.equals("count")) {
currentCount = Integer.parseInt(getValue(token));
}
}
}
private static void translateActToken(String token) {
String d;
int temp;
if(token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir") && !token.endsWith("/>")) {
if(status == 1) { //getting QuestId
d = getName(token);
questId = Integer.parseInt(d);
}
else if(status == 2) { //start/complete
d = getName(token);
isCompleteState = Integer.parseInt(d);
}
else if(status == 3) {
d = getName(token);
if(d.contains("item")) {
temp = status;
inspectQuestItemList(temp);
} else {
forwardCursor(status);
}
}
status += 1;
}
}
private static void fetchSkillbooksFromQuests() {
String line = "";
int lineNumber = 0; // add line number, thanks to Alex (CanIGetaPR)
MapleData actData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "Quest.wz")).getData("Act.img");
try {
fileReader = new InputStreamReader(new FileInputStream(wzPath + "/Quest.wz/Act.img.xml"), "UTF-8");
bufferedReader = new BufferedReader(fileReader);
while((line = bufferedReader.readLine()) != null) {
lineNumber++;
translateActToken(line);
for (MapleData questData : actData.getChildren()) {
for (MapleData questStatusData : questData.getChildren()) {
for (MapleData questNodeData : questStatusData.getChildren()) {
if (questNodeData.getName().contentEquals("item")) {
for (MapleData questItemData : questNodeData.getChildren()) {
int itemid = MapleDataTool.getInt("id", questItemData, 0);
int itemcount = MapleDataTool.getInt("count", questItemData, 0);
if (isSkillBook(itemid) && itemcount > 0) {
foundSkillbooks.put(currentItemid, SkillBookEntry.QUEST);
}
}
break;
}
}
}
bufferedReader.close();
fileReader.close();
} catch(IOException ioe) {
System.out.println("Failed to read Quest.wz file. Line " + lineNumber + ": " + line);
ioe.printStackTrace();
} catch (Exception e) {
System.out.println("Failed to parse Quest.wz XML file."); // catch this exception, thanks to YonhNi
}
}

View File

@@ -163,11 +163,11 @@ public class MapleLifeFactory {
}
stats.setFirstAttack(firstAttack > 0);
stats.setDropPeriod(MapleDataTool.getIntConvert("dropItemPeriod", monsterInfoData, stats.getDropPeriod() / 10000) * 10000);
if (!(stats.isBoss() && !hpbarBosses.contains(mid))) { // thanks Riizade, Z1peR, Anesthetic for noticing some bosses crashing players due to missing requirements
stats.setTagColor(MapleDataTool.getIntConvert("hpTagColor", monsterInfoData, 0));
stats.setTagBgColor(MapleDataTool.getIntConvert("hpTagBgcolor", monsterInfoData, 0));
}
// thanks yuxaij, Riizade, Z1peR, Anesthetic for noticing some bosses crashing players due to missing requirements
boolean hpbarBoss = stats.isBoss() && hpbarBosses.contains(mid);
stats.setTagColor(hpbarBoss ? MapleDataTool.getIntConvert("hpTagColor", monsterInfoData, 0) : 0);
stats.setTagBgColor(hpbarBoss ? MapleDataTool.getIntConvert("hpTagBgcolor", monsterInfoData, 0) : 0);
for (MapleData idata : monsterData) {
if (!idata.getName().equals("info")) {

View File

@@ -72,6 +72,11 @@ import tools.Randomizer;
import net.server.audit.LockCollector;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import net.server.channel.services.ServiceType;
import net.server.channel.services.task.MobAnimationService;
import net.server.channel.services.task.MobClearSkillService;
import net.server.channel.services.task.MobStatusService;
import net.server.channel.services.task.OverallService;
import net.server.coordinator.world.MapleMonsterAggroCoordinator;
import server.MapleStatEffect;
import server.loot.MapleLootManager;
@@ -333,7 +338,8 @@ public class MapleMonster extends AbstractLoadedMapleLife {
}
if(animationTime > 0) {
return map.getChannelServer().registerMobOnAnimationEffect(map.getId(), this.hashCode(), animationTime);
MobAnimationService service = (MobAnimationService) map.getChannelServer().getServiceAccess(ServiceType.MOB_ANIMATION);
return service.registerMobOnAnimationEffect(map.getId(), this.hashCode(), animationTime);
} else {
return true;
}
@@ -1197,7 +1203,8 @@ public class MapleMonster extends AbstractLoadedMapleLife {
if (oldEffect != null) {
oldEffect.removeActiveStatus(stat);
if (oldEffect.getStati().isEmpty()) {
ch.interruptMobStatus(mapid, oldEffect);
MobStatusService service = (MobStatusService) map.getChannelServer().getServiceAccess(ServiceType.MOB_STATUS);
service.interruptMobStatus(mapid, oldEffect);
}
}
}
@@ -1303,7 +1310,8 @@ public class MapleMonster extends AbstractLoadedMapleLife {
statiLock.unlock();
}
ch.registerMobStatus(mapid, status, cancelTask, duration + animationTime - 100, overtimeAction, overtimeDelay);
MobStatusService service = (MobStatusService) map.getChannelServer().getServiceAccess(ServiceType.MOB_STATUS);
service.registerMobStatus(mapid, status, cancelTask, duration + animationTime - 100, overtimeAction, overtimeDelay);
return true;
}
@@ -1354,7 +1362,8 @@ public class MapleMonster extends AbstractLoadedMapleLife {
statiLock.unlock();
}
map.getChannelServer().registerMobStatus(map.getId(), effect, cancelTask, duration);
MobStatusService service = (MobStatusService) map.getChannelServer().getServiceAccess(ServiceType.MOB_STATUS);
service.registerMobStatus(map.getId(), effect, cancelTask, duration);
}
public void refreshMobPosition() {
@@ -1552,7 +1561,8 @@ public class MapleMonster extends AbstractLoadedMapleLife {
}
};
mmap.getChannelServer().registerMobClearSkillAction(mmap.getId(), r, cooltime);
MobClearSkillService service = (MobClearSkillService) map.getChannelServer().getServiceAccess(ServiceType.MOB_CLEAR_SKILL);
service.registerMobClearSkillAction(mmap.getId(), r, cooltime);
}
private void clearSkill(int skillId, int level) {
@@ -1619,8 +1629,9 @@ public class MapleMonster extends AbstractLoadedMapleLife {
mons.clearAttack(attackPos);
}
};
mmap.getChannelServer().registerMobClearSkillAction(mmap.getId(), r, cooltime);
MobClearSkillService service = (MobClearSkillService) map.getChannelServer().getServiceAccess(ServiceType.MOB_CLEAR_SKILL);
service.registerMobClearSkillAction(mmap.getId(), r, cooltime);
} finally {
monsterLock.unlock();
}
@@ -1667,7 +1678,8 @@ public class MapleMonster extends AbstractLoadedMapleLife {
public void run() {
int curHp = hp.get();
if(curHp <= 1) {
map.getChannelServer().interruptMobStatus(map.getId(), status);
MobStatusService service = (MobStatusService) map.getChannelServer().getServiceAccess(ServiceType.MOB_STATUS);
service.interruptMobStatus(map.getId(), status);
return;
}
@@ -1675,7 +1687,8 @@ public class MapleMonster extends AbstractLoadedMapleLife {
if (damage >= curHp) {
damage = curHp - 1;
if (type == 1 || type == 2) {
map.getChannelServer().interruptMobStatus(map.getId(), status);
MobStatusService service = (MobStatusService) map.getChannelServer().getServiceAccess(ServiceType.MOB_STATUS);
service.interruptMobStatus(map.getId(), status);
}
}
if (damage > 0) {
@@ -1731,7 +1744,8 @@ public class MapleMonster extends AbstractLoadedMapleLife {
}
};
mmap.getChannelServer().registerMobClearSkillAction(mmap.getId(), r, milli);
MobClearSkillService service = (MobClearSkillService) mmap.getChannelServer().getServiceAccess(ServiceType.MOB_CLEAR_SKILL);
service.registerMobClearSkillAction(mmap.getId(), r, milli);
}
} finally {
monsterLock.unlock();
@@ -2213,7 +2227,8 @@ public class MapleMonster extends AbstractLoadedMapleLife {
};
// had to schedule this since mob wouldn't stick to puppet aggro who knows why
this.getMap().getChannelServer().registerOverallAction(this.getMap().getId(), r, YamlConfig.config.server.UPDATE_INTERVAL);
OverallService service = (OverallService) this.getMap().getChannelServer().getServiceAccess(ServiceType.OVERALL);
service.registerOverallAction(this.getMap().getId(), r, YamlConfig.config.server.UPDATE_INTERVAL);
}
/**

View File

@@ -32,6 +32,8 @@ import client.status.MonsterStatus;
import constants.game.GameConstants;
import java.util.LinkedList;
import java.util.Map;
import net.server.channel.services.ServiceType;
import net.server.channel.services.task.OverallService;
import tools.Randomizer;
import server.maps.MapleMap;
import server.maps.MapleMapObject;
@@ -115,7 +117,8 @@ public class MobSkill {
}
};
monster.getMap().getChannelServer().registerOverallAction(monster.getMap().getId(), toRun, animationTime);
OverallService service = (OverallService) monster.getMap().getChannelServer().getServiceAccess(ServiceType.OVERALL);
service.registerOverallAction(monster.getMap().getId(), toRun, animationTime);
}
public void applyEffect(MapleCharacter player, MapleMonster monster, boolean skill, List<MapleCharacter> banishPlayers) {

View File

@@ -37,8 +37,7 @@ public abstract class AbstractMapleMapObject implements MapleMapObject {
@Override
public void setPosition(Point position) {
this.position.x = position.x;
this.position.y = position.y;
this.position.move(position.x, position.y);
}
@Override

View File

@@ -28,7 +28,8 @@ import config.YamlConfig;
import tools.Pair;
import client.MapleCharacter;
import constants.net.ServerConstants;
import net.server.channel.services.ServiceType;
import net.server.channel.services.task.OverallService;
/**
*
@@ -131,7 +132,9 @@ public class MapleDoor {
long effectTimeLeft = 3000 - destroyDoor.getElapsedDeployTime(); // portal deployment effect duration
if (effectTimeLeft > 0) {
MapleMap town = destroyDoor.getTown();
town.getChannelServer().registerOverallAction(town.getId(), new Runnable() {
OverallService service = (OverallService) town.getChannelServer().getServiceAccess(ServiceType.OVERALL);
service.registerOverallAction(town.getId(), new Runnable() {
@Override
public void run() {
destroyDoor.broadcastRemoveDoor(owner); // thanks BHB88 for noticing doors crashing players when instantly cancelling buff

View File

@@ -65,6 +65,10 @@ import java.lang.ref.WeakReference;
import net.server.Server;
import net.server.coordinator.world.MapleMonsterAggroCoordinator;
import net.server.channel.Channel;
import net.server.channel.services.ServiceType;
import net.server.channel.services.task.FaceExpressionService;
import net.server.channel.services.task.MobMistService;
import net.server.channel.services.task.OverallService;
import net.server.world.World;
import scripting.map.MapScriptManager;
import server.MapleItemInformationProvider;
@@ -1942,7 +1946,8 @@ public class MapleMap {
public void dismissRemoveAfter(final MapleMonster monster) {
Runnable removeAfterAction = monster.popRemoveAfterAction();
if (removeAfterAction != null) {
this.getChannelServer().forceRunOverallAction(mapid, removeAfterAction);
OverallService service = (OverallService) this.getChannelServer().getServiceAccess(ServiceType.OVERALL);
service.forceRunOverallAction(mapid, removeAfterAction);
}
}
@@ -2203,7 +2208,8 @@ public class MapleMap {
}
};
this.getChannelServer().registerMobMistCancelAction(mapid, mistSchedule, duration);
MobMistService service = (MobMistService) this.getChannelServer().getServiceAccess(ServiceType.MOB_MIST);
service.registerMobMistCancelAction(mapid, mistSchedule, duration);
}
public void spawnKite(final MapleKite kite) {
@@ -2302,7 +2308,8 @@ public class MapleMap {
}
private void registerMapSchedule(Runnable r, long delay) {
this.getChannelServer().registerOverallAction(mapid, r, delay);
OverallService service = (OverallService) this.getChannelServer().getServiceAccess(ServiceType.OVERALL);
service.registerOverallAction(mapid, r, delay);
}
private void activateItemReactors(final MapleMapItem drop, final MapleClient c) {
@@ -2788,7 +2795,8 @@ public class MapleMap {
public void removePlayer(MapleCharacter chr) {
Channel cserv = chr.getClient().getChannelServer();
cserv.unregisterFaceExpression(mapid, chr);
FaceExpressionService service = (FaceExpressionService) this.getChannelServer().getServiceAccess(ServiceType.FACE_EXPRESSION);
service.unregisterFaceExpression(mapid, chr);
chr.unregisterChairBuff();
chrWLock.lock();
@@ -3548,7 +3556,8 @@ public class MapleMap {
if (reactor.getDelay() > 0) {
MapleMap reactorMap = reactor.getMap();
reactorMap.getChannelServer().registerOverallAction(reactorMap.getId(), new Runnable() {
OverallService service = (OverallService) reactorMap.getChannelServer().getServiceAccess(ServiceType.OVERALL);
service.registerOverallAction(reactorMap.getId(), new Runnable() {
@Override
public void run() {
reactor.lockReactor();

View File

@@ -36,6 +36,8 @@ import server.TimerManager;
import tools.MaplePacketCreator;
import tools.Pair;
import net.server.audit.locks.MonitoredLockType;
import net.server.channel.services.ServiceType;
import net.server.channel.services.task.OverallService;
import server.partyquest.GuardianSpawnPoint;
/**
@@ -368,14 +370,17 @@ public class MapleReactor extends AbstractMapleMapObject {
};
delayedRespawnRun = r;
map.getChannelServer().registerOverallAction(map.getId(), r, this.getDelay());
OverallService service = (OverallService) map.getChannelServer().getServiceAccess(ServiceType.OVERALL);
service.registerOverallAction(map.getId(), r, this.getDelay());
}
public boolean forceDelayedRespawn() {
Runnable r = delayedRespawnRun;
if (r != null) {
map.getChannelServer().forceRunOverallAction(map.getId(), r);
OverallService service = (OverallService) map.getChannelServer().getServiceAccess(ServiceType.OVERALL);
service.forceRunOverallAction(map.getId(), r);
return true;
} else {
return false;

View File

@@ -20,6 +20,7 @@
package server.quest.actions;
import client.MapleCharacter;
import client.MapleQuestStatus;
import provider.MapleData;
import provider.MapleDataTool;
import server.quest.MapleQuest;