Coupon buff fix + concurrency fixes

Fixed coupon buff icon not showing properly. Fixed some cases involving
coupons and buff removal not dealing properly with concurrent access.
This commit is contained in:
ronancpl
2017-06-19 17:44:01 -03:00
parent 42fe74955d
commit 7f6d420cf1
58 changed files with 345 additions and 220 deletions

View File

@@ -22,42 +22,48 @@
package client;
public enum MapleBuffStat {
//SLOW(0x1),
MORPH(0x2),
RECOVERY(0x4),
MAPLE_WARRIOR(0x8),
STANCE(0x10),
SHARP_EYES(0x20),
MANA_REFLECTION(0x40),
//ALWAYS_RIGHT(0X80),
//SLOW(0x1L),
MORPH(0x2L),
RECOVERY(0x4L),
MAPLE_WARRIOR(0x8L),
STANCE(0x10L),
SHARP_EYES(0x20L),
MANA_REFLECTION(0x40L),
//ALWAYS_RIGHT(0X80L),
//------ bgn EDITED SLOT (was unused before) --------
MAP_PROTECTION(0x100000000000000L),
//------ end EDITED SLOT ----------------------------
SHADOW_CLAW(0x100),
INFINITY(0x200),
HOLY_SHIELD(0x400),
HAMSTRING(0x800),
BLIND(0x1000),
CONCENTRATE(0x2000),
//4000
ECHO_OF_HERO(0x8000),
//10000
GHOST_MORPH(0x20000),
AURA(0x40000),
CONFUSE(0x80000),
//100000
//200000
//400000
//800000
//1000000
//2000000
//4000000
BERSERK_FURY(0x8000000),
DIVINE_BODY(0x10000000),
SHADOW_CLAW(0x100L),
INFINITY(0x200L),
HOLY_SHIELD(0x400L),
HAMSTRING(0x800L),
BLIND(0x1000L),
CONCENTRATE(0x2000L),
//4000L
ECHO_OF_HERO(0x8000L),
//10000L
GHOST_MORPH(0x20000L),
AURA(0x40000L),
CONFUSE(0x80000L),
// ---- COUPON feature (was unused anyway) ----
COUPON_EXP1(0x100000L),
COUPON_EXP2(0x200000L),
COUPON_EXP3(0x400000L),
COUPON_EXP4(0x800000L),
COUPON_DRP1(0x1000000L),
COUPON_DRP2(0x2000000L),
COUPON_DRP3(0x4000000L),
// ---- end COUPON feature ----
BERSERK_FURY(0x8000000L),
DIVINE_BODY(0x10000000L),
SPARK(0x20000000L),
//40000000
//40000000L
FINALATTACK(0x80000000L),
BATTLESHIP(0xA00000040L), // weird one
WATK(0x100000000L),

View File

@@ -46,6 +46,8 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
//import java.util.TimeZone;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
@@ -257,6 +259,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
private ScheduledFuture<?> expiretask;
private ScheduledFuture<?> recoveryTask;
private List<ScheduledFuture<?>> timers = new ArrayList<>();
private Lock couponLock = new ReentrantLock();
private NumberFormat nf = new DecimalFormat("#,###,###,###");
private ArrayList<Integer> excluded = new ArrayList<>();
private MonsterBook monsterbook;
@@ -700,9 +703,9 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
}
public void cancelBuffStats(MapleBuffStat stat) {
public synchronized void cancelBuffStats(MapleBuffStat stat) {
List<MapleBuffStat> buffStatList = Arrays.asList(stat);
deregisterBuffStats(buffStatList);
dropBuffStats(deregisterBuffStats(buffStatList));
cancelPlayerBuffs(buffStatList);
}
@@ -789,7 +792,15 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
cancelEffect(MapleItemInformationProvider.getInstance().getItemEffect(itemId), false, -1);
}
public void cancelEffect(MapleStatEffect effect, boolean overwrite, long startTime) {
public synchronized void cancelEffect(MapleStatEffect effect, boolean overwrite, long startTime) {
cancelEffect(effect, overwrite, startTime, true);
}
private void cancelEffect(MapleStatEffect effect, boolean overwrite, long startTime, boolean firstCancel) {
dropBuffStats(cancelEffectInternal(effect, overwrite, startTime));
}
private List<MapleBuffStatValueHolder> cancelEffectInternal(MapleStatEffect effect, boolean overwrite, long startTime) {
List<MapleBuffStat> buffstats;
if (!overwrite) {
buffstats = getBuffStats(effect, startTime);
@@ -800,7 +811,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
buffstats.add(statup.getLeft());
}
}
deregisterBuffStats(buffstats);
List<MapleBuffStatValueHolder> toCancel = deregisterBuffStats(buffstats);
if (effect.isMagicDoor()) {
if (!getDoors().isEmpty()) {
MapleDoor door = getDoors().iterator().next();
@@ -842,6 +853,8 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
if (!overwrite) {
cancelPlayerBuffs(buffstats);
}
return toCancel;
}
public void cancelEffectFromBuffStat(MapleBuffStat stat) {
@@ -1745,60 +1758,63 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
}
private void deregisterBuffStats(List<MapleBuffStat> stats) {
synchronized (stats) {
List<MapleBuffStatValueHolder> effectsToCancel = new ArrayList<>(stats.size());
for (MapleBuffStat stat : stats) {
MapleBuffStatValueHolder mbsvh = effects.get(stat);
if (mbsvh != null) {
effects.remove(stat);
boolean addMbsvh = true;
for (MapleBuffStatValueHolder contained : effectsToCancel) {
if (mbsvh.startTime == contained.startTime && contained.effect == mbsvh.effect) {
addMbsvh = false;
}
}
if (addMbsvh) {
effectsToCancel.add(mbsvh);
}
if (stat == MapleBuffStat.RECOVERY) {
if (recoveryTask != null) {
recoveryTask.cancel(false);
recoveryTask = null;
}
} else if (stat == MapleBuffStat.SUMMON || stat == MapleBuffStat.PUPPET) {
int summonId = mbsvh.effect.getSourceId();
MapleSummon summon = summons.get(summonId);
if (summon != null) {
getMap().broadcastMessage(MaplePacketCreator.removeSummon(summon, true), summon.getPosition());
getMap().removeMapObject(summon);
removeVisibleMapObject(summon);
summons.remove(summonId);
}
if (summon.getSkill() == DarkKnight.BEHOLDER) {
if (beholderHealingSchedule != null) {
beholderHealingSchedule.cancel(false);
beholderHealingSchedule = null;
}
if (beholderBuffSchedule != null) {
beholderBuffSchedule.cancel(false);
beholderBuffSchedule = null;
}
}
} else if (stat == MapleBuffStat.DRAGONBLOOD) {
dragonBloodSchedule.cancel(false);
dragonBloodSchedule = null;
private void dropBuffStats(List<MapleBuffStatValueHolder> effectsToCancel) {
for (MapleBuffStatValueHolder cancelEffectCancelTasks : effectsToCancel) {
if (cancelEffectCancelTasks.schedule != null) {
cancelEffectCancelTasks.schedule.cancel(false);
this.cancelEffect(cancelEffectCancelTasks.effect, false, -1, false);
}
}
}
private List<MapleBuffStatValueHolder> deregisterBuffStats(List<MapleBuffStat> stats) {
List<MapleBuffStatValueHolder> effectsToCancel = new ArrayList<>(stats.size());
for (MapleBuffStat stat : stats) {
MapleBuffStatValueHolder mbsvh = effects.get(stat);
if (mbsvh != null) {
effects.remove(stat);
boolean addMbsvh = true;
for (MapleBuffStatValueHolder contained : effectsToCancel) {
if (mbsvh.startTime == contained.startTime && contained.effect == mbsvh.effect) {
addMbsvh = false;
}
}
}
for (MapleBuffStatValueHolder cancelEffectCancelTasks : effectsToCancel) {
if (cancelEffectCancelTasks.schedule != null) {
cancelEffectCancelTasks.schedule.cancel(false);
this.cancelEffect(cancelEffectCancelTasks.effect, false, -1);
if (addMbsvh) {
effectsToCancel.add(mbsvh);
}
if (stat == MapleBuffStat.RECOVERY) {
if (recoveryTask != null) {
recoveryTask.cancel(false);
recoveryTask = null;
}
} else if (stat == MapleBuffStat.SUMMON || stat == MapleBuffStat.PUPPET) {
int summonId = mbsvh.effect.getSourceId();
MapleSummon summon = summons.get(summonId);
if (summon != null) {
getMap().broadcastMessage(MaplePacketCreator.removeSummon(summon, true), summon.getPosition());
getMap().removeMapObject(summon);
removeVisibleMapObject(summon);
summons.remove(summonId);
}
if (summon.getSkill() == DarkKnight.BEHOLDER) {
if (beholderHealingSchedule != null) {
beholderHealingSchedule.cancel(false);
beholderHealingSchedule = null;
}
if (beholderBuffSchedule != null) {
beholderBuffSchedule.cancel(false);
beholderBuffSchedule = null;
}
}
} else if (stat == MapleBuffStat.DRAGONBLOOD) {
dragonBloodSchedule.cancel(false);
dragonBloodSchedule = null;
}
}
}
return effectsToCancel;
}
public void disableDoor() {
@@ -2015,6 +2031,9 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
expiretask = TimerManager.getInstance().register(new Runnable() {
@Override
public void run() {
MapleItemInformationProvider miip = MapleItemInformationProvider.getInstance();
boolean deletedCoupon = false;
long expiration, currenttime = System.currentTimeMillis();
Set<Skill> keys = getSkills().keySet();
for (Iterator<Skill> i = keys.iterator(); i.hasNext();) {
@@ -2038,12 +2057,19 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
} else if (expiration != -1 && expiration < currenttime) {
client.announce(MaplePacketCreator.itemExpired(item.getItemId()));
toberemove.add(item);
if(miip.isRateCoupon(item.getItemId())) {
deletedCoupon = true;
}
}
}
for (Item item : toberemove) {
MapleInventoryManipulator.removeFromSlot(client, inv.getType(), item.getPosition(), item.getQuantity(), true);
}
toberemove.clear();
if(deletedCoupon) {
updateCouponRates();
}
}
}
}, 60000);
@@ -2378,10 +2404,6 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
return new ArrayList<>(doors);
}
public int getDropRate() {
return dropRate;
}
public int getEnergyBar() {
return energybar;
}
@@ -2405,6 +2427,38 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
public int getExpRate() {
return expRate;
}
public int getCouponExpRate() {
return expCoupon;
}
public int getRawExpRate() {
return expRate / (expCoupon * client.getWorldServer().getExpRate());
}
public int getDropRate() {
return dropRate;
}
public int getCouponDropRate() {
return dropCoupon;
}
public int getRawDropRate() {
return dropRate / (dropCoupon * client.getWorldServer().getDropRate());
}
public int getMesoRate() {
return mesoRate;
}
public int getCouponMesoRate() {
return mesoCoupon;
}
public int getRawMesoRate() {
return mesoRate / (mesoCoupon * client.getWorldServer().getMesoRate());
}
public int getFace() {
return face;
@@ -2675,10 +2729,6 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
return merchantmeso;
}
public int getMesoRate() {
return mesoRate;
}
public int getMesosTraded() {
return mesosTraded;
}
@@ -3511,12 +3561,12 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
for (byte i = 1; i < 5; i++) {
gainSlots(i, 4, true);
}
}
this.yellowMessage("We see you reached level " + level + ". Congratulations! As a token of your success, your inventory has been expanded a little bit.");
this.yellowMessage("You reached level " + level + ". Congratulations! As a token of your success, your inventory has been expanded a little bit.");
}
}
if (level % 20 == 0 && ServerConstants.USE_ADD_RATES_BY_LEVEL == true) { //For the drop & meso rate
revertPlayerRates();
revertLastPlayerRates();
setPlayerRates();
this.yellowMessage("You managed to get level " + level + "! Getting experience and items seems a little easier now, huh?");
}
@@ -3626,24 +3676,22 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
}
public void setPlayerRates() {
public void setPlayerRates() {
this.expRate *= GameConstants.getPlayerBonusExpRate(this.level / 20);
this.mesoRate *= GameConstants.getPlayerBonusMesoRate(this.level / 20);
this.dropRate *= GameConstants.getPlayerBonusDropRate(this.level / 20);
}
public void revertPlayerRates() {
public void revertLastPlayerRates() {
this.expRate /= GameConstants.getPlayerBonusExpRate((this.level - 1) / 20);
this.mesoRate /= GameConstants.getPlayerBonusMesoRate((this.level - 1) / 20);
this.dropRate /= GameConstants.getPlayerBonusDropRate((this.level - 1) / 20);
}
public void revertWorldRates() {
World worldz = Server.getInstance().getWorld(world);
this.expRate /= worldz.getExpRate();
this.mesoRate /= worldz.getMesoRate();
this.dropRate /= worldz.getDropRate();
public void revertPlayerRates() {
this.expRate /= GameConstants.getPlayerBonusExpRate(this.level / 20);
this.mesoRate /= GameConstants.getPlayerBonusMesoRate(this.level / 20);
this.dropRate /= GameConstants.getPlayerBonusDropRate(this.level / 20);
}
public void setWorldRates() {
@@ -3653,13 +3701,30 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
this.dropRate *= worldz.getDropRate();
}
public void revertCouponRates() {
public void revertWorldRates() {
World worldz = Server.getInstance().getWorld(world);
this.expRate /= worldz.getExpRate();
this.mesoRate /= worldz.getMesoRate();
this.dropRate /= worldz.getDropRate();
}
private void setCouponRates() {
setActiveCoupons();
activateCouponsEffects();
}
private void revertCouponRates() {
revertCouponsEffects();
}
public void setCouponRates() {
setActiveCoupons();
activateCouponsEffects();
public void updateCouponRates() {
try {
couponLock.lock();
revertCouponRates();
setCouponRates();
} finally {
couponLock.unlock();
}
}
public void resetPlayerRates() {
@@ -4508,6 +4573,8 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}, buffInterval, buffInterval);
}
} else if (effect.isRecovery()) {
int healInterval = (ServerConstants.USE_ULTRA_RECOVERY) ? 2500 : 5000;
final byte heal = (byte) effect.getX();
recoveryTask = TimerManager.getInstance().register(new Runnable() {
@Override
@@ -4516,12 +4583,12 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
client.announce(MaplePacketCreator.showOwnRecovery(heal));
getMap().broadcastMessage(MapleCharacter.this, MaplePacketCreator.showRecovery(id, heal), false);
}
}, 5000, 5000);
}, healInterval, healInterval);
}
//is it possible to maintain a list of statup effects of the same type concurrently? Pair<MapleBuffStat, Itemid>
for (Pair<MapleBuffStat, Integer> statup : effect.getStatups()) {
effects.put(statup.getLeft(), new MapleBuffStatValueHolder(effect, starttime, schedule, statup.getRight().intValue()));
effects.put(statup.getLeft(), new MapleBuffStatValueHolder(effect, starttime, schedule, statup.getRight()));
}
recalcLocalStats();
}

View File

@@ -522,19 +522,22 @@ public class Commands {
player.yellowMessage("DROP RATE");
player.message(">>Base DROP Rate: " + c.getWorldServer().getDropRate() + "x");
player.message(">>Your DROP Rate: " + player.getDropRate() / c.getWorldServer().getDropRate() + "x");
player.message(">>Your DROP Rate: " + player.getRawDropRate() + "x");
if(player.getCouponDropRate() != 1) player.message(">>Your Coupon DROP Rate: " + player.getCouponDropRate() + "x");
player.message(">>------------------------------------------------");
player.message(">>Total DROP Rate: " + player.getDropRate() + "x");
player.yellowMessage("MESO RATE");
player.message(">>Base MESO Rate: " + c.getWorldServer().getMesoRate() + "x");
player.message(">>Your MESO Rate: " + player.getMesoRate() / c.getWorldServer().getMesoRate() + "x");
player.message(">>Your MESO Rate: " + player.getRawMesoRate() + "x");
if(player.getCouponMesoRate() != 1) player.message(">>Your Coupon MESO Rate: " + player.getCouponMesoRate() + "x");
player.message(">>------------------------------------------------");
player.message(">>Total MESO Rate: " + player.getMesoRate() + "x");
player.yellowMessage("EXP RATE");
player.message(">>Base EXP Rate: " + c.getWorldServer().getExpRate() + "x");
player.message(">>Your EXP Rate: " + player.getExpRate() / c.getWorldServer().getExpRate() + "x");
player.message(">>Your EXP Rate: " + player.getRawExpRate() + "x");
if(player.getCouponExpRate() != 1) player.message(">>Your Coupon EXP Rate: " + player.getCouponExpRate() + "x");
player.message(">>------------------------------------------------");
player.message(">>Total EXP Rate: " + player.getExpRate() + "x");
/*if(c.getWorldServer().getExpRate() > ServerConstants.EXP_RATE) {
@@ -1386,8 +1389,12 @@ public class Commands {
return true;
}
player.setLevel(Integer.parseInt(sub[1]) - 1);
player.loseExp(player.getExp(), false, false);
player.loseExp(player.getExp(), false, false);
player.revertPlayerRates();
player.setLevel(Math.min(Integer.parseInt(sub[1]), player.getMaxLevel()) - 1);
player.setPlayerRates();
player.levelUp(false);
} else if (sub[0].equals("levelpro")) {
if (sub.length < 2){
@@ -1402,7 +1409,9 @@ public class Commands {
final String[] s = {"setall", String.valueOf(Short.MAX_VALUE)};
executeGMCommand(c, s, heading);
player.loseExp(player.getExp(), false, false);
player.revertPlayerRates();
player.setLevel(255);
player.setPlayerRates();
player.setFame(13337);
player.setMaxHp(30000);
player.setMaxMp(30000);
@@ -1410,8 +1419,6 @@ public class Commands {
player.updateSingleStat(MapleStat.FAME, 13337);
player.updateSingleStat(MapleStat.MAXHP, 30000);
player.updateSingleStat(MapleStat.MAXMP, 30000);
player.revertPlayerRates();
player.setPlayerRates();
player.yellowMessage("Stats maxed out.");
} else if (sub[0].equals("maxskills")) {
for (MapleData skill_ : MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "String.wz")).getData("Skill.img").getChildren()) {

View File

@@ -199,8 +199,7 @@ public class MapleInventory implements Iterable<Item> {
inventory.put(slot, item);
if(MapleItemInformationProvider.getInstance().isRateCoupon(item.getItemId())) {
owner.revertCouponRates();
owner.setCouponRates();
owner.updateCouponRates();
}
}
@@ -208,8 +207,7 @@ public class MapleInventory implements Iterable<Item> {
Item item = inventory.remove(slot);
if(item != null && MapleItemInformationProvider.getInstance().isRateCoupon(item.getItemId())) {
owner.revertCouponRates();
owner.setCouponRates();
owner.updateCouponRates();
}
}

View File

@@ -39,7 +39,7 @@ public class ServerConstants {
public static final boolean USE_DEBUG_SHOW_INFO_EQPEXP = false; //Prints on the cmd all equip exp gain info.
public static final boolean USE_MTS = false;
public static final boolean USE_FAMILY_SYSTEM = false;
public static final boolean USE_PERMISSIVE_BUFFS = true; //WARNING: Allows players that does not have increased certain buff-type skills to use it's effect. Used mainly on buff-cast commands, however making this active may generate a source for possible client-edited exploits.
public static final boolean USE_PERMISSIVE_BUFFS = false; //WARNING: Allows players that does not have increased certain buff-type skills to use it's effect. Used mainly on buff-cast commands, however making this active may generate a source for possible client-edited exploits.
public static final boolean USE_DUEY = true;
public static final boolean USE_ITEM_SORT = true;
public static final boolean USE_ITEM_SORT_BY_NAME = false; //Item sorting based on name rather than id.

View File

@@ -19,21 +19,11 @@
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;
import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import constants.ServerConstants;
import tools.DatabaseConnection;
import net.server.world.World;
import client.MapleCharacter;
import tools.FilePrinter;
/**
* @author Ronan
@@ -46,7 +36,7 @@ public class CouponWorker implements Runnable {
Server.getInstance().updateActiveCoupons();
Server.getInstance().commitActiveCoupons();
} catch(SQLException sqle) {
sqle.printStackTrace();
FilePrinter.printError(FilePrinter.EXCEPTION_CAUGHT, "Unexpected SQL error: " + sqle.getMessage() + "\n\n");
}
}
}

View File

@@ -167,8 +167,7 @@ public class Server implements Runnable {
for(MapleCharacter chr: world.getPlayerStorage().getAllCharacters()) {
if(!chr.isLoggedin()) continue;
chr.revertCouponRates();
chr.setCouponRates();
chr.updateCouponRates();
}
}
}

View File

@@ -32,21 +32,21 @@ import client.MapleClient;
* @author Matze
*/
public final class MesoDropHandler extends AbstractMaplePacketHandler {//FIX
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
MapleCharacter player = c.getPlayer();
if (!player.isAlive()) {
c.announce(MaplePacketCreator.enableActions());
return;
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
MapleCharacter player = c.getPlayer();
if (!player.isAlive()) {
c.announce(MaplePacketCreator.enableActions());
return;
}
if (!player.canDropMeso()){
player.announce(MaplePacketCreator.serverNotice(5, "Fast meso drop has been patched, cut that out. ;)"));
return;
}
slea.skip(4);
int meso = slea.readInt();
if (meso <= player.getMeso() && meso > 9 && meso < 50001) {
player.gainMeso(-meso, false, true, false);
player.getMap().spawnMesoDrop(meso, player.getPosition(), player, player, true, (byte) 2);
}
}
if (!player.canDropMeso()){
player.announce(MaplePacketCreator.serverNotice(5, "Fast meso drop has been patched, cut that out. ;)"));
return;
}
slea.skip(4);
int meso = slea.readInt();
if (meso <= player.getMeso() && meso > 9 && meso < 50001) {
player.gainMeso(-meso, false, true, false);
player.getMap().spawnMesoDrop(meso, player.getPosition(), player, player, true, (byte) 2);
}
}
}

View File

@@ -269,11 +269,10 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
if (player.getMap().getHPDec() > 0) player.resetHpDecreaseTask();
player.dispelBuffCoupons();
player.resetPlayerRates();
if(ServerConstants.USE_ADD_RATES_BY_LEVEL == true) player.setPlayerRates();
player.setWorldRates();
player.setCouponRates();
player.updateCouponRates();
}
}

View File

@@ -32,9 +32,10 @@ import constants.ServerConstants;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.Set;
@@ -114,12 +115,15 @@ public class World {
}
public void setExpRate(int exp) {
//System.out.println("Setting server EXP Rate to " + exp * ServerConstants.EXP_RATE + "x.");
for(MapleCharacter chr : getPlayerStorage().getAllCharacters()) {
List<MapleCharacter> list = new LinkedList<>(getPlayerStorage().getAllCharacters());
for(MapleCharacter chr : list) {
if(!chr.isLoggedin()) continue;
chr.revertWorldRates();
}
this.exprate = exp;
for(MapleCharacter chr : getPlayerStorage().getAllCharacters()) {
for(MapleCharacter chr : list) {
if(!chr.isLoggedin()) continue;
chr.setWorldRates();
}
}
@@ -129,11 +133,15 @@ public class World {
}
public void setDropRate(int drop) {
for(MapleCharacter chr : getPlayerStorage().getAllCharacters()) {
List<MapleCharacter> list = new LinkedList<>(getPlayerStorage().getAllCharacters());
for(MapleCharacter chr : list) {
if(!chr.isLoggedin()) continue;
chr.revertWorldRates();
}
this.droprate = drop;
for(MapleCharacter chr : getPlayerStorage().getAllCharacters()) {
for(MapleCharacter chr : list) {
if(!chr.isLoggedin()) continue;
chr.setWorldRates();
}
}
@@ -143,11 +151,15 @@ public class World {
}
public void setMesoRate(int meso) {
for(MapleCharacter chr : getPlayerStorage().getAllCharacters()) {
List<MapleCharacter> list = new LinkedList<>(getPlayerStorage().getAllCharacters());
for(MapleCharacter chr : list) {
if(!chr.isLoggedin()) continue;
chr.revertWorldRates();
}
this.mesorate = meso;
for(MapleCharacter chr : getPlayerStorage().getAllCharacters()) {
for(MapleCharacter chr : list) {
if(!chr.isLoggedin()) continue;
chr.setWorldRates();
}
}
@@ -157,13 +169,7 @@ public class World {
}
public void setBossDropRate(int bossdrop) {
for(MapleCharacter chr : getPlayerStorage().getAllCharacters()) {
chr.revertWorldRates();
}
this.bossdroprate = bossdrop;
for(MapleCharacter chr : getPlayerStorage().getAllCharacters()) {
chr.setWorldRates();
}
}
public PlayerStorage getPlayerStorage() {

View File

@@ -223,6 +223,40 @@ public class MapleStatEffect {
addBuffStatPairToListIfNotZero(statups, MapleBuffStat.JUMP, Integer.valueOf(ret.jump));
addBuffStatPairToListIfNotZero(statups, MapleBuffStat.PYRAMID_PQ, Integer.valueOf(ret.berserk));
addBuffStatPairToListIfNotZero(statups, MapleBuffStat.BOOSTER, Integer.valueOf(ret.booster));
if(MapleItemInformationProvider.getInstance().isRateCoupon(sourceid)) {
switch(MapleDataTool.getInt("expR", source, 0)) {
case 1:
addBuffStatPairToListIfNotZero(statups, MapleBuffStat.COUPON_EXP1, 1);
break;
case 2:
addBuffStatPairToListIfNotZero(statups, MapleBuffStat.COUPON_EXP2, 1);
break;
case 3:
addBuffStatPairToListIfNotZero(statups, MapleBuffStat.COUPON_EXP3, 1);
break;
case 4:
addBuffStatPairToListIfNotZero(statups, MapleBuffStat.COUPON_EXP4, 1);
break;
}
switch(MapleDataTool.getInt("drpR", source, 0)) {
case 1:
addBuffStatPairToListIfNotZero(statups, MapleBuffStat.COUPON_DRP1, 1);
break;
case 2:
addBuffStatPairToListIfNotZero(statups, MapleBuffStat.COUPON_DRP2, 1);
break;
case 3:
addBuffStatPairToListIfNotZero(statups, MapleBuffStat.COUPON_DRP3, 1);
break;
}
}
}
MapleData ltd = source.getChildByPath("lt");
if (ltd != null) {
@@ -928,7 +962,7 @@ public class MapleStatEffect {
}
private void applyBuffEffect(MapleCharacter applyfrom, MapleCharacter applyto, boolean primary) {
if (!isMonsterRiding()) {
if (!isMonsterRiding() && !isCouponBuff()) {
applyto.cancelEffect(this, true, -1);
}
@@ -1253,6 +1287,10 @@ public class MapleStatEffect {
return skill && sourceid == ChiefBandit.CHAKRA;
}
private boolean isCouponBuff() {
return MapleItemInformationProvider.getInstance().isRateCoupon(sourceid);
}
public boolean isMonsterRiding() {
return skill && (sourceid % 10000000 == 1004 || sourceid == Corsair.BATTLE_SHIP || sourceid == Beginner.SPACESHIP || sourceid == Noblesse.SPACESHIP
|| sourceid == Beginner.YETI_MOUNT1 || sourceid == Beginner.YETI_MOUNT2 || sourceid == Beginner.WITCH_BROOMSTICK || sourceid == Beginner.BALROG_MOUNT