Merge branch 'master' into credits_update
This commit is contained in:
@@ -263,7 +263,8 @@ public final class PacketProcessor {
|
||||
registerHandler(RecvOpcode.WATER_OF_LIFE, new UseWaterOfLifeHandler());
|
||||
registerHandler(RecvOpcode.ADMIN_CHAT, new AdminChatHandler());
|
||||
registerHandler(RecvOpcode.MOVE_DRAGON, new MoveDragonHandler());
|
||||
registerHandler(RecvOpcode.USE_ITEMUI, new UseItemCanvasHandler());
|
||||
registerHandler(RecvOpcode.OPEN_ITEMUI, new RaiseUIStateHandler());
|
||||
registerHandler(RecvOpcode.USE_ITEMUI, new RaiseIncExpHandler());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -495,15 +495,12 @@ public class Server {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void resetServerWorlds() {
|
||||
private void resetServerWorlds() { // thanks maple006 for noticing proprietary lists assigned to null
|
||||
wldWLock.lock();
|
||||
try {
|
||||
worlds.clear();
|
||||
worlds = null;
|
||||
channels.clear();
|
||||
channels = null;
|
||||
worldRecommendedList.clear();
|
||||
worldRecommendedList = null;
|
||||
} finally {
|
||||
wldWLock.unlock();
|
||||
}
|
||||
|
||||
@@ -239,7 +239,10 @@ public class ThreadTracker {
|
||||
}
|
||||
else {
|
||||
AtomicInteger c = lockCount.get(lockOid);
|
||||
c.decrementAndGet();
|
||||
if (c != null) { // thanks BHB for detecting an NPE here
|
||||
c.decrementAndGet();
|
||||
}
|
||||
|
||||
lockUpdate.put(lockOid, 0);
|
||||
|
||||
List<MonitoredLockType> list = threadTracker.get(tid);
|
||||
|
||||
@@ -26,6 +26,7 @@ package net.server.audit.locks;
|
||||
|
||||
public enum MonitoredLockType {
|
||||
UNDEFINED,
|
||||
INTERVAL,
|
||||
CHARACTER_CHR,
|
||||
CHARACTER_CPN,
|
||||
CHARACTER_EFF,
|
||||
@@ -96,7 +97,7 @@ public enum MonitoredLockType {
|
||||
VISITOR_MERCH,
|
||||
MAP_CHRS,
|
||||
MAP_OBJS,
|
||||
MAP_FACTORY,
|
||||
MAP_MANAGER,
|
||||
MAP_ITEM,
|
||||
MAP_LOOT,
|
||||
MAP_BOUNDS,
|
||||
|
||||
@@ -26,7 +26,6 @@ import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
@@ -64,7 +63,6 @@ import org.apache.mina.filter.codec.ProtocolCodecFilter;
|
||||
import org.apache.mina.transport.socket.SocketSessionConfig;
|
||||
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
|
||||
|
||||
import provider.MapleDataProviderFactory;
|
||||
import scripting.event.EventScriptManager;
|
||||
import server.TimerManager;
|
||||
import server.events.gm.MapleEvent;
|
||||
@@ -72,7 +70,7 @@ import server.expeditions.MapleExpedition;
|
||||
import server.expeditions.MapleExpeditionType;
|
||||
import server.maps.MapleHiredMerchant;
|
||||
import server.maps.MapleMap;
|
||||
import server.maps.MapleMapFactory;
|
||||
import server.maps.MapleMapManager;
|
||||
import server.maps.MapleMiniDungeon;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Pair;
|
||||
@@ -88,7 +86,7 @@ public final class Channel {
|
||||
private int world, channel;
|
||||
private IoAcceptor acceptor;
|
||||
private String ip, serverMessage;
|
||||
private MapleMapFactory mapFactory;
|
||||
private MapleMapManager mapManager;
|
||||
private EventScriptManager eventSM;
|
||||
private MobStatusScheduler mobStatusSchedulers[] = new MobStatusScheduler[ServerConstants.CHANNEL_LOCKS];
|
||||
private MobAnimationScheduler mobAnimationSchedulers[] = new MobAnimationScheduler[ServerConstants.CHANNEL_LOCKS];
|
||||
@@ -108,8 +106,6 @@ public final class Channel {
|
||||
private int usedDojo = 0;
|
||||
private Set<Integer> usedMC = new HashSet<>();
|
||||
|
||||
private ScheduledFuture<?> respawnTask;
|
||||
|
||||
private int[] dojoStage;
|
||||
private long[] dojoFinishTime;
|
||||
private ScheduledFuture<?>[] dojoTask;
|
||||
@@ -142,7 +138,7 @@ public final class Channel {
|
||||
this.channel = channel;
|
||||
|
||||
this.ongoingStartTime = startTime + 10000; // rude approach to a world's last channel boot time, placeholder for the 1st wedding reservation ever
|
||||
this.mapFactory = new MapleMapFactory(null, MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Map.wz")), MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/String.wz")), world, channel);
|
||||
this.mapManager = new MapleMapManager(null, world, channel);
|
||||
try {
|
||||
eventSM = new EventScriptManager(this, getEvents());
|
||||
port = 7575 + this.channel - 1;
|
||||
@@ -151,7 +147,6 @@ public final class Channel {
|
||||
IoBuffer.setUseDirectBuffer(false);
|
||||
IoBuffer.setAllocator(new SimpleBufferAllocator());
|
||||
acceptor = new NioSocketAcceptor();
|
||||
respawnTask = TimerManager.getInstance().register(new respawnMaps(), ServerConstants.RESPAWN_INTERVAL);
|
||||
acceptor.setHandler(new MapleServerHandler(world, channel));
|
||||
acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30);
|
||||
acceptor.getFilterChain().addLast("codec", (IoFilter) new ProtocolCodecFilter(new MapleCodecFactory()));
|
||||
@@ -204,13 +199,8 @@ public final class Channel {
|
||||
disconnectAwayPlayers();
|
||||
players.disconnectAll();
|
||||
|
||||
if(respawnTask != null) {
|
||||
respawnTask.cancel(false);
|
||||
respawnTask = null;
|
||||
}
|
||||
|
||||
mapFactory.dispose();
|
||||
mapFactory = null;
|
||||
mapManager.dispose();
|
||||
mapManager = null;
|
||||
|
||||
eventSM.cancel();
|
||||
eventSM = null;
|
||||
@@ -315,8 +305,8 @@ public final class Channel {
|
||||
}
|
||||
}
|
||||
|
||||
public MapleMapFactory getMapFactory() {
|
||||
return mapFactory;
|
||||
public MapleMapManager getMapFactory() {
|
||||
return mapManager;
|
||||
}
|
||||
|
||||
public int getWorld() {
|
||||
@@ -417,16 +407,6 @@ public final class Channel {
|
||||
}
|
||||
}
|
||||
|
||||
public class respawnMaps implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (MapleMap map : mapFactory.getMaps().values()) {
|
||||
map.respawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Map<Integer, MapleHiredMerchant> getHiredMerchants() {
|
||||
merchRlock.lock();
|
||||
try {
|
||||
|
||||
@@ -45,12 +45,12 @@ public final class DueyHandler extends AbstractMaplePacketHandler {
|
||||
short amount = slea.readShort();
|
||||
int mesos = slea.readInt();
|
||||
String recipient = slea.readMapleAsciiString();
|
||||
|
||||
DueyProcessor.dueySendItem(c, inventId, itemPos, amount, mesos, recipient);
|
||||
String message = slea.readByte() != 0 ? slea.readMapleAsciiString() : "";
|
||||
DueyProcessor.dueySendItem(c, inventId, itemPos, amount, mesos, message, recipient);
|
||||
} else if (operation == DueyProcessor.Actions.TOSERVER_REMOVE_PACKAGE.getCode()) {
|
||||
int packageid = slea.readInt();
|
||||
|
||||
DueyProcessor.dueyRemovePackage(c, packageid);
|
||||
DueyProcessor.dueyRemovePackage(c, packageid, true);
|
||||
} else if (operation == DueyProcessor.Actions.TOSERVER_CLAIM_PACKAGE.getCode()) {
|
||||
int packageid = slea.readInt();
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
ANSWER_TIE(0x33),
|
||||
GIVE_UP(0x34),
|
||||
EXIT_AFTER_GAME(0x38),
|
||||
CANCEL_EXIT(0x39),
|
||||
CANCEL_EXIT_AFTER_GAME(0x39),
|
||||
READY(0x3A),
|
||||
UN_READY(0x3B),
|
||||
EXPEL(0x3C),
|
||||
@@ -344,7 +344,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
MapleTrade.cancelTrade(chr, MapleTrade.TradeResult.PARTNER_CANCEL);
|
||||
} else {
|
||||
chr.closePlayerShop();
|
||||
chr.closeMiniGame();
|
||||
chr.closeMiniGame(false);
|
||||
chr.closeHiredMerchant(true);
|
||||
}
|
||||
} else if (mode == Action.OPEN_STORE.getCode() || mode == Action.OPEN_CASH.getCode()) {
|
||||
@@ -495,13 +495,14 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
if(ServerConstants.USE_ENFORCE_UNMERCHABLE_CASH && ii.isCash(item.getItemId())) {
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "Cash items are not allowed to be traded."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (ServerConstants.USE_ENFORCE_UNMERCHABLE_PET && ItemConstants.isPet(item.getItemId())) {
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "Pets are not allowed to be traded."));
|
||||
if (ii.isUnmerchable(item.getItemId())) {
|
||||
if (ItemConstants.isPet(item.getItemId())) {
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "Pets are not allowed to be traded."));
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "Cash items are not allowed to be traded."));
|
||||
}
|
||||
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -567,6 +568,15 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
if (ivItem == null || ivItem.isUntradeable()) {
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "Could not perform shop operation with that item."));
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
} else if (MapleItemInformationProvider.getInstance().isUnmerchable(ivItem.getItemId())) {
|
||||
if (ItemConstants.isPet(ivItem.getItemId())) {
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "Pets are not allowed to be sold on the Player Store."));
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "Cash items are not allowed to be sold on the Player Store."));
|
||||
}
|
||||
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
@@ -588,17 +598,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
FilePrinter.printError(FilePrinter.EXPLOITS + chr.getName() + ".txt", chr.getName() + " might of possibly packet edited Hired Merchants\nperBundle: " + perBundle + "\nperBundle * bundles (This multiplied cannot be greater than 2000): " + perBundle * bundles + "\nbundles: " + bundles + "\nprice: " + price);
|
||||
return;
|
||||
}
|
||||
|
||||
if(ServerConstants.USE_ENFORCE_UNMERCHABLE_CASH && MapleItemInformationProvider.getInstance().isCash(ivItem.getItemId())) {
|
||||
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;
|
||||
}
|
||||
|
||||
Item sellItem = ivItem.copy();
|
||||
if(!ItemConstants.isRechargeable(ivItem.getItemId())) {
|
||||
sellItem.setQuantity(perBundle);
|
||||
@@ -608,8 +608,6 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
MaplePlayerShop shop = chr.getPlayerShop();
|
||||
MapleHiredMerchant merchant = chr.getHiredMerchant();
|
||||
if (shop != null && shop.isOwner(chr)) {
|
||||
System.out.println(shopItem.getItem().getPet() + " " + shopItem.getItem().getPetId());
|
||||
System.out.println(ivItem.getPet() + " " + ivItem.getPetId());
|
||||
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;
|
||||
@@ -770,10 +768,20 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
MapleCharacter visitor = miniGame.getVisitor();
|
||||
|
||||
if(visitor != null) {
|
||||
visitor.closeMiniGame();
|
||||
visitor.announce(MaplePacketCreator.getMiniGameClose(5));
|
||||
visitor.closeMiniGame(false);
|
||||
visitor.announce(MaplePacketCreator.getMiniGameClose(true, 5));
|
||||
}
|
||||
}
|
||||
} else if (mode == Action.EXIT_AFTER_GAME.getCode()) {
|
||||
MapleMiniGame miniGame = chr.getMiniGame();
|
||||
if(miniGame != null) {
|
||||
miniGame.setQuitAfterGame(chr, true);
|
||||
}
|
||||
} else if (mode == Action.CANCEL_EXIT_AFTER_GAME.getCode()) {
|
||||
MapleMiniGame miniGame = chr.getMiniGame();
|
||||
if(miniGame != null) {
|
||||
miniGame.setQuitAfterGame(chr, false);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
c.releaseClient();
|
||||
|
||||
59
src/net/server/channel/handlers/RaiseIncExpHandler.java
Normal file
59
src/net/server/channel/handlers/RaiseIncExpHandler.java
Normal file
@@ -0,0 +1,59 @@
|
||||
package net.server.channel.handlers;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import client.MapleClient;
|
||||
import client.inventory.MapleInventory;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import server.MapleItemInformationProvider;
|
||||
import server.MapleItemInformationProvider.QuestConsItem;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Xari
|
||||
* @author Ronan - added concurrency protection and quest progress limit
|
||||
*/
|
||||
public class RaiseIncExpHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
byte inventorytype = slea.readByte();//nItemIT
|
||||
short slot = slea.readShort();//nSlotPosition
|
||||
int itemid = slea.readInt();//nItemID
|
||||
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
QuestConsItem consItem = ii.getQuestConsumablesInfo(itemid);
|
||||
if (consItem == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int questid = consItem.questid;
|
||||
Map<Integer, Integer> consumables = consItem.items;
|
||||
|
||||
int consId;
|
||||
MapleInventory inv = c.getPlayer().getInventory(MapleInventoryType.getByType(inventorytype));
|
||||
inv.lockInventory();
|
||||
try {
|
||||
consId = inv.getItem(slot).getItemId();
|
||||
if (!consumables.containsKey(consId) || !c.getPlayer().haveItem(consId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.getByType(inventorytype), (short) slot, (short) 1, false, true);
|
||||
} finally {
|
||||
inv.unlockInventory();
|
||||
}
|
||||
|
||||
int nextValue = Math.min(consumables.get(consId) + Integer.parseInt(c.getPlayer().getQuestInfo(questid)), consItem.exp * consItem.grade);
|
||||
c.getPlayer().updateQuestInfo(questid, "" + nextValue);
|
||||
} finally {
|
||||
c.releaseClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/net/server/channel/handlers/RaiseUIStateHandler.java
Normal file
37
src/net/server/channel/handlers/RaiseUIStateHandler.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package net.server.channel.handlers;
|
||||
|
||||
import client.MapleClient;
|
||||
import client.MapleQuestStatus;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import server.quest.MapleQuest;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Xari
|
||||
*/
|
||||
public class RaiseUIStateHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
int questid = slea.readShort();
|
||||
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
MapleQuest quest = MapleQuest.getInstance(questid);
|
||||
MapleQuestStatus mqs = c.getPlayer().getQuest(quest);
|
||||
if (mqs.getStatus() == MapleQuestStatus.Status.NOT_STARTED) {
|
||||
quest.forceStart(c.getPlayer(), 22000);
|
||||
c.getPlayer().updateQuestInfo(quest.getId(), "0");
|
||||
} else if (mqs.getStatus() == MapleQuestStatus.Status.STARTED) {
|
||||
c.announce(MaplePacketCreator.updateQuest(mqs, false));
|
||||
} else {
|
||||
//c.announce(MaplePacketCreator.updateQuestInfo(mqs.getQuestID(), 22000, "0"));
|
||||
}
|
||||
} finally {
|
||||
c.releaseClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -422,8 +422,8 @@ public final class RingActionHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
Item weddingTicket = new Item(newItemId, (short) 0, (short) 1);
|
||||
weddingTicket.setExpiration(expiration);
|
||||
|
||||
DueyProcessor.addItemToDB(weddingTicket, 1, 0, groom, guest);
|
||||
|
||||
DueyProcessor.dueyCreatePackage(weddingTicket, 0, groom, guest);
|
||||
}
|
||||
} else {
|
||||
c.getPlayer().dropMessage(5, "Wedding is already under way. You cannot invite any more guests for the event.");
|
||||
|
||||
@@ -26,6 +26,7 @@ import client.MapleClient;
|
||||
import client.Skill;
|
||||
import client.SkillFactory;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.MapleInventory;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import java.util.Map;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
@@ -41,38 +42,57 @@ public final class SkillBookHandler extends AbstractMaplePacketHandler {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
slea.readInt();
|
||||
short slot = (short) slea.readShort();
|
||||
int itemId = slea.readInt();
|
||||
|
||||
boolean canuse;
|
||||
boolean success = false;
|
||||
int skill = 0;
|
||||
int maxlevel = 0;
|
||||
|
||||
MapleCharacter player = c.getPlayer();
|
||||
Item toUse = c.getPlayer().getInventory(MapleInventoryType.USE).getItem(slot);
|
||||
if (toUse != null && toUse.getQuantity() == 1) {
|
||||
if (toUse.getItemId() != itemId) {
|
||||
return;
|
||||
}
|
||||
Map<String, Integer> skilldata = MapleItemInformationProvider.getInstance().getSkillStats(toUse.getItemId(), c.getPlayer().getJob().getId());
|
||||
boolean canuse;
|
||||
boolean success = false;
|
||||
int skill = 0;
|
||||
int maxlevel = 0;
|
||||
if (skilldata == null) {
|
||||
return;
|
||||
}
|
||||
Skill skill2 = SkillFactory.getSkill(skilldata.get("skillid"));
|
||||
if (skilldata.get("skillid") == 0) {
|
||||
canuse = false;
|
||||
} else if ((player.getSkillLevel(skill2) >= skilldata.get("reqSkillLevel") || skilldata.get("reqSkillLevel") == 0) && player.getMasterLevel(skill2) < skilldata.get("masterLevel")) {
|
||||
canuse = true;
|
||||
if (MapleItemInformationProvider.rollSuccessChance(skilldata.get("success"))) {
|
||||
success = true;
|
||||
player.changeSkillLevel(skill2, player.getSkillLevel(skill2), Math.max(skilldata.get("masterLevel"), player.getMasterLevel(skill2)), -1);
|
||||
} else {
|
||||
success = false;
|
||||
//player.dropMessage("The skill book lights up, but the skill winds up as if nothing happened.");
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
MapleInventory inv = c.getPlayer().getInventory(MapleInventoryType.USE);
|
||||
Item toUse = inv.getItem(slot);
|
||||
if (toUse == null || toUse.getItemId() != itemId) {
|
||||
return;
|
||||
}
|
||||
MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.USE, slot, (short) 1, false);
|
||||
} else {
|
||||
canuse = false;
|
||||
Map<String, Integer> skilldata = MapleItemInformationProvider.getInstance().getSkillStats(toUse.getItemId(), c.getPlayer().getJob().getId());
|
||||
if (skilldata == null) {
|
||||
return;
|
||||
}
|
||||
Skill skill2 = SkillFactory.getSkill(skilldata.get("skillid"));
|
||||
if (skilldata.get("skillid") == 0) {
|
||||
canuse = false;
|
||||
} else if ((player.getSkillLevel(skill2) >= skilldata.get("reqSkillLevel") || skilldata.get("reqSkillLevel") == 0) && player.getMasterLevel(skill2) < skilldata.get("masterLevel")) {
|
||||
inv.lockInventory();
|
||||
try {
|
||||
Item used = inv.getItem(slot);
|
||||
if (used != toUse || toUse.getQuantity() < 1) { // thanks ClouD for noticing skillbooks not being usable when stacked
|
||||
return;
|
||||
}
|
||||
|
||||
MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.USE, slot, (short) 1, false);
|
||||
} finally {
|
||||
inv.unlockInventory();
|
||||
}
|
||||
|
||||
canuse = true;
|
||||
if (MapleItemInformationProvider.rollSuccessChance(skilldata.get("success"))) {
|
||||
success = true;
|
||||
player.changeSkillLevel(skill2, player.getSkillLevel(skill2), Math.max(skilldata.get("masterLevel"), player.getMasterLevel(skill2)), -1);
|
||||
} else {
|
||||
success = false;
|
||||
//player.dropMessage("The skill book lights up, but the skill winds up as if nothing happened.");
|
||||
}
|
||||
} else {
|
||||
canuse = false;
|
||||
}
|
||||
} finally {
|
||||
c.releaseClient();
|
||||
}
|
||||
|
||||
// thanks Vcoc for noting skill book result not showing for all in area
|
||||
|
||||
@@ -101,6 +101,7 @@ public final class SpecialMoveHandler extends AbstractMaplePacketHandler {
|
||||
monster.aggroClearDamages();
|
||||
monster.aggroMonsterDamage(chr, 1);
|
||||
|
||||
// thanks onechord for pointing out Magnet disconnecting the caster (issue would actually happen upon failing to catch mob)
|
||||
monster.aggroSwitchController(chr, true);
|
||||
}
|
||||
}
|
||||
@@ -133,12 +134,20 @@ public final class SpecialMoveHandler extends AbstractMaplePacketHandler {
|
||||
} else {
|
||||
skill.getEffect(skillLevel).applyEchoOfHero(chr);
|
||||
}
|
||||
} else if(chr.canDoor()) {
|
||||
//update door lists
|
||||
chr.cancelMagicDoor();
|
||||
skill.getEffect(skillLevel).applyTo(chr, pos);
|
||||
} else {
|
||||
chr.message("Please wait 5 seconds before casting Mystic Door again.");
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
if (chr.canDoor()) {
|
||||
chr.cancelMagicDoor();
|
||||
skill.getEffect(skillLevel).applyTo(chr, pos);
|
||||
} else {
|
||||
chr.message("Please wait 5 seconds before casting Mystic Door again.");
|
||||
}
|
||||
} finally {
|
||||
c.releaseClient();
|
||||
}
|
||||
}
|
||||
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -36,6 +36,7 @@ import client.inventory.ModifyInventory;
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import client.inventory.manipulator.MapleKarmaManipulator;
|
||||
import client.processor.AssignAPProcessor;
|
||||
import client.processor.DueyProcessor;
|
||||
import constants.GameConstants;
|
||||
import constants.ItemConstants;
|
||||
import constants.ServerConstants;
|
||||
@@ -47,7 +48,6 @@ import java.util.List;
|
||||
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import net.server.Server;
|
||||
import scripting.npc.NPCScriptManager;
|
||||
import server.MapleItemInformationProvider;
|
||||
import server.MapleShop;
|
||||
import server.MapleShopFactory;
|
||||
@@ -130,7 +130,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
|
||||
if (victim != null) {
|
||||
MapleMap targetMap = victim.getMap();
|
||||
if (!FieldLimit.CANNOTVIPROCK.check(targetMap.getFieldLimit()) && (targetMap.getForcedReturnId() == 999999999 || targetMap.getId() < 100000000)) {
|
||||
if (victim.gmLevel() <= player.gmLevel()) {
|
||||
if (!victim.isGM() || victim.gmLevel() <= player.gmLevel()) { // thanks Yoboes for noticing non-GM's being unreachable through rocks
|
||||
player.forceChangeMap(targetMap, targetMap.findClosestPlayerSpawnpoint(victim.getPosition()));
|
||||
success = true;
|
||||
} else {
|
||||
@@ -294,7 +294,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
|
||||
if (item == null) //hack
|
||||
{
|
||||
return;
|
||||
} else if (item.isUntradeable()) {
|
||||
} else if (item.isUntradeable() || ii.isUnmerchable(item.getItemId())) {
|
||||
player.dropMessage(1, "You cannot trade this item.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
@@ -398,7 +398,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
|
||||
ii.getItemEffect(itemId).applyTo(player);
|
||||
remove(c, position, itemId);
|
||||
} else if (itemType == 533) {
|
||||
NPCScriptManager.getInstance().start(c, 9010009, null);
|
||||
DueyProcessor.dueySendTalk(c);
|
||||
} else if (itemType == 537) {
|
||||
if (GameConstants.isFreeMarketRoom(player.getMapId())) {
|
||||
player.dropMessage(5, "You cannot use the chalkboard here.");
|
||||
@@ -409,7 +409,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
|
||||
player.setChalkboard(slea.readMapleAsciiString());
|
||||
player.getMap().broadcastMessage(MaplePacketCreator.useChalkboard(player, false));
|
||||
player.getClient().announce(MaplePacketCreator.enableActions());
|
||||
remove(c, position, itemId);
|
||||
//remove(c, position, itemId); thanks Conrad for noticing chalkboards shouldn't be depleted upon use
|
||||
} else if (itemType == 539) {
|
||||
List<String> strLines = new LinkedList<>();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
package net.server.channel.handlers;
|
||||
|
||||
import client.MapleClient;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
public final class UseItemCanvasHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
}
|
||||
}
|
||||
@@ -237,7 +237,9 @@ public class MapleMonsterAggroCoordinator {
|
||||
}
|
||||
|
||||
if (mobAggro.isEmpty()) { // all aggro on this mob expired
|
||||
am.getLeft().aggroResetAggro();
|
||||
if (!am.getLeft().isBoss()) {
|
||||
am.getLeft().aggroResetAggro();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,10 @@ public class MaplePartySearchCoordinator {
|
||||
private Map<Integer, MapleCharacter> searchLeaders = new HashMap<>();
|
||||
private Map<Integer, LeaderSearchMetadata> searchSettings = new HashMap<>();
|
||||
|
||||
private Map<MapleCharacter, LeaderSearchMetadata> timeoutLeaders = new HashMap<>();
|
||||
|
||||
private int updateCount = 0;
|
||||
|
||||
private static Map<Integer, Set<Integer>> mapNeighbors = fetchNeighbouringMaps();
|
||||
private static Map<Integer, MapleJob> jobTable = instantiateJobTable();
|
||||
|
||||
@@ -245,11 +249,21 @@ public class MaplePartySearchCoordinator {
|
||||
addQueueLeader(leader);
|
||||
}
|
||||
|
||||
private void registerPartyLeader(MapleCharacter leader, LeaderSearchMetadata settings) {
|
||||
if (searchLeaders.containsKey(leader.getId())) return;
|
||||
|
||||
searchSettings.put(leader.getId(), settings);
|
||||
searchLeaders.put(leader.getId(), leader);
|
||||
addQueueLeader(leader);
|
||||
}
|
||||
|
||||
public void unregisterPartyLeader(MapleCharacter leader) {
|
||||
MapleCharacter toRemove = searchLeaders.remove(leader.getId());
|
||||
if (toRemove != null) {
|
||||
removeQueueLeader(toRemove);
|
||||
searchSettings.remove(leader.getId());
|
||||
} else {
|
||||
unregisterLongTermPartyLeader(leader);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,6 +321,41 @@ public class MaplePartySearchCoordinator {
|
||||
return new Pair<>(queuedLeaders, nextLeaders);
|
||||
}
|
||||
|
||||
private void registerLongTermPartyLeaders(List<Pair<MapleCharacter, LeaderSearchMetadata>> recycledLeaders) {
|
||||
leaderQueueRLock.lock();
|
||||
try {
|
||||
for (Pair<MapleCharacter, LeaderSearchMetadata> p : recycledLeaders) {
|
||||
timeoutLeaders.put(p.getLeft(), p.getRight());
|
||||
}
|
||||
} finally {
|
||||
leaderQueueRLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void unregisterLongTermPartyLeader(MapleCharacter leader) {
|
||||
leaderQueueRLock.lock();
|
||||
try {
|
||||
timeoutLeaders.remove(leader);
|
||||
} finally {
|
||||
leaderQueueRLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void reinstateLongTermPartyLeaders() {
|
||||
Map<MapleCharacter, LeaderSearchMetadata> timeoutLeadersCopy;
|
||||
leaderQueueWLock.lock();
|
||||
try {
|
||||
timeoutLeadersCopy = new HashMap<>(timeoutLeaders);
|
||||
timeoutLeaders.clear();
|
||||
} finally {
|
||||
leaderQueueWLock.unlock();
|
||||
}
|
||||
|
||||
for (Entry<MapleCharacter, LeaderSearchMetadata> e : timeoutLeadersCopy.entrySet()) {
|
||||
registerPartyLeader(e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public void runPartySearch() {
|
||||
Pair<List<MapleCharacter>, List<MapleCharacter>> queuedLeaders = fetchQueuedLeaders();
|
||||
|
||||
@@ -356,10 +405,28 @@ public class MaplePartySearchCoordinator {
|
||||
}
|
||||
}
|
||||
|
||||
List<Pair<MapleCharacter, LeaderSearchMetadata>> recycledLeaders = new LinkedList<>();
|
||||
for (MapleCharacter leader : expiredLeaders) {
|
||||
if (leader.isLoggedinWorld()) leader.dropMessage(5, "Your Party Search token session expired, please stop your Party Search and retry again later.");
|
||||
searchLeaders.remove(leader.getId());
|
||||
searchSettings.remove(leader.getId());
|
||||
LeaderSearchMetadata settings = searchSettings.remove(leader.getId());
|
||||
|
||||
if (leader.isLoggedinWorld()) {
|
||||
if (settings != null) {
|
||||
recycledLeaders.add(new Pair<>(leader, settings));
|
||||
if (ServerConstants.USE_DEBUG && leader.isGM()) leader.dropMessage(5, "Your Party Search token session is now on waiting queue for up to 7 minutes, to get it working right away please stop your Party Search and retry again later.");
|
||||
} else {
|
||||
leader.dropMessage(5, "Your Party Search token session expired, please stop your Party Search and retry again later.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!recycledLeaders.isEmpty()) {
|
||||
registerLongTermPartyLeaders(recycledLeaders);
|
||||
}
|
||||
|
||||
updateCount++;
|
||||
if (updateCount % 77 == 0) {
|
||||
reinstateLongTermPartyLeaders();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,8 +34,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
|
||||
|
||||
import java.awt.geom.Line2D;
|
||||
import tools.IntervalBuilder;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -44,84 +43,7 @@ import java.awt.geom.Line2D;
|
||||
public class PartySearchStorage {
|
||||
|
||||
private List<PartySearchCharacter> storage = new ArrayList<>(20);
|
||||
private PartySearchEmptyIntervals emptyManager = new PartySearchEmptyIntervals();
|
||||
|
||||
private class PartySearchEmptyIntervals {
|
||||
|
||||
private List<Line2D> emptyLimits = new ArrayList<>();
|
||||
|
||||
private void refitEmptyIntervals(int st, int en, int minLevel, int maxLevel) {
|
||||
List<Line2D> checkLimits = new ArrayList<>(emptyLimits.subList(st, en));
|
||||
|
||||
float newLimitX1, newLimitX2;
|
||||
if (!checkLimits.isEmpty()) {
|
||||
Line2D firstLimit = checkLimits.get(0);
|
||||
Line2D lastLimit = checkLimits.get(checkLimits.size() - 1);
|
||||
|
||||
newLimitX1 = (float) ((minLevel < firstLimit.getX1()) ? minLevel : firstLimit.getX1());
|
||||
newLimitX2 = (float) ((maxLevel > lastLimit.getX2()) ? maxLevel : lastLimit.getX2());
|
||||
|
||||
for (Line2D limit : checkLimits) {
|
||||
emptyLimits.remove(st);
|
||||
}
|
||||
} else {
|
||||
newLimitX1 = minLevel;
|
||||
newLimitX2 = maxLevel;
|
||||
}
|
||||
|
||||
emptyLimits.add(st, new Line2D.Float((float) newLimitX1, 0, (float) newLimitX2, 0));
|
||||
}
|
||||
|
||||
private int bsearchInterval(int level) {
|
||||
int st = 0, en = emptyLimits.size() - 1;
|
||||
|
||||
int mid, idx;
|
||||
while (en >= st) {
|
||||
idx = (st + en) / 2;
|
||||
mid = (int) emptyLimits.get(idx).getX1();
|
||||
|
||||
if (mid == level) {
|
||||
return idx;
|
||||
} else if (mid < level) {
|
||||
st = idx + 1;
|
||||
} else {
|
||||
en = idx - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return en;
|
||||
}
|
||||
|
||||
public void addEmptyInterval(int fromLevel, int toLevel) {
|
||||
synchronized (emptyLimits) { // adding intervals occurs on a same-thread process, so this is actually not performance grinding
|
||||
int st = bsearchInterval(fromLevel);
|
||||
if (st < 0) {
|
||||
st = 0;
|
||||
} else if (emptyLimits.get(st).getX2() < fromLevel) {
|
||||
st += 1;
|
||||
}
|
||||
|
||||
int en = bsearchInterval(toLevel);
|
||||
if (en < st) en = st - 1;
|
||||
|
||||
refitEmptyIntervals(st, en + 1, fromLevel, toLevel);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isInEmptyInterval(int minLevel, int maxLevel) {
|
||||
synchronized (emptyLimits) {
|
||||
int idx = bsearchInterval(minLevel);
|
||||
return idx >= 0 && maxLevel <= emptyLimits.get(idx).getX2();
|
||||
}
|
||||
}
|
||||
|
||||
public void clearEmptyInterval() {
|
||||
synchronized (emptyLimits) {
|
||||
emptyLimits.clear();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
private IntervalBuilder emptyIntervals = new IntervalBuilder();
|
||||
|
||||
private final ReentrantReadWriteLock psLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_PARTY_SEARCH_STORAGE, true);
|
||||
private final ReadLock psRLock = psLock.readLock();
|
||||
@@ -183,7 +105,7 @@ public class PartySearchStorage {
|
||||
psWLock.unlock();
|
||||
}
|
||||
|
||||
emptyManager.clearEmptyInterval();
|
||||
emptyIntervals.clear();
|
||||
}
|
||||
|
||||
private static int bsearchStorage(List<PartySearchCharacter> storage, int level) {
|
||||
@@ -207,7 +129,7 @@ public class PartySearchStorage {
|
||||
}
|
||||
|
||||
public MapleCharacter callPlayer(int callerCid, int callerMapid, int minLevel, int maxLevel) {
|
||||
if (emptyManager.isInEmptyInterval(minLevel, maxLevel)) {
|
||||
if (emptyIntervals.inInterval(minLevel, maxLevel)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -230,7 +152,7 @@ public class PartySearchStorage {
|
||||
}
|
||||
}
|
||||
|
||||
emptyManager.addEmptyInterval(minLevel, maxLevel);
|
||||
emptyIntervals.addInterval(minLevel, maxLevel);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ public final class AfterLoginHandler extends AbstractMaplePacketHandler {
|
||||
c3 = slea.readByte();
|
||||
}
|
||||
if (c2 == 1 && c3 == 1) {
|
||||
if (c.getPin() == null) {
|
||||
if (c.getPin() == null || c.getPin().equals("")) {
|
||||
c.announce(MaplePacketCreator.registerPin());
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.requestPin());
|
||||
|
||||
Reference in New Issue
Block a user