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:
ronancpl
2019-08-28 03:49:05 -03:00
parent 19e70ddf87
commit efbce82a8b
77 changed files with 959 additions and 293 deletions
+190 -69
View File
@@ -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));
}
}
+24 -9
View File
@@ -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) {
+4
View File
@@ -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;
+1
View File
@@ -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;
+13
View File
@@ -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);
+28 -17
View File
@@ -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++) {
+26 -15
View File
@@ -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 {