Mob aggro overhaul + STR-DEX autoassign patch + Guild & Pl. Store fix
Reviewed Monster Magnet skill effect on mobs, now properly showing up to other players. Reworked concurrency protection on login handler, now properly synchronizing requests for the same accountId. Reviewed an expedition issue with attempting to send packets on loggedoff players. Expeditions no longer holds a character object list of its own, rather finding players through their id on the world storage. Added stat requirement definition as first message on 1st job NPCs. Fixed an issue with area triggered NPC conversations not getting properly disposed. Reviewed "launch.bat", internally referencing Java7 engine. Assuming it has been installed in the default directory on the system, it's no longer necessary to set precedence on PATH for it. Fixed need for "reapproval from the 3rd job instructors" to attempt Zakum once more. Reviewed goto command. Non-GM's no longer has access to area maps, only towns. Implemented a new server flag for enforcing base rates (server rate: 1x) on players level 10 or lower. Fixed player guild tooltips not being properly marshalled to others on the map, at the event of several guild actions. Reviewed event system allowing creation of same-name events, potentially leading to null EIM problems. Refactored some locks on MapleCharacter, potentially solving a deadlock case within expiring/forfeiting quest methods. Implemented a major overhaul on mob aggro system, now updating player aggro in real-time based on their latest DPS. Properly refactored and encapsulated aggro mechanics. Fixed NPE on disposing events with alive mobs. Added server-side birthday check handling when opening player shops or merchants having cash items in store. Fixed player shop tooltip not finishing properly when shop owner closes store with visitors still in there. Fixed forceChangeMap method not warping players to the designated event map (rather sending them to the starting event map). Reviewed "summon" command, now using forceChangeMap, also changing channels if needed. Reworked HeavenMS autoassigner: STR-based classes now ups a bit more DEX than before, since its a vital attribute for accuracy on their actions. Added meso ceil check on player transactions. Trades now gets suspended if the max amount is reached. Implemented server-side item limit check on player shops and merchants. Fixed an exploit with merchants, on where players would be able to save the selling item on DB before taking from inventory.
This commit is contained in:
@@ -273,7 +273,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
private ScheduledFuture<?> pendantOfSpirit = null; //1122017
|
||||
private Lock chrLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHARACTER_CHR, true);
|
||||
private Lock evtLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHARACTER_EVT, true);
|
||||
private Lock petLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHARACTER_PET, true); // for meso & quest tasks as well
|
||||
private Lock petLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHARACTER_PET, true);
|
||||
private Lock prtLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHARACTER_PRT);
|
||||
private Lock cpnLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHARACTER_CPN);
|
||||
private Map<Integer, Set<Integer>> excluded = new LinkedHashMap<>();
|
||||
@@ -852,17 +852,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
List<Pair<MapleBuffStat, Integer>> ldsstat = Collections.singletonList(new Pair<MapleBuffStat, Integer>(MapleBuffStat.DARKSIGHT, 0));
|
||||
getMap().broadcastGMMessage(this, MaplePacketCreator.giveForeignBuff(id, ldsstat), false);
|
||||
for (MapleMonster mon : this.getControlledMonsters()) {
|
||||
mon.lockMonster();
|
||||
try {
|
||||
mon.setController(null);
|
||||
mon.setControllerHasAggro(false);
|
||||
mon.setControllerKnowsAboutAggro(false);
|
||||
mon.getMap().updateMonsterController(mon);
|
||||
} finally {
|
||||
mon.unlockMonster();
|
||||
}
|
||||
}
|
||||
this.releaseControlledMonsters();
|
||||
}
|
||||
announce(MaplePacketCreator.enableActions());
|
||||
}
|
||||
@@ -1356,7 +1346,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
}
|
||||
|
||||
mapEim.registerPlayer(this);
|
||||
// thanks Thora for finding an issue with players not being actually warped into the target event map (rather sent to the event starting map)
|
||||
mapEim.registerPlayer(this, false);
|
||||
}
|
||||
|
||||
MapleMap to = target; // warps directly to the target intead of the target's map id, this allows GMs to patrol players inside instances.
|
||||
@@ -1708,29 +1699,48 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
}
|
||||
|
||||
public void checkMonsterAggro(MapleMonster monster) {
|
||||
monster.lockMonster();
|
||||
try {
|
||||
if (!monster.isControllerHasAggro()) {
|
||||
if (monster.getController() == this) {
|
||||
monster.setControllerHasAggro(true);
|
||||
} else {
|
||||
monster.switchController(this, true);
|
||||
}
|
||||
public void controlMonster(MapleMonster monster) {
|
||||
if (cpnLock.tryLock()) {
|
||||
try {
|
||||
controlled.add(monster);
|
||||
} finally {
|
||||
cpnLock.unlock();
|
||||
}
|
||||
} finally {
|
||||
monster.unlockMonster();
|
||||
}
|
||||
}
|
||||
|
||||
public void controlMonster(MapleMonster monster, boolean aggro) {
|
||||
monster.lockMonster();
|
||||
|
||||
public void stopControllingMonster(MapleMonster monster) {
|
||||
if (cpnLock.tryLock()) {
|
||||
try {
|
||||
controlled.remove(monster);
|
||||
} finally {
|
||||
cpnLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getNumControlledMonsters() {
|
||||
cpnLock.lock();
|
||||
try {
|
||||
monster.setController(this);
|
||||
controlled.add(monster);
|
||||
client.announce(MaplePacketCreator.controlMonster(monster, false, aggro));
|
||||
return controlled.size();
|
||||
} finally {
|
||||
monster.unlockMonster();
|
||||
cpnLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void releaseControlledMonsters() {
|
||||
Collection<MapleMonster> controlledMonsters;
|
||||
|
||||
cpnLock.lock();
|
||||
try {
|
||||
controlledMonsters = new ArrayList<>(controlled);
|
||||
controlled.clear();
|
||||
} finally {
|
||||
cpnLock.unlock();
|
||||
}
|
||||
|
||||
for (MapleMonster monster : controlledMonsters) {
|
||||
monster.aggroRedirectController();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2967,6 +2977,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canHoldMeso(int gain) { // thanks lucasziron found pointing out a need to check space availability for mesos on player transactions
|
||||
long nextMeso = (long) meso.get() + gain;
|
||||
return nextMeso <= Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
public void gainMeso(int gain) {
|
||||
gainMeso(gain, true, false, true);
|
||||
}
|
||||
@@ -4124,11 +4139,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
return Collections.unmodifiableList(ret);
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<MapleMonster> getControlledMonsters() {
|
||||
return Collections.unmodifiableCollection(controlled);
|
||||
}
|
||||
|
||||
|
||||
public List<MapleRing> getCrushRings() {
|
||||
Collections.sort(crushRings);
|
||||
return crushRings;
|
||||
@@ -4343,7 +4354,15 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
return gachaexp.get();
|
||||
}
|
||||
|
||||
public boolean hasNoviceExpRate() {
|
||||
return ServerConstants.USE_ENFORCE_NOVICE_EXPRATE && isBeginnerJob() && level < 11;
|
||||
}
|
||||
|
||||
public int getExpRate() {
|
||||
if (hasNoviceExpRate()) { // base exp rate 1x for early levels idea thanks to Vcoc
|
||||
return 1;
|
||||
}
|
||||
|
||||
return expRate;
|
||||
}
|
||||
|
||||
@@ -4383,7 +4402,17 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
public int getRawMesoRate() {
|
||||
return mesoRate / (mesoCoupon * getWorldServer().getMesoRate());
|
||||
}
|
||||
|
||||
|
||||
public int getQuestExpRate() {
|
||||
World w = getWorldServer();
|
||||
return w.getExpRate() * w.getQuestRate();
|
||||
}
|
||||
|
||||
public int getQuestMesoRate() {
|
||||
World w = getWorldServer();
|
||||
return w.getMesoRate() * w.getQuestRate();
|
||||
}
|
||||
|
||||
public int getFace() {
|
||||
return face;
|
||||
}
|
||||
@@ -4829,10 +4858,6 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
}
|
||||
|
||||
public int getNumControlledMonsters() {
|
||||
return controlled.size();
|
||||
}
|
||||
|
||||
public MapleParty getParty() {
|
||||
prtLock.lock();
|
||||
try {
|
||||
@@ -5635,9 +5660,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
public boolean isGuildLeader() { // true on guild master or jr. master
|
||||
return guildid > 0 && guildRank < 3;
|
||||
}
|
||||
|
||||
|
||||
public void leaveMap() {
|
||||
controlled.clear();
|
||||
releaseControlledMonsters();
|
||||
visibleMapObjects.clear();
|
||||
setChair(0);
|
||||
if (hpDecreaseTask != null) {
|
||||
@@ -7772,17 +7797,22 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
throw new RuntimeException("Character not in database (" + id + ")");
|
||||
}
|
||||
|
||||
List<MaplePet> petList = new LinkedList<>();
|
||||
petLock.lock();
|
||||
try {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (pets[i] != null) {
|
||||
pets[i].saveToDb();
|
||||
petList.add(pets[i]);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
petLock.unlock();
|
||||
}
|
||||
|
||||
for (MaplePet pet : petList) {
|
||||
pet.saveToDb();
|
||||
}
|
||||
|
||||
for(Entry<Integer, Set<Integer>> es: getExcluded().entrySet()) { // this set is already protected
|
||||
try (PreparedStatement ps2 = con.prepareStatement("DELETE FROM petignores WHERE petid=?")) {
|
||||
ps2.setInt(1, es.getKey());
|
||||
@@ -8912,11 +8942,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
}, duration);
|
||||
}
|
||||
|
||||
public void stopControllingMonster(MapleMonster monster) {
|
||||
controlled.remove(monster);
|
||||
}
|
||||
|
||||
|
||||
public void unequipAllPets() {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
MaplePet pet = getPet(i);
|
||||
@@ -9046,19 +9072,19 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
|
||||
public void cancelQuestExpirationTask() {
|
||||
petLock.lock();
|
||||
evtLock.lock();
|
||||
try {
|
||||
if (questExpireTask != null) {
|
||||
questExpireTask.cancel(false);
|
||||
questExpireTask = null;
|
||||
}
|
||||
} finally {
|
||||
petLock.unlock();
|
||||
evtLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void forfeitExpirableQuests() {
|
||||
petLock.lock();
|
||||
evtLock.lock();
|
||||
try {
|
||||
for(MapleQuest quest : questExpirations.keySet()) {
|
||||
quest.forfeit(this);
|
||||
@@ -9066,12 +9092,12 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
|
||||
questExpirations.clear();
|
||||
} finally {
|
||||
petLock.unlock();
|
||||
evtLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void questExpirationTask() {
|
||||
petLock.lock();
|
||||
evtLock.lock();
|
||||
try {
|
||||
if(!questExpirations.isEmpty()) {
|
||||
if(questExpireTask == null) {
|
||||
@@ -9084,12 +9110,12 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
petLock.unlock();
|
||||
evtLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void runQuestExpireTask() {
|
||||
petLock.lock();
|
||||
evtLock.lock();
|
||||
try {
|
||||
long timeNow = Server.getInstance().getCurrentTime();
|
||||
List<MapleQuest> expireList = new LinkedList<>();
|
||||
@@ -9110,12 +9136,12 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
petLock.unlock();
|
||||
evtLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void registerQuestExpire(MapleQuest quest, long time) {
|
||||
petLock.lock();
|
||||
evtLock.lock();
|
||||
try {
|
||||
if(questExpireTask == null) {
|
||||
questExpireTask = TimerManager.getInstance().register(new Runnable() {
|
||||
@@ -9128,7 +9154,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
|
||||
questExpirations.put(quest, Server.getInstance().getCurrentTime() + time);
|
||||
} finally {
|
||||
petLock.unlock();
|
||||
evtLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9671,7 +9697,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
if (pendantOfSpirit != null) { pendantOfSpirit.cancel(true); }
|
||||
pendantOfSpirit = null;
|
||||
|
||||
petLock.lock();
|
||||
evtLock.lock();
|
||||
try {
|
||||
if (questExpireTask != null) {
|
||||
questExpireTask.cancel(false);
|
||||
@@ -9681,7 +9707,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
questExpirations = null;
|
||||
}
|
||||
} finally {
|
||||
petLock.unlock();
|
||||
evtLock.unlock();
|
||||
}
|
||||
|
||||
if (maplemount != null) {
|
||||
|
||||
@@ -175,22 +175,22 @@ public class MapleClient {
|
||||
return chars;
|
||||
}
|
||||
|
||||
public List<String> loadCharacterNames(int serverId) {
|
||||
public List<String> loadCharacterNames(int worldId) {
|
||||
List<String> chars = new ArrayList<>(15);
|
||||
for (CharNameAndId cni : loadCharactersInternal(serverId)) {
|
||||
for (CharNameAndId cni : loadCharactersInternal(worldId)) {
|
||||
chars.add(cni.name);
|
||||
}
|
||||
return chars;
|
||||
}
|
||||
|
||||
private List<CharNameAndId> loadCharactersInternal(int serverId) {
|
||||
private List<CharNameAndId> loadCharactersInternal(int worldId) {
|
||||
PreparedStatement ps;
|
||||
List<CharNameAndId> chars = new ArrayList<>(15);
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
ps = con.prepareStatement("SELECT id, name FROM characters WHERE accountid = ? AND world = ?");
|
||||
ps.setInt(1, this.getAccID());
|
||||
ps.setInt(2, serverId);
|
||||
ps.setInt(2, worldId);
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
chars.add(new CharNameAndId(rs.getString("name"), rs.getInt("id")));
|
||||
@@ -882,7 +882,7 @@ public class MapleClient {
|
||||
}
|
||||
|
||||
public final void disconnect(final boolean shutdown, final boolean cashshop) {
|
||||
if (isDisconnecting()) {
|
||||
if (canDisconnect()) {
|
||||
ThreadManager.getInstance().newTask(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -893,12 +893,12 @@ public class MapleClient {
|
||||
}
|
||||
|
||||
public final void forceDisconnect() {
|
||||
if (isDisconnecting()) {
|
||||
if (canDisconnect()) {
|
||||
disconnectInternal(true, false);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized boolean isDisconnecting() {
|
||||
private synchronized boolean canDisconnect() {
|
||||
if (disconnecting) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public class RatesCommand extends Command {
|
||||
|
||||
// travel rates not applicable since it's intrinsically a server/environment rate rather than a character rate
|
||||
String showMsg_ = "#eCHARACTER RATES#n" + "\r\n\r\n";
|
||||
showMsg_ += "EXP Rate: #e#b" + player.getExpRate() + "x#k#n" + "\r\n";
|
||||
showMsg_ += "EXP Rate: #e#b" + player.getExpRate() + "x#k#n" + (player.hasNoviceExpRate() ? " - novice rate" : "") + "\r\n";
|
||||
showMsg_ += "MESO Rate: #e#b" + player.getMesoRate() + "x#k#n" + "\r\n";
|
||||
showMsg_ += "DROP Rate: #e#b" + player.getDropRate() + "x#k#n" + "\r\n";
|
||||
showMsg_ += "BOSS DROP Rate: #e#b" + player.getBossDropRate() + "x#k#n" + "\r\n";
|
||||
|
||||
@@ -40,7 +40,7 @@ public class ShowRatesCommand extends Command {
|
||||
showMsg += "World EXP Rate: #k" + c.getWorldServer().getExpRate() + "x#k" + "\r\n";
|
||||
showMsg += "Player EXP Rate: #k" + player.getRawExpRate() + "x#k" + "\r\n";
|
||||
if(player.getCouponExpRate() != 1) showMsg += "Coupon EXP Rate: #k" + player.getCouponExpRate() + "x#k" + "\r\n";
|
||||
showMsg += "EXP Rate: #e#b" + player.getExpRate() + "x#k#n" + "\r\n";
|
||||
showMsg += "EXP Rate: #e#b" + player.getExpRate() + "x#k#n" + (player.hasNoviceExpRate() ? " - novice rate" : "") + "\r\n";
|
||||
|
||||
showMsg += "\r\n" + "#eMESO RATE#n" + "\r\n";
|
||||
showMsg += "World MESO Rate: #k" + c.getWorldServer().getMesoRate() + "x#k" + "\r\n";
|
||||
@@ -66,7 +66,7 @@ public class ShowRatesCommand extends Command {
|
||||
}
|
||||
|
||||
showMsg += "\r\n";
|
||||
showMsg += "World TRAVEL Rate: #e#b" + c.getWorldServer().getTravelRate() + "x#k#n" + "\r\nServer\r\nPlayer";
|
||||
showMsg += "World TRAVEL Rate: #e#b" + c.getWorldServer().getTravelRate() + "x#k#n" + "\r\n";
|
||||
|
||||
player.showHint(showMsg, 300);
|
||||
}
|
||||
|
||||
@@ -41,8 +41,6 @@ public class GotoCommand extends Command {
|
||||
|
||||
@Override
|
||||
public void execute(MapleClient c, String[] params) {
|
||||
final HashMap<String, Integer> gotomaps = GameConstants.GOTO_MAPS;
|
||||
|
||||
MapleCharacter player = c.getPlayer();
|
||||
if (params.length < 1){
|
||||
player.yellowMessage("Syntax: @goto <map name>");
|
||||
@@ -54,6 +52,13 @@ public class GotoCommand extends Command {
|
||||
return;
|
||||
}
|
||||
|
||||
HashMap<String, Integer> gotomaps;
|
||||
if (player.isGM()) {
|
||||
gotomaps = new HashMap<>(GameConstants.GOTO_AREAS); // distinct map registry for GM/users suggested thanks to Vcoc
|
||||
} else {
|
||||
gotomaps = new HashMap<>(GameConstants.GOTO_TOWNS);
|
||||
}
|
||||
|
||||
if (gotomaps.containsKey(params[0])) {
|
||||
MapleMap target = c.getChannelServer().getMapFactory().getMap(gotomaps.get(params[0]));
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ package client.command.commands.gm2;
|
||||
import client.command.Command;
|
||||
import client.MapleClient;
|
||||
import client.MapleCharacter;
|
||||
import server.maps.MapleMap;
|
||||
import net.server.Server;
|
||||
import net.server.channel.Channel;
|
||||
|
||||
@@ -54,33 +55,26 @@ public class SummonCommand extends Command {
|
||||
}
|
||||
}
|
||||
if (victim != null) {
|
||||
boolean changingEvent = true;
|
||||
|
||||
if (victim.getEventInstance() != null) {
|
||||
if (player.getEventInstance() != null && victim.getEventInstance().getLeaderId() == player.getEventInstance().getLeaderId()) {
|
||||
changingEvent = false;
|
||||
} else {
|
||||
victim.getEventInstance().unregisterPlayer(victim);
|
||||
}
|
||||
if (!victim.isLoggedinWorld()) {
|
||||
player.dropMessage(6, "Player currently not logged in or unreachable.");
|
||||
return;
|
||||
}
|
||||
|
||||
//Attempt to join the warpers instance.
|
||||
if (player.getEventInstance() != null && changingEvent) {
|
||||
if (player.getClient().getChannel() == victim.getClient().getChannel()) {
|
||||
player.getEventInstance().registerPlayer(victim);
|
||||
victim.saveLocationOnWarp();
|
||||
victim.changeMap(player.getEventInstance().getMapInstance(player.getMapId()), player.getMap().findClosestPortal(player.getPosition()));
|
||||
} else {
|
||||
player.dropMessage("Target isn't on your channel, not able to warp into event instance.");
|
||||
}
|
||||
} else {//If victim isn't in an event instance or is in the same event instance as the one the caller is, just warp them.
|
||||
victim.saveLocationOnWarp();
|
||||
victim.changeMap(player.getMapId(), player.getMap().findClosestPortal(player.getPosition()));
|
||||
}
|
||||
if (player.getClient().getChannel() != victim.getClient().getChannel()) {//And then change channel if needed.
|
||||
victim.dropMessage("Changing channel, please wait a moment.");
|
||||
victim.getClient().changeChannel(player.getClient().getChannel());
|
||||
}
|
||||
|
||||
try {
|
||||
for (int i = 0; i < 7; i++) { // poll for a while until the player reconnects
|
||||
if (victim.isLoggedinWorld()) break;
|
||||
Thread.sleep(1777);
|
||||
}
|
||||
} catch (InterruptedException e) {}
|
||||
|
||||
MapleMap map = player.getMap();
|
||||
victim.saveLocationOnWarp();
|
||||
victim.forceChangeMap(map, map.findClosestPortal(player.getPosition()));
|
||||
} else {
|
||||
player.dropMessage(6, "Unknown player.");
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import client.command.Command;
|
||||
import server.maps.MapleMap;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
public class WarpAreaCommand extends Command {
|
||||
@@ -54,7 +53,7 @@ public class WarpAreaCommand extends Command {
|
||||
|
||||
Point pos = player.getPosition();
|
||||
|
||||
Collection<MapleCharacter> characters = new ArrayList<>(player.getMap().getCharacters());
|
||||
Collection<MapleCharacter> characters = player.getMap().getAllPlayers();
|
||||
|
||||
for (MapleCharacter victim : characters) {
|
||||
if (victim.getPosition().distanceSq(pos) <= 50000) {
|
||||
|
||||
@@ -28,7 +28,6 @@ import client.MapleClient;
|
||||
import client.command.Command;
|
||||
import server.maps.MapleMap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
public class WarpMapCommand extends Command {
|
||||
@@ -51,7 +50,7 @@ public class WarpMapCommand extends Command {
|
||||
return;
|
||||
}
|
||||
|
||||
Collection<MapleCharacter> characters = new ArrayList<>(player.getMap().getCharacters());
|
||||
Collection<MapleCharacter> characters = player.getMap().getAllPlayers();
|
||||
|
||||
for (MapleCharacter victim : characters) {
|
||||
victim.saveLocationOnWarp();
|
||||
|
||||
@@ -30,6 +30,8 @@ import net.server.Server;
|
||||
import net.server.channel.Channel;
|
||||
import server.expeditions.MapleExpedition;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class ExpedsCommand extends Command {
|
||||
{
|
||||
setDescription("");
|
||||
@@ -53,11 +55,11 @@ public class ExpedsCommand extends Command {
|
||||
player.yellowMessage(">> Size: " + exped.getMembers().size());
|
||||
player.yellowMessage(">> Leader: " + exped.getLeader().getName());
|
||||
int memId = 2;
|
||||
for (MapleCharacter member : exped.getMembers()) {
|
||||
if (exped.isLeader(member)) {
|
||||
for (Entry<Integer, String> e : exped.getMembers().entrySet()) {
|
||||
if (exped.isLeader(e.getKey())) {
|
||||
continue;
|
||||
}
|
||||
player.yellowMessage(">>> Member " + memId + ": " + member.getName());
|
||||
player.yellowMessage(">>> Member " + memId + ": " + e.getValue());
|
||||
memId++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ import client.MapleClient;
|
||||
import client.MapleCharacter;
|
||||
import server.maps.MapleMap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
public class ReloadMapCommand extends Command {
|
||||
@@ -42,7 +41,7 @@ public class ReloadMapCommand extends Command {
|
||||
MapleMap newMap = c.getChannelServer().getMapFactory().resetMap(player.getMapId());
|
||||
int callerid = c.getPlayer().getId();
|
||||
|
||||
Collection<MapleCharacter> characters = new ArrayList<>(player.getMap().getCharacters());
|
||||
Collection<MapleCharacter> characters = player.getMap().getAllPlayers();
|
||||
|
||||
for (MapleCharacter chr : characters) {
|
||||
chr.saveLocationOnWarp();
|
||||
|
||||
@@ -23,12 +23,11 @@
|
||||
*/
|
||||
package client.command.commands.gm3;
|
||||
|
||||
import client.command.Command;
|
||||
import client.MapleClient;
|
||||
import client.MapleCharacter;
|
||||
import client.command.Command;
|
||||
import client.MapleClient;
|
||||
import client.MapleCharacter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.List;
|
||||
|
||||
public class WarpSnowBallCommand extends Command {
|
||||
{
|
||||
@@ -38,7 +37,7 @@ public class WarpSnowBallCommand extends Command {
|
||||
@Override
|
||||
public void execute(MapleClient c, String[] params) {
|
||||
MapleCharacter player = c.getPlayer();
|
||||
List<MapleCharacter> chars = new ArrayList<>(player.getMap().getCharacters());
|
||||
List<MapleCharacter> chars = player.getMap().getAllPlayers();
|
||||
for (MapleCharacter chr : chars) {
|
||||
chr.saveLocationOnWarp();
|
||||
chr.changeMap(109060000, chr.getTeam());
|
||||
|
||||
@@ -72,7 +72,7 @@ public class DebugCommand extends Command {
|
||||
for (MapleMapObject monstermo : monsters) {
|
||||
MapleMonster monster = (MapleMonster) monstermo;
|
||||
MapleCharacter controller = monster.getController();
|
||||
player.message("Monster ID: " + monster.getId() + " Aggro target: " + ((controller != null) ? controller.getName() : "<none>"));
|
||||
player.message("Monster ID: " + monster.getId() + " Aggro target: " + ((controller != null) ? controller.getName() + " Has aggro: " + monster.isControllerHasAggro() + " Knowns aggro: " + monster.isControllerKnowsAboutAggro() : "<none>"));
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -56,8 +56,13 @@ public class Item implements Comparable<Item> {
|
||||
this.id = id;
|
||||
this.position = position;
|
||||
this.quantity = quantity;
|
||||
if (petid > -1) { // issue with null "pet" having petid > -1 found thanks to MedicOP
|
||||
this.pet = MaplePet.loadFromDb(id, position, petid);
|
||||
if (this.pet == null) {
|
||||
petid = -1;
|
||||
}
|
||||
}
|
||||
this.petid = petid;
|
||||
if (petid > -1) this.pet = MaplePet.loadFromDb(id, position, petid);
|
||||
this.flag = 0;
|
||||
this.log = new LinkedList<>();
|
||||
}
|
||||
@@ -121,10 +126,6 @@ public class Item implements Comparable<Item> {
|
||||
public int getPetId() {
|
||||
return petid;
|
||||
}
|
||||
|
||||
public void setPetId(int id) {
|
||||
this.petid = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Item other) {
|
||||
|
||||
@@ -134,10 +134,10 @@ public class AssignAPProcessor {
|
||||
luk = scStat;
|
||||
str = 0; dex = 0;
|
||||
|
||||
if(luk + chr.getLuk() > CAP) {
|
||||
if(ServerConstants.USE_AUTOASSIGN_SECONDARY_CAP && luk + chr.getLuk() > CAP) {
|
||||
temp = luk + chr.getLuk() - CAP;
|
||||
luk -= temp;
|
||||
int_ += temp;
|
||||
scStat -= temp;
|
||||
prStat += temp;
|
||||
}
|
||||
|
||||
primary = MapleStat.INT;
|
||||
@@ -160,10 +160,10 @@ public class AssignAPProcessor {
|
||||
str = scStat;
|
||||
int_ = 0; luk = 0;
|
||||
|
||||
if(str + chr.getStr() > CAP) {
|
||||
if(ServerConstants.USE_AUTOASSIGN_SECONDARY_CAP && str + chr.getStr() > CAP) {
|
||||
temp = str + chr.getStr() - CAP;
|
||||
str -= temp;
|
||||
dex += temp;
|
||||
scStat -= temp;
|
||||
prStat += temp;
|
||||
}
|
||||
|
||||
primary = MapleStat.DEX;
|
||||
@@ -186,10 +186,10 @@ public class AssignAPProcessor {
|
||||
str = scStat;
|
||||
int_ = 0; luk = 0;
|
||||
|
||||
if(str + chr.getStr() > CAP) {
|
||||
if(ServerConstants.USE_AUTOASSIGN_SECONDARY_CAP && str + chr.getStr() > CAP) {
|
||||
temp = str + chr.getStr() - CAP;
|
||||
str -= temp;
|
||||
dex += temp;
|
||||
scStat -= temp;
|
||||
prStat += temp;
|
||||
}
|
||||
|
||||
primary = MapleStat.DEX;
|
||||
@@ -240,15 +240,15 @@ public class AssignAPProcessor {
|
||||
str = trStat;
|
||||
int_ = 0;
|
||||
|
||||
if(dex + chr.getDex() > CAP) {
|
||||
if(ServerConstants.USE_AUTOASSIGN_SECONDARY_CAP && dex + chr.getDex() > CAP) {
|
||||
temp = dex + chr.getDex() - CAP;
|
||||
dex -= temp;
|
||||
luk += temp;
|
||||
scStat -= temp;
|
||||
prStat += temp;
|
||||
}
|
||||
if(str + chr.getStr() > CAP) {
|
||||
if(ServerConstants.USE_AUTOASSIGN_SECONDARY_CAP && str + chr.getStr() > CAP) {
|
||||
temp = str + chr.getStr() - CAP;
|
||||
str -= temp;
|
||||
luk += temp;
|
||||
trStat -= temp;
|
||||
prStat += temp;
|
||||
}
|
||||
|
||||
primary = MapleStat.LUK;
|
||||
@@ -258,50 +258,64 @@ public class AssignAPProcessor {
|
||||
break;
|
||||
|
||||
case BRAWLER:
|
||||
CAP = 120;
|
||||
|
||||
scStat = chr.getLevel() - (chr.getDex() + dex - eqpDex);
|
||||
if(scStat < 0) scStat = 0;
|
||||
scStat = Math.min(scStat, tempAp);
|
||||
|
||||
if(tempAp > scStat) tempAp -= scStat;
|
||||
else tempAp = 0;
|
||||
|
||||
prStat = tempAp;
|
||||
str = prStat;
|
||||
dex = scStat;
|
||||
int_ = 0; luk = 0;
|
||||
|
||||
if(dex + chr.getDex() > CAP) {
|
||||
temp = dex + chr.getDex() - CAP;
|
||||
dex -= temp;
|
||||
str += temp;
|
||||
}
|
||||
|
||||
primary = MapleStat.STR;
|
||||
secondary = MapleStat.DEX;
|
||||
|
||||
break;
|
||||
|
||||
default: //warrior, beginner, ...
|
||||
CAP = 80;
|
||||
CAP = 300;
|
||||
|
||||
boolean highDex = false; // thanks lucasziron & Vcoc for finding out DEX autoassigning poorly for STR-based characters
|
||||
if (chr.getLevel() < 40) {
|
||||
if (chr.getDex() >= (2 * chr.getLevel()) + 2) {
|
||||
highDex = true;
|
||||
}
|
||||
} else {
|
||||
if (chr.getDex() >= chr.getLevel() + 42) {
|
||||
highDex = true;
|
||||
}
|
||||
}
|
||||
|
||||
// other classes will start favoring more DEX only if a level-based threshold is reached.
|
||||
if(!highDex) {
|
||||
scStat = 0;
|
||||
if(chr.getDex() < 80) {
|
||||
scStat = (2 * chr.getLevel()) - (chr.getDex() + dex - eqpDex);
|
||||
if(scStat < 0) scStat = 0;
|
||||
|
||||
scStat = ((2 * chr.getLevel()) / 3) - (chr.getDex() + dex - eqpDex);
|
||||
if(scStat < 0) scStat = 0;
|
||||
scStat = Math.min(scStat, tempAp);
|
||||
scStat = Math.min(80 - chr.getDex(), scStat);
|
||||
scStat = Math.min(tempAp, scStat);
|
||||
tempAp -= scStat;
|
||||
}
|
||||
|
||||
if(tempAp > scStat) tempAp -= scStat;
|
||||
else tempAp = 0;
|
||||
temp = (chr.getLevel() + 40) - Math.max(80, scStat + chr.getDex() + dex - eqpDex);
|
||||
if(temp < 0) temp = 0;
|
||||
temp = Math.min(tempAp, temp);
|
||||
scStat += temp;
|
||||
tempAp -= temp;
|
||||
} else {
|
||||
scStat = 0;
|
||||
if(chr.getDex() < 96) {
|
||||
scStat = (int)(2.4 * chr.getLevel()) - (chr.getDex() + dex - eqpDex);
|
||||
if(scStat < 0) scStat = 0;
|
||||
|
||||
scStat = Math.min(96 - chr.getDex(), scStat);
|
||||
scStat = Math.min(tempAp, scStat);
|
||||
tempAp -= scStat;
|
||||
}
|
||||
|
||||
temp = 96 + (int)(1.2 * (chr.getLevel() - 40)) - Math.max(96, scStat + chr.getDex() + dex - eqpDex);
|
||||
if(temp < 0) temp = 0;
|
||||
temp = Math.min(tempAp, temp);
|
||||
scStat += temp;
|
||||
tempAp -= temp;
|
||||
}
|
||||
|
||||
prStat = tempAp;
|
||||
str = prStat;
|
||||
dex = scStat;
|
||||
int_ = 0; luk = 0;
|
||||
|
||||
if(dex + chr.getDex() > CAP) {
|
||||
if(ServerConstants.USE_AUTOASSIGN_SECONDARY_CAP && dex + chr.getDex() > CAP) {
|
||||
temp = dex + chr.getDex() - CAP;
|
||||
dex -= temp;
|
||||
str += temp;
|
||||
scStat -= temp;
|
||||
prStat += temp;
|
||||
}
|
||||
|
||||
primary = MapleStat.STR;
|
||||
|
||||
@@ -469,6 +469,11 @@ public class DueyProcessor {
|
||||
}
|
||||
|
||||
if (dp.getItem() != null) {
|
||||
if (!c.getPlayer().canHoldMeso(dp.getMesos())) {
|
||||
c.announce(MaplePacketCreator.sendDueyMSG(Actions.TOCLIENT_RECV_UNKNOWN_ERROR.getCode()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!MapleInventoryManipulator.checkSpace(c, dp.getItem().getItemId(), dp.getItem().getQuantity(), dp.getItem().getOwner())) {
|
||||
int itemid = dp.getItem().getItemId();
|
||||
if(MapleItemInformationProvider.getInstance().isPickupRestricted(itemid) && c.getPlayer().getInventory(ItemConstants.getInventoryType(itemid)).findById(itemid) != null) {
|
||||
@@ -483,17 +488,7 @@ public class DueyProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
long gainmesos;
|
||||
long totalmesos = (long) dp.getMesos() + c.getPlayer().getMeso();
|
||||
|
||||
if (totalmesos < 0 || dp.getMesos() < 0) {
|
||||
gainmesos = 0;
|
||||
} else {
|
||||
totalmesos = Math.min(totalmesos, Integer.MAX_VALUE);
|
||||
gainmesos = totalmesos - c.getPlayer().getMeso();
|
||||
}
|
||||
|
||||
c.getPlayer().gainMeso((int)gainmesos, false);
|
||||
c.getPlayer().gainMeso(dp.getMesos(), false);
|
||||
|
||||
removeItemFromDB(packageid);
|
||||
c.announce(MaplePacketCreator.removeItemFromDuey(false, packageid));
|
||||
|
||||
@@ -47,7 +47,7 @@ import tools.Pair;
|
||||
*/
|
||||
public class FredrickProcessor {
|
||||
private static boolean canRetrieveFromFredrick(MapleCharacter chr, List<Pair<Item, MapleInventoryType>> items) {
|
||||
if (chr.getMeso() + chr.getMerchantMeso() < 0) {
|
||||
if (!chr.canHoldMeso(chr.getMerchantMeso())) {
|
||||
return false;
|
||||
}
|
||||
return MapleInventory.checkSpotsAndOwnership(chr, items);
|
||||
|
||||
@@ -45,9 +45,8 @@ public class GameConstants {
|
||||
return(EXP_RATE_GAIN[slot]);
|
||||
}
|
||||
|
||||
// used by the "goto" command
|
||||
public static final HashMap<String, Integer> GOTO_MAPS = new HashMap<String, Integer>() {{
|
||||
put("gmmap", 180000000);
|
||||
// used by the "goto" command for players
|
||||
public static final HashMap<String, Integer> GOTO_TOWNS = new HashMap<String, Integer>() {{
|
||||
put("southperry", 60000);
|
||||
put("amherst", 1000000);
|
||||
put("henesys", 100000000);
|
||||
@@ -72,6 +71,25 @@ public class GameConstants {
|
||||
put("korean", 222000000);
|
||||
put("ellin", 300000000);
|
||||
put("nlc", 600000000);
|
||||
put("showa", 801000000);
|
||||
put("shrine", 800000000);
|
||||
put("ariant", 260000000);
|
||||
put("magatia", 261000000);
|
||||
put("singapore", 540000000);
|
||||
put("quay", 541000000);
|
||||
put("kampung", 551000000);
|
||||
put("amoria", 680000000);
|
||||
put("temple", 270000100);
|
||||
put("square", 103040000);
|
||||
put("neo", 240070000);
|
||||
put("mushking", 106020000);
|
||||
}};
|
||||
|
||||
// used by the "goto" command for only-GMs
|
||||
public static final HashMap<String, Integer> GOTO_AREAS = new HashMap<String, Integer>() {{
|
||||
putAll(GOTO_TOWNS);
|
||||
|
||||
put("gmmap", 180000000);
|
||||
put("excavation", 990000000);
|
||||
put("mushmom", 100000005);
|
||||
put("griffey", 240020101);
|
||||
@@ -80,25 +98,13 @@ public class GameConstants {
|
||||
put("balrog", 105090900);
|
||||
put("zakum", 211042300);
|
||||
put("papu", 220080001);
|
||||
put("showa", 801000000);
|
||||
put("guild", 200000301);
|
||||
put("shrine", 800000000);
|
||||
put("skelegon", 240040511);
|
||||
put("hpq", 100000200);
|
||||
put("pianus", 230040420);
|
||||
put("horntail", 240050400);
|
||||
put("pinkbean", 270050000);
|
||||
put("ariant", 260000000);
|
||||
put("magatia", 261000000);
|
||||
put("singapore", 540000000);
|
||||
put("quay", 541000000);
|
||||
put("kampung", 551000000);
|
||||
put("keep", 610020006);
|
||||
put("amoria", 680000000);
|
||||
put("temple", 270000100);
|
||||
put("square", 103040000);
|
||||
put("neo", 240070000);
|
||||
put("mushking", 106020000);
|
||||
put("dojo", 925020001);
|
||||
put("bosspq", 970030000);
|
||||
put("fm", 910000000);
|
||||
|
||||
@@ -76,13 +76,15 @@ public class ServerConstants {
|
||||
public static final boolean USE_ITEM_SORT_BY_NAME = false; //Item sorting based on name rather than id.
|
||||
public static final boolean USE_PARTY_SEARCH = false;
|
||||
public static final boolean USE_PARTY_FOR_STARTERS = true; //Players level 10 or below can create/invite other players on the given level range.
|
||||
public static final boolean USE_AUTOASSIGN_STARTERS_AP = false; //Beginners level 10 or below have their AP autoassigned (they can't choose to levelup a stat). Set true if the localhost doesn't support AP assigning for beginners level 10 or below.
|
||||
public static final boolean USE_AUTOASSIGN_STARTERS_AP = false; //Beginners level 10 or below have their AP autoassigned (they can't choose to levelup a stat). Set true ONLY if the localhost doesn't support AP assigning for beginners level 10 or below.
|
||||
public static final boolean USE_AUTOASSIGN_SECONDARY_CAP = true;//Prevents AP autoassign from spending on secondary stats after the player class' cap (defined on the autoassign handler) has been reached.
|
||||
public static final boolean USE_AUTOBAN = false; //Commands the server to detect infractors automatically.
|
||||
public static final boolean USE_AUTOBAN_LOG = true; //Log autoban related messages. Still logs even with USE_AUTOBAN disabled.
|
||||
public static final boolean USE_AUTOSAVE = true; //Enables server autosaving feature (saves characters to DB each 1 hour).
|
||||
public static final boolean USE_SERVER_AUTOASSIGNER = true; //HeavenMS-builtin autoassigner, uses algorithm based on distributing AP accordingly with required secondary stat on equipments.
|
||||
public static final boolean USE_REFRESH_RANK_MOVE = true;
|
||||
public static final boolean USE_ENFORCE_ADMIN_ACCOUNT = false; //Forces accounts having GM characters to be treated as a "GM account" by the client (localhost). Some of the GM account perks is the ability to FLY, but unable to TRADE.
|
||||
public static final boolean USE_ENFORCE_NOVICE_EXPRATE = false; //Hardsets experience rate 1x for beginners level 10 or under. Ideal for roaming on novice areas without caring too much about losing some stats.
|
||||
public static final boolean USE_ENFORCE_HPMP_SWAP = false; //Forces players to reuse stats (via AP Resetting) located on HP/MP pool only inside the HP/MP stats.
|
||||
public static final boolean USE_ENFORCE_MOB_LEVEL_RANGE = true; //Players N levels below the killed mob will gain no experience from defeating it.
|
||||
public static final boolean USE_ENFORCE_JOB_LEVEL_RANGE = false;//Caps the player level on the minimum required to advance their current jobs.
|
||||
@@ -166,11 +168,13 @@ public class ServerConstants {
|
||||
public static final int ITEM_EXPIRE_CHECK = 10 * 1000; //Interval between item expiring tasks on maps, which checks and makes disappear expired items.
|
||||
public static final int ITEM_LIMIT_ON_MAP = 200; //Max number of items allowed on a map.
|
||||
public static final int MAP_VISITED_SIZE = 5; //Max length for last mapids visited by a player. This is used to recover and update drops on these maps accordingly with player actions.
|
||||
public static final int MAP_DAMAGE_OVERTIME_INTERVAL = 5000;//Interval in seconds between map environment damage (e.g. El Nath and Aqua Road surrondings).
|
||||
public static final int MAP_DAMAGE_OVERTIME_INTERVAL = 5000;//Interval in milliseconds between map environment damage (e.g. El Nath and Aqua Road surrondings).
|
||||
|
||||
//Channel Mob Disease Monitor Configuration
|
||||
public static final int MOB_STATUS_MONITOR_PROC = 200; //Frequency in milliseconds between each proc on the mob disease monitor schedule.
|
||||
public static final int MOB_STATUS_MONITOR_LIFE = 84; //Idle proc count the mob disease monitor is allowed to be there before closing it due to inactivity.
|
||||
public static final int MOB_STATUS_MONITOR_PROC = 200; //Frequency in milliseconds between each proc on the mob disease monitor schedule.
|
||||
public static final int MOB_STATUS_MONITOR_LIFE = 84; //Idle proc count the mob disease monitor is allowed to be there before closing it due to inactivity.
|
||||
public static final int MOB_STATUS_AGGRO_PERSISTENCE = 2; //Idle proc count on aggro update for a mob to keep following the current controller, given him/her is the leading damage dealer.
|
||||
public static final int MOB_STATUS_AGGRO_INTERVAL = 5000; //Interval in milliseconds between aggro logistics update.
|
||||
|
||||
//Some Gameplay Enhancing Configurations
|
||||
//Scroll Configuration
|
||||
|
||||
@@ -228,6 +228,8 @@ public enum SendOpcode {
|
||||
GIVE_FOREIGN_BUFF(0xC7),
|
||||
CANCEL_FOREIGN_BUFF(0xC8),
|
||||
UPDATE_PARTYMEMBER_HP(0xC9),
|
||||
GUILD_NAME_CHANGED(0xCA),
|
||||
GUILD_MARK_CHANGED(0xCB),
|
||||
THROW_GRENADE(0xCC),
|
||||
CANCEL_CHAIR(0xCD),
|
||||
SHOW_ITEM_GAIN_INCHAT(0xCE),
|
||||
@@ -257,8 +259,8 @@ public enum SendOpcode {
|
||||
DAMAGE_MONSTER(0xF6),
|
||||
ARIANT_THING(0xF9),
|
||||
SHOW_MONSTER_HP(0xFA),
|
||||
SHOW_DRAGGED(0xFB),//CATCH
|
||||
CATCH_MONSTER(0xFC),
|
||||
CATCH_MONSTER(0xFB),
|
||||
CATCH_MONSTER_WITH_ITEM(0xFC),
|
||||
SHOW_MAGNET(0xFD),
|
||||
SPAWN_NPC(0x101),
|
||||
REMOVE_NPC(0x102),
|
||||
|
||||
@@ -35,7 +35,6 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import constants.ServerConstants;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import server.TimerManager;
|
||||
import tools.FilePrinter;
|
||||
|
||||
@@ -84,6 +84,7 @@ public enum MonitoredLockType {
|
||||
VISITOR_PSHOP,
|
||||
STORAGE,
|
||||
MOB,
|
||||
MOB_AGGRO,
|
||||
MOB_ANI,
|
||||
MOB_EXT,
|
||||
MOB_STATI,
|
||||
@@ -96,6 +97,8 @@ public enum MonitoredLockType {
|
||||
MAP_ITEM,
|
||||
MAP_LOOT,
|
||||
MAP_BOUNDS,
|
||||
MAP_AGGRO,
|
||||
MAP_AGGRO_IDLE,
|
||||
MINIDUNGEON,
|
||||
REACTOR,
|
||||
REACTOR_HIT;
|
||||
|
||||
@@ -274,7 +274,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
|
||||
totDamageToOneMonster += eachd;
|
||||
}
|
||||
totDamage += totDamageToOneMonster;
|
||||
player.checkMonsterAggro(monster);
|
||||
monster.aggroMonsterDamage(player, totDamageToOneMonster);
|
||||
if (player.getBuffedValue(MapleBuffStat.PICKPOCKET) != null && (attack.skill == 0 || attack.skill == Rogue.DOUBLE_STAB || attack.skill == Bandit.SAVAGE_BLOW || attack.skill == ChiefBandit.ASSAULTER || attack.skill == ChiefBandit.BAND_OF_THIEVES || attack.skill == Shadower.ASSASSINATE || attack.skill == Shadower.TAUNT || attack.skill == Shadower.BOOMERANG_STEP)) {
|
||||
Skill pickpocket = SkillFactory.getSkill(ChiefBandit.PICKPOCKET);
|
||||
int picklv = (player.isGM()) ? pickpocket.getMaxLevel() : player.getSkillLevel(pickpocket);
|
||||
|
||||
@@ -51,11 +51,11 @@ public abstract class AbstractMovementPacketHandler extends AbstractMaplePacketH
|
||||
short ypos = lea.readShort();
|
||||
short xwobble = lea.readShort();
|
||||
short ywobble = lea.readShort();
|
||||
short unk = lea.readShort();
|
||||
short fh = lea.readShort();
|
||||
byte newstate = lea.readByte();
|
||||
short duration = lea.readShort();
|
||||
AbsoluteLifeMovement alm = new AbsoluteLifeMovement(command, new Point(xpos, ypos), duration, newstate);
|
||||
alm.setUnk(unk);
|
||||
alm.setFh(fh);
|
||||
alm.setPixelsPerSecond(new Point(xwobble, ywobble));
|
||||
res.add(alm);
|
||||
break;
|
||||
@@ -105,11 +105,11 @@ public abstract class AbstractMovementPacketHandler extends AbstractMaplePacketH
|
||||
/*case 11: { // Chair
|
||||
short xpos = lea.readShort();
|
||||
short ypos = lea.readShort();
|
||||
short unk = lea.readShort();
|
||||
short fh = lea.readShort();
|
||||
byte newstate = lea.readByte();
|
||||
short duration = lea.readShort();
|
||||
ChairMovement cm = new ChairMovement(command, new Point(xpos, ypos), duration, newstate);
|
||||
cm.setUnk(unk);
|
||||
cm.setFh(fh);
|
||||
res.add(cm);
|
||||
break;
|
||||
}*/
|
||||
@@ -118,14 +118,14 @@ public abstract class AbstractMovementPacketHandler extends AbstractMaplePacketH
|
||||
short ypos = lea.readShort();
|
||||
short xwobble = lea.readShort();
|
||||
short ywobble = lea.readShort();
|
||||
short unk = lea.readShort();
|
||||
short fh = lea.readShort();
|
||||
short ofh = lea.readShort();
|
||||
byte newstate = lea.readByte();
|
||||
short duration = lea.readShort();
|
||||
JumpDownMovement jdm = new JumpDownMovement(command, new Point(xpos, ypos), duration, newstate);
|
||||
jdm.setUnk(unk);
|
||||
jdm.setFh(fh);
|
||||
jdm.setPixelsPerSecond(new Point(xwobble, ywobble));
|
||||
jdm.setFH(fh);
|
||||
jdm.setOriginFh(ofh);
|
||||
res.add(jdm);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -32,32 +32,15 @@ public final class AutoAggroHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
if (c.getPlayer().isHidden()) return; // Don't auto aggro GM's in hide...
|
||||
MapleCharacter player = c.getPlayer();
|
||||
if (player.isHidden()) return; // Don't auto aggro GM's in hide...
|
||||
|
||||
MapleMap map = c.getPlayer().getMap();
|
||||
MapleMap map = player.getMap();
|
||||
int oid = slea.readInt();
|
||||
|
||||
MapleMonster monster = map.getMonsterByOid(oid);
|
||||
if (monster != null) {
|
||||
MapleCharacter currentController = monster.getController();
|
||||
monster.lockMonster();
|
||||
try {
|
||||
if (currentController != null) {
|
||||
if (!monster.isControllerHasAggro()) {
|
||||
if (map.getCharacterById(currentController.getId()) == null) {
|
||||
monster.switchController(c.getPlayer(), true);
|
||||
} else {
|
||||
monster.switchController(currentController, true);
|
||||
}
|
||||
} else if (map.getCharacterById(currentController.getId()) == null) {
|
||||
monster.switchController(c.getPlayer(), true);
|
||||
}
|
||||
} else {
|
||||
monster.switchController(c.getPlayer(), true);
|
||||
}
|
||||
} finally {
|
||||
monster.unlockMonster();
|
||||
}
|
||||
monster.aggroAutoAggroUpdate(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,10 +47,6 @@ public final class GuildOperationHandler extends AbstractMaplePacketHandler {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void restancePlayer(MapleCharacter mc) {
|
||||
mc.broadcastStance();
|
||||
}
|
||||
|
||||
private class Invited {
|
||||
public String name;
|
||||
public int gid;
|
||||
@@ -131,7 +127,8 @@ public final class GuildOperationHandler extends AbstractMaplePacketHandler {
|
||||
c.announce(MaplePacketCreator.showGuildInfo(mc));
|
||||
|
||||
c.getPlayer().dropMessage(1, "You have successfully created a Guild.");
|
||||
restancePlayer(mc);
|
||||
mc.getGuild().broadcastNameChanged();
|
||||
mc.getGuild().broadcastEmblemChanged();
|
||||
break;
|
||||
case 0x05:
|
||||
if (mc.getGuildId() <= 0 || mc.getGuildRank() > 2) {
|
||||
@@ -191,7 +188,8 @@ public final class GuildOperationHandler extends AbstractMaplePacketHandler {
|
||||
if(allianceId > 0) Server.getInstance().getAlliance(allianceId).updateAlliancePackets(mc);
|
||||
|
||||
mc.saveGuildStatus(); // update database
|
||||
restancePlayer(mc);
|
||||
mc.getMap().broadcastMessage(mc, MaplePacketCreator.guildNameChanged(mc.getId(), mc.getGuild().getName())); // thanks Vcoc for pointing out an issue with updating guild tooltip to players in the map
|
||||
mc.getMap().broadcastMessage(mc, MaplePacketCreator.guildMarkChanged(mc.getId(), mc.getGuild()));
|
||||
break;
|
||||
case 0x07:
|
||||
cid = slea.readInt();
|
||||
@@ -212,7 +210,7 @@ public final class GuildOperationHandler extends AbstractMaplePacketHandler {
|
||||
mc.getMGC().setGuildId(0);
|
||||
mc.getMGC().setGuildRank(5);
|
||||
mc.saveGuildStatus();
|
||||
restancePlayer(mc);
|
||||
mc.getMap().broadcastMessage(mc, MaplePacketCreator.guildNameChanged(mc.getId(), ""));
|
||||
break;
|
||||
case 0x08:
|
||||
allianceId = mc.getGuild().getAllianceId();
|
||||
@@ -272,7 +270,8 @@ public final class GuildOperationHandler extends AbstractMaplePacketHandler {
|
||||
}
|
||||
|
||||
mc.gainMeso(-ServerConstants.CHANGE_EMBLEM_COST, true, false, true);
|
||||
restancePlayer(mc);
|
||||
mc.getGuild().broadcastNameChanged();
|
||||
mc.getGuild().broadcastEmblemChanged();
|
||||
break;
|
||||
case 0x10:
|
||||
if (mc.getGuildId() <= 0 || mc.getGuildRank() > 2) {
|
||||
|
||||
@@ -77,8 +77,6 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
|
||||
|
||||
boolean isAttack = inRangeInclusive(rawActivity, 24, 41);
|
||||
boolean isSkill = inRangeInclusive(rawActivity, 42, 59);
|
||||
|
||||
boolean currentController = (monster.getController() == player);
|
||||
|
||||
MobSkill toUse = null;
|
||||
int useSkillId = 0, useSkillLevel = 0;
|
||||
@@ -112,10 +110,6 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
|
||||
|
||||
int atkStatus = monster.canUseAttack(castPos, isSkill);
|
||||
if (atkStatus < 1) {
|
||||
if (!currentController) {
|
||||
return;
|
||||
}
|
||||
|
||||
rawActivity = -1;
|
||||
pOption = 0;
|
||||
}
|
||||
@@ -147,24 +141,8 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
|
||||
Point startPos = new Point(start_x, start_y - 2);
|
||||
List<LifeMovementFragment> res = parseMovement(slea);
|
||||
|
||||
boolean aggro;
|
||||
monster.lockMonster();
|
||||
try {
|
||||
if (!currentController) {
|
||||
if (monster.isAttackedBy(player)) {
|
||||
monster.switchController(player, true);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
aggro = monster.isControllerHasAggro();
|
||||
if (aggro) {
|
||||
monster.setControllerKnowsAboutAggro(true);
|
||||
}
|
||||
} finally {
|
||||
monster.unlockMonster();
|
||||
}
|
||||
Boolean aggro = monster.aggroMoveLifeUpdate(player);
|
||||
if (aggro == null) return;
|
||||
|
||||
if (nextUse != null) {
|
||||
c.announce(MaplePacketCreator.moveMonsterResponse(objectid, moveid, mobMp, aggro, nextSkillId, nextSkillLevel));
|
||||
@@ -174,7 +152,7 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
|
||||
|
||||
if (res != null) {
|
||||
if (ServerConstants.USE_DEBUG_SHOW_RCVD_MVLIFE) {
|
||||
System.out.println((isSkill ? "SKILL " : (isAttack ? "ATTCK " : " ")) + "castPos: " + castPos + " rawAct: " + rawActivity + " opt: " + pOption + " skillID: " + useSkillId + " skillLV: " + useSkillLevel + " " + "allowSkill: " + nextMovementCouldBeSkill);
|
||||
System.out.println((isSkill ? "SKILL " : (isAttack ? "ATTCK " : " ")) + "castPos: " + castPos + " rawAct: " + rawActivity + " opt: " + pOption + " skillID: " + useSkillId + " skillLV: " + useSkillLevel + " " + "allowSkill: " + nextMovementCouldBeSkill + " mobMp: " + mobMp);
|
||||
}
|
||||
|
||||
map.broadcastMessage(player, MaplePacketCreator.moveMonster(objectid, nextMovementCouldBeSkill, rawActivity, useSkillId, useSkillLevel, pOption, startPos, res), monster.getPosition());
|
||||
|
||||
@@ -35,6 +35,7 @@ import net.AbstractMaplePacketHandler;
|
||||
import server.MapleItemInformationProvider;
|
||||
import server.MapleTrade;
|
||||
import constants.GameConstants;
|
||||
import java.sql.SQLException;
|
||||
import server.maps.FieldLimit;
|
||||
import server.maps.MapleHiredMerchant;
|
||||
import server.maps.MapleMapObject;
|
||||
@@ -61,8 +62,8 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
CHAT(6),
|
||||
CHAT_THING(8),
|
||||
EXIT(0xA),
|
||||
OPEN(0xB),
|
||||
TRADE_BIRTHDAY(0x0E),
|
||||
OPEN_STORE(0xB),
|
||||
OPEN_CASH(0xE),
|
||||
SET_ITEMS(0xF),
|
||||
SET_MESO(0x10),
|
||||
CONFIRM(0x11),
|
||||
@@ -74,7 +75,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
REMOVE_ITEM(0x1B),
|
||||
BAN_PLAYER(0x1C),
|
||||
MERCHANT_THING(0x1D),
|
||||
OPEN_STORE(0x1E),
|
||||
OPEN_THING(0x1E),
|
||||
PUT_ITEM(0x21),
|
||||
MERCHANT_BUY(0x22),
|
||||
TAKE_ITEM_BACK(0x26),
|
||||
@@ -136,7 +137,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
try {
|
||||
byte mode = slea.readByte();
|
||||
final MapleCharacter chr = c.getPlayer();
|
||||
|
||||
|
||||
if (mode == Action.CREATE.getCode()) {
|
||||
if(!chr.isAlive()) { // thanks GabrielSin for pointing this
|
||||
chr.getClient().announce(MaplePacketCreator.getMiniRoomError(4));
|
||||
@@ -333,14 +334,25 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
chr.closeMiniGame();
|
||||
chr.closeHiredMerchant(true);
|
||||
}
|
||||
} else if (mode == Action.OPEN.getCode()) {
|
||||
} else if (mode == Action.OPEN_STORE.getCode() || mode == Action.OPEN_CASH.getCode()) {
|
||||
if (isTradeOpen(chr)) return;
|
||||
|
||||
if (mode == Action.OPEN_STORE.getCode()) {
|
||||
slea.readByte(); //01
|
||||
} else {
|
||||
slea.readShort();
|
||||
int birthday = slea.readInt();
|
||||
if (!CashOperationHandler.checkBirthday(c, birthday)) { // birthday check here found thanks to lucasziron
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "Please check again the birthday date."));
|
||||
return;
|
||||
}
|
||||
|
||||
c.announce(MaplePacketCreator.hiredMerchantOwnerMaintenanceLeave());
|
||||
}
|
||||
|
||||
MaplePlayerShop shop = chr.getPlayerShop();
|
||||
MapleHiredMerchant merchant = chr.getHiredMerchant();
|
||||
if (shop != null && shop.isOwner(chr)) {
|
||||
slea.readByte();//01
|
||||
|
||||
if(ServerConstants.USE_ERASE_PERMIT_ON_OPENSHOP) {
|
||||
try {
|
||||
MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, shop.getItemId(), 1, true, false);
|
||||
@@ -355,7 +367,6 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
chr.getMap().addMapObject(merchant);
|
||||
chr.setHiredMerchant(null);
|
||||
chr.getMap().broadcastMessage(MaplePacketCreator.spawnHiredMerchantBox(merchant));
|
||||
slea.readByte();
|
||||
}
|
||||
} else if (mode == Action.READY.getCode()) {
|
||||
MapleMiniGame game = chr.getMiniGame();
|
||||
@@ -528,7 +539,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "Cash items are not allowed to be sold on the Player Store."));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (ServerConstants.USE_ENFORCE_UNMERCHABLE_PET && ItemConstants.isPet(ivItem.getItemId())) {
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "Pets are not allowed to be sold on the Player Store."));
|
||||
return;
|
||||
@@ -543,26 +554,44 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
MaplePlayerShop shop = chr.getPlayerShop();
|
||||
MapleHiredMerchant merchant = chr.getHiredMerchant();
|
||||
if (shop != null && shop.isOwner(chr)) {
|
||||
if (shop.isOpen()) {
|
||||
if (shop.isOpen() || !shop.addItem(shopItem)) { // thanks Vcoc for pointing an exploit with unlimited shop slots
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "You can't sell it anymore."));
|
||||
return;
|
||||
}
|
||||
|
||||
shop.addItem(shopItem);
|
||||
|
||||
if (ItemConstants.isRechargeable(ivItem.getItemId())) {
|
||||
MapleInventoryManipulator.removeFromSlot(c, ivType, slot, ivItem.getQuantity(), true);
|
||||
} else {
|
||||
MapleInventoryManipulator.removeFromSlot(c, ivType, slot, (short) (bundles * perBundle), true);
|
||||
}
|
||||
|
||||
c.announce(MaplePacketCreator.getPlayerShopItemUpdate(shop));
|
||||
} else if (merchant != null && merchant.isOwner(chr)) {
|
||||
if (merchant.isOpen()) {
|
||||
if (ivType.equals(MapleInventoryType.CASH) && merchant.isPublished()) {
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "Cash items are only allowed to be sold when first opening the store."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (merchant.isOpen() || !merchant.addItem(shopItem)) { // thanks Vcoc for pointing an exploit with unlimited shop slots
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "You can't sell it anymore."));
|
||||
return;
|
||||
}
|
||||
|
||||
merchant.addItem(shopItem);
|
||||
|
||||
if (ItemConstants.isRechargeable(ivItem.getItemId())) {
|
||||
MapleInventoryManipulator.removeFromSlot(c, ivType, slot, ivItem.getQuantity(), true);
|
||||
} else {
|
||||
MapleInventoryManipulator.removeFromSlot(c, ivType, slot, (short) (bundles * perBundle), true);
|
||||
}
|
||||
|
||||
c.announce(MaplePacketCreator.updateHiredMerchant(merchant, chr));
|
||||
}
|
||||
if (ItemConstants.isRechargeable(ivItem.getItemId())) {
|
||||
MapleInventoryManipulator.removeFromSlot(c, ivType, slot, ivItem.getQuantity(), true);
|
||||
|
||||
try {
|
||||
merchant.saveItems(false); // thanks Masterrulax for realizing yet another dupe with merchants/Fredrick
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
MapleInventoryManipulator.removeFromSlot(c, ivType, slot, (short) (bundles * perBundle), true);
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "You can't sell without owning a shop."));
|
||||
}
|
||||
} else if (mode == Action.REMOVE_ITEM.getCode()) {
|
||||
if (isTradeOpen(chr)) return;
|
||||
@@ -616,8 +645,9 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
MaplePlayerShop shop = chr.getPlayerShop();
|
||||
MapleHiredMerchant merchant = chr.getHiredMerchant();
|
||||
if (shop != null && shop.isVisitor(chr)) {
|
||||
shop.buy(c, itemid, quantity);
|
||||
shop.broadcast(MaplePacketCreator.getPlayerShopItemUpdate(shop));
|
||||
if (shop.buy(c, itemid, quantity)) {
|
||||
shop.broadcast(MaplePacketCreator.getPlayerShopItemUpdate(shop));
|
||||
}
|
||||
} else if (merchant != null && !merchant.isOwner(chr)) {
|
||||
merchant.buy(c, itemid, quantity);
|
||||
merchant.broadcastToVisitorsThreadsafe(MaplePacketCreator.updateHiredMerchant(merchant, chr));
|
||||
@@ -661,6 +691,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
if (merchant.isOwner(chr)) {
|
||||
merchant.clearMessages();
|
||||
merchant.setOpen(true);
|
||||
merchant.getMap().broadcastMessage(MaplePacketCreator.updateHiredMerchantBox(merchant));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,9 @@ import constants.ServerConstants;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import net.server.coordinator.MapleEventRecallCoordinator;
|
||||
import net.server.coordinator.MapleSessionCoordinator;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
@@ -72,6 +74,25 @@ import tools.packets.Wedding;
|
||||
|
||||
public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
private static Set<Integer> attemptingLoginAccounts = new HashSet<>();
|
||||
|
||||
private boolean tryAcquireAccount(int accId) {
|
||||
synchronized (attemptingLoginAccounts) {
|
||||
if (attemptingLoginAccounts.contains(accId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
attemptingLoginAccounts.add(accId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void releaseAccount(int accId) {
|
||||
synchronized (attemptingLoginAccounts) {
|
||||
attemptingLoginAccounts.remove(accId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean validateState(MapleClient c) {
|
||||
return !c.isLoggedIn();
|
||||
@@ -82,298 +103,313 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
|
||||
final int cid = slea.readInt();
|
||||
final Server server = Server.getInstance();
|
||||
|
||||
World wserv = server.getWorld(c.getWorld());
|
||||
if(wserv == null) {
|
||||
c.disconnect(true, false);
|
||||
return;
|
||||
}
|
||||
|
||||
Channel cserv = wserv.getChannel(c.getChannel());
|
||||
if(cserv == null) {
|
||||
c.setChannel(1);
|
||||
cserv = wserv.getChannel(c.getChannel());
|
||||
|
||||
if(cserv == null) {
|
||||
c.disconnect(true, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
MapleCharacter player = wserv.getPlayerStorage().getCharacterById(cid);
|
||||
boolean newcomer = false;
|
||||
|
||||
IoSession session = c.getSession();
|
||||
String remoteHwid;
|
||||
if (player == null) {
|
||||
if (!server.validateCharacteridInTransition((InetSocketAddress) session.getRemoteAddress(), cid)) {
|
||||
c.disconnect(true, false);
|
||||
return;
|
||||
}
|
||||
|
||||
remoteHwid = MapleSessionCoordinator.getInstance().getGameSessionHwid(session);
|
||||
if (remoteHwid == null) {
|
||||
c.disconnect(true, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (c.tryacquireClient()) { // thanks MedicOP for assisting on concurrency protection here
|
||||
try {
|
||||
player = MapleCharacter.loadCharFromDB(cid, c, true);
|
||||
newcomer = true;
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
remoteHwid = player.getClient().getHWID();
|
||||
}
|
||||
|
||||
if (player == null) { //If you are still getting null here then please just uninstall the game >.>, we dont need you fucking with the logs
|
||||
c.disconnect(true, false);
|
||||
return;
|
||||
}
|
||||
|
||||
c.setPlayer(player);
|
||||
c.setAccID(player.getAccountID());
|
||||
|
||||
boolean allowLogin = true;
|
||||
|
||||
/* is this check really necessary?
|
||||
if (state == MapleClient.LOGIN_SERVER_TRANSITION || state == MapleClient.LOGIN_NOTLOGGEDIN) {
|
||||
List<String> charNames = c.loadCharacterNames(c.getWorld());
|
||||
if(!newcomer) {
|
||||
charNames.remove(player.getName());
|
||||
}
|
||||
|
||||
for (String charName : charNames) {
|
||||
if(wserv.getPlayerStorage().getCharacterByName(charName) != null) {
|
||||
allowLogin = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
c.lockClient(); // Sync this to prevent wrong login state for double channel changes
|
||||
try {
|
||||
int state = c.getLoginState();
|
||||
if (state != MapleClient.LOGIN_SERVER_TRANSITION || !allowLogin) {
|
||||
c.setPlayer(null);
|
||||
c.setAccID(0);
|
||||
|
||||
if (state == MapleClient.LOGIN_LOGGEDIN) {
|
||||
World wserv = server.getWorld(c.getWorld());
|
||||
if(wserv == null) {
|
||||
c.disconnect(true, false);
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.getAfterLoginError(7));
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
c.updateLoginState(MapleClient.LOGIN_LOGGEDIN);
|
||||
} finally {
|
||||
c.unlockClient();
|
||||
}
|
||||
|
||||
if (!newcomer) {
|
||||
c.setCharacterSlots((byte) player.getClient().getCharacterSlots());
|
||||
player.newClient(c);
|
||||
}
|
||||
|
||||
int hwidLen = remoteHwid.length();
|
||||
session.setAttribute(MapleClient.CLIENT_HWID, remoteHwid);
|
||||
session.setAttribute(MapleClient.CLIENT_NIBBLEHWID, remoteHwid.substring(hwidLen - 8, hwidLen));
|
||||
c.setHWID(remoteHwid);
|
||||
|
||||
cserv.addPlayer(player);
|
||||
wserv.addPlayer(player);
|
||||
player.setEnteredChannelWorld();
|
||||
|
||||
List<PlayerBuffValueHolder> buffs = server.getPlayerBuffStorage().getBuffsFromStorage(cid);
|
||||
if (buffs != null) {
|
||||
List<Pair<Long, PlayerBuffValueHolder>> timedBuffs = getLocalStartTimes(buffs);
|
||||
player.silentGiveBuffs(timedBuffs);
|
||||
}
|
||||
|
||||
Map<MapleDisease, Pair<Long, MobSkill>> diseases = server.getPlayerBuffStorage().getDiseasesFromStorage(cid);
|
||||
if (diseases != null) {
|
||||
player.silentApplyDiseases(diseases);
|
||||
}
|
||||
|
||||
c.announce(MaplePacketCreator.getCharInfo(player));
|
||||
if (!player.isHidden()) {
|
||||
if(player.isGM() && ServerConstants.USE_AUTOHIDE_GM) {
|
||||
player.toggleHide(true);
|
||||
}
|
||||
}
|
||||
player.sendKeymap();
|
||||
player.sendMacros();
|
||||
|
||||
// pot bindings being passed through other characters on the account detected thanks to Croosade dev team
|
||||
MapleKeyBinding autohpPot = player.getKeymap().get(91);
|
||||
player.announce(MaplePacketCreator.sendAutoHpPot(autohpPot != null ? autohpPot.getAction() : 0));
|
||||
|
||||
MapleKeyBinding autompPot = player.getKeymap().get(92);
|
||||
player.announce(MaplePacketCreator.sendAutoMpPot(autompPot != null ? autompPot.getAction() : 0));
|
||||
|
||||
player.getMap().addPlayer(player);
|
||||
player.visitMap(player.getMap());
|
||||
|
||||
BuddyList bl = player.getBuddylist();
|
||||
int buddyIds[] = bl.getBuddyIds();
|
||||
wserv.loggedOn(player.getName(), player.getId(), c.getChannel(), buddyIds);
|
||||
for (CharacterIdChannelPair onlineBuddy : wserv.multiBuddyFind(player.getId(), buddyIds)) {
|
||||
BuddylistEntry ble = bl.get(onlineBuddy.getCharacterId());
|
||||
ble.setChannel(onlineBuddy.getChannel());
|
||||
bl.put(ble);
|
||||
}
|
||||
c.announce(MaplePacketCreator.updateBuddylist(bl.getBuddies()));
|
||||
|
||||
c.announce(MaplePacketCreator.loadFamily(player));
|
||||
if (player.getFamilyId() > 0) {
|
||||
MapleFamily f = wserv.getFamily(player.getFamilyId());
|
||||
if (f == null) {
|
||||
f = new MapleFamily(player.getId());
|
||||
wserv.addFamily(player.getFamilyId(), f);
|
||||
}
|
||||
player.setFamily(f);
|
||||
c.announce(MaplePacketCreator.getFamilyInfo(f.getMember(player.getId())));
|
||||
}
|
||||
if (player.getGuildId() > 0) {
|
||||
MapleGuild playerGuild = server.getGuild(player.getGuildId(), player.getWorld(), player);
|
||||
if (playerGuild == null) {
|
||||
player.deleteGuild(player.getGuildId());
|
||||
player.getMGC().setGuildId(0);
|
||||
player.getMGC().setGuildRank(5);
|
||||
} else {
|
||||
playerGuild.getMGC(player.getId()).setCharacter(player);
|
||||
player.setMGC(playerGuild.getMGC(player.getId()));
|
||||
server.setGuildMemberOnline(player, true, c.getChannel());
|
||||
c.announce(MaplePacketCreator.showGuildInfo(player));
|
||||
int allianceId = player.getGuild().getAllianceId();
|
||||
if (allianceId > 0) {
|
||||
MapleAlliance newAlliance = server.getAlliance(allianceId);
|
||||
if (newAlliance == null) {
|
||||
newAlliance = MapleAlliance.loadAlliance(allianceId);
|
||||
if (newAlliance != null) {
|
||||
server.addAlliance(allianceId, newAlliance);
|
||||
} else {
|
||||
player.getGuild().setAllianceId(0);
|
||||
}
|
||||
}
|
||||
if (newAlliance != null) {
|
||||
c.announce(MaplePacketCreator.updateAllianceInfo(newAlliance, c));
|
||||
c.announce(MaplePacketCreator.allianceNotice(newAlliance.getId(), newAlliance.getNotice()));
|
||||
|
||||
if (newcomer) {
|
||||
server.allianceMessage(allianceId, MaplePacketCreator.allianceMemberOnline(player, true), player.getId(), -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
player.showNote();
|
||||
if (player.getParty() != null) {
|
||||
MaplePartyCharacter pchar = player.getMPC();
|
||||
|
||||
//Use this in case of enabling party HPbar HUD when logging in, however "you created a party" will appear on chat.
|
||||
//c.announce(MaplePacketCreator.partyCreated(pchar));
|
||||
|
||||
pchar.setChannel(c.getChannel());
|
||||
pchar.setMapId(player.getMapId());
|
||||
pchar.setOnline(true);
|
||||
wserv.updateParty(player.getParty().getId(), PartyOperation.LOG_ONOFF, pchar);
|
||||
player.updatePartyMemberHP();
|
||||
}
|
||||
|
||||
MapleInventory eqpInv = player.getInventory(MapleInventoryType.EQUIPPED);
|
||||
eqpInv.lockInventory();
|
||||
try {
|
||||
for(Item it : eqpInv.list()) {
|
||||
player.equippedItem((Equip) it);
|
||||
}
|
||||
} finally {
|
||||
eqpInv.unlockInventory();
|
||||
}
|
||||
|
||||
c.announce(MaplePacketCreator.updateBuddylist(player.getBuddylist().getBuddies()));
|
||||
|
||||
CharacterNameAndId pendingBuddyRequest = c.getPlayer().getBuddylist().pollPendingRequest();
|
||||
if (pendingBuddyRequest != null) {
|
||||
c.announce(MaplePacketCreator.requestBuddylistAdd(pendingBuddyRequest.getId(), c.getPlayer().getId(), pendingBuddyRequest.getName()));
|
||||
}
|
||||
|
||||
c.announce(MaplePacketCreator.updateGender(player));
|
||||
player.checkMessenger();
|
||||
c.announce(MaplePacketCreator.enableReport());
|
||||
player.changeSkillLevel(SkillFactory.getSkill(10000000 * player.getJobType() + 12), (byte) (player.getLinkedLevel() / 10), 20, -1);
|
||||
player.checkBerserk(player.isHidden());
|
||||
|
||||
if (newcomer) {
|
||||
for(MaplePet pet : player.getPets()) {
|
||||
if(pet != null)
|
||||
wserv.registerPetHunger(player, player.getPetIndex(pet));
|
||||
}
|
||||
|
||||
player.reloadQuestExpirations();
|
||||
|
||||
/*
|
||||
if (!c.hasVotedAlready()){
|
||||
player.announce(MaplePacketCreator.earnTitleMessage("You can vote now! Vote and earn a vote point!"));
|
||||
}
|
||||
*/
|
||||
if (player.isGM()){
|
||||
Server.getInstance().broadcastGMMessage(c.getWorld(), MaplePacketCreator.earnTitleMessage((player.gmLevel() < 6 ? "GM " : "Admin ") + player.getName() + " has logged in"));
|
||||
}
|
||||
|
||||
if(diseases != null) {
|
||||
for(Entry<MapleDisease, Pair<Long, MobSkill>> e : diseases.entrySet()) {
|
||||
final List<Pair<MapleDisease, Integer>> debuff = Collections.singletonList(new Pair<>(e.getKey(), Integer.valueOf(e.getValue().getRight().getX())));
|
||||
c.announce(MaplePacketCreator.giveDebuff(debuff, e.getValue().getRight()));
|
||||
Channel cserv = wserv.getChannel(c.getChannel());
|
||||
if(cserv == null) {
|
||||
c.setChannel(1);
|
||||
cserv = wserv.getChannel(c.getChannel());
|
||||
|
||||
if(cserv == null) {
|
||||
c.disconnect(true, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
MapleCharacter player = wserv.getPlayerStorage().getCharacterById(cid);
|
||||
boolean newcomer = false;
|
||||
|
||||
IoSession session = c.getSession();
|
||||
String remoteHwid;
|
||||
if (player == null) {
|
||||
if (!server.validateCharacteridInTransition((InetSocketAddress) session.getRemoteAddress(), cid)) {
|
||||
c.disconnect(true, false);
|
||||
return;
|
||||
}
|
||||
|
||||
remoteHwid = MapleSessionCoordinator.getInstance().getGameSessionHwid(session);
|
||||
if (remoteHwid == null) {
|
||||
c.disconnect(true, false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
player = MapleCharacter.loadCharFromDB(cid, c, true);
|
||||
newcomer = true;
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
remoteHwid = player.getClient().getHWID();
|
||||
}
|
||||
|
||||
if (player == null) { //If you are still getting null here then please just uninstall the game >.>, we dont need you fucking with the logs
|
||||
c.disconnect(true, false);
|
||||
return;
|
||||
}
|
||||
|
||||
c.setPlayer(player);
|
||||
c.setAccID(player.getAccountID());
|
||||
|
||||
boolean allowLogin = true;
|
||||
|
||||
/* is this check really necessary?
|
||||
if (state == MapleClient.LOGIN_SERVER_TRANSITION || state == MapleClient.LOGIN_NOTLOGGEDIN) {
|
||||
List<String> charNames = c.loadCharacterNames(c.getWorld());
|
||||
if(!newcomer) {
|
||||
charNames.remove(player.getName());
|
||||
}
|
||||
|
||||
for (String charName : charNames) {
|
||||
if(wserv.getPlayerStorage().getCharacterByName(charName) != null) {
|
||||
allowLogin = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
int accId = c.getAccID();
|
||||
if (tryAcquireAccount(accId)) { // Sync this to prevent wrong login state for double loggedin handling
|
||||
try {
|
||||
int state = c.getLoginState();
|
||||
if (state != MapleClient.LOGIN_SERVER_TRANSITION || !allowLogin) {
|
||||
c.setPlayer(null);
|
||||
c.setAccID(0);
|
||||
|
||||
if (state == MapleClient.LOGIN_LOGGEDIN) {
|
||||
c.disconnect(true, false);
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.getAfterLoginError(7));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
c.updateLoginState(MapleClient.LOGIN_LOGGEDIN);
|
||||
} finally {
|
||||
releaseAccount(accId);
|
||||
}
|
||||
} else {
|
||||
c.setPlayer(null);
|
||||
c.setAccID(0);
|
||||
c.announce(MaplePacketCreator.getAfterLoginError(10));
|
||||
return;
|
||||
}
|
||||
|
||||
player.announceDiseases();
|
||||
if (!newcomer) {
|
||||
c.setCharacterSlots((byte) player.getClient().getCharacterSlots());
|
||||
player.newClient(c);
|
||||
}
|
||||
|
||||
int hwidLen = remoteHwid.length();
|
||||
session.setAttribute(MapleClient.CLIENT_HWID, remoteHwid);
|
||||
session.setAttribute(MapleClient.CLIENT_NIBBLEHWID, remoteHwid.substring(hwidLen - 8, hwidLen));
|
||||
c.setHWID(remoteHwid);
|
||||
|
||||
cserv.addPlayer(player);
|
||||
wserv.addPlayer(player);
|
||||
player.setEnteredChannelWorld();
|
||||
|
||||
List<PlayerBuffValueHolder> buffs = server.getPlayerBuffStorage().getBuffsFromStorage(cid);
|
||||
if (buffs != null) {
|
||||
List<Pair<Long, PlayerBuffValueHolder>> timedBuffs = getLocalStartTimes(buffs);
|
||||
player.silentGiveBuffs(timedBuffs);
|
||||
}
|
||||
|
||||
Map<MapleDisease, Pair<Long, MobSkill>> diseases = server.getPlayerBuffStorage().getDiseasesFromStorage(cid);
|
||||
if (diseases != null) {
|
||||
player.silentApplyDiseases(diseases);
|
||||
}
|
||||
|
||||
c.announce(MaplePacketCreator.getCharInfo(player));
|
||||
if (!player.isHidden()) {
|
||||
if(player.isGM() && ServerConstants.USE_AUTOHIDE_GM) {
|
||||
player.toggleHide(true);
|
||||
}
|
||||
}
|
||||
player.sendKeymap();
|
||||
player.sendMacros();
|
||||
|
||||
// pot bindings being passed through other characters on the account detected thanks to Croosade dev team
|
||||
MapleKeyBinding autohpPot = player.getKeymap().get(91);
|
||||
player.announce(MaplePacketCreator.sendAutoHpPot(autohpPot != null ? autohpPot.getAction() : 0));
|
||||
|
||||
MapleKeyBinding autompPot = player.getKeymap().get(92);
|
||||
player.announce(MaplePacketCreator.sendAutoMpPot(autompPot != null ? autompPot.getAction() : 0));
|
||||
|
||||
player.getMap().addPlayer(player);
|
||||
player.visitMap(player.getMap());
|
||||
|
||||
BuddyList bl = player.getBuddylist();
|
||||
int buddyIds[] = bl.getBuddyIds();
|
||||
wserv.loggedOn(player.getName(), player.getId(), c.getChannel(), buddyIds);
|
||||
for (CharacterIdChannelPair onlineBuddy : wserv.multiBuddyFind(player.getId(), buddyIds)) {
|
||||
BuddylistEntry ble = bl.get(onlineBuddy.getCharacterId());
|
||||
ble.setChannel(onlineBuddy.getChannel());
|
||||
bl.put(ble);
|
||||
}
|
||||
c.announce(MaplePacketCreator.updateBuddylist(bl.getBuddies()));
|
||||
|
||||
c.announce(MaplePacketCreator.loadFamily(player));
|
||||
if (player.getFamilyId() > 0) {
|
||||
MapleFamily f = wserv.getFamily(player.getFamilyId());
|
||||
if (f == null) {
|
||||
f = new MapleFamily(player.getId());
|
||||
wserv.addFamily(player.getFamilyId(), f);
|
||||
}
|
||||
player.setFamily(f);
|
||||
c.announce(MaplePacketCreator.getFamilyInfo(f.getMember(player.getId())));
|
||||
}
|
||||
if (player.getGuildId() > 0) {
|
||||
MapleGuild playerGuild = server.getGuild(player.getGuildId(), player.getWorld(), player);
|
||||
if (playerGuild == null) {
|
||||
player.deleteGuild(player.getGuildId());
|
||||
player.getMGC().setGuildId(0);
|
||||
player.getMGC().setGuildRank(5);
|
||||
} else {
|
||||
playerGuild.getMGC(player.getId()).setCharacter(player);
|
||||
player.setMGC(playerGuild.getMGC(player.getId()));
|
||||
server.setGuildMemberOnline(player, true, c.getChannel());
|
||||
c.announce(MaplePacketCreator.showGuildInfo(player));
|
||||
int allianceId = player.getGuild().getAllianceId();
|
||||
if (allianceId > 0) {
|
||||
MapleAlliance newAlliance = server.getAlliance(allianceId);
|
||||
if (newAlliance == null) {
|
||||
newAlliance = MapleAlliance.loadAlliance(allianceId);
|
||||
if (newAlliance != null) {
|
||||
server.addAlliance(allianceId, newAlliance);
|
||||
} else {
|
||||
player.getGuild().setAllianceId(0);
|
||||
}
|
||||
}
|
||||
if (newAlliance != null) {
|
||||
c.announce(MaplePacketCreator.updateAllianceInfo(newAlliance, c));
|
||||
c.announce(MaplePacketCreator.allianceNotice(newAlliance.getId(), newAlliance.getNotice()));
|
||||
|
||||
if (newcomer) {
|
||||
server.allianceMessage(allianceId, MaplePacketCreator.allianceMemberOnline(player, true), player.getId(), -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
player.showNote();
|
||||
if (player.getParty() != null) {
|
||||
MaplePartyCharacter pchar = player.getMPC();
|
||||
|
||||
//Use this in case of enabling party HPbar HUD when logging in, however "you created a party" will appear on chat.
|
||||
//c.announce(MaplePacketCreator.partyCreated(pchar));
|
||||
|
||||
pchar.setChannel(c.getChannel());
|
||||
pchar.setMapId(player.getMapId());
|
||||
pchar.setOnline(true);
|
||||
wserv.updateParty(player.getParty().getId(), PartyOperation.LOG_ONOFF, pchar);
|
||||
player.updatePartyMemberHP();
|
||||
}
|
||||
|
||||
MapleInventory eqpInv = player.getInventory(MapleInventoryType.EQUIPPED);
|
||||
eqpInv.lockInventory();
|
||||
try {
|
||||
for(Item it : eqpInv.list()) {
|
||||
player.equippedItem((Equip) it);
|
||||
}
|
||||
} finally {
|
||||
eqpInv.unlockInventory();
|
||||
}
|
||||
|
||||
c.announce(MaplePacketCreator.updateBuddylist(player.getBuddylist().getBuddies()));
|
||||
|
||||
CharacterNameAndId pendingBuddyRequest = c.getPlayer().getBuddylist().pollPendingRequest();
|
||||
if (pendingBuddyRequest != null) {
|
||||
c.announce(MaplePacketCreator.requestBuddylistAdd(pendingBuddyRequest.getId(), c.getPlayer().getId(), pendingBuddyRequest.getName()));
|
||||
}
|
||||
|
||||
c.announce(MaplePacketCreator.updateGender(player));
|
||||
player.checkMessenger();
|
||||
c.announce(MaplePacketCreator.enableReport());
|
||||
player.changeSkillLevel(SkillFactory.getSkill(10000000 * player.getJobType() + 12), (byte) (player.getLinkedLevel() / 10), 20, -1);
|
||||
player.checkBerserk(player.isHidden());
|
||||
|
||||
if (newcomer) {
|
||||
for(MaplePet pet : player.getPets()) {
|
||||
if(pet != null)
|
||||
wserv.registerPetHunger(player, player.getPetIndex(pet));
|
||||
}
|
||||
|
||||
player.reloadQuestExpirations();
|
||||
|
||||
/*
|
||||
if (!c.hasVotedAlready()){
|
||||
player.announce(MaplePacketCreator.earnTitleMessage("You can vote now! Vote and earn a vote point!"));
|
||||
}
|
||||
*/
|
||||
if (player.isGM()){
|
||||
Server.getInstance().broadcastGMMessage(c.getWorld(), MaplePacketCreator.earnTitleMessage((player.gmLevel() < 6 ? "GM " : "Admin ") + player.getName() + " has logged in"));
|
||||
}
|
||||
|
||||
if(diseases != null) {
|
||||
for(Entry<MapleDisease, Pair<Long, MobSkill>> e : diseases.entrySet()) {
|
||||
final List<Pair<MapleDisease, Integer>> debuff = Collections.singletonList(new Pair<>(e.getKey(), Integer.valueOf(e.getValue().getRight().getX())));
|
||||
c.announce(MaplePacketCreator.giveDebuff(debuff, e.getValue().getRight()));
|
||||
}
|
||||
|
||||
player.announceDiseases();
|
||||
}
|
||||
} else {
|
||||
if(player.isRidingBattleship()) {
|
||||
player.announceBattleshipHp();
|
||||
}
|
||||
}
|
||||
|
||||
player.buffExpireTask();
|
||||
player.diseaseExpireTask();
|
||||
player.skillCooldownTask();
|
||||
player.expirationTask();
|
||||
player.questExpirationTask();
|
||||
if (GameConstants.hasSPTable(player.getJob()) && player.getJob().getId() != 2001) {
|
||||
player.createDragon();
|
||||
}
|
||||
|
||||
player.commitExcludedItems();
|
||||
showDueyNotification(c, player);
|
||||
|
||||
if (player.getMap().getHPDec() > 0) player.resetHpDecreaseTask();
|
||||
|
||||
player.resetPlayerRates();
|
||||
if(ServerConstants.USE_ADD_RATES_BY_LEVEL == true) player.setPlayerRates();
|
||||
player.setWorldRates();
|
||||
player.updateCouponRates();
|
||||
|
||||
player.receivePartyMemberHP();
|
||||
|
||||
if(player.getPartnerId() > 0) {
|
||||
int partnerId = player.getPartnerId();
|
||||
final MapleCharacter partner = wserv.getPlayerStorage().getCharacterById(partnerId);
|
||||
|
||||
if(partner != null && !partner.isAwayFromWorld()) {
|
||||
player.announce(Wedding.OnNotifyWeddingPartnerTransfer(partnerId, partner.getMapId()));
|
||||
partner.announce(Wedding.OnNotifyWeddingPartnerTransfer(player.getId(), player.getMapId()));
|
||||
}
|
||||
}
|
||||
|
||||
if (newcomer) {
|
||||
EventInstanceManager eim = MapleEventRecallCoordinator.getInstance().recallEventInstance(cid);
|
||||
if (eim != null) {
|
||||
eim.registerPlayer(player);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
c.releaseClient();
|
||||
}
|
||||
} else {
|
||||
if(player.isRidingBattleship()) {
|
||||
player.announceBattleshipHp();
|
||||
}
|
||||
}
|
||||
|
||||
player.buffExpireTask();
|
||||
player.diseaseExpireTask();
|
||||
player.skillCooldownTask();
|
||||
player.expirationTask();
|
||||
player.questExpirationTask();
|
||||
if (GameConstants.hasSPTable(player.getJob()) && player.getJob().getId() != 2001) {
|
||||
player.createDragon();
|
||||
}
|
||||
|
||||
player.commitExcludedItems();
|
||||
showDueyNotification(c, player);
|
||||
|
||||
if (player.getMap().getHPDec() > 0) player.resetHpDecreaseTask();
|
||||
|
||||
player.resetPlayerRates();
|
||||
if(ServerConstants.USE_ADD_RATES_BY_LEVEL == true) player.setPlayerRates();
|
||||
player.setWorldRates();
|
||||
player.updateCouponRates();
|
||||
|
||||
player.receivePartyMemberHP();
|
||||
|
||||
if(player.getPartnerId() > 0) {
|
||||
int partnerId = player.getPartnerId();
|
||||
final MapleCharacter partner = wserv.getPlayerStorage().getCharacterById(partnerId);
|
||||
|
||||
if(partner != null && !partner.isAwayFromWorld()) {
|
||||
player.announce(Wedding.OnNotifyWeddingPartnerTransfer(partnerId, partner.getMapId()));
|
||||
partner.announce(Wedding.OnNotifyWeddingPartnerTransfer(player.getId(), player.getMapId()));
|
||||
}
|
||||
}
|
||||
|
||||
if (newcomer) {
|
||||
EventInstanceManager eim = MapleEventRecallCoordinator.getInstance().recallEventInstance(cid);
|
||||
if (eim != null) {
|
||||
eim.registerPlayer(player);
|
||||
}
|
||||
c.announce(MaplePacketCreator.getAfterLoginError(10));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -91,26 +91,22 @@ public final class SpecialMoveHandler extends AbstractMaplePacketHandler {
|
||||
}
|
||||
if (skillid == Hero.MONSTER_MAGNET || skillid == Paladin.MONSTER_MAGNET || skillid == DarkKnight.MONSTER_MAGNET) { // Monster Magnet
|
||||
int num = slea.readInt();
|
||||
int mobId;
|
||||
byte success;
|
||||
for (int i = 0; i < num; i++) {
|
||||
mobId = slea.readInt();
|
||||
success = slea.readByte();
|
||||
chr.getMap().broadcastMessage(chr, MaplePacketCreator.showMagnet(mobId, success), false);
|
||||
MapleMonster monster = chr.getMap().getMonsterByOid(mobId);
|
||||
int mobOid = slea.readInt();
|
||||
byte success = slea.readByte();
|
||||
chr.getMap().broadcastMessage(chr, MaplePacketCreator.catchMonster(mobOid, success), false);
|
||||
MapleMonster monster = chr.getMap().getMonsterByOid(mobOid);
|
||||
if (monster != null) {
|
||||
if (!monster.isBoss()) {
|
||||
monster.lockMonster();
|
||||
try {
|
||||
monster.switchController(chr, monster.isControllerHasAggro());
|
||||
} finally {
|
||||
monster.unlockMonster();
|
||||
}
|
||||
monster.aggroClearDamages();
|
||||
monster.aggroMonsterDamage(chr, 1);
|
||||
|
||||
monster.aggroSwitchController(chr, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
byte direction = slea.readByte();
|
||||
chr.getMap().broadcastMessage(chr, MaplePacketCreator.showBuffeffect(chr.getId(), skillid, chr.getSkillLevel(skillid), direction), false);
|
||||
byte direction = slea.readByte(); // thanks MedicOP for pointing some 3rd-party related issues with Magnet
|
||||
chr.getMap().broadcastMessage(chr, MaplePacketCreator.showBuffeffect(chr.getId(), skillid, chr.getSkillLevel(skillid), 1, direction), false);
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
} else if (skillid == Brawler.MP_RECOVERY) {// MP Recovery
|
||||
|
||||
@@ -191,7 +191,7 @@ public final class TakeDamageHandler extends AbstractMaplePacketHandler {
|
||||
damage -= bouncedamage;
|
||||
map.damageMonster(chr, attacker, bouncedamage);
|
||||
map.broadcastMessage(chr, MaplePacketCreator.damageMonster(oid, bouncedamage), false, true);
|
||||
chr.checkMonsterAggro(attacker);
|
||||
attacker.aggroMonsterDamage(chr, bouncedamage);
|
||||
}
|
||||
if (attacker != null && damagefrom == -1 && chr.getBuffedValue(MapleBuffStat.BODY_PRESSURE) != null) {
|
||||
Skill skill = SkillFactory.getSkill(Aran.BODY_PRESSURE);
|
||||
|
||||
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2018 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.server.coordinator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import constants.ServerConstants;
|
||||
import client.MapleCharacter;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import net.server.Server;
|
||||
import net.server.audit.LockCollector;
|
||||
import server.life.MapleMonster;
|
||||
import server.maps.MapleMap;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.MonitoredReentrantLock;
|
||||
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
import server.TimerManager;
|
||||
import tools.Pair;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ronan
|
||||
*/
|
||||
public class MapleMonsterAggroCoordinator {
|
||||
|
||||
private MonitoredReentrantLock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MAP_AGGRO);
|
||||
private MonitoredReentrantLock idleLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MAP_AGGRO_IDLE, true);
|
||||
private long lastStopTime = Server.getInstance().getCurrentTime();
|
||||
|
||||
private ScheduledFuture<?> aggroMonitor = null;
|
||||
|
||||
private Map<MapleMonster, Map<Integer, PlayerAggroEntry>> mobAggroEntries = new HashMap<>();
|
||||
private Map<MapleMonster, List<PlayerAggroEntry>> mobSortedAggros = new HashMap<>();
|
||||
|
||||
private class PlayerAggroEntry {
|
||||
protected int cid;
|
||||
protected int averageDamage = 0;
|
||||
protected int currentDamageInstances = 0;
|
||||
protected long accumulatedDamage = 0;
|
||||
|
||||
protected int expireStreak = 0;
|
||||
protected int updateStreak = 0;
|
||||
protected int toNextUpdate = 0;
|
||||
protected int entryRank = -1;
|
||||
|
||||
protected PlayerAggroEntry(int cid) {
|
||||
this.cid = cid;
|
||||
}
|
||||
}
|
||||
|
||||
public void stopAggroCoordinator() {
|
||||
idleLock.lock();
|
||||
try {
|
||||
if (aggroMonitor == null) return;
|
||||
|
||||
aggroMonitor.cancel(false);
|
||||
aggroMonitor = null;
|
||||
} finally {
|
||||
idleLock.unlock();
|
||||
}
|
||||
|
||||
lastStopTime = Server.getInstance().getCurrentTime();
|
||||
}
|
||||
|
||||
public void startAggroCoordinator() {
|
||||
idleLock.lock();
|
||||
try {
|
||||
if (aggroMonitor != null) return;
|
||||
|
||||
aggroMonitor = TimerManager.getInstance().register(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
runAggroUpdate(1);
|
||||
runSortLeadingCharactersAggro();
|
||||
}
|
||||
}, ServerConstants.MOB_STATUS_AGGRO_INTERVAL, ServerConstants.MOB_STATUS_AGGRO_INTERVAL);
|
||||
} finally {
|
||||
idleLock.unlock();
|
||||
}
|
||||
|
||||
int timeDelta = (int) Math.ceil((Server.getInstance().getCurrentTime() - lastStopTime) / ServerConstants.MOB_STATUS_AGGRO_INTERVAL);
|
||||
if (timeDelta > 0) {
|
||||
runAggroUpdate(timeDelta);
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateEntryExpiration(PlayerAggroEntry pae) {
|
||||
pae.toNextUpdate = (int) Math.ceil((120000L / ServerConstants.MOB_STATUS_AGGRO_INTERVAL) / Math.pow(2, pae.expireStreak + pae.currentDamageInstances));
|
||||
}
|
||||
|
||||
private static void insertEntryDamage(PlayerAggroEntry pae, int damage) {
|
||||
synchronized (pae) {
|
||||
long totalDamage = pae.averageDamage;
|
||||
totalDamage *= pae.currentDamageInstances;
|
||||
totalDamage += damage;
|
||||
|
||||
pae.expireStreak = 0;
|
||||
pae.updateStreak = 0;
|
||||
updateEntryExpiration(pae);
|
||||
|
||||
pae.currentDamageInstances += 1;
|
||||
pae.averageDamage = (int)(totalDamage / pae.currentDamageInstances);
|
||||
pae.accumulatedDamage = totalDamage;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean expiredAfterUpdateEntryDamage(PlayerAggroEntry pae, int deltaTime) {
|
||||
synchronized (pae) {
|
||||
pae.updateStreak += 1;
|
||||
pae.toNextUpdate -= deltaTime;
|
||||
|
||||
if (pae.toNextUpdate <= 0) { // reached dmg instance expire time
|
||||
pae.expireStreak += 1;
|
||||
updateEntryExpiration(pae);
|
||||
|
||||
pae.currentDamageInstances -= 1;
|
||||
if (pae.currentDamageInstances < 1) { // expired aggro for this player
|
||||
return true;
|
||||
}
|
||||
pae.accumulatedDamage = pae.averageDamage * pae.currentDamageInstances;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void addAggroDamage(MapleMonster mob, int cid, int damage) { // assumption: should not trigger after dispose()
|
||||
if (!mob.isAlive()) return;
|
||||
|
||||
List<PlayerAggroEntry> sortedAggro = mobSortedAggros.get(mob);
|
||||
Map<Integer, PlayerAggroEntry> mobAggro = mobAggroEntries.get(mob);
|
||||
if (mobAggro == null) {
|
||||
if (lock.tryLock()) { // can run unreliably, as fast as possible... try lock that is!
|
||||
try {
|
||||
mobAggro = mobAggroEntries.get(mob);
|
||||
if (mobAggro == null) {
|
||||
mobAggro = new HashMap<>();
|
||||
mobAggroEntries.put(mob, mobAggro);
|
||||
|
||||
sortedAggro = new LinkedList<>();
|
||||
mobSortedAggros.put(mob, sortedAggro);
|
||||
} else {
|
||||
sortedAggro = mobSortedAggros.get(mob);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PlayerAggroEntry aggroEntry = mobAggro.get(cid);
|
||||
if (aggroEntry == null) {
|
||||
aggroEntry = new PlayerAggroEntry(cid);
|
||||
|
||||
synchronized (mobAggro) {
|
||||
synchronized (sortedAggro) {
|
||||
PlayerAggroEntry mappedEntry = mobAggro.get(cid);
|
||||
|
||||
if (mappedEntry == null) {
|
||||
mobAggro.put(aggroEntry.cid, aggroEntry);
|
||||
sortedAggro.add(aggroEntry);
|
||||
} else {
|
||||
aggroEntry = mappedEntry;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (damage < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
insertEntryDamage(aggroEntry, damage);
|
||||
}
|
||||
|
||||
private void runAggroUpdate(int deltaTime) {
|
||||
List<Pair<MapleMonster, Map<Integer, PlayerAggroEntry>>> aggroMobs = new LinkedList<>();
|
||||
lock.lock();
|
||||
try {
|
||||
for (Entry<MapleMonster, Map<Integer, PlayerAggroEntry>> e : mobAggroEntries.entrySet()) {
|
||||
aggroMobs.add(new Pair<>(e.getKey(), e.getValue()));
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
for (Pair<MapleMonster, Map<Integer, PlayerAggroEntry>> am : aggroMobs) {
|
||||
Map<Integer, PlayerAggroEntry> mobAggro = am.getRight();
|
||||
List<PlayerAggroEntry> sortedAggro = mobSortedAggros.get(am.getLeft());
|
||||
|
||||
if (sortedAggro != null) {
|
||||
List<Integer> toRemove = new LinkedList<>();
|
||||
List<Integer> toRemoveIdx = new ArrayList<>(mobAggro.size());
|
||||
List<Integer> toRemoveByFetch = new LinkedList<>();
|
||||
|
||||
synchronized (mobAggro) {
|
||||
synchronized (sortedAggro) {
|
||||
for (PlayerAggroEntry pae : mobAggro.values()) {
|
||||
if (expiredAfterUpdateEntryDamage(pae, deltaTime)) {
|
||||
toRemove.add(pae.cid);
|
||||
if (pae.entryRank > -1) toRemoveIdx.add(pae.entryRank);
|
||||
else toRemoveByFetch.add(pae.cid);
|
||||
}
|
||||
}
|
||||
|
||||
if (!toRemove.isEmpty()) {
|
||||
for (Integer cid : toRemove) {
|
||||
mobAggro.remove(cid);
|
||||
}
|
||||
|
||||
if (mobAggro.isEmpty()) { // all aggro on this mob expired
|
||||
am.getLeft().aggroResetAggro();
|
||||
}
|
||||
}
|
||||
|
||||
if (!toRemoveIdx.isEmpty()) {
|
||||
Collections.sort(toRemoveIdx, new Comparator<Integer>() { // last to first indexes
|
||||
@Override
|
||||
public int compare(Integer p1, Integer p2) {
|
||||
return p1 < p2 ? 1 : p1.equals(p2) ? 0 : -1;
|
||||
}
|
||||
});
|
||||
|
||||
for (int idx : toRemoveIdx) {
|
||||
sortedAggro.remove(idx);
|
||||
}
|
||||
}
|
||||
|
||||
if (!toRemoveByFetch.isEmpty()) {
|
||||
for (Integer cid : toRemoveByFetch) {
|
||||
for (int i = 0; i < sortedAggro.size(); i++) {
|
||||
if (cid.equals(sortedAggro.get(i).cid)) {
|
||||
sortedAggro.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void insertionSortAggroList(List<PlayerAggroEntry> paeList) {
|
||||
for (int i = 1; i < paeList.size(); i++) {
|
||||
PlayerAggroEntry pae = paeList.get(i);
|
||||
long curAccDmg = pae.accumulatedDamage;
|
||||
|
||||
int j = i - 1;
|
||||
while (j >= 0 && curAccDmg > paeList.get(j).accumulatedDamage) {
|
||||
j -= 1;
|
||||
}
|
||||
|
||||
j += 1;
|
||||
if (j != i) {
|
||||
paeList.remove(i);
|
||||
paeList.add(j, pae);
|
||||
}
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (PlayerAggroEntry pae : paeList) {
|
||||
pae.entryRank = i;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isLeadingCharacterAggro(MapleMonster mob, MapleCharacter player) {
|
||||
// by assuming the quasi-sorted nature of "mobAggroList", this method
|
||||
// returns whether the player given as parameter can be elected as next aggro leader
|
||||
|
||||
List<PlayerAggroEntry> mobAggroList = mobSortedAggros.get(mob);
|
||||
if (mobAggroList != null) {
|
||||
synchronized (mobAggroList) {
|
||||
mobAggroList = new ArrayList<>(mobAggroList.subList(0, Math.min(mobAggroList.size(), 5)));
|
||||
}
|
||||
|
||||
MapleMap map = mob.getMap();
|
||||
for (PlayerAggroEntry pae : mobAggroList) {
|
||||
MapleCharacter chr = map.getCharacterById(pae.cid);
|
||||
if (chr != null) {
|
||||
if (player.getId() == pae.cid) {
|
||||
return true;
|
||||
} else if (pae.updateStreak < ServerConstants.MOB_STATUS_AGGRO_PERSISTENCE && chr.isAlive()) { // verifies currently leading players activity
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void runSortLeadingCharactersAggro() {
|
||||
List<List<PlayerAggroEntry>> aggroList;
|
||||
lock.lock();
|
||||
try {
|
||||
aggroList = new ArrayList<>(mobSortedAggros.values());
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
for (List<PlayerAggroEntry> mobAggroList : aggroList) {
|
||||
synchronized (mobAggroList) {
|
||||
insertionSortAggroList(mobAggroList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAggroEntries(MapleMonster mob) {
|
||||
lock.lock();
|
||||
try {
|
||||
mobAggroEntries.remove(mob);
|
||||
mobSortedAggros.remove(mob);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
stopAggroCoordinator();
|
||||
|
||||
lock.lock();
|
||||
try {
|
||||
mobAggroEntries.clear();
|
||||
mobSortedAggros.clear();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
disposeLocks();
|
||||
}
|
||||
|
||||
private void disposeLocks() {
|
||||
LockCollector.getInstance().registerDisposeAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
emptyLocks();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void emptyLocks() {
|
||||
lock = lock.dispose();
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,7 @@ import java.util.Set;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
|
||||
import net.server.PlayerStorage;
|
||||
import net.server.Server;
|
||||
import net.server.channel.Channel;
|
||||
import tools.DatabaseConnection;
|
||||
@@ -109,11 +110,11 @@ public class MapleGuild {
|
||||
con.close();
|
||||
} catch (SQLException se) {
|
||||
se.printStackTrace();
|
||||
System.out.println("Unable to read guild information from sql" + se);
|
||||
System.out.println("Unable to read guild information from sql: " + se);
|
||||
}
|
||||
}
|
||||
|
||||
public void buildNotifications() {
|
||||
private void buildNotifications() {
|
||||
if (!bDirty) {
|
||||
return;
|
||||
}
|
||||
@@ -260,10 +261,10 @@ public class MapleGuild {
|
||||
return name;
|
||||
}
|
||||
|
||||
public java.util.Collection<MapleGuildCharacter> getMembers() {
|
||||
public List<MapleGuildCharacter> getMembers() {
|
||||
membersLock.lock();
|
||||
try {
|
||||
return java.util.Collections.unmodifiableCollection(members);
|
||||
return new ArrayList<>(members);
|
||||
} finally {
|
||||
membersLock.unlock();
|
||||
}
|
||||
@@ -277,6 +278,30 @@ public class MapleGuild {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public void broadcastNameChanged() {
|
||||
PlayerStorage ps = Server.getInstance().getWorld(world).getPlayerStorage();
|
||||
|
||||
for (MapleGuildCharacter mgc : getMembers()) {
|
||||
MapleCharacter chr = ps.getCharacterById(mgc.getId());
|
||||
if (chr == null || !chr.isLoggedinWorld()) continue;
|
||||
|
||||
byte[] packet = MaplePacketCreator.guildNameChanged(chr.getId(), this.getName());
|
||||
chr.getMap().broadcastMessage(chr, packet);
|
||||
}
|
||||
}
|
||||
|
||||
public void broadcastEmblemChanged() {
|
||||
PlayerStorage ps = Server.getInstance().getWorld(world).getPlayerStorage();
|
||||
|
||||
for (MapleGuildCharacter mgc : getMembers()) {
|
||||
MapleCharacter chr = ps.getCharacterById(mgc.getId());
|
||||
if (chr == null || !chr.isLoggedinWorld()) continue;
|
||||
|
||||
byte[] packet = MaplePacketCreator.guildMarkChanged(chr.getId(), this);
|
||||
chr.getMap().broadcastMessage(chr, packet);
|
||||
}
|
||||
}
|
||||
|
||||
public void broadcast(final byte[] packet) {
|
||||
broadcast(packet, -1, BCOp.NONE);
|
||||
}
|
||||
|
||||
@@ -612,7 +612,15 @@ public class World {
|
||||
mc.saveGuildStatus();
|
||||
}
|
||||
if (bDifferentGuild) {
|
||||
mc.broadcastStance();
|
||||
if (mc.isLoggedinWorld()) {
|
||||
MapleGuild guild = Server.getInstance().getGuild(guildid);
|
||||
if (guild != null) {
|
||||
mc.getMap().broadcastMessage(mc, MaplePacketCreator.guildNameChanged(cid, guild.getName()));
|
||||
mc.getMap().broadcastMessage(mc, MaplePacketCreator.guildMarkChanged(cid, guild));
|
||||
} else {
|
||||
mc.getMap().broadcastMessage(mc, MaplePacketCreator.guildNameChanged(cid, ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -237,6 +237,15 @@ public class AbstractPlayerInteraction {
|
||||
return intList;
|
||||
}
|
||||
|
||||
public boolean canHoldAll(List<Double> itemids) {
|
||||
List<Double> quantity = new LinkedList<>();
|
||||
for (int i = 0; i < itemids.size(); i++) {
|
||||
quantity.add(1.0);
|
||||
}
|
||||
|
||||
return canHoldAll(itemids, quantity);
|
||||
}
|
||||
|
||||
public boolean canHoldAll(List<Double> itemids, List<Double> quantity) {
|
||||
return canHoldAll(convertToIntegerArray(itemids), convertToIntegerArray(quantity), true);
|
||||
}
|
||||
@@ -554,8 +563,6 @@ public class AbstractPlayerInteraction {
|
||||
|
||||
if(expires >= 0)
|
||||
item.setExpiration(System.currentTimeMillis() + expires);
|
||||
|
||||
item.setPetId(petId);
|
||||
|
||||
if (!MapleInventoryManipulator.checkSpace(c, id, quantity, "")) {
|
||||
c.getPlayer().dropMessage(1, "Your inventory is full. Please remove an item from your " + ItemConstants.getInventoryType(id).name() + " inventory.");
|
||||
|
||||
@@ -229,7 +229,11 @@ public class EventInstanceManager {
|
||||
|
||||
}
|
||||
|
||||
public synchronized void registerPlayer(final MapleCharacter chr) {
|
||||
public synchronized void registerPlayer(final MapleCharacter chr) {
|
||||
registerPlayer(chr, true);
|
||||
}
|
||||
|
||||
public synchronized void registerPlayer(final MapleCharacter chr, boolean runEntryScript) {
|
||||
if (chr == null || !chr.isLoggedinWorld() || disposed) {
|
||||
return;
|
||||
}
|
||||
@@ -246,10 +250,12 @@ public class EventInstanceManager {
|
||||
wL.unlock();
|
||||
}
|
||||
|
||||
try {
|
||||
em.getIv().invokeFunction("playerEntry", EventInstanceManager.this, chr);
|
||||
} catch (ScriptException | NoSuchMethodException ex) {
|
||||
ex.printStackTrace();
|
||||
if (runEntryScript) {
|
||||
try {
|
||||
em.getIv().invokeFunction("playerEntry", EventInstanceManager.this, chr);
|
||||
} catch (ScriptException | NoSuchMethodException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,7 +378,7 @@ public class EventInstanceManager {
|
||||
private void registerExpeditionTeam(MapleExpedition exped, int recruitMap) {
|
||||
expedition = exped;
|
||||
|
||||
for (MapleCharacter chr: exped.getMembers()) {
|
||||
for (MapleCharacter chr: exped.getActiveMembers()) {
|
||||
if (chr.getMapId() == recruitMap) {
|
||||
registerPlayer(chr);
|
||||
}
|
||||
@@ -628,7 +634,6 @@ public class EventInstanceManager {
|
||||
sL.lock();
|
||||
try {
|
||||
if(!eventCleared) em.disposeInstance(name);
|
||||
em = null;
|
||||
} finally {
|
||||
sL.unlock();
|
||||
}
|
||||
@@ -636,10 +641,11 @@ public class EventInstanceManager {
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mapFactory.dispose(); // reactors issue on dispose event maps found thanks to MedicOP
|
||||
mapFactory.dispose(); // issues from instantly disposing some event objects found thanks to MedicOP
|
||||
wL.lock();
|
||||
try {
|
||||
mapFactory = null;
|
||||
em = null;
|
||||
} finally {
|
||||
wL.unlock();
|
||||
}
|
||||
@@ -675,7 +681,6 @@ public class EventInstanceManager {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if(em == null) return;
|
||||
em.getIv().invokeFunction(methodName, EventInstanceManager.this);
|
||||
} catch (ScriptException | NoSuchMethodException ex) {
|
||||
ex.printStackTrace();
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
package scripting.event;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
@@ -116,13 +115,16 @@ public class EventManager {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
Collection<EventInstanceManager> eimList;
|
||||
synchronized(instances) {
|
||||
for(EventInstanceManager eim : instances.values()) {
|
||||
eim.dispose(true);
|
||||
}
|
||||
eimList = getInstances();
|
||||
instances.clear();
|
||||
}
|
||||
|
||||
for(EventInstanceManager eim : eimList) {
|
||||
eim.dispose(true);
|
||||
}
|
||||
|
||||
List<EventInstanceManager> readyEims;
|
||||
queueLock.lock();
|
||||
try {
|
||||
@@ -253,7 +255,9 @@ public class EventManager {
|
||||
}
|
||||
|
||||
public Collection<EventInstanceManager> getInstances() {
|
||||
return Collections.unmodifiableCollection(instances.values());
|
||||
synchronized (instances) {
|
||||
return new LinkedList<>(instances.values());
|
||||
}
|
||||
}
|
||||
|
||||
public EventInstanceManager newInstance(String name) {
|
||||
@@ -265,7 +269,13 @@ public class EventManager {
|
||||
ret.setName(name);
|
||||
}
|
||||
|
||||
instances.put(name, ret);
|
||||
synchronized (instances) {
|
||||
if (instances.containsKey(name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
instances.put(name, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -274,7 +284,10 @@ public class EventManager {
|
||||
@Override
|
||||
public void run() {
|
||||
freeLobbyInstance(name);
|
||||
instances.remove(name);
|
||||
|
||||
synchronized (instances) {
|
||||
instances.remove(name);
|
||||
}
|
||||
}
|
||||
}, ServerConstants.EVENT_LOBBY_DELAY * 1000);
|
||||
}
|
||||
@@ -379,14 +392,17 @@ public class EventManager {
|
||||
if(!startLobbyInstance(lobbyId)) return false;
|
||||
}
|
||||
|
||||
EventInstanceManager eim = (EventInstanceManager) (iv.invokeFunction("setup", leader.getClient().getChannel()));
|
||||
if(eim == null) {
|
||||
EventInstanceManager eim;
|
||||
try {
|
||||
eim = (EventInstanceManager) (iv.invokeFunction("setup", leader.getClient().getChannel()));
|
||||
instanceLocks.put(eim.getName(), lobbyId);
|
||||
} catch (NullPointerException npe) {
|
||||
if(lobbyId > -1) {
|
||||
setLockLobby(lobbyId, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
instanceLocks.put(eim.getName(), lobbyId);
|
||||
|
||||
eim.setLeader(leader);
|
||||
|
||||
exped.start();
|
||||
@@ -442,14 +458,16 @@ public class EventManager {
|
||||
}
|
||||
}
|
||||
|
||||
EventInstanceManager eim = (EventInstanceManager) (iv.invokeFunction("setup", difficulty, (lobbyId > -1) ? lobbyId : leader.getId()));
|
||||
if(eim == null) {
|
||||
EventInstanceManager eim;
|
||||
try {
|
||||
eim = (EventInstanceManager) (iv.invokeFunction("setup", difficulty, (lobbyId > -1) ? lobbyId : leader.getId()));
|
||||
instanceLocks.put(eim.getName(), lobbyId);
|
||||
} catch (NullPointerException npe) {
|
||||
if(lobbyId > -1) {
|
||||
setLockLobby(lobbyId, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
instanceLocks.put(eim.getName(), lobbyId);
|
||||
eim.setLeader(leader);
|
||||
|
||||
if(chr != null) eim.registerPlayer(chr);
|
||||
@@ -500,14 +518,17 @@ public class EventManager {
|
||||
if(!startLobbyInstance(lobbyId)) return false;
|
||||
}
|
||||
|
||||
EventInstanceManager eim = (EventInstanceManager) (iv.invokeFunction("setup", (Object) null));
|
||||
if(eim == null) {
|
||||
EventInstanceManager eim;
|
||||
try {
|
||||
eim = (EventInstanceManager) (iv.invokeFunction("setup", (Object) null));
|
||||
instanceLocks.put(eim.getName(), lobbyId);
|
||||
} catch (NullPointerException npe) {
|
||||
if(lobbyId > -1) {
|
||||
setLockLobby(lobbyId, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
instanceLocks.put(eim.getName(), lobbyId);
|
||||
|
||||
eim.setLeader(leader);
|
||||
|
||||
eim.registerParty(party, map);
|
||||
@@ -559,14 +580,17 @@ public class EventManager {
|
||||
if(!startLobbyInstance(lobbyId)) return false;
|
||||
}
|
||||
|
||||
EventInstanceManager eim = (EventInstanceManager) (iv.invokeFunction("setup", difficulty, (lobbyId > -1) ? lobbyId : party.getLeaderId()));
|
||||
if(eim == null) {
|
||||
EventInstanceManager eim;
|
||||
try {
|
||||
eim = (EventInstanceManager) (iv.invokeFunction("setup", difficulty, (lobbyId > -1) ? lobbyId : party.getLeaderId()));
|
||||
instanceLocks.put(eim.getName(), lobbyId);
|
||||
} catch (NullPointerException npe) {
|
||||
if(lobbyId > -1) {
|
||||
setLockLobby(lobbyId, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
instanceLocks.put(eim.getName(), lobbyId);
|
||||
|
||||
eim.setLeader(leader);
|
||||
|
||||
eim.registerParty(party, map);
|
||||
|
||||
@@ -100,6 +100,7 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
|
||||
|
||||
public void dispose() {
|
||||
NPCScriptManager.getInstance().dispose(this);
|
||||
getClient().announce(MaplePacketCreator.enableActions());
|
||||
}
|
||||
|
||||
public void sendNext(String text) {
|
||||
|
||||
@@ -202,6 +202,10 @@ public class MapleTrade {
|
||||
return exchangeMeso;
|
||||
}
|
||||
|
||||
private boolean fitsMeso() {
|
||||
return chr.canHoldMeso(exchangeMeso - getFee(exchangeMeso));
|
||||
}
|
||||
|
||||
private boolean fitsInInventory() {
|
||||
List<Pair<Item, MapleInventoryType>> tradeItems = new LinkedList<>();
|
||||
for (Item item : exchangeItems) {
|
||||
@@ -218,6 +222,18 @@ public class MapleTrade {
|
||||
if (partner.isLocked()) {
|
||||
local.complete1();
|
||||
partner.complete1();
|
||||
if (!local.fitsMeso()) {
|
||||
cancelTrade(c);
|
||||
c.message("There is not enough meso inventory space to complete the trade.");
|
||||
partner.getChr().message("Partner does not have enough meso inventory space to complete the trade.");
|
||||
return;
|
||||
}
|
||||
else if (!partner.fitsMeso()) {
|
||||
cancelTrade(c);
|
||||
c.message("Partner does not have enough meso inventory space to complete the trade.");
|
||||
partner.getChr().message("There is not enough meso inventory space to complete the trade.");
|
||||
return;
|
||||
}
|
||||
if (!local.fitsInInventory()) {
|
||||
cancelTrade(c);
|
||||
c.message("There is not enough inventory space to complete the trade.");
|
||||
|
||||
@@ -23,11 +23,17 @@
|
||||
package server.expeditions;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
|
||||
import net.server.PlayerStorage;
|
||||
import net.server.Server;
|
||||
import server.TimerManager;
|
||||
import server.life.MapleMonster;
|
||||
@@ -78,18 +84,18 @@ public class MapleExpedition {
|
||||
private MapleExpeditionType type;
|
||||
private boolean registering;
|
||||
private MapleMap startMap;
|
||||
private ArrayList<String> bossLogs;
|
||||
private List<String> bossLogs;
|
||||
private ScheduledFuture<?> schedule;
|
||||
private List<MapleCharacter> members = new ArrayList<>();
|
||||
private List<Integer> banned = new ArrayList<>();
|
||||
private Map<Integer, String> members = new ConcurrentHashMap<>();
|
||||
private List<Integer> banned = new CopyOnWriteArrayList<>();
|
||||
private long startTime;
|
||||
|
||||
public MapleExpedition(MapleCharacter player, MapleExpeditionType met) {
|
||||
leader = player;
|
||||
members.add(leader);
|
||||
members.put(player.getId(), player.getName());
|
||||
startMap = player.getMap();
|
||||
type = met;
|
||||
bossLogs = new ArrayList<String>();
|
||||
bossLogs = new CopyOnWriteArrayList<>();
|
||||
beginRegistration();
|
||||
}
|
||||
|
||||
@@ -109,7 +115,7 @@ public class MapleExpedition {
|
||||
@Override
|
||||
public void run() {
|
||||
if (registering){
|
||||
leader.getClient().getChannelServer().getExpeditions().remove(exped);
|
||||
startMap.getChannelServer().getExpeditions().remove(exped);
|
||||
startMap.broadcastMessage(MaplePacketCreator.serverNotice(6, "[Expedition] The time limit has been reached. Expedition has been disbanded."));
|
||||
|
||||
dispose(false);
|
||||
@@ -134,7 +140,7 @@ public class MapleExpedition {
|
||||
broadcastExped(MaplePacketCreator.removeClock());
|
||||
broadcastExped(MaplePacketCreator.serverNotice(6, "[Expedition] The expedition has started! Good luck, brave heroes!"));
|
||||
startTime = System.currentTimeMillis();
|
||||
Server.getInstance().broadcastGMMessage(leader.getWorld(), MaplePacketCreator.serverNotice(6, "[Expedition] " + type.toString() + " Expedition started with leader: " + leader.getName()));
|
||||
Server.getInstance().broadcastGMMessage(startMap.getWorld(), MaplePacketCreator.serverNotice(6, "[Expedition] " + type.toString() + " Expedition started with leader: " + leader.getName()));
|
||||
}
|
||||
|
||||
public String addMember(MapleCharacter player) {
|
||||
@@ -147,22 +153,21 @@ public class MapleExpedition {
|
||||
if (members.size() >= type.getMaxSize()){ //Would be a miracle if anybody ever saw this
|
||||
return "Sorry, this expedition is full!";
|
||||
}
|
||||
if (members.add(player)){
|
||||
player.announce(MaplePacketCreator.getClock((int)(startTime - System.currentTimeMillis()) / 1000));
|
||||
broadcastExped(MaplePacketCreator.serverNotice(6, "[Expedition] " + player.getName() + " has joined the expedition!"));
|
||||
return "You have registered for the expedition successfully!";
|
||||
}
|
||||
return "Sorry, something went really wrong. Report this on the forum with a screenshot!";
|
||||
|
||||
members.put(player.getId(), player.getName());
|
||||
player.announce(MaplePacketCreator.getClock((int)(startTime - System.currentTimeMillis()) / 1000));
|
||||
broadcastExped(MaplePacketCreator.serverNotice(6, "[Expedition] " + player.getName() + " has joined the expedition!"));
|
||||
return "You have registered for the expedition successfully!";
|
||||
}
|
||||
|
||||
private void broadcastExped(byte [] data){
|
||||
for (MapleCharacter member : members){
|
||||
member.getClient().announce(data);
|
||||
private void broadcastExped(byte[] packet){
|
||||
for (MapleCharacter chr : getActiveMembers()){
|
||||
chr.announce(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removeMember(MapleCharacter chr) {
|
||||
if(members.remove(chr)) {
|
||||
if(members.remove(chr.getId()) != null) {
|
||||
chr.announce(MaplePacketCreator.removeClock());
|
||||
broadcastExped(MaplePacketCreator.serverNotice(6, "[Expedition] " + chr.getName() + " has left the expedition."));
|
||||
chr.dropMessage(6, "[Expedition] You have left this expedition.");
|
||||
@@ -175,9 +180,42 @@ public class MapleExpedition {
|
||||
public MapleExpeditionType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public List<MapleCharacter> getMembers() {
|
||||
return members;
|
||||
|
||||
public List<MapleCharacter> getActiveMembers() { // thanks MedicOP for figuring out an issue with broadcasting packets to offline members
|
||||
PlayerStorage ps = startMap.getWorldServer().getPlayerStorage();
|
||||
|
||||
List<MapleCharacter> activeMembers = new LinkedList<>();
|
||||
for (Integer chrid : getMembers().keySet()){
|
||||
MapleCharacter chr = ps.getCharacterById(chrid);
|
||||
if (chr != null && chr.isLoggedinWorld()) {
|
||||
activeMembers.add(chr);
|
||||
}
|
||||
}
|
||||
|
||||
return activeMembers;
|
||||
}
|
||||
|
||||
public Map<Integer, String> getMembers() {
|
||||
return new HashMap<>(members);
|
||||
}
|
||||
|
||||
public List<Entry<Integer, String>> getMemberList() {
|
||||
List<Entry<Integer, String>> memberList = new LinkedList<>();
|
||||
Entry<Integer, String> leaderEntry = null;
|
||||
|
||||
for (Entry<Integer, String> e : getMembers().entrySet()) {
|
||||
if (!isLeader(e.getKey())) {
|
||||
memberList.add(e);
|
||||
} else {
|
||||
leaderEntry = e;
|
||||
}
|
||||
}
|
||||
|
||||
if (leaderEntry != null) {
|
||||
memberList.add(0, leaderEntry);
|
||||
}
|
||||
|
||||
return memberList;
|
||||
}
|
||||
|
||||
public MapleCharacter getLeader(){
|
||||
@@ -189,16 +227,15 @@ public class MapleExpedition {
|
||||
}
|
||||
|
||||
public boolean contains(MapleCharacter player) {
|
||||
for (MapleCharacter member : members){
|
||||
if (member.getId() == player.getId()){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return members.containsKey(player.getId());
|
||||
}
|
||||
|
||||
public boolean isLeader(MapleCharacter player) {
|
||||
return leader.equals(player);
|
||||
return isLeader(player.getId());
|
||||
}
|
||||
|
||||
public boolean isLeader(int playerid) {
|
||||
return leader.getId() == playerid;
|
||||
}
|
||||
|
||||
public boolean isRegistering(){
|
||||
@@ -209,15 +246,20 @@ public class MapleExpedition {
|
||||
return !registering;
|
||||
}
|
||||
|
||||
public void ban(MapleCharacter player) {
|
||||
if (!banned.contains(player.getId())) {
|
||||
banned.add(player.getId());
|
||||
members.remove(player);
|
||||
public void ban(Entry<Integer, String> chr) {
|
||||
int cid = chr.getKey();
|
||||
|
||||
if (!banned.contains(cid)) {
|
||||
banned.add(cid);
|
||||
members.remove(cid);
|
||||
|
||||
broadcastExped(MaplePacketCreator.serverNotice(6, "[Expedition] " + player.getName() + " has been banned from the expedition."));
|
||||
broadcastExped(MaplePacketCreator.serverNotice(6, "[Expedition] " + chr.getValue() + " has been banned from the expedition."));
|
||||
|
||||
player.announce(MaplePacketCreator.removeClock());
|
||||
player.dropMessage(6, "[Expedition] You have been banned from this expedition.");
|
||||
MapleCharacter player = startMap.getWorldServer().getPlayerStorage().getCharacterById(cid);
|
||||
if (player != null && player.isLoggedinWorld()) {
|
||||
player.announce(MaplePacketCreator.removeClock());
|
||||
player.dropMessage(6, "[Expedition] You have been banned from this expedition.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +267,7 @@ public class MapleExpedition {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public ArrayList<String> getBossLogs(){
|
||||
public List<String> getBossLogs(){
|
||||
return bossLogs;
|
||||
}
|
||||
|
||||
|
||||
+262
-136
@@ -55,7 +55,6 @@ import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import net.server.audit.locks.MonitoredReentrantLock;
|
||||
import net.server.Server;
|
||||
import net.server.channel.Channel;
|
||||
import net.server.world.World;
|
||||
import net.server.world.MapleParty;
|
||||
@@ -72,6 +71,7 @@ import tools.Randomizer;
|
||||
import net.server.audit.LockCollector;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
import net.server.coordinator.MapleMonsterAggroCoordinator;
|
||||
|
||||
public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
private ChangeableStats ostats = null; //unused, v83 WZs offers no support for changeable stats.
|
||||
@@ -100,6 +100,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
private MonitoredReentrantLock monsterLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MOB, true);
|
||||
private MonitoredReentrantLock statiLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MOB_STATI);
|
||||
private MonitoredReentrantLock animationLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MOB_ANI);
|
||||
private MonitoredReentrantLock aggroUpdateLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MOB_AGGRO);
|
||||
|
||||
public MapleMonster(int id, MapleMonsterStats stats) {
|
||||
super(id);
|
||||
@@ -578,31 +579,10 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
}
|
||||
|
||||
private Pair<MapleCharacter, Boolean> removeController() {
|
||||
this.lockMonster();
|
||||
try {
|
||||
MapleCharacter chrController = getController();
|
||||
boolean hadAggro = isControllerHasAggro();
|
||||
|
||||
if (chrController != null) { // this can/should only happen when a hidden gm attacks the monster
|
||||
chrController.announce(MaplePacketCreator.stopControllingMonster(this.getObjectId()));
|
||||
chrController.stopControllingMonster(this);
|
||||
}
|
||||
|
||||
setController(null);
|
||||
setControllerHasAggro(false);
|
||||
setControllerKnowsAboutAggro(false);
|
||||
|
||||
return new Pair<>(chrController, hadAggro);
|
||||
} finally {
|
||||
this.unlockMonster();
|
||||
}
|
||||
}
|
||||
|
||||
public MapleCharacter killBy(final MapleCharacter killer) {
|
||||
distributeExperience(killer != null ? killer.getId() : 0);
|
||||
|
||||
final Pair<MapleCharacter, Boolean> lastController = removeController();
|
||||
final Pair<MapleCharacter, Boolean> lastController = aggroRemoveController();
|
||||
final List<Integer> toSpawn = this.getRevives();
|
||||
if (toSpawn != null) {
|
||||
final MapleMap reviveMap = map;
|
||||
@@ -675,7 +655,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
reviveMap.killMonster(reviveMap.getMonsterById(i), killer, true);
|
||||
}
|
||||
} else if (controller != null) {
|
||||
mob.switchController(controller, aggro);
|
||||
mob.aggroSwitchController(controller, aggro);
|
||||
}
|
||||
|
||||
if(eim != null) {
|
||||
@@ -725,10 +705,12 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
|
||||
private synchronized void processMonsterKilled(boolean hasKiller) {
|
||||
if(!hasKiller) {
|
||||
if(!hasKiller) { // players won't gain EXP from a mob that has no killer, but a quest count they should
|
||||
dispatchUpdateQuestMobCount();
|
||||
}
|
||||
|
||||
this.aggroClearDamages();
|
||||
|
||||
MonsterListener[] listenersList;
|
||||
statiLock.lock();
|
||||
try {
|
||||
@@ -794,46 +776,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
public boolean isAlive() {
|
||||
return this.hp.get() > 0;
|
||||
}
|
||||
|
||||
public MapleCharacter getController() {
|
||||
monsterLock.lock();
|
||||
try {
|
||||
return controller.get();
|
||||
} finally {
|
||||
monsterLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void setController(MapleCharacter controller) {
|
||||
monsterLock.lock();
|
||||
try {
|
||||
this.controller = new WeakReference<>(controller);
|
||||
} finally {
|
||||
monsterLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void switchController(MapleCharacter newController, boolean immediateAggro) {
|
||||
this.lockMonster();
|
||||
try {
|
||||
MapleCharacter controllers = getController();
|
||||
if (controllers == newController) {
|
||||
return;
|
||||
}
|
||||
|
||||
removeController();
|
||||
|
||||
newController.controlMonster(this, immediateAggro);
|
||||
setController(newController);
|
||||
if (immediateAggro) {
|
||||
setControllerHasAggro(true);
|
||||
}
|
||||
setControllerKnowsAboutAggro(false);
|
||||
} finally {
|
||||
this.unlockMonster();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void addListener(MonsterListener listener) {
|
||||
statiLock.lock();
|
||||
try {
|
||||
@@ -843,46 +786,28 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isControllerHasAggro() {
|
||||
monsterLock.lock();
|
||||
try {
|
||||
return fake ? false : controllerHasAggro;
|
||||
} finally {
|
||||
monsterLock.unlock();
|
||||
}
|
||||
public MapleCharacter getController() {
|
||||
return controller.get();
|
||||
}
|
||||
|
||||
public void setControllerHasAggro(boolean controllerHasAggro) {
|
||||
monsterLock.lock();
|
||||
try {
|
||||
if (fake) {
|
||||
return;
|
||||
}
|
||||
this.controllerHasAggro = controllerHasAggro;
|
||||
} finally {
|
||||
monsterLock.unlock();
|
||||
}
|
||||
private void setController(MapleCharacter controller) {
|
||||
this.controller = new WeakReference<>(controller);
|
||||
}
|
||||
|
||||
public boolean isControllerHasAggro() {
|
||||
return fake ? false : controllerHasAggro;
|
||||
}
|
||||
|
||||
private void setControllerHasAggro(boolean controllerHasAggro) {
|
||||
if (!fake) this.controllerHasAggro = controllerHasAggro;
|
||||
}
|
||||
|
||||
public boolean isControllerKnowsAboutAggro() {
|
||||
monsterLock.lock();
|
||||
try {
|
||||
return fake ? false : controllerKnowsAboutAggro;
|
||||
} finally {
|
||||
monsterLock.unlock();
|
||||
}
|
||||
return fake ? false : controllerKnowsAboutAggro;
|
||||
}
|
||||
|
||||
public void setControllerKnowsAboutAggro(boolean controllerKnowsAboutAggro) {
|
||||
monsterLock.lock();
|
||||
try {
|
||||
if (fake) {
|
||||
return;
|
||||
}
|
||||
this.controllerKnowsAboutAggro = controllerKnowsAboutAggro;
|
||||
} finally {
|
||||
monsterLock.unlock();
|
||||
}
|
||||
private void setControllerKnowsAboutAggro(boolean controllerKnowsAboutAggro) {
|
||||
if (!fake) this.controllerKnowsAboutAggro = controllerKnowsAboutAggro;
|
||||
}
|
||||
|
||||
public byte[] makeBossHPBarPacket() {
|
||||
@@ -956,15 +881,29 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
}
|
||||
|
||||
private MapleCharacter getActiveController() {
|
||||
MapleCharacter chr = getController();
|
||||
|
||||
if (chr != null && chr.isLoggedinWorld() && chr.getMap() == this.getMap()) {
|
||||
return chr;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void broadcastMonsterStatusMessage(byte[] packet) {
|
||||
map.broadcastMessage(packet, getPosition());
|
||||
|
||||
MapleCharacter chrController = getActiveController();
|
||||
if (chrController != null && !chrController.isMapObjectVisible(MapleMonster.this)) {
|
||||
chrController.announce(packet);
|
||||
}
|
||||
}
|
||||
|
||||
private int broadcastStatusEffect(final MonsterStatusEffect status) {
|
||||
int animationTime = status.getSkill().getAnimationTime();
|
||||
byte[] packet = MaplePacketCreator.applyMonsterStatus(getObjectId(), status, null);
|
||||
map.broadcastMessage(packet, getPosition());
|
||||
|
||||
MapleCharacter chrController = getController();
|
||||
if (chrController != null && !chrController.isMapObjectVisible(this)) {
|
||||
chrController.getClient().announce(packet);
|
||||
}
|
||||
broadcastMonsterStatusMessage(packet);
|
||||
|
||||
return animationTime;
|
||||
}
|
||||
@@ -1041,12 +980,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
public void run() {
|
||||
if (isAlive()) {
|
||||
byte[] packet = MaplePacketCreator.cancelMonsterStatus(getObjectId(), status.getStati());
|
||||
map.broadcastMessage(packet, getPosition());
|
||||
|
||||
MapleCharacter controller = getController();
|
||||
if (controller != null && !controller.isMapObjectVisible(MapleMonster.this)) {
|
||||
controller.getClient().announce(packet);
|
||||
}
|
||||
broadcastMonsterStatusMessage(packet);
|
||||
}
|
||||
|
||||
statiLock.lock();
|
||||
@@ -1148,12 +1082,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
public void run() {
|
||||
if (isAlive()) {
|
||||
byte[] packet = MaplePacketCreator.cancelMonsterStatus(getObjectId(), stats);
|
||||
map.broadcastMessage(packet, getPosition());
|
||||
|
||||
MapleCharacter controller = getController();
|
||||
if (controller != null && !controller.isMapObjectVisible(MapleMonster.this)) {
|
||||
controller.getClient().announce(packet);
|
||||
}
|
||||
broadcastMonsterStatusMessage(packet);
|
||||
|
||||
statiLock.lock();
|
||||
try {
|
||||
@@ -1168,7 +1097,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
};
|
||||
final MonsterStatusEffect effect = new MonsterStatusEffect(stats, null, skill, true);
|
||||
byte[] packet = MaplePacketCreator.applyMonsterStatus(getObjectId(), effect, reflection);
|
||||
map.broadcastMessage(packet, getPosition());
|
||||
broadcastMonsterStatusMessage(packet);
|
||||
|
||||
statiLock.lock();
|
||||
try {
|
||||
@@ -1180,11 +1109,6 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
statiLock.unlock();
|
||||
}
|
||||
|
||||
MapleCharacter controller = getController();
|
||||
if (controller != null && !controller.isMapObjectVisible(this)) {
|
||||
controller.getClient().announce(packet);
|
||||
}
|
||||
|
||||
map.getChannelServer().registerMobStatus(map.getId(), effect, cancelTask, duration);
|
||||
}
|
||||
|
||||
@@ -1193,30 +1117,28 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
|
||||
public void resetMobPosition(Point newPoint) {
|
||||
removeController();
|
||||
aggroRemoveController();
|
||||
|
||||
setPosition(newPoint);
|
||||
map.broadcastMessage(MaplePacketCreator.moveMonster(this.getObjectId(), false, -1, 0, 0, 0, this.getPosition(), this.getIdleMovement()));
|
||||
map.moveMonster(this, this.getPosition());
|
||||
map.updateMonsterController(this);
|
||||
|
||||
aggroUpdateController();
|
||||
}
|
||||
|
||||
private void debuffMobStat(MonsterStatus stat) {
|
||||
MonsterStatusEffect oldEffect;
|
||||
statiLock.lock();
|
||||
try {
|
||||
if (isBuffed(stat)) {
|
||||
final MonsterStatusEffect oldEffect = stati.get(stat);
|
||||
byte[] packet = MaplePacketCreator.cancelMonsterStatus(getObjectId(), oldEffect.getStati());
|
||||
map.broadcastMessage(packet, getPosition());
|
||||
|
||||
MapleCharacter chrController = getController();
|
||||
if (chrController != null && !chrController.isMapObjectVisible(MapleMonster.this)) {
|
||||
chrController.getClient().announce(packet);
|
||||
}
|
||||
stati.remove(stat);
|
||||
}
|
||||
oldEffect = stati.remove(stat);
|
||||
} finally {
|
||||
statiLock.unlock();
|
||||
}
|
||||
|
||||
if (oldEffect != null) {
|
||||
byte[] packet = MaplePacketCreator.cancelMonsterStatus(getObjectId(), oldEffect.getStati());
|
||||
broadcastMonsterStatusMessage(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public void debuffMob(int skillid) {
|
||||
@@ -1280,7 +1202,11 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
public MapleMap getMap() {
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
public MapleMonsterAggroCoordinator getMapAggroCoordinator() {
|
||||
return map.getAggroCoordinator();
|
||||
}
|
||||
|
||||
public List<Pair<Integer, Integer>> getSkills() {
|
||||
return stats.getSkills();
|
||||
}
|
||||
@@ -1679,6 +1605,206 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
changeLevelByDifficulty(difficulty, pqMob);
|
||||
}
|
||||
|
||||
private MapleCharacter getNextControllerCandidate() {
|
||||
int mincontrolled = Integer.MAX_VALUE;
|
||||
MapleCharacter newController = null;
|
||||
|
||||
int mincontrolleddead = Integer.MAX_VALUE;
|
||||
MapleCharacter newControllerDead = null;
|
||||
|
||||
for (MapleCharacter chr : getMap().getAllPlayers()) {
|
||||
if (!chr.isHidden()) {
|
||||
int ctrlMonsSize = chr.getNumControlledMonsters();
|
||||
|
||||
if (chr.isAlive()) {
|
||||
if (ctrlMonsSize < mincontrolled) {
|
||||
mincontrolled = ctrlMonsSize;
|
||||
newController = chr;
|
||||
}
|
||||
} else {
|
||||
if (ctrlMonsSize < mincontrolleddead) {
|
||||
mincontrolleddead = ctrlMonsSize;
|
||||
newControllerDead = chr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newController != null ? newController : newControllerDead;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes controllability status from the current controller of this mob.
|
||||
*
|
||||
*/
|
||||
private Pair<MapleCharacter, Boolean> aggroRemoveController() {
|
||||
MapleCharacter chrController;
|
||||
boolean hadAggro;
|
||||
|
||||
aggroUpdateLock.lock();
|
||||
try {
|
||||
chrController = getActiveController();
|
||||
hadAggro = isControllerHasAggro();
|
||||
|
||||
this.setController(null);
|
||||
this.setControllerHasAggro(false);
|
||||
this.setControllerKnowsAboutAggro(false);
|
||||
} finally {
|
||||
aggroUpdateLock.unlock();
|
||||
}
|
||||
|
||||
if (chrController != null) { // this can/should only happen when a hidden gm attacks the monster
|
||||
chrController.announce(MaplePacketCreator.stopControllingMonster(this.getObjectId()));
|
||||
chrController.stopControllingMonster(this);
|
||||
}
|
||||
|
||||
return new Pair<>(chrController, hadAggro);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass over the mob controllability and updates aggro status on the new
|
||||
* player controller.
|
||||
*
|
||||
*/
|
||||
public void aggroSwitchController(MapleCharacter newController, boolean immediateAggro) {
|
||||
if (aggroUpdateLock.tryLock()) {
|
||||
try {
|
||||
MapleCharacter prevController = getController();
|
||||
if (prevController == newController) {
|
||||
return;
|
||||
}
|
||||
|
||||
aggroRemoveController();
|
||||
if (!(newController != null && newController.isLoggedinWorld() && newController.getMap() == this.getMap())) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setController(newController);
|
||||
this.setControllerHasAggro(immediateAggro);
|
||||
this.setControllerKnowsAboutAggro(false);
|
||||
} finally {
|
||||
aggroUpdateLock.unlock();
|
||||
}
|
||||
|
||||
newController.announce(MaplePacketCreator.controlMonster(this, false, immediateAggro));
|
||||
newController.controlMonster(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Automagically finds a new controller for the given monster from the chars
|
||||
* on the map it is from...
|
||||
*
|
||||
*/
|
||||
public void aggroUpdateController() {
|
||||
MapleCharacter chrController = this.getActiveController();
|
||||
if (chrController != null && chrController.isAlive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MapleCharacter newController = getNextControllerCandidate();
|
||||
if (newController == null) { // was a new controller found? (if not no one is on the map)
|
||||
return;
|
||||
}
|
||||
|
||||
this.aggroSwitchController(newController, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures controllability removal of the current player controller, and
|
||||
* fetches for any player on the map to start controlling in place.
|
||||
*
|
||||
*/
|
||||
public void aggroRedirectController() {
|
||||
this.aggroRemoveController(); // don't care if new controller not found, at least remove current controller
|
||||
this.aggroUpdateController();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current aggro status on the specified player, or null if the
|
||||
* specified player is currently not this mob's controller.
|
||||
*
|
||||
*/
|
||||
public Boolean aggroMoveLifeUpdate(MapleCharacter player) {
|
||||
MapleCharacter chrController = getController();
|
||||
if (chrController != null && player.getId() == chrController.getId()) {
|
||||
boolean aggro = this.isControllerHasAggro();
|
||||
if (aggro) {
|
||||
this.setControllerKnowsAboutAggro(true);
|
||||
}
|
||||
|
||||
return aggro;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes auto aggro for the player passed as parameter, does nothing if
|
||||
* there is already an active controller for this mob.
|
||||
*
|
||||
*/
|
||||
public void aggroAutoAggroUpdate(MapleCharacter player) {
|
||||
MapleCharacter chrController = this.getActiveController();
|
||||
|
||||
if (chrController == null) {
|
||||
this.aggroSwitchController(player, true);
|
||||
} else if (chrController.getId() == player.getId()) {
|
||||
this.setControllerHasAggro(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applied damage input for this mob, enough damage taken implies
|
||||
* an aggro target update for the attacker shortly.
|
||||
*
|
||||
*/
|
||||
public void aggroMonsterDamage(MapleCharacter attacker, int damage) {
|
||||
MapleMonsterAggroCoordinator mmac = this.getMapAggroCoordinator();
|
||||
mmac.addAggroDamage(this, attacker.getId(), damage);
|
||||
|
||||
MapleCharacter chrController = this.getController(); // aggro based on DPS rather than first-come-first-served, now live after suggestions thanks to MedicOP, Thora, Vcoc
|
||||
if (chrController != attacker) {
|
||||
if (this.getMapAggroCoordinator().isLeadingCharacterAggro(this, attacker)) {
|
||||
this.aggroSwitchController(attacker, true);
|
||||
}
|
||||
|
||||
/*
|
||||
For some reason, some mobs loses aggro on controllers if other players also attacks them.
|
||||
Maybe it was intended by Nexon to interchange controllers at every attack...
|
||||
|
||||
else if (chrController != null) {
|
||||
chrController.announce(MaplePacketCreator.stopControllingMonster(this.getObjectId()));
|
||||
chrController.announce(MaplePacketCreator.controlMonster(this, false, true));
|
||||
}
|
||||
*/
|
||||
} else {
|
||||
this.setControllerHasAggro(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all applied damage input for this mob, doesn't refresh target aggro.
|
||||
*
|
||||
*/
|
||||
public void aggroClearDamages() {
|
||||
this.getMapAggroCoordinator().removeAggroEntries(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears this mob aggro on the current controller.
|
||||
*
|
||||
*/
|
||||
public void aggroResetAggro() {
|
||||
aggroUpdateLock.lock();
|
||||
try {
|
||||
this.setControllerHasAggro(false);
|
||||
this.setControllerKnowsAboutAggro(false);
|
||||
} finally {
|
||||
aggroUpdateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public final void disposeLocks() {
|
||||
LockCollector.getInstance().registerDisposeAction(new Runnable() {
|
||||
@Override
|
||||
|
||||
@@ -52,8 +52,9 @@ public class MapleMonsterInformationProvider {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private final Map<Integer, List<MonsterDropEntry>> drops = new HashMap<>();
|
||||
private final Map<Integer, List<MonsterDropEntry>> drops = new HashMap<>();
|
||||
private final List<MonsterGlobalDropEntry> globaldrops = new ArrayList<>();
|
||||
private final Map<Integer, List<MonsterGlobalDropEntry>> continentdrops = new HashMap<>();
|
||||
|
||||
private final Map<Integer, List<Integer>> dropsChancePool = new HashMap<>(); // thanks to ronan
|
||||
private final Set<Integer> hasNoMultiEquipDrops = new HashSet<>();
|
||||
@@ -70,9 +71,24 @@ public class MapleMonsterInformationProvider {
|
||||
protected MapleMonsterInformationProvider() {
|
||||
retrieveGlobal();
|
||||
}
|
||||
|
||||
public final List<MonsterGlobalDropEntry> getGlobalDrop() {
|
||||
return globaldrops;
|
||||
|
||||
public final List<MonsterGlobalDropEntry> getRelevantGlobalDrops(int mapid) {
|
||||
int continentid = mapid / 100000000;
|
||||
|
||||
List<MonsterGlobalDropEntry> contiItems = continentdrops.get(continentid);
|
||||
if (contiItems == null) { // continent separated global drops found thanks to marcuswoon
|
||||
contiItems = new ArrayList<>();
|
||||
|
||||
for (MonsterGlobalDropEntry e : globaldrops) {
|
||||
if (e.continentid < 0 || e.continentid == continentid) {
|
||||
contiItems.add(e);
|
||||
}
|
||||
}
|
||||
|
||||
continentdrops.put(continentid, contiItems);
|
||||
}
|
||||
|
||||
return contiItems;
|
||||
}
|
||||
|
||||
private void retrieveGlobal() {
|
||||
@@ -90,8 +106,7 @@ public class MapleMonsterInformationProvider {
|
||||
new MonsterGlobalDropEntry(
|
||||
rs.getInt("itemid"),
|
||||
rs.getInt("chance"),
|
||||
rs.getInt("continent"),
|
||||
rs.getByte("dropType"),
|
||||
rs.getByte("continent"),
|
||||
rs.getInt("minimum_quantity"),
|
||||
rs.getInt("maximum_quantity"),
|
||||
rs.getShort("questid")));
|
||||
|
||||
@@ -25,15 +25,14 @@ package server.life;
|
||||
* @author LightPepsi
|
||||
*/
|
||||
public class MonsterGlobalDropEntry {
|
||||
public MonsterGlobalDropEntry(int itemId, int chance, int continent, byte dropType, int Minimum, int Maximum, short questid) {
|
||||
public MonsterGlobalDropEntry(int itemId, int chance, int continent, int Minimum, int Maximum, short questid) {
|
||||
this.itemId = itemId;
|
||||
this.chance = chance;
|
||||
this.dropType = dropType;
|
||||
this.questid = questid;
|
||||
this.continentid = continent;
|
||||
this.Minimum = Minimum;
|
||||
this.Maximum = Maximum;
|
||||
}
|
||||
public byte dropType;
|
||||
public int itemId, chance, Minimum, Maximum;
|
||||
public int itemId, chance, Minimum, Maximum, continentid;
|
||||
public short questid;
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ public class MapleHiredMerchant extends AbstractMapleMapObject {
|
||||
private List<Pair<String, Byte>> messages = new LinkedList<>();
|
||||
private List<SoldItem> sold = new LinkedList<>();
|
||||
private AtomicBoolean open = new AtomicBoolean();
|
||||
private boolean published = false;
|
||||
private MapleMap map;
|
||||
private Lock visitorLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.VISITOR_MERCH, true);
|
||||
|
||||
@@ -100,10 +101,14 @@ public class MapleHiredMerchant extends AbstractMapleMapObject {
|
||||
visitorLock.lock();
|
||||
try {
|
||||
byte count = 0;
|
||||
for (MapleCharacter visitor : visitors) {
|
||||
if (visitor != null) {
|
||||
count++;
|
||||
if (this.isOpen()) {
|
||||
for (MapleCharacter visitor : visitors) {
|
||||
if (visitor != null) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
count = (byte) (visitors.length + 1);
|
||||
}
|
||||
|
||||
return new byte[]{count, (byte) (visitors.length + 1)};
|
||||
@@ -471,15 +476,12 @@ public class MapleHiredMerchant extends AbstractMapleMapObject {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addItem(MaplePlayerShopItem item) {
|
||||
public boolean addItem(MaplePlayerShopItem item) {
|
||||
synchronized (items) {
|
||||
if (items.size() >= 16) return false;
|
||||
|
||||
items.add(item);
|
||||
}
|
||||
|
||||
try {
|
||||
this.saveItems(false);
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -521,13 +523,18 @@ public class MapleHiredMerchant extends AbstractMapleMapObject {
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
|
||||
public boolean isPublished() {
|
||||
return published;
|
||||
}
|
||||
|
||||
public boolean isOpen() {
|
||||
return open.get();
|
||||
}
|
||||
|
||||
public void setOpen(boolean set) {
|
||||
open.getAndSet(set);
|
||||
published = true;
|
||||
}
|
||||
|
||||
public int getItemId() {
|
||||
|
||||
+28
-106
@@ -62,6 +62,7 @@ import net.server.audit.locks.MonitoredReentrantReadWriteLock;
|
||||
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
import java.lang.ref.WeakReference;
|
||||
import net.server.Server;
|
||||
import net.server.coordinator.MapleMonsterAggroCoordinator;
|
||||
import net.server.channel.Channel;
|
||||
import net.server.world.World;
|
||||
import scripting.map.MapScriptManager;
|
||||
@@ -140,6 +141,7 @@ public class MapleMap {
|
||||
private int fieldType;
|
||||
private int fieldLimit = 0;
|
||||
private int mobCapacity = -1;
|
||||
private MapleMonsterAggroCoordinator aggroMonitor = null; // aggroMonitor activity in sync with itemMonitor
|
||||
private ScheduledFuture<?> mapMonitor = null;
|
||||
private ScheduledFuture<?> itemMonitor = null;
|
||||
private ScheduledFuture<?> expireItemsTask = null;
|
||||
@@ -185,6 +187,8 @@ public class MapleMap {
|
||||
final ReentrantReadWriteLock objectLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.MAP_OBJS, true);
|
||||
objectRLock = objectLock.readLock();
|
||||
objectWLock = objectLock.writeLock();
|
||||
|
||||
aggroMonitor = new MapleMonsterAggroCoordinator();
|
||||
}
|
||||
|
||||
public void setEventInstance(EventInstanceManager eim) {
|
||||
@@ -709,7 +713,7 @@ public class MapleMap {
|
||||
if(useBaseRate) chRate = 1;
|
||||
|
||||
final MapleMonsterInformationProvider mi = MapleMonsterInformationProvider.getInstance();
|
||||
final List<MonsterGlobalDropEntry> globalEntry = mi.getGlobalDrop();
|
||||
final List<MonsterGlobalDropEntry> globalEntry = mi.getRelevantGlobalDrops(this.getId());
|
||||
|
||||
final List<MonsterDropEntry> dropEntry = new ArrayList<>();
|
||||
final List<MonsterDropEntry> visibleQuestEntry = new ArrayList<>();
|
||||
@@ -777,6 +781,7 @@ public class MapleMap {
|
||||
if(itemMonitorTimeout == 0) {
|
||||
if(itemMonitor != null) {
|
||||
stopItemMonitor();
|
||||
aggroMonitor.stopAggroCoordinator();
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -1144,12 +1149,10 @@ public class MapleMap {
|
||||
}
|
||||
|
||||
public List<MapleCharacter> getAllPlayers() {
|
||||
List<MapleCharacter> character = new LinkedList<>();
|
||||
List<MapleCharacter> character;
|
||||
chrRLock.lock();
|
||||
try {
|
||||
for (MapleCharacter a : characters) {
|
||||
character.add(a);
|
||||
}
|
||||
character = new ArrayList<>(characters);
|
||||
} finally {
|
||||
chrRLock.unlock();
|
||||
}
|
||||
@@ -1316,7 +1319,7 @@ public class MapleMap {
|
||||
if (mons != null) {
|
||||
if (mons.getId() == 8800000) {
|
||||
makeMonsterReal(mons);
|
||||
updateMonsterController(mons);
|
||||
mons.aggroUpdateController();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1552,88 +1555,6 @@ public class MapleMap {
|
||||
}
|
||||
}
|
||||
|
||||
private MapleCharacter getNextControllerCandidate() {
|
||||
int mincontrolled = Integer.MAX_VALUE;
|
||||
MapleCharacter newController = null;
|
||||
|
||||
int mincontrolleddead = Integer.MAX_VALUE;
|
||||
MapleCharacter newControllerDead = null;
|
||||
|
||||
chrRLock.lock();
|
||||
try {
|
||||
for (MapleCharacter chr : characters) {
|
||||
if (!chr.isHidden()) {
|
||||
int ctrlMonsSize = chr.getControlledMonsters().size();
|
||||
|
||||
if (chr.isAlive()) {
|
||||
if (ctrlMonsSize < mincontrolled) {
|
||||
mincontrolled = ctrlMonsSize;
|
||||
newController = chr;
|
||||
}
|
||||
} else {
|
||||
if (ctrlMonsSize < mincontrolleddead) {
|
||||
mincontrolleddead = ctrlMonsSize;
|
||||
newControllerDead = chr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
chrRLock.unlock();
|
||||
}
|
||||
|
||||
return newController != null ? newController : newControllerDead;
|
||||
}
|
||||
|
||||
/**
|
||||
* Automagically finds a new controller for the given monster from the chars
|
||||
* on the map...
|
||||
*
|
||||
* @param monster
|
||||
*/
|
||||
public void updateMonsterController(MapleMonster monster) {
|
||||
monster.lockMonster();
|
||||
try {
|
||||
if (!monster.isAlive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MapleCharacter newController;
|
||||
MapleCharacter chrController = monster.getController();
|
||||
if (chrController != null) {
|
||||
if (chrController.getMap() != this) {
|
||||
chrController.stopControllingMonster(monster);
|
||||
newController = getNextControllerCandidate();
|
||||
} else {
|
||||
if (chrController.isAlive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
newController = getNextControllerCandidate();
|
||||
if (newController == null || !newController.isAlive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
chrController.stopControllingMonster(monster);
|
||||
}
|
||||
} else {
|
||||
newController = getNextControllerCandidate();
|
||||
}
|
||||
|
||||
if (newController != null) { // was a new controller found? (if not no one is on the map)
|
||||
if (monster.isFirstAttack()) {
|
||||
newController.controlMonster(monster, true);
|
||||
monster.setControllerHasAggro(true);
|
||||
monster.setControllerKnowsAboutAggro(true);
|
||||
} else {
|
||||
newController.controlMonster(monster, false);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
monster.unlockMonster();
|
||||
}
|
||||
}
|
||||
|
||||
private Map<Integer, MapleMapObject> getCopyMapObjects() {
|
||||
objectRLock.lock();
|
||||
try {
|
||||
@@ -1862,7 +1783,7 @@ public class MapleMap {
|
||||
}
|
||||
});
|
||||
|
||||
updateMonsterController(monster);
|
||||
monster.aggroUpdateController();
|
||||
|
||||
if (monster.hasBossHPBar()) {
|
||||
broadcastBossHpMessage(monster, monster.hashCode(), monster.makeBossHPBarPacket(), monster.getPosition());
|
||||
@@ -1948,7 +1869,7 @@ public class MapleMap {
|
||||
}
|
||||
}, null);
|
||||
|
||||
updateMonsterController(monster);
|
||||
monster.aggroUpdateController();
|
||||
|
||||
if (monster.hasBossHPBar()) {
|
||||
broadcastBossHpMessage(monster, monster.hashCode(), monster.makeBossHPBarPacket(), monster.getPosition());
|
||||
@@ -1993,7 +1914,8 @@ public class MapleMap {
|
||||
c.announce(MaplePacketCreator.spawnMonster(monster, true, effect));
|
||||
}
|
||||
});
|
||||
updateMonsterController(monster);
|
||||
|
||||
monster.aggroUpdateController();
|
||||
|
||||
if (monster.hasBossHPBar()) {
|
||||
broadcastBossHpMessage(monster, monster.hashCode(), monster.makeBossHPBarPacket(), monster.getPosition());
|
||||
@@ -2019,7 +1941,8 @@ public class MapleMap {
|
||||
public void makeMonsterReal(final MapleMonster monster) {
|
||||
monster.setFake(false);
|
||||
broadcastMessage(MaplePacketCreator.makeMonsterReal(monster));
|
||||
updateMonsterController(monster);
|
||||
|
||||
monster.aggroUpdateController();
|
||||
}
|
||||
|
||||
public void spawnReactor(final MapleReactor reactor) {
|
||||
@@ -2403,7 +2326,10 @@ public class MapleMap {
|
||||
chr.setMapId(mapid);
|
||||
|
||||
if (chrSize == 1) {
|
||||
if(!hasItemMonitor()) startItemMonitor();
|
||||
if(!hasItemMonitor()) {
|
||||
startItemMonitor();
|
||||
aggroMonitor.startAggroCoordinator();
|
||||
}
|
||||
|
||||
if (onFirstUserEnter.length() != 0 && !chr.hasEntered(onFirstUserEnter, mapid) && MapScriptManager.getInstance().scriptExists(onFirstUserEnter, true)) {
|
||||
chr.enteredScript(onFirstUserEnter, mapid);
|
||||
@@ -2688,19 +2614,8 @@ public class MapleMap {
|
||||
broadcastGMMessage(MaplePacketCreator.removePlayerFromMap(chr.getId()));
|
||||
}
|
||||
|
||||
for (MapleMonster monster : chr.getControlledMonsters()) {
|
||||
monster.lockMonster();
|
||||
try {
|
||||
monster.setController(null);
|
||||
monster.setControllerHasAggro(false);
|
||||
monster.setControllerKnowsAboutAggro(false);
|
||||
updateMonsterController(monster);
|
||||
} finally {
|
||||
monster.unlockMonster();
|
||||
}
|
||||
}
|
||||
|
||||
chr.leaveMap();
|
||||
|
||||
for (MapleSummon summon : new ArrayList<>(chr.getSummonsValues())) {
|
||||
if (summon.isStationary()) {
|
||||
chr.cancelEffectFromBuffStat(MapleBuffStat.PUPPET);
|
||||
@@ -2926,7 +2841,7 @@ public class MapleMap {
|
||||
if (isNonRangedType(o.getType())) {
|
||||
o.sendSpawnData(mapleClient);
|
||||
} else if (o.getType() == MapleMapObjectType.MONSTER) {
|
||||
updateMonsterController((MapleMonster) o);
|
||||
((MapleMonster) o).aggroUpdateController();
|
||||
} else if (o.getType() == MapleMapObjectType.SUMMON) {
|
||||
MapleSummon summon = (MapleSummon) o;
|
||||
if (summon.getOwner() == chr) {
|
||||
@@ -3038,6 +2953,10 @@ public class MapleMap {
|
||||
mapArea.setBounds(vrLeft, vrTop, vrRight - vrLeft, vrBottom - vrTop);
|
||||
}
|
||||
|
||||
public MapleMonsterAggroCoordinator getAggroCoordinator() {
|
||||
return aggroMonitor;
|
||||
}
|
||||
|
||||
/**
|
||||
* it's threadsafe, gtfo :D
|
||||
*
|
||||
@@ -4091,6 +4010,9 @@ public class MapleMap {
|
||||
|
||||
chrWLock.lock();
|
||||
try {
|
||||
aggroMonitor.dispose();
|
||||
aggroMonitor = null;
|
||||
|
||||
if(itemMonitor != null) {
|
||||
itemMonitor.cancel(false);
|
||||
itemMonitor = null;
|
||||
|
||||
@@ -103,11 +103,15 @@ public class MaplePlayerShop extends AbstractMapleMapObject {
|
||||
visitorLock.lock();
|
||||
try {
|
||||
byte count = 0;
|
||||
for (MapleCharacter visitor : visitors) {
|
||||
if (visitor != null) {
|
||||
count++;
|
||||
//if (this.isOpen()) {
|
||||
for (MapleCharacter visitor : visitors) {
|
||||
if (visitor != null) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
//} else { shouldn't happen since there isn't a "closed" state for player shops.
|
||||
// count = (byte) (visitors.length + 1);
|
||||
//}
|
||||
|
||||
return new byte[]{count, (byte) visitors.length};
|
||||
} finally {
|
||||
@@ -198,9 +202,12 @@ public class MaplePlayerShop extends AbstractMapleMapObject {
|
||||
}
|
||||
}
|
||||
|
||||
public void addItem(MaplePlayerShopItem item) {
|
||||
public boolean addItem(MaplePlayerShopItem item) {
|
||||
synchronized (items) {
|
||||
if (items.size() >= 16) return false;
|
||||
|
||||
items.add(item);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,7 +248,7 @@ public class MaplePlayerShop extends AbstractMapleMapObject {
|
||||
* @param item
|
||||
* @param quantity
|
||||
*/
|
||||
public void buy(MapleClient c, int item, short quantity) {
|
||||
public boolean buy(MapleClient c, int item, short quantity) {
|
||||
synchronized (items) {
|
||||
if (isVisitor(c.getPlayer())) {
|
||||
MaplePlayerShopItem pItem = items.get(item);
|
||||
@@ -250,10 +257,10 @@ public class MaplePlayerShop extends AbstractMapleMapObject {
|
||||
newItem.setQuantity((short) ((pItem.getItem().getQuantity() * quantity)));
|
||||
if (quantity < 1 || !pItem.isExist() || pItem.getBundles() < quantity) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
return false;
|
||||
} else if (newItem.getInventoryType().equals(MapleInventoryType.EQUIP) && newItem.getQuantity() > 1) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
MapleKarmaManipulator.toggleKarmaFlagToUntradeable(newItem);
|
||||
@@ -264,6 +271,12 @@ public class MaplePlayerShop extends AbstractMapleMapObject {
|
||||
|
||||
if (c.getPlayer().getMeso() >= price) {
|
||||
if (canBuy(c, newItem)) {
|
||||
if (!owner.canHoldMeso(price)) {
|
||||
owner.dropMessage(1, "Transaction failed since the shop owner can't hold any more mesos.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
|
||||
c.getPlayer().gainMeso(-price, false);
|
||||
owner.gainMeso(price, true);
|
||||
|
||||
@@ -286,13 +299,21 @@ public class MaplePlayerShop extends AbstractMapleMapObject {
|
||||
}
|
||||
} else {
|
||||
c.getPlayer().dropMessage(1, "Your inventory is full. Please clean a slot before buying this item.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
c.getPlayer().dropMessage(1, "You don't have enough mesos to purchase this item.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} finally {
|
||||
visitorLock.unlock();
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -418,9 +439,9 @@ public class MaplePlayerShop extends AbstractMapleMapObject {
|
||||
}
|
||||
|
||||
public void closeShop() {
|
||||
owner.getMap().broadcastMessage(MaplePacketCreator.removePlayerShopBox(this));
|
||||
clearChatLog();
|
||||
removeVisitors();
|
||||
owner.getMap().broadcastMessage(MaplePacketCreator.removePlayerShopBox(this));
|
||||
}
|
||||
|
||||
public void sendShop(MapleClient c) {
|
||||
|
||||
@@ -26,7 +26,7 @@ import tools.data.output.LittleEndianWriter;
|
||||
|
||||
public class AbsoluteLifeMovement extends AbstractLifeMovement {
|
||||
private Point pixelsPerSecond;
|
||||
private int unk;
|
||||
private int fh;
|
||||
|
||||
public AbsoluteLifeMovement(int type, Point position, int duration, int newstate) {
|
||||
super(type, position, duration, newstate);
|
||||
@@ -40,12 +40,12 @@ public class AbsoluteLifeMovement extends AbstractLifeMovement {
|
||||
this.pixelsPerSecond = wobble;
|
||||
}
|
||||
|
||||
public int getUnk() {
|
||||
return unk;
|
||||
public int getFh() { // unk -> fh, thanks Spoon for pointing this out
|
||||
return fh;
|
||||
}
|
||||
|
||||
public void setUnk(int unk) {
|
||||
this.unk = unk;
|
||||
public void setFh(int fh) {
|
||||
this.fh = fh;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -55,7 +55,7 @@ public class AbsoluteLifeMovement extends AbstractLifeMovement {
|
||||
lew.writeShort(getPosition().y);
|
||||
lew.writeShort(pixelsPerSecond.x);
|
||||
lew.writeShort(pixelsPerSecond.y);
|
||||
lew.writeShort(unk);
|
||||
lew.writeShort(fh);
|
||||
lew.write(getNewstate());
|
||||
lew.writeShort(getDuration());
|
||||
}
|
||||
|
||||
@@ -25,18 +25,18 @@ import java.awt.Point;
|
||||
import tools.data.output.LittleEndianWriter;
|
||||
|
||||
public class ChairMovement extends AbstractLifeMovement {
|
||||
private int unk;
|
||||
private int fh;
|
||||
|
||||
public ChairMovement(int type, Point position, int duration, int newstate) {
|
||||
super(type, position, duration, newstate);
|
||||
}
|
||||
|
||||
public int getUnk() {
|
||||
return unk;
|
||||
public int getFh() {
|
||||
return fh;
|
||||
}
|
||||
|
||||
public void setUnk(int unk) {
|
||||
this.unk = unk;
|
||||
public void setFh(int fh) {
|
||||
this.fh = fh;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,7 +44,7 @@ public class ChairMovement extends AbstractLifeMovement {
|
||||
lew.write(getType());
|
||||
lew.writeShort(getPosition().x);
|
||||
lew.writeShort(getPosition().y);
|
||||
lew.writeShort(unk);
|
||||
lew.writeShort(fh);
|
||||
lew.write(getNewstate());
|
||||
lew.writeShort(getDuration());
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@ import tools.data.output.LittleEndianWriter;
|
||||
|
||||
public class JumpDownMovement extends AbstractLifeMovement {
|
||||
private Point pixelsPerSecond;
|
||||
private int unk;
|
||||
private int fh;
|
||||
private int originFh;
|
||||
|
||||
public JumpDownMovement(int type, Point position, int duration, int newstate) {
|
||||
super(type, position, duration, newstate);
|
||||
@@ -41,22 +41,22 @@ public class JumpDownMovement extends AbstractLifeMovement {
|
||||
this.pixelsPerSecond = wobble;
|
||||
}
|
||||
|
||||
public int getUnk() {
|
||||
return unk;
|
||||
}
|
||||
|
||||
public void setUnk(int unk) {
|
||||
this.unk = unk;
|
||||
}
|
||||
|
||||
public int getFH() {
|
||||
public int getFh() {
|
||||
return fh;
|
||||
}
|
||||
|
||||
public void setFH(int fh) {
|
||||
public void setFh(int fh) {
|
||||
this.fh = fh;
|
||||
}
|
||||
|
||||
public int getOriginFh() {
|
||||
return originFh;
|
||||
}
|
||||
|
||||
public void setOriginFh(int fh) { // fh actually originFh, thanks Spoon for pointing this out
|
||||
this.originFh = fh;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(LittleEndianWriter lew) {
|
||||
lew.write(getType());
|
||||
@@ -64,8 +64,8 @@ public class JumpDownMovement extends AbstractLifeMovement {
|
||||
lew.writeShort(getPosition().y);
|
||||
lew.writeShort(pixelsPerSecond.x);
|
||||
lew.writeShort(pixelsPerSecond.y);
|
||||
lew.writeShort(unk);
|
||||
lew.writeShort(fh);
|
||||
lew.writeShort(originFh);
|
||||
lew.write(getNewstate());
|
||||
lew.writeShort(getDuration());
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ package server.quest.actions;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import constants.ServerConstants;
|
||||
import net.server.world.World;
|
||||
import provider.MapleData;
|
||||
import provider.MapleDataTool;
|
||||
import server.quest.MapleQuest;
|
||||
@@ -53,15 +52,10 @@ public class ExpAction extends MapleQuestAction {
|
||||
}
|
||||
|
||||
public static void runAction(MapleCharacter chr, int gain) {
|
||||
if (chr.isBeginnerJob() && chr.getLevel() < 10) {
|
||||
chr.gainExp(gain, true, true);
|
||||
} else {
|
||||
if(!ServerConstants.USE_QUEST_RATE) {
|
||||
chr.gainExp(gain * chr.getExpRate(), true, true);
|
||||
} else {
|
||||
World w = chr.getClient().getWorldServer();
|
||||
chr.gainExp(gain * w.getExpRate() * w.getQuestRate(), true, true);
|
||||
}
|
||||
}
|
||||
if (!ServerConstants.USE_QUEST_RATE) {
|
||||
chr.gainExp(gain * chr.getExpRate(), true, true);
|
||||
} else {
|
||||
chr.gainExp(gain * chr.getQuestExpRate(), true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ package server.quest.actions;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import constants.ServerConstants;
|
||||
import net.server.world.World;
|
||||
import provider.MapleData;
|
||||
import provider.MapleDataTool;
|
||||
import server.quest.MapleQuest;
|
||||
@@ -54,14 +53,13 @@ public class MesoAction extends MapleQuestAction {
|
||||
}
|
||||
|
||||
public static void runAction(MapleCharacter chr, int gain) {
|
||||
if(gain < 0) {
|
||||
if (gain < 0) {
|
||||
chr.gainMeso(gain, true, false, true);
|
||||
} else {
|
||||
if(!ServerConstants.USE_QUEST_RATE) {
|
||||
if (!ServerConstants.USE_QUEST_RATE) {
|
||||
chr.gainMeso(gain * chr.getMesoRate(), true, false, true);
|
||||
} else {
|
||||
World w = chr.getClient().getWorldServer();
|
||||
chr.gainMeso(gain * w.getMesoRate() * w.getQuestRate(), true, false, true);
|
||||
chr.gainMeso(gain * chr.getQuestMesoRate(), true, false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +40,8 @@ public class LogHelper {
|
||||
String log = expedition.getType().toString() + " EXPEDITION\r\n";
|
||||
log += getTimeString(expedition.getStartTime()) + "\r\n";
|
||||
|
||||
for (MapleCharacter member : expedition.getMembers()){
|
||||
log += ">>" + member.getName() + "\r\n";
|
||||
for (String memberName : expedition.getMembers().values()){
|
||||
log += ">>" + memberName + "\r\n";
|
||||
}
|
||||
log += "BOSS KILLS\r\n";
|
||||
for (String message: expedition.getBossLogs()){
|
||||
|
||||
@@ -1841,6 +1841,30 @@ public class MaplePacketCreator {
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
/**
|
||||
* Guild Name & Mark update packet, thanks to Arnah (Vertisy)
|
||||
*
|
||||
* @param guildName The Guild name, blank for nothing.
|
||||
*/
|
||||
public static byte[] guildNameChanged(int chrid, String guildName){
|
||||
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.GUILD_NAME_CHANGED.getValue());
|
||||
mplew.writeInt(chrid);
|
||||
mplew.writeMapleAsciiString(guildName);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] guildMarkChanged(int chrid, MapleGuild guild){
|
||||
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.GUILD_MARK_CHANGED.getValue());
|
||||
mplew.writeInt(chrid);
|
||||
mplew.writeShort(guild.getLogoBG());
|
||||
mplew.write(guild.getLogoBGColor());
|
||||
mplew.writeShort(guild.getLogo());
|
||||
mplew.write(guild.getLogoColor());
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a packet spawning a player as a mapobject to other clients.
|
||||
*
|
||||
@@ -3384,7 +3408,7 @@ public class MaplePacketCreator {
|
||||
public static byte[] showBuffeffect(int cid, int skillid, int effectid) {
|
||||
return showBuffeffect(cid, skillid, effectid, (byte) 3);
|
||||
}
|
||||
|
||||
|
||||
public static byte[] showBuffeffect(int cid, int skillid, int effectid, byte direction) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.SHOW_FOREIGN_EFFECT.getValue());
|
||||
@@ -3396,6 +3420,19 @@ public class MaplePacketCreator {
|
||||
mplew.writeLong(0);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] showBuffeffect(int cid, int skillid, int skilllv, int effectid, byte direction) { // updated packet structure found thanks to Rien dev team
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.SHOW_FOREIGN_EFFECT.getValue());
|
||||
mplew.writeInt(cid);
|
||||
mplew.write(effectid);
|
||||
mplew.writeInt(skillid);
|
||||
mplew.write(0);
|
||||
mplew.write(skilllv);
|
||||
mplew.write(direction);
|
||||
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] showOwnBuffEffect(int skillid, int effectid) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
@@ -4597,13 +4634,21 @@ public class MaplePacketCreator {
|
||||
mplew.writeInt(skillId);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] showMagnet(int mobid, byte success) { // Monster Magnet
|
||||
|
||||
public static byte[] catchMonster(int mobOid, byte success) { // updated packet structure found thanks to Rien dev team
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.SHOW_MAGNET.getValue());
|
||||
mplew.writeInt(mobid);
|
||||
mplew.writeShort(SendOpcode.CATCH_MONSTER.getValue());
|
||||
mplew.writeInt(mobOid);
|
||||
mplew.write(success);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] catchMonster(int mobOid, int itemid, byte success) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.CATCH_MONSTER_WITH_ITEM.getValue());
|
||||
mplew.writeInt(mobOid);
|
||||
mplew.writeInt(itemid);
|
||||
mplew.write(success);
|
||||
mplew.skip(10); //Mmmk
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
@@ -4932,15 +4977,6 @@ public class MaplePacketCreator {
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] catchMonster(int monsobid, int itemid, byte success) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.CATCH_MONSTER.getValue());
|
||||
mplew.writeInt(monsobid);
|
||||
mplew.writeInt(itemid);
|
||||
mplew.write(success);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] catchMessage(int message) { // not done, I guess
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.BRIDLE_MOB_CATCH_FAIL.getValue());
|
||||
@@ -5599,7 +5635,7 @@ public class MaplePacketCreator {
|
||||
}
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
|
||||
public static byte[] hiredMerchantOwnerLeave() {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue());
|
||||
@@ -5607,6 +5643,14 @@ public class MaplePacketCreator {
|
||||
mplew.write(0);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] hiredMerchantOwnerMaintenanceLeave() {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue());
|
||||
mplew.write(PlayerInteractionHandler.Action.REAL_CLOSE_MERCHANT.getCode());
|
||||
mplew.write(5);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] hiredMerchantMaintenanceMessage() {
|
||||
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(5);
|
||||
|
||||
Reference in New Issue
Block a user