Conditional Buffs & M. Magnet & Loot Patch + Exp Split + Duey Rework

Fixed Monster Magnet skill when used on bosses disconnecting the caster.
Improved conditional buff system, no longer updating buffs that are not supposed to toggle.
Added party hunting in the conditional buffs system.
Refactored usage of DB by Duey. Registered Duey items now make use of the same table as the other inventory items.
Fixed non-encapsulated unlocking in reactor class.
Fixed stylish NPCs disconnecting players when trying to display empty styles list.
Fixed a deadlock case within recently implemented update buff effects (conditional buffs mechanic).
Fixed AOE mobskills not behaving well for fixed mobs (those shouldn't take into account attribute "facingLeft").
Fixed non-flipping mobs having attribute "facingLeft" updated according to controller position.
Revised aggro system no longer having bosses expire player chase.
Fixed chalkboard being depleted upon use.
Refactored MapleMapFactory, looking for normalization of the Factory design pattern the class was intended to make use at its conception.
Added MP replenishing system for mobs, gains based on its level.
Fixed indisponibility of one-of-a-kind loots due to the killer's team already having one sample each.
Reworked the EXP split system within the source. New behavior is expected to be GMS-like.
Adjusted interaction within the NPC Nein Spirit's Baby Dragon area. Only players who interacts with quests within can access the area now. One player at a time, with a timeout timer.
Fixed check of level requisites for expeditions.
This commit is contained in:
ronancpl
2019-06-15 15:10:56 -03:00
parent 04b11e2518
commit a39a210c1f
54 changed files with 1217 additions and 866 deletions

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

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

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

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

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

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