Pooled Io Write + EXP loss & Detached morph patch + Storages in World
Fixed autocommit getting called early/unavailable in a few catch blocks, when trying to save player. Fixed the missing variable declaration in several quest scripts that were recently formatted. Reworked the EXP loss formula applied at a knock-out. The new formula follows past discussions in several MS forums. Fixed a deadlock issue related with party HP and party doors management. Refactored management of packets sent to client through an IoSession. New system no longer makes use of a synchronized statement when calling announce(packet), rather makes use of dedicated threads to send queued packets for the respective players. Fixed SP reset allowing increase of unexpected skills. Refactored storages, no longer instantiated as coupled with the character, rather instantiated in a map within the world object, with accountid as key. Reviewed usage of character objects of offline party members, that weren't being properly checked. Fixed some unexpected cases with buffs and morphs (within the enhanced buff system) making the latter show up as another morph figure. Added a "priority buff" perspective within the enhanced buff system, to let such priority items/skills take awareness over other buffs. (This would be vital for some quests, as the one reported in #514 ) Fixed EXP gains in certain scenarios showing up with less amount than the expected (due to float point operations). Fixed a critical bug that have emerged in a recent mount-skill update, issue happened due to an improper object initialization. Fixed mount information packet not being relayed to the player at world login time, rendering some quests not startable until the mob levels up.
This commit is contained in:
+190
-69
@@ -22,7 +22,6 @@
|
||||
*/
|
||||
package client;
|
||||
|
||||
import server.minigame.MapleRockPaperScissor;
|
||||
import java.awt.Point;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.sql.Connection;
|
||||
@@ -56,6 +55,8 @@ import java.util.regex.Pattern;
|
||||
import net.server.PlayerBuffValueHolder;
|
||||
import net.server.PlayerCoolDownValueHolder;
|
||||
import net.server.Server;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
import net.server.coordinator.MapleInviteCoordinator;
|
||||
import net.server.guild.MapleAlliance;
|
||||
import net.server.guild.MapleGuild;
|
||||
@@ -68,11 +69,11 @@ import net.server.world.PartyOperation;
|
||||
import net.server.world.World;
|
||||
import scripting.AbstractPlayerInteraction;
|
||||
import scripting.event.EventInstanceManager;
|
||||
import scripting.item.ItemScriptManager;
|
||||
import server.CashShop;
|
||||
import server.MapleItemInformationProvider;
|
||||
import server.MapleItemInformationProvider.ScriptedItem;
|
||||
import server.MapleMarriage;
|
||||
import server.MaplePortal;
|
||||
import server.MapleShop;
|
||||
import server.MapleStatEffect;
|
||||
import server.MapleStorage;
|
||||
@@ -84,23 +85,28 @@ import server.events.RescueGaga;
|
||||
import server.events.gm.MapleFitness;
|
||||
import server.events.gm.MapleOla;
|
||||
import server.life.MapleMonster;
|
||||
import server.life.MaplePlayerNPC;
|
||||
import server.life.MobSkill;
|
||||
import server.life.MobSkillFactory;
|
||||
import server.maps.FieldLimit;
|
||||
import server.maps.MapleHiredMerchant;
|
||||
import server.maps.MapleDoor;
|
||||
import server.maps.MapleDragon;
|
||||
import server.maps.MapleMap;
|
||||
import server.maps.MapleMapEffect;
|
||||
import server.maps.MapleMapManager;
|
||||
import server.maps.MapleMapItem;
|
||||
import server.maps.MapleMapObject;
|
||||
import server.maps.MapleMapObjectType;
|
||||
import server.maps.MapleMiniGame;
|
||||
import server.maps.MapleMiniGame.MiniGameResult;
|
||||
import server.life.MaplePlayerNPC;
|
||||
import server.maps.MaplePlayerShop;
|
||||
import server.maps.MaplePlayerShopItem;
|
||||
import server.maps.MaplePortal;
|
||||
import server.maps.MapleSummon;
|
||||
import server.maps.SavedLocation;
|
||||
import server.maps.SavedLocationType;
|
||||
import server.minigame.MapleRockPaperScissor;
|
||||
import server.partyquest.AriantColiseum;
|
||||
import server.partyquest.MonsterCarnival;
|
||||
import server.partyquest.MonsterCarnivalParty;
|
||||
@@ -162,14 +168,7 @@ import constants.skills.Shadower;
|
||||
import constants.skills.Sniper;
|
||||
import constants.skills.Swordsman;
|
||||
import constants.skills.ThunderBreaker;
|
||||
import scripting.item.ItemScriptManager;
|
||||
import server.life.MobSkillFactory;
|
||||
import server.maps.MapleMapItem;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
import org.apache.mina.util.ConcurrentHashSet;
|
||||
import scripting.quest.QuestActionManager;
|
||||
import server.maps.FieldLimit;
|
||||
|
||||
public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
private static final MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
@@ -1583,7 +1582,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
|
||||
List<MapleCharacter> partyMembers = new LinkedList<>();
|
||||
for(MapleCharacter mc : (exPartyMembers != null) ? exPartyMembers : this.getPartyMembers()) {
|
||||
for(MapleCharacter mc : (exPartyMembers != null) ? exPartyMembers : this.getPartyMembersOnline()) {
|
||||
if(mc.isLoggedinWorld()) {
|
||||
partyMembers.add(mc);
|
||||
}
|
||||
@@ -2605,7 +2604,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
|
||||
public void dispel() {
|
||||
if(!(ServerConstants.USE_UNDISPEL_HOLY_SHIELD && this.isActiveBuffedValue(Bishop.HOLY_SHIELD))) {
|
||||
if(!(ServerConstants.USE_UNDISPEL_HOLY_SHIELD && this.hasActiveBuff(Bishop.HOLY_SHIELD))) {
|
||||
List<MapleBuffStatValueHolder> mbsvhList = getAllStatups();
|
||||
for (MapleBuffStatValueHolder mbsvh : mbsvhList) {
|
||||
if (mbsvh.effect.isSkill()) {
|
||||
@@ -2701,7 +2700,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
public void giveDebuff(final MapleDisease disease, MobSkill skill) {
|
||||
if (!hasDisease(disease) && getDiseasesSize() < 2) {
|
||||
if (!(disease == MapleDisease.SEDUCE || disease == MapleDisease.STUN)) {
|
||||
if (isActiveBuffedValue(Bishop.HOLY_SHIELD)) {
|
||||
if (hasActiveBuff(Bishop.HOLY_SHIELD)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -3480,17 +3479,44 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasActiveBuff(int sourceid) {
|
||||
LinkedList<MapleBuffStatValueHolder> allBuffs;
|
||||
|
||||
effLock.lock();
|
||||
chrLock.lock();
|
||||
try {
|
||||
allBuffs = new LinkedList<>(effects.values());
|
||||
} finally {
|
||||
chrLock.unlock();
|
||||
effLock.unlock();
|
||||
}
|
||||
|
||||
for (MapleBuffStatValueHolder mbsvh : allBuffs) {
|
||||
if (mbsvh.effect.getBuffSourceId() == sourceid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private List<Pair<MapleBuffStat, Integer>> getActiveStatupsFromSourceid(int sourceid) { // already under effLock & chrLock
|
||||
List<Pair<MapleBuffStat, Integer>> ret = new ArrayList<>();
|
||||
|
||||
List<Pair<MapleBuffStat, Integer>> singletonStatups = new ArrayList<>();
|
||||
for(Entry<MapleBuffStat, MapleBuffStatValueHolder> bel : buffEffects.get(sourceid).entrySet()) {
|
||||
MapleBuffStat mbs = bel.getKey();
|
||||
MapleBuffStatValueHolder mbsvh = effects.get(bel.getKey());
|
||||
|
||||
Pair<MapleBuffStat, Integer> p;
|
||||
if(mbsvh != null) {
|
||||
ret.add(new Pair<>(mbs, mbsvh.value));
|
||||
p = new Pair<>(mbs, mbsvh.value);
|
||||
} else {
|
||||
ret.add(new Pair<>(mbs, 0));
|
||||
p = new Pair<>(mbs, 0);
|
||||
}
|
||||
|
||||
if (!isSingletonStatup(mbs)) { // thanks resinate, Egg Daddy for pointing out morph issues when updating it along with other statups
|
||||
ret.add(p);
|
||||
} else {
|
||||
singletonStatups.add(p);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3501,6 +3527,17 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
});
|
||||
|
||||
if (!singletonStatups.isEmpty()) {
|
||||
Collections.sort(singletonStatups, new Comparator<Pair<MapleBuffStat, Integer>>() {
|
||||
@Override
|
||||
public int compare(Pair<MapleBuffStat, Integer> p1, Pair<MapleBuffStat, Integer> p2) {
|
||||
return p1.getLeft().compareTo(p2.getLeft());
|
||||
}
|
||||
});
|
||||
|
||||
ret.addAll(singletonStatups);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -3776,12 +3813,29 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
|
||||
public boolean cancelEffect(MapleStatEffect effect, boolean overwrite, long startTime) {
|
||||
boolean ret;
|
||||
|
||||
effLock.lock();
|
||||
try {
|
||||
return cancelEffect(effect, overwrite, startTime, true);
|
||||
ret = cancelEffect(effect, overwrite, startTime, true);
|
||||
} finally {
|
||||
effLock.unlock();
|
||||
}
|
||||
|
||||
if (effect.isMagicDoor() && ret) {
|
||||
prtLock.lock();
|
||||
effLock.lock();
|
||||
try {
|
||||
if (!hasBuffFromSourceid(Priest.MYSTIC_DOOR)) {
|
||||
MapleDoor.attemptRemoveDoor(this);
|
||||
}
|
||||
} finally {
|
||||
effLock.unlock();
|
||||
prtLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static MapleStatEffect getEffectFromBuffSource(Map<MapleBuffStat, MapleBuffStatValueHolder> buffSource) {
|
||||
@@ -3882,9 +3936,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
buffstats = extractLeastRelevantStatEffectsIfFull(effect);
|
||||
}
|
||||
|
||||
if (effect.isMagicDoor()) {
|
||||
MapleDoor.attemptRemoveDoor(this);
|
||||
} else if (effect.isMapChair()) {
|
||||
if (effect.isMapChair()) {
|
||||
stopChairTask();
|
||||
}
|
||||
|
||||
@@ -4163,6 +4215,44 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
return topologicalSortEffects(buffEffects);
|
||||
}
|
||||
|
||||
private List<Pair<Integer, Pair<MapleStatEffect, Long>>> propagatePriorityBuffEffectUpdates(Set<MapleBuffStat> retrievedStats) {
|
||||
List<Pair<Integer, Pair<MapleStatEffect, Long>>> priorityUpdateEffects = new LinkedList<>();
|
||||
Map<MapleBuffStatValueHolder, MapleStatEffect> yokeStats = new LinkedHashMap<>();
|
||||
|
||||
// priority buffsources: override buffstats for the client to perceive those as "currently buffed"
|
||||
Set<MapleBuffStatValueHolder> mbsvhList = new LinkedHashSet<>();
|
||||
for (MapleBuffStatValueHolder mbsvh : getAllStatups()) {
|
||||
mbsvhList.add(mbsvh);
|
||||
}
|
||||
|
||||
for (MapleBuffStatValueHolder mbsvh : mbsvhList) {
|
||||
MapleStatEffect mse = mbsvh.effect;
|
||||
int buffSourceId = mse.getBuffSourceId();
|
||||
if (isPriorityBuffSourceid(buffSourceId) && !hasActiveBuff(buffSourceId)) {
|
||||
for (Pair<MapleBuffStat, Integer> ps : mse.getStatups()) {
|
||||
MapleBuffStat mbs = ps.getLeft();
|
||||
if (retrievedStats.contains(mbs)) {
|
||||
MapleBuffStatValueHolder mbsvhe = effects.get(mbs);
|
||||
|
||||
// this shouldn't even be null...
|
||||
//if (mbsvh != null) {
|
||||
yokeStats.put(mbsvh, mbsvhe.effect);
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Entry<MapleBuffStatValueHolder, MapleStatEffect> e : yokeStats.entrySet()) {
|
||||
MapleBuffStatValueHolder mbsvhPriority = e.getKey();
|
||||
MapleStatEffect mseActive = e.getValue();
|
||||
|
||||
priorityUpdateEffects.add(new Pair<>(mseActive.getBuffSourceId(), new Pair<>(mbsvhPriority.effect, mbsvhPriority.startTime)));
|
||||
}
|
||||
|
||||
return priorityUpdateEffects;
|
||||
}
|
||||
|
||||
private void propagateBuffEffectUpdates(Map<Integer, Pair<MapleStatEffect, Long>> retrievedEffects, Set<MapleBuffStat> retrievedStats, Set<MapleBuffStat> removedStats) {
|
||||
cancelInactiveBuffStats(retrievedStats, removedStats);
|
||||
if (retrievedStats.isEmpty()) {
|
||||
@@ -4251,6 +4341,18 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
activeStatups.clear();
|
||||
}
|
||||
|
||||
List<Pair<Integer, Pair<MapleStatEffect, Long>>> priorityEffects = propagatePriorityBuffEffectUpdates(retrievedStats);
|
||||
for(Pair<Integer, Pair<MapleStatEffect, Long>> lmse: priorityEffects) {
|
||||
Pair<MapleStatEffect, Long> msel = lmse.getRight();
|
||||
|
||||
for(Pair<MapleBuffStat, Integer> statup : getActiveStatupsFromSourceid(lmse.getLeft())) {
|
||||
activeStatups.add(statup);
|
||||
}
|
||||
|
||||
msel.getLeft().updateBuffEffect(this, activeStatups, msel.getRight());
|
||||
activeStatups.clear();
|
||||
}
|
||||
|
||||
if (this.isRidingBattleship()) {
|
||||
List<Pair<MapleBuffStat, Integer>> statups = new ArrayList<>(1);
|
||||
statups.add(new Pair<>(MapleBuffStat.MONSTER_RIDING, 0));
|
||||
@@ -4299,6 +4401,18 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isPriorityBuffSourceid(int sourceid) {
|
||||
switch(sourceid) {
|
||||
case -2022631:
|
||||
case -2022632:
|
||||
case -2022633:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void addItemEffectHolderCount(MapleBuffStat stat) {
|
||||
Byte val = buffEffectsCount.get(stat);
|
||||
if (val != null) {
|
||||
@@ -4454,6 +4568,18 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
addItemEffectHolderCount(statup.getKey());
|
||||
}
|
||||
|
||||
// should also propagate update from buffs shared with priority sourceids
|
||||
Set<MapleBuffStat> updated = appliedStatups.keySet();
|
||||
for (MapleBuffStatValueHolder mbsvh : this.getAllStatups()) {
|
||||
if (isPriorityBuffSourceid(mbsvh.effect.getBuffSourceId())) {
|
||||
for (Pair<MapleBuffStat, Integer> p : mbsvh.effect.getStatups()) {
|
||||
if (updated.contains(p.getLeft())) {
|
||||
retrievedStats.add(p.getLeft());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!isSilent) {
|
||||
addItemEffectHolder(sourceid, expirationtime, appliedStatups);
|
||||
for (Entry<MapleBuffStat, MapleBuffStatValueHolder> statup : toDeploy.entrySet()) {
|
||||
@@ -5361,14 +5487,17 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
}
|
||||
|
||||
public List<MapleCharacter> getPartyMembers() {
|
||||
public List<MapleCharacter> getPartyMembersOnline() {
|
||||
List<MapleCharacter> list = new LinkedList<>();
|
||||
|
||||
prtLock.lock();
|
||||
try {
|
||||
if(party != null) {
|
||||
for(MaplePartyCharacter partyMembers: party.getMembers()) {
|
||||
list.add(partyMembers.getPlayer());
|
||||
for(MaplePartyCharacter mpc: party.getMembers()) {
|
||||
MapleCharacter mc = mpc.getPlayer();
|
||||
if (mc != null) {
|
||||
list.add(mc);
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@@ -5385,11 +5514,13 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
prtLock.lock();
|
||||
try {
|
||||
if(party != null) {
|
||||
for(MaplePartyCharacter partyMembers: party.getMembers()) {
|
||||
MapleCharacter chr = partyMembers.getPlayer();
|
||||
MapleMap chrMap = chr.getMap();
|
||||
if(chrMap != null && chrMap.hashCode() == thisMapHash && chr.isLoggedinWorld()) {
|
||||
list.add(chr);
|
||||
for(MaplePartyCharacter mpc: party.getMembers()) {
|
||||
MapleCharacter chr = mpc.getPlayer();
|
||||
if (chr != null) {
|
||||
MapleMap chrMap = chr.getMap();
|
||||
if(chrMap != null && chrMap.hashCode() == thisMapHash && chr.isLoggedinWorld()) {
|
||||
list.add(chr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5405,10 +5536,13 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
|
||||
public boolean isPartyMember(int cid) {
|
||||
for(MapleCharacter mpcu: getPartyMembers()) {
|
||||
if(mpcu.getId() == cid) {
|
||||
return true;
|
||||
prtLock.lock();
|
||||
try {
|
||||
if(party != null) {
|
||||
return party.getMemberById(cid) != null;
|
||||
}
|
||||
} finally {
|
||||
prtLock.unlock();
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -5988,26 +6122,6 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
dropMessage(1, "Your guild already reached the maximum capacity of players.");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isActiveBuffedValue(int skillid) {
|
||||
LinkedList<MapleBuffStatValueHolder> allBuffs;
|
||||
|
||||
effLock.lock();
|
||||
chrLock.lock();
|
||||
try {
|
||||
allBuffs = new LinkedList<>(effects.values());
|
||||
} finally {
|
||||
chrLock.unlock();
|
||||
effLock.unlock();
|
||||
}
|
||||
|
||||
for (MapleBuffStatValueHolder mbsvh : allBuffs) {
|
||||
if (mbsvh.effect.isSkill() && mbsvh.effect.getSourceId() == skillid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean canBuyback(int fee, boolean usingMesos) {
|
||||
return (usingMesos ? this.getMeso() : cashshop.getCash(1)) >= fee;
|
||||
@@ -7313,7 +7427,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
rs.close();
|
||||
ps.close();
|
||||
ret.buddylist.loadFromDb(charid);
|
||||
ret.storage = MapleStorage.loadOrCreateFromDB(ret.accountid, ret.world);
|
||||
ret.storage = wserv.getAccountStorage(ret.accountid);
|
||||
|
||||
int startHp = ret.hp, startMp = ret.mp;
|
||||
ret.reapplyLocalStats();
|
||||
@@ -7428,8 +7542,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
|
||||
public MapleMount mount(int id, int skillid) {
|
||||
maplemount = new MapleMount(this, id, skillid);
|
||||
return maplemount;
|
||||
MapleMount mount = maplemount;
|
||||
mount.setItemId(id);
|
||||
mount.setSkillId(skillid);
|
||||
return mount;
|
||||
}
|
||||
|
||||
private void playerDead() {
|
||||
@@ -7467,22 +7583,22 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
} else if (getJob() != MapleJob.BEGINNER) { //Hmm...
|
||||
if (!FieldLimit.NO_EXP_DECREASE.check(getMap().getFieldLimit())) { // thanks Conrad for noticing missing FieldLimit check
|
||||
int XPdummy = ExpTable.getExpNeededForLevel(getLevel());
|
||||
if (getMap().isTown()) {
|
||||
|
||||
if (getMap().isTown()) { // thanks MindLove, SIayerMonkey, HaItsNotOver for noting players only lose 1% on town maps
|
||||
XPdummy /= 100;
|
||||
}
|
||||
if (XPdummy == ExpTable.getExpNeededForLevel(getLevel())) {
|
||||
if (getLuk() <= 100 && getLuk() > 8) {
|
||||
XPdummy *= (200 - getLuk()) / 2000;
|
||||
} else if (getLuk() < 8) {
|
||||
} else {
|
||||
if (getLuk() < 50) { // thanks Taiketo, Quit, Fishanelli for noting player EXP loss are fixed, 50-LUK threshold
|
||||
XPdummy /= 10;
|
||||
} else {
|
||||
XPdummy /= 20;
|
||||
}
|
||||
}
|
||||
if (getExp() > XPdummy) {
|
||||
|
||||
int curExp = getExp();
|
||||
if (curExp > XPdummy) {
|
||||
loseExp(XPdummy, false, false);
|
||||
} else {
|
||||
loseExp(getExp(), false, false);
|
||||
loseExp(curExp, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7792,6 +7908,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
|
||||
private void updateLocalStats() {
|
||||
prtLock.lock();
|
||||
effLock.lock();
|
||||
statWlock.lock();
|
||||
try {
|
||||
@@ -7809,6 +7926,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
} finally {
|
||||
statWlock.unlock();
|
||||
effLock.unlock();
|
||||
prtLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8447,8 +8565,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
|
||||
ItemFactory.INVENTORY.saveItems(itemsWithType, id, con);
|
||||
|
||||
deleteWhereCharacterId(con, "DELETE FROM skills WHERE characterid = ?");
|
||||
ps = con.prepareStatement("INSERT INTO skills (characterid, skillid, skilllevel, masterlevel, expiration) VALUES (?, ?, ?, ?, ?)");
|
||||
ps = con.prepareStatement("REPLACE INTO skills (characterid, skillid, skilllevel, masterlevel, expiration) VALUES (?, ?, ?, ?, ?)");
|
||||
ps.setInt(1, id);
|
||||
for (Entry<Skill, SkillEntry> skill : skills.entrySet()) {
|
||||
ps.setInt(2, skill.getKey().getId());
|
||||
@@ -8590,10 +8707,6 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
|
||||
}
|
||||
|
||||
con.commit();
|
||||
con.setAutoCommit(true);
|
||||
|
||||
|
||||
if (cashshop != null) {
|
||||
cashshop.save(con);
|
||||
}
|
||||
@@ -8603,6 +8716,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
usedStorage = false;
|
||||
}
|
||||
|
||||
con.commit();
|
||||
con.setAutoCommit(true); // only commit after finishing all "con" usages, thanks Zygon
|
||||
|
||||
} catch (SQLException | RuntimeException t) {
|
||||
FilePrinter.printError(FilePrinter.SAVE_CHAR, t, "Error saving " + name + " Level: " + level + " Job: " + job.getId());
|
||||
try {
|
||||
@@ -8612,6 +8728,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
} catch (Exception e) {
|
||||
FilePrinter.printError(FilePrinter.SAVE_CHAR, e, "Error saving " + name + " Level: " + level + " Job: " + job.getId());
|
||||
try {
|
||||
con.rollback(); // thanks Zygon
|
||||
} catch (SQLException se) {
|
||||
FilePrinter.printError(FilePrinter.SAVE_CHAR, se, "Error trying to rollback " + name);
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
con.setAutoCommit(true);
|
||||
@@ -9923,7 +10044,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
if (!this.isHidden() || client.getPlayer().gmLevel() > 1) {
|
||||
client.announce(MaplePacketCreator.spawnPlayerMapObject(client, this, false));
|
||||
|
||||
if (buffEffects.containsKey(getJobMapChair(job))) { // mustn't effLock, chrLock this function
|
||||
if (buffEffects.containsKey(getJobMapChair(job))) { // mustn't effLock, chrLock sendSpawnData
|
||||
client.announce(MaplePacketCreator.giveForeignChairSkillEffect(id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,8 +47,6 @@ import java.util.concurrent.locks.Lock;
|
||||
import jdk.nashorn.api.scripting.NashornScriptEngine;
|
||||
import tools.*;
|
||||
|
||||
import javax.script.ScriptEngine;
|
||||
|
||||
import net.server.Server;
|
||||
import net.server.coordinator.MapleSessionCoordinator;
|
||||
import net.server.coordinator.MapleSessionCoordinator.AntiMulticlientResult;
|
||||
@@ -63,6 +61,7 @@ import net.server.world.World;
|
||||
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
|
||||
import net.server.world.announcer.MapleAnnouncerCoordinator;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import constants.GameConstants;
|
||||
import constants.ServerConstants;
|
||||
@@ -74,7 +73,6 @@ import scripting.npc.NPCScriptManager;
|
||||
import scripting.quest.QuestActionManager;
|
||||
import scripting.quest.QuestScriptManager;
|
||||
import server.life.MapleMonster;
|
||||
import server.MapleTrade;
|
||||
import server.ThreadManager;
|
||||
import server.maps.*;
|
||||
import server.quest.MapleQuest;
|
||||
@@ -85,7 +83,7 @@ import net.server.coordinator.MapleLoginBypassCoordinator;
|
||||
|
||||
public class MapleClient {
|
||||
|
||||
public static final int LOGIN_NOTLOGGEDIN = 0;
|
||||
public static final int LOGIN_NOTLOGGEDIN = 0;
|
||||
public static final int LOGIN_SERVER_TRANSITION = 1;
|
||||
public static final int LOGIN_LOGGEDIN = 2;
|
||||
public static final String CLIENT_KEY = "CLIENT";
|
||||
@@ -96,7 +94,8 @@ public class MapleClient {
|
||||
private MapleAESOFB send;
|
||||
private MapleAESOFB receive;
|
||||
private final IoSession session;
|
||||
private MapleCharacter player;
|
||||
private MapleCharacter player;
|
||||
private MapleAnnouncerCoordinator announcer = MapleAnnouncerCoordinator.getInstance();
|
||||
private int channel = 1;
|
||||
private int accId = -4;
|
||||
private boolean loggedIn = false;
|
||||
@@ -122,12 +121,13 @@ public class MapleClient {
|
||||
private final Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT, true);
|
||||
private final Lock encoderLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT_ENCODER, true);
|
||||
private static final Lock loginLocks[] = new Lock[200]; // thanks Masterrulax & try2hack for pointing out a bottleneck issue here
|
||||
private Calendar tempBanCalendar;
|
||||
private Calendar tempBanCalendar;
|
||||
private int votePoints;
|
||||
private int voteTime = -1;
|
||||
private int visibleWorlds;
|
||||
private long lastNpcClick;
|
||||
private long sessionId;
|
||||
private long lastPacket = System.currentTimeMillis();
|
||||
private int lang = 0;
|
||||
|
||||
static {
|
||||
@@ -135,6 +135,14 @@ public class MapleClient {
|
||||
loginLocks[i] = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT_LOGIN, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateLastPacket() {
|
||||
lastPacket = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public long getLastPacket() {
|
||||
return lastPacket;
|
||||
}
|
||||
|
||||
public MapleClient(MapleAESOFB send, MapleAESOFB receive, IoSession session) {
|
||||
this.send = send;
|
||||
@@ -877,9 +885,9 @@ public class MapleClient {
|
||||
MapleMap map = player.getMap();
|
||||
final MapleParty party = player.getParty();
|
||||
final int idz = player.getId();
|
||||
final MaplePartyCharacter chrp = new MaplePartyCharacter(player);
|
||||
|
||||
if (party != null) {
|
||||
final MaplePartyCharacter chrp = new MaplePartyCharacter(player);
|
||||
chrp.setOnline(false);
|
||||
wserv.updateParty(party.getId(), PartyOperation.LOG_ONOFF, chrp);
|
||||
if (party.getLeader().getId() == idz && map != null) {
|
||||
@@ -1135,6 +1143,13 @@ public class MapleClient {
|
||||
|
||||
public void setWorld(int world) {
|
||||
this.world = world;
|
||||
|
||||
World wserv = Server.getInstance().getWorld(world);
|
||||
if (wserv != null) {
|
||||
this.announcer = wserv.getAnnouncerCoordinator();
|
||||
} else {
|
||||
this.announcer = MapleAnnouncerCoordinator.getInstance();
|
||||
}
|
||||
}
|
||||
|
||||
public void pongReceived() {
|
||||
@@ -1459,8 +1474,8 @@ public class MapleClient {
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void announce(final byte[] packet) {//MINA CORE IS A FUCKING BITCH AND I HATE IT <3
|
||||
session.write(packet);
|
||||
public void announce(final byte[] packet) { // thanks GitGud for noticing an opportunity for improvement by overcoming "synchronized announce"
|
||||
announcer.append(session, packet);
|
||||
}
|
||||
|
||||
public void announceHint(String msg, int length) {
|
||||
|
||||
@@ -107,6 +107,10 @@ public class MapleMount {
|
||||
public void setItemId(int newitemid) {
|
||||
this.itemid = newitemid;
|
||||
}
|
||||
|
||||
public void setSkillId(int newskillid) {
|
||||
this.skillid = newskillid;
|
||||
}
|
||||
|
||||
public void setActive(boolean set) {
|
||||
this.active = set;
|
||||
|
||||
@@ -254,6 +254,7 @@ public class CommandsExecutor {
|
||||
addCommand("drop", 2, ItemDropCommand.class);
|
||||
addCommand("level", 2, LevelCommand.class);
|
||||
addCommand("levelpro", 2, LevelProCommand.class);
|
||||
addCommand("setslot", 2, SetSlotCommand.class);
|
||||
addCommand("setstat", 2, SetStatCommand.class);
|
||||
addCommand("maxstat", 2, MaxStatCommand.class);
|
||||
addCommand("maxskill", 2, MaxSkillCommand.class);
|
||||
|
||||
@@ -30,7 +30,7 @@ import constants.GameConstants;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import net.server.Server;
|
||||
import server.MaplePortal;
|
||||
import server.maps.MaplePortal;
|
||||
import server.maps.FieldLimit;
|
||||
import server.maps.MapleMap;
|
||||
import server.maps.MapleMapManager;
|
||||
|
||||
@@ -26,7 +26,7 @@ package client.command.commands.gm2;
|
||||
import client.command.Command;
|
||||
import client.MapleClient;
|
||||
import client.MapleCharacter;
|
||||
import server.MaplePortal;
|
||||
import server.maps.MaplePortal;
|
||||
import server.maps.MapleMap;
|
||||
|
||||
public class JailCommand extends Command {
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
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
|
||||
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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
@Author: Ronan
|
||||
*/
|
||||
package client.command.commands.gm2;
|
||||
|
||||
import client.*;
|
||||
import client.command.Command;
|
||||
|
||||
public class SetSlotCommand extends Command {
|
||||
{
|
||||
setDescription("");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(MapleClient c, String[] params) {
|
||||
MapleCharacter player = c.getPlayer();
|
||||
if (params.length < 1) {
|
||||
player.yellowMessage("Syntax: !setslot <newlevel>");
|
||||
return;
|
||||
}
|
||||
|
||||
int slots = (Integer.parseInt(params[0]) / 4) * 4;
|
||||
for (int i = 1; i < 5; i++) {
|
||||
int curSlots = player.getSlots(i);
|
||||
if (slots <= -curSlots) {
|
||||
continue;
|
||||
}
|
||||
|
||||
player.gainSlots(i, slots - curSlots, true);
|
||||
}
|
||||
|
||||
player.yellowMessage("Slots updated.");
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ import client.command.Command;
|
||||
import client.MapleClient;
|
||||
import client.MapleCharacter;
|
||||
import net.server.Server;
|
||||
import server.MaplePortal;
|
||||
import server.maps.MaplePortal;
|
||||
import server.TimerManager;
|
||||
import server.life.MapleMonster;
|
||||
import server.life.SpawnPoint;
|
||||
|
||||
@@ -83,6 +83,19 @@ public class MapleInventory implements Iterable<Item> {
|
||||
public void setSlotLimit(int newLimit) {
|
||||
lock.lock();
|
||||
try {
|
||||
if (newLimit < slotLimit) {
|
||||
List<Short> toRemove = new LinkedList<>();
|
||||
for (Item it : list()) {
|
||||
if (it.getPosition() > newLimit) {
|
||||
toRemove.add(it.getPosition());
|
||||
}
|
||||
}
|
||||
|
||||
for (Short slot : toRemove) {
|
||||
removeSlot(slot);
|
||||
}
|
||||
}
|
||||
|
||||
slotLimit = (byte) newLimit;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
|
||||
@@ -683,9 +683,27 @@ public class MapleInventoryManipulator {
|
||||
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(2, source, src))));
|
||||
chr.equipChanged();
|
||||
}
|
||||
|
||||
private static boolean isDisappearingItemDrop(Item it) {
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
if (ii.isDropRestricted(it.getItemId())) {
|
||||
return true;
|
||||
} else if (ii.isCash(it.getItemId())) {
|
||||
if (ServerConstants.USE_ENFORCE_UNMERCHABLE_CASH) { // thanks Ari for noticing cash drops not available server-side
|
||||
return true;
|
||||
} else if (ItemConstants.isPet(it.getItemId()) && ServerConstants.USE_ENFORCE_UNMERCHABLE_PET) {
|
||||
return true;
|
||||
}
|
||||
} else if (isDroppedItemRestricted(it)) {
|
||||
return true;
|
||||
} else if (ItemConstants.isWeddingRing(it.getItemId())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void drop(MapleClient c, MapleInventoryType type, short src, short quantity) {
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
if (src < 0) {
|
||||
type = MapleInventoryType.EQUIPPED;
|
||||
}
|
||||
@@ -698,14 +716,21 @@ public class MapleInventoryManipulator {
|
||||
return;
|
||||
}
|
||||
int itemId = source.getItemId();
|
||||
if (ItemConstants.isPet(itemId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MapleMap map = chr.getMap();
|
||||
if ((!ItemConstants.isRechargeable(itemId) && source.getQuantity() < quantity) || quantity < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int petid = source.getPetId();
|
||||
if (petid > -1) {
|
||||
int petIdx = chr.getPetIndex(petid);
|
||||
if(petIdx > -1) {
|
||||
MaplePet pet = chr.getPet(petIdx);
|
||||
chr.unequipPet(pet, true);
|
||||
}
|
||||
}
|
||||
|
||||
Point dropPos = new Point(chr.getPosition());
|
||||
if (quantity < source.getQuantity() && !ItemConstants.isRechargeable(itemId)) {
|
||||
Item target = source.copy();
|
||||
@@ -721,11 +746,9 @@ public class MapleInventoryManipulator {
|
||||
NewYearCardRecord.removeAllNewYearCard(false, chr);
|
||||
c.getAbstractPlayerInteraction().removeAll(4301000);
|
||||
}
|
||||
} else if (ItemConstants.isWeddingRing(source.getItemId())) {
|
||||
map.disappearingItemDrop(chr, chr, target, dropPos);
|
||||
}
|
||||
|
||||
if (ii.isDropRestricted(target.getItemId()) || ii.isCash(target.getItemId()) || isDroppedItemRestricted(target)) {
|
||||
if (isDisappearingItemDrop(target)) {
|
||||
map.disappearingItemDrop(chr, chr, target, dropPos);
|
||||
} else {
|
||||
map.spawnItemDrop(chr, chr, target, dropPos, true, true);
|
||||
@@ -754,11 +777,9 @@ public class MapleInventoryManipulator {
|
||||
NewYearCardRecord.removeAllNewYearCard(false, chr);
|
||||
c.getAbstractPlayerInteraction().removeAll(4301000);
|
||||
}
|
||||
} else if (ItemConstants.isWeddingRing(source.getItemId())) {
|
||||
map.disappearingItemDrop(chr, chr, source, dropPos);
|
||||
}
|
||||
|
||||
if (ii.isDropRestricted(itemId) || ii.isCash(itemId) || isDroppedItemRestricted(source)) {
|
||||
if (isDisappearingItemDrop(source)) {
|
||||
map.disappearingItemDrop(chr, chr, source, dropPos);
|
||||
} else {
|
||||
map.spawnItemDrop(chr, chr, source, dropPos, true, true);
|
||||
|
||||
@@ -40,31 +40,42 @@ import tools.MaplePacketCreator;
|
||||
*/
|
||||
public class AssignSPProcessor {
|
||||
|
||||
public static boolean canSPAssign(MapleClient c, int skillid) {
|
||||
if (skillid == Aran.HIDDEN_FULL_DOUBLE || skillid == Aran.HIDDEN_FULL_TRIPLE || skillid == Aran.HIDDEN_OVER_DOUBLE || skillid == Aran.HIDDEN_OVER_TRIPLE) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
|
||||
MapleCharacter player = c.getPlayer();
|
||||
if ((!GameConstants.isPqSkillMap(player.getMapId()) && GameConstants.isPqSkill(skillid)) || (!player.isGM() && GameConstants.isGMSkills(skillid)) || (!GameConstants.isInJobTree(skillid, player.getJob().getId()) && !player.isGM())) {
|
||||
AutobanFactory.PACKET_EDIT.alert(player, "tried to packet edit in distributing sp.");
|
||||
FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use skill " + skillid + " without it being in their job.");
|
||||
|
||||
final MapleClient client = c;
|
||||
ThreadManager.getInstance().newTask(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
client.disconnect(true, false);
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void SPAssignAction(MapleClient c, int skillid) {
|
||||
c.lockClient();
|
||||
try {
|
||||
if (skillid == Aran.HIDDEN_FULL_DOUBLE || skillid == Aran.HIDDEN_FULL_TRIPLE || skillid == Aran.HIDDEN_OVER_DOUBLE || skillid == Aran.HIDDEN_OVER_TRIPLE) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
if (!canSPAssign(c, skillid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
MapleCharacter player = c.getPlayer();
|
||||
int remainingSp = player.getRemainingSps()[GameConstants.getSkillBook(skillid/10000)];
|
||||
boolean isBeginnerSkill = false;
|
||||
if ((!GameConstants.isPqSkillMap(player.getMapId()) && GameConstants.isPqSkill(skillid)) || (!player.isGM() && GameConstants.isGMSkills(skillid)) || (!GameConstants.isInJobTree(skillid, player.getJob().getId()) && !player.isGM())) {
|
||||
AutobanFactory.PACKET_EDIT.alert(player, "tried to packet edit in distributing sp.");
|
||||
FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use skill " + skillid + " without it being in their job.");
|
||||
|
||||
final MapleClient client = c;
|
||||
ThreadManager.getInstance().newTask(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
client.disconnect(true, false);
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (skillid % 10000000 > 999 && skillid % 10000000 < 1003) {
|
||||
int total = 0;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
|
||||
@@ -45,6 +45,7 @@ import tools.data.input.SeekableLittleEndianAccessor;
|
||||
public class StorageProcessor {
|
||||
|
||||
public static void storageAction(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
MapleStorage storage = chr.getStorage();
|
||||
byte mode = slea.readByte();
|
||||
@@ -69,7 +70,7 @@ public class StorageProcessor {
|
||||
slot = storage.getSlot(MapleInventoryType.getByType(type), slot);
|
||||
Item item = storage.getItem(slot);
|
||||
if (item != null) {
|
||||
if (MapleItemInformationProvider.getInstance().isPickupRestricted(item.getItemId()) && chr.haveItemWithId(item.getItemId(), true)) {
|
||||
if (ii.isPickupRestricted(item.getItemId()) && chr.haveItemWithId(item.getItemId(), true)) {
|
||||
c.announce(MaplePacketCreator.getStorageError((byte) 0x0C));
|
||||
return;
|
||||
}
|
||||
@@ -83,13 +84,20 @@ public class StorageProcessor {
|
||||
}
|
||||
|
||||
if (MapleInventoryManipulator.checkSpace(c, item.getItemId(), item.getQuantity(), item.getOwner())) {
|
||||
item = storage.takeOut(slot);//actually the same but idc
|
||||
String itemName = MapleItemInformationProvider.getInstance().getName(item.getItemId());
|
||||
FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " took out " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")");
|
||||
chr.setUsedStorage();
|
||||
MapleKarmaManipulator.toggleKarmaFlagToUntradeable(item);
|
||||
MapleInventoryManipulator.addFromDrop(c, item, false);
|
||||
storage.sendTakenOut(c, item.getInventoryType());
|
||||
if (storage.takeOut(item)) {
|
||||
chr.setUsedStorage();
|
||||
|
||||
MapleKarmaManipulator.toggleKarmaFlagToUntradeable(item);
|
||||
MapleInventoryManipulator.addFromDrop(c, item, false);
|
||||
|
||||
String itemName = ii.getName(item.getItemId());
|
||||
FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " took out " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")");
|
||||
|
||||
storage.sendTakenOut(c, item.getInventoryType());
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.getStorageError((byte) 0x0A));
|
||||
}
|
||||
@@ -149,11 +157,14 @@ public class StorageProcessor {
|
||||
|
||||
MapleKarmaManipulator.toggleKarmaFlagToUntradeable(item);
|
||||
item.setQuantity(quantity);
|
||||
storage.store(item);
|
||||
storage.sendStored(c, ItemConstants.getInventoryType(itemId));
|
||||
String itemName = MapleItemInformationProvider.getInstance().getName(item.getItemId());
|
||||
FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " stored " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")");
|
||||
|
||||
storage.store(item); // inside a critical section, "!(storage.isFull())" is still in effect...
|
||||
chr.setUsedStorage();
|
||||
|
||||
String itemName = ii.getName(item.getItemId());
|
||||
FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " stored " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")");
|
||||
|
||||
storage.sendStored(c, ItemConstants.getInventoryType(itemId));
|
||||
}
|
||||
} else if (mode == 6) { // arrange items
|
||||
if(ServerConstants.USE_STORAGE_ITEM_SORT) storage.arrangeItems(c);
|
||||
@@ -178,14 +189,14 @@ public class StorageProcessor {
|
||||
}
|
||||
storage.setMeso(storageMesos - meso);
|
||||
chr.gainMeso(meso, false, true, false);
|
||||
FilePrinter.print(FilePrinter.STORAGE + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + (meso > 0 ? " took out " : " stored ") + Math.abs(meso) + " mesos");
|
||||
chr.setUsedStorage();
|
||||
FilePrinter.print(FilePrinter.STORAGE + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + (meso > 0 ? " took out " : " stored ") + Math.abs(meso) + " mesos");
|
||||
storage.sendMeso(c);
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
storage.sendMeso(c);
|
||||
} else if (mode == 8) {// close
|
||||
} else if (mode == 8) {// close... unless the player decides to enter cash shop!
|
||||
storage.close();
|
||||
}
|
||||
} finally {
|
||||
|
||||
@@ -16,7 +16,7 @@ import tools.Pair;
|
||||
public class ScriptableNPCConstants {
|
||||
|
||||
public static final Set<Pair<Integer, String>> SCRIPTABLE_NPCS = new HashSet<Pair<Integer, String>>(){{
|
||||
add(new Pair<>(9200000, "Cody"));
|
||||
//add(new Pair<>(9200000, "Cody"));
|
||||
add(new Pair<>(9001105, "Grandpa Moon Bunny"));
|
||||
}};
|
||||
|
||||
|
||||
@@ -93,8 +93,8 @@ public class ServerConstants {
|
||||
public static final boolean USE_ENFORCE_JOB_LEVEL_RANGE = false;//Caps the player level on the minimum required to advance their current jobs.
|
||||
public static final boolean USE_ENFORCE_JOB_SP_RANGE = false; //Caps the player SP level on the total obtainable by their current jobs. After changing jobs, missing SP will be retrieved.
|
||||
public static final boolean USE_ENFORCE_ITEM_SUGGESTION = false;//Forces the Owl of Minerva and the Cash Shop to always display the defined item array instead of those featured by the players.
|
||||
public static final boolean USE_ENFORCE_UNMERCHABLE_CASH = true;//Forces players to not sell CASH items via merchants.
|
||||
public static final boolean USE_ENFORCE_UNMERCHABLE_PET = false; //Forces players to not sell pets via merchants. (since non-named pets gets dirty name and other possible DB-related issues)
|
||||
public static final boolean USE_ENFORCE_UNMERCHABLE_CASH = true;//Forces players to not sell CASH items via merchants, drops of it disappears.
|
||||
public static final boolean USE_ENFORCE_UNMERCHABLE_PET = true; //Forces players to not sell pets via merchants, drops of it disappears. (since non-named pets gets dirty name and other possible DB-related issues)
|
||||
public static final boolean USE_ENFORCE_MERCHANT_SAVE = true; //Forces automatic DB save on merchant owners, at every item movement on shop.
|
||||
public static final boolean USE_ENFORCE_MDOOR_POSITION = false; //Forces mystic door to be spawned near spawnpoints.
|
||||
public static final boolean USE_SPAWN_CLEAN_MDOOR = false; //Makes mystic doors to be spawned without deploy animation. This clears disconnecting issues that may happen when trying to cancel doors a couple seconds after deployment.
|
||||
@@ -309,6 +309,9 @@ public class ServerConstants {
|
||||
public static final int BUYBACK_RETURN_MINUTES = 1; //Sets the maximum amount of time the player can wait before decide to buyback.
|
||||
public static final int BUYBACK_COOLDOWN_MINUTES = 7; //Sets the time the player must wait before using buyback again.
|
||||
|
||||
// Login timeout by shavit
|
||||
public static long TIMEOUT_DURATION = 3600000L; // Kicks clients who don't send any packet to the game server in due time (in millisseconds).
|
||||
|
||||
//Event End Timestamp
|
||||
public static final long EVENT_END_TIMESTAMP = 1428897600000L;
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ public class Aran {
|
||||
public static final int FREEZE_STANDING = 21121003;
|
||||
public static final int SNOW_CHARGE = 21111005;
|
||||
public static final int HEROS_WILL = 21121008;
|
||||
public static final int HIGH_DEFENSE = 21120004;
|
||||
public static final int BODY_PRESSURE = 21101003;
|
||||
public static final int COMBO_DRAIN = 21100005;
|
||||
public static final int COMBO_SMASH = 21100004;
|
||||
|
||||
@@ -198,6 +198,7 @@ public class MapleServerHandler extends IoHandlerAdapter {
|
||||
FilePrinter.printError(FilePrinter.PACKET_HANDLER + packetHandler.getClass().getName() + ".txt", t, "Error for " + (client.getPlayer() == null ? "" : "player ; " + client.getPlayer() + " on map ; " + client.getPlayer().getMapId() + " - ") + "account ; " + client.getAccountName() + "\r\n" + slea.toString());
|
||||
//client.announce(MaplePacketCreator.enableActions());//bugs sometimes
|
||||
}
|
||||
client.updateLastPacket();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
import net.MapleServerHandler;
|
||||
import net.mina.MapleCodecFactory;
|
||||
import net.server.channel.Channel;
|
||||
import net.server.coordinator.MapleSessionCoordinator;
|
||||
import net.server.guild.MapleAlliance;
|
||||
import net.server.guild.MapleGuild;
|
||||
import net.server.guild.MapleGuildCharacter;
|
||||
@@ -70,6 +71,7 @@ import net.server.worker.RankingLoginWorker;
|
||||
import net.server.worker.ReleaseLockWorker;
|
||||
import net.server.worker.RespawnWorker;
|
||||
import net.server.world.World;
|
||||
import net.server.world.announcer.MapleAnnouncerCoordinator;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.buffer.SimpleBufferAllocator;
|
||||
@@ -94,7 +96,6 @@ import constants.GameConstants;
|
||||
import constants.OpcodeConstants;
|
||||
import constants.ServerConstants;
|
||||
import java.util.TimeZone;
|
||||
import net.server.coordinator.MapleSessionCoordinator;
|
||||
import server.CashShop.CashItemFactory;
|
||||
import server.MapleSkillbookInformationProvider;
|
||||
import server.ThreadManager;
|
||||
@@ -949,12 +950,17 @@ public class Server {
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
MapleAnnouncerCoordinator.getInstance().init();
|
||||
System.out.println();
|
||||
|
||||
if(ServerConstants.USE_FAMILY_SYSTEM) {
|
||||
timeToTake = System.currentTimeMillis();
|
||||
MapleFamily.loadAllFamilies();
|
||||
System.out.println("Families loaded in " + ((System.currentTimeMillis() - timeToTake) / 1000.0) + " seconds\r\n");
|
||||
}
|
||||
|
||||
|
||||
System.out.println();
|
||||
|
||||
acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30);
|
||||
acceptor.setHandler(new MapleServerHandler());
|
||||
try {
|
||||
@@ -964,7 +970,7 @@ public class Server {
|
||||
}
|
||||
|
||||
System.out.println("Listening on port 8484\r\n\r\n");
|
||||
|
||||
|
||||
System.out.println("HeavenMS is now online.\r\n");
|
||||
online = true;
|
||||
|
||||
@@ -1446,6 +1452,11 @@ public class Server {
|
||||
} finally {
|
||||
lgnWLock.unlock();
|
||||
}
|
||||
|
||||
for (World wserv : this.getWorlds()) {
|
||||
wserv.clearAccountCharacterView(accountid);
|
||||
wserv.unregisterAccountStorage(accountid);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -1708,6 +1719,32 @@ public class Server {
|
||||
return gmLevel;
|
||||
}
|
||||
|
||||
public void loadAccountStorages(MapleClient c) {
|
||||
int accountId = c.getAccID();
|
||||
Set<Integer> accWorlds = new HashSet<>();
|
||||
lgnWLock.lock();
|
||||
try {
|
||||
Set<Integer> chars = accountChars.get(accountId);
|
||||
|
||||
for (Integer cid : chars) {
|
||||
Integer worldid = worldChars.get(cid);
|
||||
if (worldid != null) {
|
||||
accWorlds.add(worldid);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lgnWLock.unlock();
|
||||
}
|
||||
|
||||
List<World> worldList = this.getWorlds();
|
||||
for (Integer worldid : accWorlds) {
|
||||
if (worldid < worldList.size()) {
|
||||
World wserv = worldList.get(worldid);
|
||||
wserv.registerAccountStorage(accountId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String getRemoteIp(IoSession session) {
|
||||
return MapleSessionCoordinator.getSessionRemoteAddress(session);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ public enum MonitoredLockType {
|
||||
CHARACTER_STA,
|
||||
CLIENT,
|
||||
CLIENT_ENCODER,
|
||||
CLIENT_SESSION,
|
||||
CLIENT_LOGIN,
|
||||
BOOK,
|
||||
ITEM,
|
||||
|
||||
@@ -30,7 +30,7 @@ import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import server.MaplePortal;
|
||||
import server.maps.MaplePortal;
|
||||
import server.MapleTrade;
|
||||
import server.maps.MapleMap;
|
||||
import tools.FilePrinter;
|
||||
|
||||
@@ -23,7 +23,7 @@ package net.server.channel.handlers;
|
||||
|
||||
import client.MapleClient;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import server.MaplePortal;
|
||||
import server.maps.MaplePortal;
|
||||
import server.MapleTrade;
|
||||
import server.MapleTrade.TradeResult;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
@@ -29,7 +29,7 @@ import client.MapleClient;
|
||||
import constants.GameConstants;
|
||||
import java.awt.Point;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import server.MaplePortal;
|
||||
import server.maps.MaplePortal;
|
||||
import server.maps.MapleMapObject;
|
||||
import server.maps.MapleMapObjectType;
|
||||
import server.maps.MaplePlayerShop;
|
||||
|
||||
@@ -101,16 +101,19 @@ public final class MonsterCarnivalHandler extends AbstractMaplePacketHandler {
|
||||
final MapleDisease dis = skill.getDisease();
|
||||
MapleParty enemies = c.getPlayer().getParty().getEnemy();
|
||||
if (skill.targetsAll) {
|
||||
int chanceAcerto = 0;
|
||||
int hitChance = 0;
|
||||
if (dis.getDisease() == 121 || dis.getDisease() == 122 || dis.getDisease() == 125 || dis.getDisease() == 126) {
|
||||
chanceAcerto = (int) (Math.random() * 100);
|
||||
hitChance = (int) (Math.random() * 100);
|
||||
}
|
||||
if (chanceAcerto <= 80) {
|
||||
for (MaplePartyCharacter chrS : enemies.getPartyMembers()) {
|
||||
if (dis == null) {
|
||||
chrS.getPlayer().dispel();
|
||||
} else {
|
||||
chrS.getPlayer().giveDebuff(dis, skill.getSkill());
|
||||
if (hitChance <= 80) {
|
||||
for (MaplePartyCharacter mpc : enemies.getPartyMembers()) {
|
||||
MapleCharacter mc = mpc.getPlayer();
|
||||
if (mc != null) {
|
||||
if (dis == null) {
|
||||
mc.dispel();
|
||||
} else {
|
||||
mc.giveDebuff(dis, skill.getSkill());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ public final class PartyOperationHandler extends AbstractMaplePacketHandler {
|
||||
}
|
||||
case 2: { // leave/disband
|
||||
if (party != null) {
|
||||
List<MapleCharacter> partymembers = player.getPartyMembers();
|
||||
List<MapleCharacter> partymembers = player.getPartyMembersOnline();
|
||||
|
||||
MapleParty.leaveParty(party, c);
|
||||
player.updatePartySearchAvailability(true);
|
||||
|
||||
@@ -34,7 +34,7 @@ import constants.ServerConstants;
|
||||
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import server.MapleItemInformationProvider;
|
||||
import server.MaplePortal;
|
||||
import server.maps.MaplePortal;
|
||||
import server.MapleTrade;
|
||||
import constants.GameConstants;
|
||||
import server.maps.FieldLimit;
|
||||
|
||||
@@ -53,6 +53,7 @@ import client.MapleDisease;
|
||||
import client.MapleFamily;
|
||||
import client.MapleFamilyEntry;
|
||||
import client.MapleKeyBinding;
|
||||
import client.MapleMount;
|
||||
import client.SkillFactory;
|
||||
import client.inventory.Equip;
|
||||
import client.inventory.Item;
|
||||
@@ -352,8 +353,14 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
if (newcomer) {
|
||||
for(MaplePet pet : player.getPets()) {
|
||||
if(pet != null)
|
||||
if(pet != null) {
|
||||
wserv.registerPetHunger(player, player.getPetIndex(pet));
|
||||
}
|
||||
}
|
||||
|
||||
MapleMount mount = player.getMount(); // thanks Ari for noticing a scenario where Silver Mane quest couldn't be started
|
||||
if (mount.getItemId() != 0) {
|
||||
player.announce(MaplePacketCreator.updateMount(player.getId(), mount, false));
|
||||
}
|
||||
|
||||
player.reloadQuestExpirations();
|
||||
|
||||
@@ -36,6 +36,7 @@ import client.inventory.ModifyInventory;
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import client.inventory.manipulator.MapleKarmaManipulator;
|
||||
import client.processor.AssignAPProcessor;
|
||||
import client.processor.AssignSPProcessor;
|
||||
import client.processor.DueyProcessor;
|
||||
import constants.GameConstants;
|
||||
import constants.ItemConstants;
|
||||
@@ -156,6 +157,10 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
if (itemId > 5050000) {
|
||||
int SPTo = slea.readInt();
|
||||
if (!AssignSPProcessor.canSPAssign(c, SPTo)) { // exploit found thanks to Arnah
|
||||
return;
|
||||
}
|
||||
|
||||
int SPFrom = slea.readInt();
|
||||
Skill skillSPTo = SkillFactory.getSkill(SPTo);
|
||||
Skill skillSPFrom = SkillFactory.getSkill(SPFrom);
|
||||
|
||||
@@ -71,7 +71,9 @@ public class MatchCheckerCPQChallenge implements MatchCheckerListenerRecipe {
|
||||
|
||||
List<MaplePartyCharacter> chrMembers = new LinkedList<>();
|
||||
for (MaplePartyCharacter mpc : chr.getParty().getMembers()) {
|
||||
chrMembers.add(mpc);
|
||||
if (mpc.isOnline()) {
|
||||
chrMembers.add(mpc);
|
||||
}
|
||||
}
|
||||
|
||||
if (message.contentEquals("cpq1")) {
|
||||
|
||||
@@ -91,8 +91,13 @@ public class MapleAlliance {
|
||||
List<MapleCharacter> mcl = new LinkedList<>();
|
||||
|
||||
for(MaplePartyCharacter mpc: party.getMembers()) {
|
||||
if(mpc.getPlayer().getGuildRank() == 1 && mpc.getPlayer().getMapId() == party.getLeader().getPlayer().getMapId())
|
||||
mcl.add(mpc.getPlayer());
|
||||
MapleCharacter chr = mpc.getPlayer();
|
||||
if (chr != null) {
|
||||
MapleCharacter lchr = party.getLeader().getPlayer();
|
||||
if (chr.getGuildRank() == 1 && lchr != null && chr.getMapId() == lchr.getMapId()) {
|
||||
mcl.add(chr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!mcl.isEmpty() && !mcl.get(0).isPartyLeader()) {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package net.server.worker;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import constants.ServerConstants;
|
||||
import net.server.world.World;
|
||||
import tools.FilePrinter;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Shavit
|
||||
*/
|
||||
public class TimeoutWorker extends BaseWorker implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
long time = System.currentTimeMillis();
|
||||
Collection<MapleCharacter> chars = wserv.getPlayerStorage().getAllCharacters();
|
||||
for(MapleCharacter chr : chars) {
|
||||
if(time - chr.getClient().getLastPacket() > ServerConstants.TIMEOUT_DURATION) {
|
||||
FilePrinter.print(FilePrinter.DCS + chr.getClient().getAccountName(), chr.getName() + " auto-disconnected due to inactivity.");
|
||||
chr.getClient().disconnect(true, chr.getCashShop().isOpened());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TimeoutWorker(World world) {
|
||||
super(world);
|
||||
}
|
||||
}
|
||||
@@ -144,6 +144,23 @@ public class MapleParty {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public List<MaplePartyCharacter> getPartyMembersOnline() {
|
||||
lock.lock();
|
||||
try {
|
||||
List<MaplePartyCharacter> ret = new LinkedList<>();
|
||||
|
||||
for (MaplePartyCharacter mpc : members) {
|
||||
if (mpc.isOnline()) {
|
||||
ret.add(mpc);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// used whenever entering PQs: will draw every party member that can attempt a target PQ while ingnoring those unfit.
|
||||
public Collection<MaplePartyCharacter> getEligibleMembers() {
|
||||
@@ -452,7 +469,7 @@ public class MapleParty {
|
||||
if (expelled != null) {
|
||||
MapleCharacter emc = expelled.getPlayer();
|
||||
if(emc != null) {
|
||||
List<MapleCharacter> partyMembers = emc.getPartyMembers();
|
||||
List<MapleCharacter> partyMembers = emc.getPartyMembersOnline();
|
||||
|
||||
MapleMap map = emc.getMap();
|
||||
if(map != null) map.removePartyMember(emc);
|
||||
|
||||
@@ -82,6 +82,9 @@ public class MaplePartyCharacter {
|
||||
|
||||
public void setOnline(boolean online) {
|
||||
this.online = online;
|
||||
if (!online) {
|
||||
this.character = null; // thanks Feras for noticing offline party members retaining whole character object unnecessarily
|
||||
}
|
||||
}
|
||||
|
||||
public int getMapId() {
|
||||
|
||||
@@ -60,12 +60,32 @@ import java.util.WeakHashMap;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
|
||||
import scripting.event.EventInstanceManager;
|
||||
import server.MapleStorage;
|
||||
import server.TimerManager;
|
||||
import server.maps.AbstractMapleMapObject;
|
||||
import server.maps.MapleHiredMerchant;
|
||||
import server.maps.MapleMap;
|
||||
import server.maps.MapleMiniDungeon;
|
||||
import server.maps.MapleMiniDungeonInfo;
|
||||
import server.maps.MaplePlayerShop;
|
||||
import server.maps.MaplePlayerShopItem;
|
||||
import server.maps.AbstractMapleMapObject;
|
||||
import net.server.PlayerStorage;
|
||||
import net.server.Server;
|
||||
import net.server.audit.LockCollector;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.MonitoredReentrantLock;
|
||||
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
|
||||
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
import net.server.channel.Channel;
|
||||
import net.server.channel.CharacterIdChannelPair;
|
||||
import net.server.coordinator.MapleInviteCoordinator;
|
||||
import net.server.coordinator.MapleInviteCoordinator.InviteResult;
|
||||
import net.server.coordinator.MapleInviteCoordinator.InviteType;
|
||||
import net.server.coordinator.MapleMatchCheckerCoordinator;
|
||||
import net.server.coordinator.MaplePartySearchCoordinator;
|
||||
import net.server.guild.MapleGuild;
|
||||
import net.server.guild.MapleGuildCharacter;
|
||||
import net.server.guild.MapleGuildSummary;
|
||||
import net.server.worker.CharacterAutosaverWorker;
|
||||
import net.server.worker.FamilyDailyResetWorker;
|
||||
import net.server.worker.FishingWorker;
|
||||
@@ -76,30 +96,13 @@ import net.server.worker.PartySearchWorker;
|
||||
import net.server.worker.PetFullnessWorker;
|
||||
import net.server.worker.ServerMessageWorker;
|
||||
import net.server.worker.TimedMapObjectWorker;
|
||||
import net.server.worker.TimeoutWorker;
|
||||
import net.server.worker.WeddingReservationWorker;
|
||||
import net.server.PlayerStorage;
|
||||
import net.server.Server;
|
||||
import net.server.audit.LockCollector;
|
||||
import net.server.channel.Channel;
|
||||
import net.server.channel.CharacterIdChannelPair;
|
||||
import net.server.guild.MapleGuild;
|
||||
import net.server.guild.MapleGuildCharacter;
|
||||
import net.server.guild.MapleGuildSummary;
|
||||
import net.server.world.announcer.MapleAnnouncerCoordinator;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Pair;
|
||||
import tools.packets.Fishing;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.MonitoredReentrantLock;
|
||||
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
|
||||
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
import net.server.coordinator.MapleInviteCoordinator;
|
||||
import net.server.coordinator.MapleInviteCoordinator.InviteResult;
|
||||
import net.server.coordinator.MapleInviteCoordinator.InviteType;
|
||||
import net.server.coordinator.MapleMatchCheckerCoordinator;
|
||||
import net.server.coordinator.MaplePartySearchCoordinator;
|
||||
import server.maps.MapleMiniDungeon;
|
||||
import server.maps.MapleMiniDungeonInfo;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -122,12 +125,14 @@ public class World {
|
||||
private PlayerStorage players = new PlayerStorage();
|
||||
private MapleMatchCheckerCoordinator matchChecker = new MapleMatchCheckerCoordinator();
|
||||
private MaplePartySearchCoordinator partySearch = new MaplePartySearchCoordinator();
|
||||
private MapleAnnouncerCoordinator announcer = new MapleAnnouncerCoordinator();
|
||||
|
||||
private final ReentrantReadWriteLock chnLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_CHANNELS, true);
|
||||
private ReadLock chnRLock = chnLock.readLock();
|
||||
private WriteLock chnWLock = chnLock.writeLock();
|
||||
|
||||
private Map<Integer, SortedMap<Integer, MapleCharacter>> accountChars = new HashMap<>();
|
||||
private Map<Integer, MapleStorage> accountStorages = new HashMap<>();
|
||||
private MonitoredReentrantLock accountCharsLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.WORLD_CHARS, true);
|
||||
|
||||
private Set<Integer> queuedGuilds = new HashSet<>();
|
||||
@@ -178,6 +183,7 @@ public class World {
|
||||
private ScheduledFuture<?> mapOwnershipSchedule;
|
||||
private ScheduledFuture<?> fishingSchedule;
|
||||
private ScheduledFuture<?> partySearchSchedule;
|
||||
private ScheduledFuture<?> timeoutSchedule;
|
||||
|
||||
public World(int world, int flag, String eventmsg, int exprate, int droprate, int bossdroprate, int mesorate, int questrate, int travelrate, int fishingrate) {
|
||||
this.id = world;
|
||||
@@ -211,12 +217,15 @@ public class World {
|
||||
mapOwnershipSchedule = tman.register(new MapOwnershipWorker(this), 20 * 1000, 20 * 1000);
|
||||
fishingSchedule = tman.register(new FishingWorker(this), 10 * 1000, 10 * 1000);
|
||||
partySearchSchedule = tman.register(new PartySearchWorker(this), 10 * 1000, 10 * 1000);
|
||||
timeoutSchedule = tman.register(new TimeoutWorker(this), 10 * 1000, 10 * 1000);
|
||||
|
||||
if(ServerConstants.USE_FAMILY_SYSTEM) {
|
||||
long timeLeft = Server.getTimeLeftForNextDay();
|
||||
FamilyDailyResetWorker.resetEntitlementUsage(this);
|
||||
tman.register(new FamilyDailyResetWorker(this), 24 * 60 * 60 * 1000, timeLeft);
|
||||
}
|
||||
|
||||
announcer.init();
|
||||
}
|
||||
|
||||
public int getChannelsSize() {
|
||||
@@ -440,6 +449,41 @@ public class World {
|
||||
}
|
||||
}
|
||||
|
||||
public void clearAccountCharacterView(Integer accountId) {
|
||||
accountCharsLock.lock();
|
||||
try {
|
||||
SortedMap<Integer, MapleCharacter> accChars = accountChars.remove(accountId);
|
||||
if (accChars != null) {
|
||||
accChars.clear();
|
||||
}
|
||||
} finally {
|
||||
accountCharsLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void registerAccountStorage(Integer accountId) {
|
||||
MapleStorage storage = MapleStorage.loadOrCreateFromDB(accountId, this.id);
|
||||
accountCharsLock.lock();
|
||||
try {
|
||||
accountStorages.put(accountId, storage);
|
||||
} finally {
|
||||
accountCharsLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void unregisterAccountStorage(Integer accountId) {
|
||||
accountCharsLock.lock();
|
||||
try {
|
||||
accountStorages.remove(accountId);
|
||||
} finally {
|
||||
accountCharsLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public MapleStorage getAccountStorage(Integer accountId) {
|
||||
return accountStorages.get(accountId);
|
||||
}
|
||||
|
||||
private static List<Entry<Integer, SortedMap<Integer, MapleCharacter>>> getSortedAccountCharacterView(Map<Integer, SortedMap<Integer, MapleCharacter>> map) {
|
||||
List<Entry<Integer, SortedMap<Integer, MapleCharacter>>> list = new ArrayList<>(map.size());
|
||||
for(Entry<Integer, SortedMap<Integer, MapleCharacter>> e : map.entrySet()) {
|
||||
@@ -512,6 +556,10 @@ public class World {
|
||||
public MaplePartySearchCoordinator getPartySearchCoordinator() {
|
||||
return partySearch;
|
||||
}
|
||||
|
||||
public MapleAnnouncerCoordinator getAnnouncerCoordinator() {
|
||||
return announcer;
|
||||
}
|
||||
|
||||
public void addPlayer(MapleCharacter chr) {
|
||||
players.addPlayer(chr);
|
||||
@@ -909,7 +957,7 @@ public class World {
|
||||
chr.setParty(party);
|
||||
chr.setMPC(partychar);
|
||||
}
|
||||
chr.getClient().announce(MaplePacketCreator.updateParty(chr.getClient().getChannel(), party, operation, target));
|
||||
chr.announce(MaplePacketCreator.updateParty(chr.getClient().getChannel(), party, operation, target));
|
||||
}
|
||||
}
|
||||
switch (operation) {
|
||||
@@ -917,7 +965,7 @@ public class World {
|
||||
case EXPEL:
|
||||
MapleCharacter chr = getPlayerStorage().getCharacterById(target.getId());
|
||||
if (chr != null) {
|
||||
chr.getClient().announce(MaplePacketCreator.updateParty(chr.getClient().getChannel(), party, operation, target));
|
||||
chr.announce(MaplePacketCreator.updateParty(chr.getClient().getChannel(), party, operation, target));
|
||||
chr.setParty(null);
|
||||
chr.setMPC(null);
|
||||
}
|
||||
@@ -948,25 +996,25 @@ public class World {
|
||||
break;
|
||||
case CHANGE_LEADER:
|
||||
MapleCharacter mc = party.getLeader().getPlayer();
|
||||
MapleCharacter newLeader = target.getPlayer();
|
||||
|
||||
EventInstanceManager eim = mc.getEventInstance();
|
||||
|
||||
if(eim != null && eim.isEventLeader(mc)) {
|
||||
eim.changedLeader(newLeader);
|
||||
} else {
|
||||
int oldLeaderMapid = mc.getMapId();
|
||||
|
||||
if (MapleMiniDungeonInfo.isDungeonMap(oldLeaderMapid)) {
|
||||
if (oldLeaderMapid != newLeader.getMapId()) {
|
||||
MapleMiniDungeon mmd = newLeader.getClient().getChannelServer().getMiniDungeon(oldLeaderMapid);
|
||||
if(mmd != null) {
|
||||
mmd.close();
|
||||
if (mc != null) {
|
||||
EventInstanceManager eim = mc.getEventInstance();
|
||||
|
||||
if(eim != null && eim.isEventLeader(mc)) {
|
||||
eim.changedLeader(target);
|
||||
} else {
|
||||
int oldLeaderMapid = mc.getMapId();
|
||||
|
||||
if (MapleMiniDungeonInfo.isDungeonMap(oldLeaderMapid)) {
|
||||
if (oldLeaderMapid != target.getMapId()) {
|
||||
MapleMiniDungeon mmd = mc.getClient().getChannelServer().getMiniDungeon(oldLeaderMapid);
|
||||
if(mmd != null) {
|
||||
mmd.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
party.setLeader(target);
|
||||
}
|
||||
party.setLeader(target);
|
||||
break;
|
||||
default:
|
||||
System.out.println("Unhandled updateParty operation " + operation.name());
|
||||
@@ -2114,9 +2162,15 @@ public class World {
|
||||
partySearchSchedule = null;
|
||||
}
|
||||
|
||||
if(timeoutSchedule != null) {
|
||||
timeoutSchedule.cancel(false);
|
||||
timeoutSchedule = null;
|
||||
}
|
||||
|
||||
players.disconnectAll();
|
||||
players = null;
|
||||
|
||||
announcer.shutdown();
|
||||
clearWorldData();
|
||||
System.out.println("Finished shutting down world " + id + "\r\n");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
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
|
||||
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.world.announcer;
|
||||
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import net.server.world.announcer.MapleAnnouncerEntryPool.SessionPacket;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ronan
|
||||
*/
|
||||
public class MapleAnnouncerCoordinator {
|
||||
|
||||
private static final MapleAnnouncerCoordinator instance = new MapleAnnouncerCoordinator();
|
||||
|
||||
public static MapleAnnouncerCoordinator getInstance() { // world-agnostic Announcer coordinator
|
||||
return instance;
|
||||
}
|
||||
|
||||
private MapleAnnouncerEntryPool pool = new MapleAnnouncerEntryPool();
|
||||
private ConcurrentLinkedQueue<SessionPacket> queue = new ConcurrentLinkedQueue<>();
|
||||
private Thread t;
|
||||
|
||||
public void append(IoSession io, byte[] packet) {
|
||||
queue.offer(pool.getSessionPacket(io, packet));
|
||||
}
|
||||
|
||||
public void init() {
|
||||
final Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
while (!Thread.interrupted()) {
|
||||
try {
|
||||
SessionPacket p = queue.poll();
|
||||
if (p != null) {
|
||||
IoSession session = p.getSession();
|
||||
byte[] packet = p.getPacket();
|
||||
|
||||
session.write(packet);
|
||||
pool.returnSessionPacket(p);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
t = new Thread(r);
|
||||
t.start();
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
t.interrupt();
|
||||
try {
|
||||
t.join();
|
||||
} catch (InterruptedException ie) {
|
||||
ie.printStackTrace();
|
||||
}
|
||||
|
||||
queue.clear();
|
||||
pool.shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
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
|
||||
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.world.announcer;
|
||||
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ronan
|
||||
*/
|
||||
public class MapleAnnouncerEntryPool {
|
||||
|
||||
private ConcurrentLinkedQueue<SessionPacket> instancedPairs = new ConcurrentLinkedQueue<>();
|
||||
private final static int initialCount = 20000; // initial length of the instanced pool
|
||||
|
||||
public MapleAnnouncerEntryPool() {
|
||||
for (int i = 0; i < initialCount; i++) {
|
||||
instancedPairs.offer(new SessionPacket());
|
||||
}
|
||||
}
|
||||
|
||||
public class SessionPacket {
|
||||
|
||||
private IoSession session;
|
||||
private byte[] packet;
|
||||
|
||||
public IoSession getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
public byte[] getPacket() {
|
||||
return packet;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public SessionPacket getSessionPacket(IoSession session, byte[] packet) {
|
||||
SessionPacket sp = instancedPairs.poll();
|
||||
if (sp == null) {
|
||||
sp = new SessionPacket();
|
||||
}
|
||||
|
||||
sp.session = session;
|
||||
sp.packet = packet;
|
||||
return sp;
|
||||
}
|
||||
|
||||
public void returnSessionPacket(SessionPacket sp) {
|
||||
instancedPairs.offer(sp);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
instancedPairs.clear();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -139,28 +139,15 @@ public class AbstractPlayerInteraction {
|
||||
}
|
||||
|
||||
public void warpParty(int id, int portalId, int fromMinId, int fromMaxId) {
|
||||
for (MapleCharacter mc : getPartyMembers()) {
|
||||
if(mc.getMapId() >= fromMinId && mc.getMapId() <= fromMaxId) {
|
||||
mc.changeMap(id, portalId);
|
||||
for (MapleCharacter mc : this.getPlayer().getPartyMembersOnline()) {
|
||||
if (mc.isLoggedinWorld()) {
|
||||
if(mc.getMapId() >= fromMinId && mc.getMapId() <= fromMaxId) {
|
||||
mc.changeMap(id, portalId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<MapleCharacter> getPartyMembers() {
|
||||
if (getPlayer().getParty() == null) {
|
||||
return null;
|
||||
}
|
||||
List<MapleCharacter> chars = new LinkedList<>();
|
||||
for (Channel channel : Server.getInstance().getChannelsFromWorld(getPlayer().getWorld())) {
|
||||
for (MapleCharacter chr : channel.getPartyMembers(getPlayer().getParty())) {
|
||||
if (chr != null) {
|
||||
chars.add(chr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return chars;
|
||||
}
|
||||
|
||||
public MapleMap getWarpMap(int map) {
|
||||
return getPlayer().getWarpMap(map);
|
||||
}
|
||||
@@ -804,9 +791,14 @@ public class AbstractPlayerInteraction {
|
||||
removeAll(id);
|
||||
return;
|
||||
}
|
||||
for (MaplePartyCharacter chr : getParty().getMembers()) {
|
||||
if (chr != null && chr.isOnline() && chr.getPlayer().getClient() != null){
|
||||
removeAll(id, chr.getPlayer().getClient());
|
||||
for (MaplePartyCharacter mpc : getParty().getMembers()) {
|
||||
if (mpc == null || !mpc.isOnline()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MapleCharacter chr = mpc.getPlayer();
|
||||
if (chr != null && chr.getClient() != null){
|
||||
removeAll(id, chr.getClient());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -837,9 +829,14 @@ public class AbstractPlayerInteraction {
|
||||
|
||||
if(instance) {
|
||||
for(MaplePartyCharacter member: party.getMembers()) {
|
||||
if(member == null || !member.isOnline() || member.getPlayer().getEventInstance() == null){
|
||||
if(member == null || !member.isOnline()){
|
||||
size--;
|
||||
}
|
||||
} else {
|
||||
MapleCharacter chr = member.getPlayer();
|
||||
if(chr != null && chr.getEventInstance() == null) {
|
||||
size--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -849,6 +846,9 @@ public class AbstractPlayerInteraction {
|
||||
continue;
|
||||
}
|
||||
MapleCharacter player = member.getPlayer();
|
||||
if(player == null) {
|
||||
continue;
|
||||
}
|
||||
if(instance && player.getEventInstance() == null){
|
||||
continue; // They aren't in the instance, don't give EXP.
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ import javax.script.*;
|
||||
|
||||
import constants.ServerConstants;
|
||||
import jdk.nashorn.api.scripting.NashornScriptEngine;
|
||||
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
|
||||
import tools.FilePrinter;
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
*/
|
||||
package scripting.event;
|
||||
|
||||
import tools.Pair;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@@ -39,7 +38,7 @@ import net.server.audit.locks.MonitoredReentrantReadWriteLock;
|
||||
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
import net.server.world.MapleParty;
|
||||
import net.server.world.MaplePartyCharacter;
|
||||
import server.MaplePortal;
|
||||
import server.maps.MaplePortal;
|
||||
import server.TimerManager;
|
||||
import server.MapleStatEffect;
|
||||
import server.expeditions.MapleExpedition;
|
||||
@@ -67,6 +66,7 @@ import server.ThreadManager;
|
||||
import server.life.MapleLifeFactory;
|
||||
import server.life.MapleNPC;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Pair;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -366,9 +366,13 @@ public class EventInstanceManager {
|
||||
}
|
||||
|
||||
public void registerParty(MapleParty party, MapleMap map) {
|
||||
for (MaplePartyCharacter pc : party.getEligibleMembers()) {
|
||||
MapleCharacter c = map.getCharacterById(pc.getId());
|
||||
registerPlayer(c);
|
||||
for (MaplePartyCharacter mpc : party.getEligibleMembers()) {
|
||||
if (mpc.isOnline()) { // thanks resinate
|
||||
MapleCharacter chr = map.getCharacterById(mpc.getId());
|
||||
if (chr != null) {
|
||||
registerPlayer(chr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,7 +472,7 @@ public class EventInstanceManager {
|
||||
} catch (ScriptException | NoSuchMethodException ex) {} // optional
|
||||
}
|
||||
|
||||
public synchronized void changedLeader(final MapleCharacter ldr) {
|
||||
public synchronized void changedLeader(final MaplePartyCharacter ldr) {
|
||||
try {
|
||||
invokeScriptFunction("changedLeader", EventInstanceManager.this, ldr);
|
||||
} catch (ScriptException | NoSuchMethodException ex) {
|
||||
|
||||
@@ -759,7 +759,7 @@ public class EventManager {
|
||||
return(new ArrayList<>());
|
||||
}
|
||||
try {
|
||||
Object p = iv.invokeFunction("getEligibleParty", party.getPartyMembers());
|
||||
Object p = iv.invokeFunction("getEligibleParty", party.getPartyMembersOnline());
|
||||
|
||||
if(p != null) {
|
||||
List<MaplePartyCharacter> lmpc;
|
||||
|
||||
@@ -684,8 +684,7 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
|
||||
map = cs.getMapFactory().getMap(980000100 + 100 * field);
|
||||
mapExit = cs.getMapFactory().getMap(980000000);
|
||||
for (MaplePartyCharacter mpc : c.getPlayer().getParty().getMembers()) {
|
||||
final MapleCharacter mc;
|
||||
mc = ps.getCharacterById(mpc.getId());
|
||||
final MapleCharacter mc = ps.getCharacterById(mpc.getId());
|
||||
if (mc != null) {
|
||||
mc.setChallenged(false);
|
||||
mc.changeMap(map, map.getPortal(0));
|
||||
@@ -780,11 +779,15 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
|
||||
PlayerStorage ps = c.getChannelServer().getPlayerStorage();
|
||||
for (MaplePartyCharacter mpc : getPlayer().getParty().getMembers()) {
|
||||
MapleCharacter mc = ps.getCharacterById(mpc.getId());
|
||||
mc.setMonsterCarnival(null);
|
||||
if (mc != null) {
|
||||
mc.setMonsterCarnival(null);
|
||||
}
|
||||
}
|
||||
for (MaplePartyCharacter mpc : challenger.getParty().getMembers()) {
|
||||
MapleCharacter mc = ps.getCharacterById(mpc.getId());
|
||||
mc.setMonsterCarnival(null);
|
||||
if (mc != null) {
|
||||
mc.setMonsterCarnival(null);
|
||||
}
|
||||
}
|
||||
} catch (NullPointerException npe) {
|
||||
warpoutCPQLobby(lobbyMap);
|
||||
@@ -826,11 +829,15 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
|
||||
PlayerStorage ps = c.getChannelServer().getPlayerStorage();
|
||||
for (MaplePartyCharacter mpc : getPlayer().getParty().getMembers()) {
|
||||
MapleCharacter mc = ps.getCharacterById(mpc.getId());
|
||||
mc.setMonsterCarnival(null);
|
||||
if (mc != null) {
|
||||
mc.setMonsterCarnival(null);
|
||||
}
|
||||
}
|
||||
for (MaplePartyCharacter mpc : challenger.getParty().getMembers()) {
|
||||
MapleCharacter mc = ps.getCharacterById(mpc.getId());
|
||||
mc.setMonsterCarnival(null);
|
||||
if (mc != null) {
|
||||
mc.setMonsterCarnival(null);
|
||||
}
|
||||
}
|
||||
} catch (NullPointerException npe) {
|
||||
warpoutCPQLobby(lobbyMap);
|
||||
@@ -905,8 +912,7 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
|
||||
mapExit = cs.getMapFactory().getMap(980030000);
|
||||
map = cs.getMapFactory().getMap(980031000 + 1000 * field);
|
||||
for (MaplePartyCharacter mpc : c.getPlayer().getParty().getMembers()) {
|
||||
final MapleCharacter mc;
|
||||
mc = ps.getCharacterById(mpc.getId());
|
||||
final MapleCharacter mc = ps.getCharacterById(mpc.getId());
|
||||
if (mc != null) {
|
||||
mc.setChallenged(false);
|
||||
mc.changeMap(map, map.getPortal(0));
|
||||
|
||||
@@ -27,7 +27,7 @@ import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import scripting.AbstractPlayerInteraction;
|
||||
import server.MaplePortal;
|
||||
import server.maps.MaplePortal;
|
||||
import server.quest.MapleQuest;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
@@ -35,7 +35,7 @@ import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineFactory;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import javax.script.ScriptException;
|
||||
import server.MaplePortal;
|
||||
import server.maps.MaplePortal;
|
||||
import tools.FilePrinter;
|
||||
|
||||
public class PortalScriptManager {
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
*/
|
||||
package server;
|
||||
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.util.ArrayList;
|
||||
@@ -40,6 +39,7 @@ import server.maps.MapleMap;
|
||||
import server.maps.MapleMapObject;
|
||||
import server.maps.MapleMapObjectType;
|
||||
import server.maps.MapleMist;
|
||||
import server.maps.MaplePortal;
|
||||
import server.maps.MapleSummon;
|
||||
import server.maps.SummonMovementType;
|
||||
import tools.ArrayMap;
|
||||
@@ -55,6 +55,7 @@ import client.SkillFactory;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.MapleInventory;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import client.status.MonsterStatus;
|
||||
import client.status.MonsterStatusEffect;
|
||||
import constants.ItemConstants;
|
||||
@@ -1004,21 +1005,26 @@ public class MapleStatEffect {
|
||||
}
|
||||
}
|
||||
if (isShadowClaw()) {
|
||||
int projectile = 0;
|
||||
MapleInventory use = applyto.getInventory(MapleInventoryType.USE);
|
||||
for (int i = 1; i <= use.getSlotLimit(); i++) { // impose order...
|
||||
Item item = use.getItem((short) i);
|
||||
if (item != null) {
|
||||
if (ItemConstants.isThrowingStar(item.getItemId()) && item.getQuantity() >= 200) {
|
||||
projectile = item.getItemId();
|
||||
break;
|
||||
use.lockInventory();
|
||||
try {
|
||||
Item projectile = null;
|
||||
for (int i = 1; i <= use.getSlotLimit(); i++) { // impose order...
|
||||
Item item = use.getItem((short) i);
|
||||
if (item != null) {
|
||||
if (ItemConstants.isThrowingStar(item.getItemId()) && item.getQuantity() >= 200) {
|
||||
projectile = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (projectile == 0) {
|
||||
return false;
|
||||
} else {
|
||||
MapleInventoryManipulator.removeById(applyto.getClient(), MapleInventoryType.USE, projectile, 200, false, true);
|
||||
if (projectile == null) {
|
||||
return false;
|
||||
} else {
|
||||
MapleInventoryManipulator.removeFromSlot(applyto.getClient(), MapleInventoryType.USE, projectile.getPosition(), (short) 200, false, true);
|
||||
}
|
||||
} finally {
|
||||
use.unlockInventory();
|
||||
}
|
||||
}
|
||||
SummonMovementType summonMovementType = getSummonMovementType();
|
||||
@@ -1125,8 +1131,11 @@ public class MapleStatEffect {
|
||||
} else if (cureDebuffs.size() > 0) { // by Drago-Dragohe4rt
|
||||
for (final MapleDisease debuff : cureDebuffs) {
|
||||
if (applyfrom.getParty() != null) {
|
||||
for (MaplePartyCharacter chrs : applyfrom.getParty().getPartyMembers()) {
|
||||
chrs.getPlayer().dispelDebuff(debuff);
|
||||
for (MaplePartyCharacter mpc : applyfrom.getParty().getPartyMembers()) {
|
||||
MapleCharacter chr = mpc.getPlayer();
|
||||
if (chr != null) {
|
||||
chr.dispelDebuff(debuff);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
applyfrom.dispelDebuff(debuff);
|
||||
|
||||
@@ -58,13 +58,12 @@ public class MapleStorage {
|
||||
private int meso;
|
||||
private byte slots;
|
||||
private Map<MapleInventoryType, List<Item>> typeItems = new HashMap<>();
|
||||
private List<Item> items;
|
||||
private List<Item> items = new LinkedList<>();
|
||||
private Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.STORAGE, true);
|
||||
|
||||
private MapleStorage(int id, byte slots, int meso) {
|
||||
this.id = id;
|
||||
this.slots = slots;
|
||||
this.items = new LinkedList<>();
|
||||
this.meso = meso;
|
||||
}
|
||||
|
||||
@@ -119,15 +118,20 @@ public class MapleStorage {
|
||||
return slots;
|
||||
}
|
||||
|
||||
public synchronized boolean gainSlots(int slots) {
|
||||
slots += this.slots;
|
||||
public boolean gainSlots(int slots) {
|
||||
lock.lock();
|
||||
try {
|
||||
slots += this.slots;
|
||||
|
||||
if (slots <= 48) {
|
||||
this.slots = (byte) slots;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (slots <= 48) {
|
||||
this.slots = (byte) slots;
|
||||
return true;
|
||||
return false;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void saveToDB(Connection con) {
|
||||
@@ -160,29 +164,33 @@ public class MapleStorage {
|
||||
}
|
||||
}
|
||||
|
||||
public Item takeOut(byte slot) {
|
||||
Item ret;
|
||||
|
||||
public boolean takeOut(Item item) {
|
||||
lock.lock();
|
||||
try {
|
||||
ret = items.remove(slot);
|
||||
boolean ret = items.remove(item);
|
||||
|
||||
MapleInventoryType type = ret.getInventoryType();
|
||||
MapleInventoryType type = item.getInventoryType();
|
||||
typeItems.put(type, new ArrayList<>(filterItems(type)));
|
||||
|
||||
return ret;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void store(Item item) {
|
||||
public boolean store(Item item) {
|
||||
lock.lock();
|
||||
try {
|
||||
if (isFull()) { // thanks Optimist for noticing unrestricted amount of insertions here
|
||||
return false;
|
||||
}
|
||||
|
||||
items.add(item);
|
||||
|
||||
MapleInventoryType type = item.getInventoryType();
|
||||
typeItems.put(type, new ArrayList<>(filterItems(type)));
|
||||
|
||||
return true;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
@@ -196,7 +204,7 @@ public class MapleStorage {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private List<Item> filterItems(MapleInventoryType type) {
|
||||
List<Item> storageItems = getItems();
|
||||
List<Item> ret = new LinkedList<>();
|
||||
@@ -208,7 +216,7 @@ public class MapleStorage {
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
public byte getSlot(MapleInventoryType type, byte slot) {
|
||||
lock.lock();
|
||||
try {
|
||||
@@ -225,7 +233,7 @@ public class MapleStorage {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void sendStorage(MapleClient c, int npcId) {
|
||||
if (c.getPlayer().getLevel() < 15){
|
||||
c.getPlayer().dropMessage(1, "You may only use the storage once you have reached level 15.");
|
||||
@@ -287,7 +295,7 @@ public class MapleStorage {
|
||||
for (MapleInventoryType type : MapleInventoryType.values()) {
|
||||
typeItems.put(type, new ArrayList<>(items));
|
||||
}
|
||||
|
||||
|
||||
c.announce(MaplePacketCreator.arrangeStorage(slots, items));
|
||||
} finally {
|
||||
lock.unlock();
|
||||
@@ -353,7 +361,7 @@ public class MapleStorage {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void close() {
|
||||
lock.lock();
|
||||
try {
|
||||
@@ -362,4 +370,5 @@ public class MapleStorage {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -703,7 +703,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
exp = Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
return (int) exp;
|
||||
return (int) Math.round(exp); // operations on float point are not point-precise... thanks IxianMace for noticing -1 EXP gains
|
||||
}
|
||||
|
||||
private void giveExpToCharacter(MapleCharacter attacker, Float personalExp, Float partyExp, boolean white, boolean hasPartySharers) {
|
||||
|
||||
@@ -331,7 +331,7 @@ public class MobSkill {
|
||||
if (lt != null && rb != null && skill) {
|
||||
int i = 0;
|
||||
for (MapleCharacter character : getPlayersInRange(monster, player)) {
|
||||
if (!character.isActiveBuffedValue(2321005)) { // holy shield
|
||||
if (!character.hasActiveBuff(2321005)) { // holy shield
|
||||
if (disease.equals(MapleDisease.SEDUCE)) {
|
||||
if (i < 10) {
|
||||
character.giveDebuff(MapleDisease.SEDUCE, this);
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
package server.maps;
|
||||
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import server.MaplePortal;
|
||||
import server.TimerManager;
|
||||
|
||||
public class MapMonitor {
|
||||
|
||||
@@ -25,7 +25,6 @@ import java.awt.Point;
|
||||
import java.util.Collection;
|
||||
import tools.Pair;
|
||||
|
||||
import server.MaplePortal;
|
||||
import client.MapleCharacter;
|
||||
import constants.ServerConstants;
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ import client.MapleCharacter;
|
||||
import constants.GameConstants;
|
||||
import java.awt.Point;
|
||||
import scripting.portal.PortalScriptManager;
|
||||
import server.MaplePortal;
|
||||
import tools.MaplePacketCreator;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.MonitoredReentrantLock;
|
||||
|
||||
@@ -68,7 +68,6 @@ import net.server.channel.Channel;
|
||||
import net.server.world.World;
|
||||
import scripting.map.MapScriptManager;
|
||||
import server.MapleItemInformationProvider;
|
||||
import server.MaplePortal;
|
||||
import server.MapleStatEffect;
|
||||
import server.TimerManager;
|
||||
import server.events.gm.MapleCoconut;
|
||||
|
||||
@@ -35,7 +35,6 @@ import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import server.PortalFactory;
|
||||
import server.life.AbstractLoadedMapleLife;
|
||||
import server.life.MapleLifeFactory;
|
||||
import server.life.MapleMonster;
|
||||
@@ -163,7 +162,7 @@ public class MapleMapFactory {
|
||||
|
||||
map.setFieldLimit(MapleDataTool.getInt(infoData.getChildByPath("fieldLimit"), 0));
|
||||
map.setMobInterval((short) MapleDataTool.getInt(infoData.getChildByPath("createMobInterval"), 5000));
|
||||
PortalFactory portalFactory = new PortalFactory();
|
||||
MaplePortalFactory portalFactory = new MaplePortalFactory();
|
||||
for (MapleData portal : mapData.getChildByPath("portal")) {
|
||||
map.addPortal(portalFactory.makePortal(MapleDataTool.getInt(portal.getChildByPath("pt")), portal));
|
||||
}
|
||||
|
||||
@@ -21,8 +21,6 @@
|
||||
*/
|
||||
package server.maps;
|
||||
|
||||
import server.MaplePortal;
|
||||
|
||||
public class MapleMapPortal extends MapleGenericPortal {
|
||||
public MapleMapPortal() {
|
||||
super(MaplePortal.MAP_PORTAL);
|
||||
|
||||
@@ -19,7 +19,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 server;
|
||||
package server.maps;
|
||||
|
||||
import java.awt.Point;
|
||||
import client.MapleClient;
|
||||
@@ -19,7 +19,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 server;
|
||||
package server.maps;
|
||||
|
||||
import java.awt.Point;
|
||||
|
||||
@@ -28,10 +28,10 @@ import provider.MapleDataTool;
|
||||
import server.maps.MapleGenericPortal;
|
||||
import server.maps.MapleMapPortal;
|
||||
|
||||
public class PortalFactory {
|
||||
public class MaplePortalFactory {
|
||||
private int nextDoorPortal;
|
||||
|
||||
public PortalFactory() {
|
||||
public MaplePortalFactory() {
|
||||
nextDoorPortal = 0x80;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ public class MonsterCarnival {
|
||||
private ScheduledFuture<?> timer, effectTimer, respawnTask;
|
||||
private long startTime = 0;
|
||||
private int summonsR = 0, summonsB = 0, room = 0;
|
||||
private MapleCharacter leader1, leader2, Grupo1, Grupo2;
|
||||
private MapleCharacter leader1, leader2, team1, team2;
|
||||
private int redCP, blueCP, redTotalCP, blueTotalCP, redTimeupCP, blueTimeupCP;
|
||||
private boolean cpq1;
|
||||
|
||||
@@ -60,7 +60,7 @@ public class MonsterCarnival {
|
||||
if (p1.getLeader().getId() == mc.getId()) {
|
||||
leader1 = mc;
|
||||
}
|
||||
Grupo1 = mc;
|
||||
team1 = mc;
|
||||
}
|
||||
}
|
||||
for (MaplePartyCharacter mpc : p2.getMembers()) {
|
||||
@@ -74,15 +74,21 @@ public class MonsterCarnival {
|
||||
if (p2.getLeader().getId() == mc.getId()) {
|
||||
leader2 = mc;
|
||||
}
|
||||
Grupo2 = mc;
|
||||
team2 = mc;
|
||||
}
|
||||
}
|
||||
if (Grupo1 == null || Grupo2 == null) {
|
||||
for (MaplePartyCharacter mpc : p2.getMembers()) {
|
||||
mpc.getPlayer().dropMessage(5, LanguageConstants.getMessage(mpc.getPlayer(), LanguageConstants.CPQError));
|
||||
if (team1 == null || team2 == null) {
|
||||
for (MaplePartyCharacter mpc : p1.getMembers()) {
|
||||
MapleCharacter chr = mpc.getPlayer();
|
||||
if (chr != null) {
|
||||
chr.dropMessage(5, LanguageConstants.getMessage(chr, LanguageConstants.CPQError));
|
||||
}
|
||||
}
|
||||
for (MaplePartyCharacter mpc : p2.getMembers()) {
|
||||
mpc.getPlayer().dropMessage(5, LanguageConstants.getMessage(mpc.getPlayer(), LanguageConstants.CPQError));
|
||||
MapleCharacter chr = mpc.getPlayer();
|
||||
if (chr != null) {
|
||||
chr.dropMessage(5, LanguageConstants.getMessage(chr, LanguageConstants.CPQError));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ public class FilePrinter {
|
||||
DELETED_CHAR = "players/deletedchars/",
|
||||
UNHANDLED_EVENT = "game/DoesNotExist.txt",
|
||||
SESSION = "players/Sessions.txt",
|
||||
DCS = "game/disconnections/",
|
||||
EXPLOITS = "game/exploits/",
|
||||
STORAGE = "game/storage/",
|
||||
PACKET_LOGS = "game/packetlogs/",
|
||||
|
||||
@@ -702,7 +702,8 @@ public class MaplePacketCreator {
|
||||
*/
|
||||
public static byte[] getAuthSuccess(MapleClient c) {
|
||||
Server.getInstance().loadAccountCharacters(c); // locks the login session until data is recovered from the cache or the DB.
|
||||
|
||||
Server.getInstance().loadAccountStorages(c);
|
||||
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.LOGIN_STATUS.getValue());
|
||||
mplew.writeInt(0);
|
||||
@@ -8216,7 +8217,7 @@ public class MaplePacketCreator {
|
||||
mplew.writeInt(exp);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
|
||||
public static byte[] spawnDragon(MapleDragon dragon) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.SPAWN_DRAGON.getValue());
|
||||
|
||||
Reference in New Issue
Block a user