Merge branch 'master' into credits_update

This commit is contained in:
ronancpl
2019-06-15 15:56:15 -03:00
146 changed files with 7810 additions and 2802 deletions

View File

@@ -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());
}
}
}

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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();

View File

@@ -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();

View 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();
}
}
}
}

View 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();
}
}
}
}

View File

@@ -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.");

View File

@@ -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

View File

@@ -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 {

View File

@@ -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++) {

View File

@@ -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());
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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());