MapleCouponInstaller + EXP/DROP Coupon System

Added mechanics for the EXP/DROP cash coupons (such as enabling
out-of-time active coupons via command and automatic update of active
ones over the designed interval). Created MapleCouponInstaller as means
to gather info about the coupon intervals and rates from the WZs (the
generated SQL table is already updated on the db_database.sql file).
This commit is contained in:
ronancpl
2017-05-30 13:40:27 -03:00
parent 7f178a3d80
commit e1c95352c8
706 changed files with 4539 additions and 13386 deletions

View File

@@ -0,0 +1,52 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.server;
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;
/**
* @author Ronan
* @info Thread responsible for maintaining coupons EXP & DROP effects active
*/
public class CouponWorker implements Runnable {
@Override
public void run() {
try {
Server.getInstance().updateActiveCoupons();
Server.getInstance().commitActiveCoupons();
} catch(SQLException sqle) {
sqle.printStackTrace();
}
}
}

View File

@@ -26,6 +26,7 @@ import java.io.IOException;
import java.net.InetSocketAddress;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
@@ -62,10 +63,13 @@ import tools.Pair;
import client.MapleCharacter;
import client.SkillFactory;
import constants.ServerConstants;
import java.util.Calendar;
import server.quest.MapleQuest;
public class Server implements Runnable {
private final Map<Integer, Integer> couponRates = new LinkedHashMap<>();
private final List<Integer> activeCoupons = new LinkedList<>();
private IoAcceptor acceptor;
private List<Map<Integer, String>> channels = new LinkedList<>();
private List<World> worlds = new ArrayList<>();
@@ -123,6 +127,107 @@ public class Server implements Runnable {
public String getIP(int world, int channel) {
return channels.get(world).get(channel);
}
private long getTimeLeftForNextHour() {
Calendar nextHour = Calendar.getInstance();
nextHour.add(Calendar.HOUR, 1);
nextHour.set(Calendar.MINUTE, 0);
nextHour.set(Calendar.SECOND, 0);
return Math.max(0, nextHour.getTimeInMillis() - System.currentTimeMillis());
}
public Map<Integer, Integer> getCouponRates() {
return couponRates;
}
private void loadCouponRates(Connection c) throws SQLException {
PreparedStatement ps = c.prepareStatement("SELECT couponid, rate FROM nxcoupons");
ResultSet rs = ps.executeQuery();
while(rs.next()) {
int cid = rs.getInt("couponid");
int rate = rs.getInt("rate");
couponRates.put(cid, rate);
}
rs.close();
ps.close();
}
public List<Integer> getActiveCoupons() {
synchronized(activeCoupons) {
return activeCoupons;
}
}
public void commitActiveCoupons() {
for(World world: getWorlds()) {
for(MapleCharacter chr: world.getPlayerStorage().getAllCharacters()) {
if(!chr.isLoggedin()) continue;
chr.revertCouponRates();
chr.setCouponRates();
}
}
}
public void toggleCoupon(Integer couponId) {
if(MapleItemInformationProvider.getInstance().isRateCoupon(couponId)) {
synchronized(activeCoupons) {
if(activeCoupons.contains(couponId)) {
activeCoupons.remove(couponId);
}
else {
activeCoupons.add(couponId);
}
commitActiveCoupons();
}
}
}
public void updateActiveCoupons() throws SQLException {
synchronized(activeCoupons) {
activeCoupons.clear();
Calendar c = Calendar.getInstance();
int weekDay = c.get(Calendar.DAY_OF_WEEK);
int hourDay = c.get(Calendar.HOUR_OF_DAY);
Connection con = null;
try {
con = DatabaseConnection.getConnection();
int weekdayMask = (1 << weekDay);
PreparedStatement ps = con.prepareStatement("SELECT couponid FROM nxcoupons WHERE (activeday & ?) = ? AND starthour <= ? AND endhour > ?");
ps.setInt(1, weekdayMask);
ps.setInt(2, weekdayMask);
ps.setInt(3, hourDay);
ps.setInt(4, hourDay);
ResultSet rs = ps.executeQuery();
while(rs.next()) {
activeCoupons.add(rs.getInt("couponid"));
}
rs.close();
ps.close();
con.close();
} catch (SQLException ex) {
ex.printStackTrace();
try {
if(con != null && !con.isClosed())
con.close();
} catch (SQLException ex2) {
ex2.printStackTrace();
}
}
}
}
@Override
public void run() {
@@ -150,6 +255,9 @@ public class Server implements Runnable {
ps = c.prepareStatement("UPDATE characters SET HasMerchant = 0");
ps.executeUpdate();
ps.close();
loadCouponRates(c);
updateActiveCoupons();
} catch (SQLException sqle) {
sqle.printStackTrace();
}
@@ -157,10 +265,14 @@ public class Server implements Runnable {
IoBuffer.setAllocator(new SimpleBufferAllocator());
acceptor = new NioSocketAcceptor();
acceptor.getFilterChain().addLast("codec", (IoFilter) new ProtocolCodecFilter(new MapleCodecFactory()));
TimerManager tMan = TimerManager.getInstance();
tMan.start();
tMan.register(tMan.purge(), 300000);//Purging ftw...
tMan.register(new RankingWorker(), ServerConstants.RANKING_INTERVAL);
tMan.register(tMan.purge(), ServerConstants.PURGING_INTERVAL);//Purging ftw...
long timeLeft = getTimeLeftForNextHour();
tMan.register(new CouponWorker(), ServerConstants.COUPON_INTERVAL, timeLeft);
tMan.register(new RankingWorker(), ServerConstants.RANKING_INTERVAL, timeLeft);
long timeToTake = System.currentTimeMillis();
SkillFactory.loadAllSkills();
@@ -176,7 +288,6 @@ public class Server implements Runnable {
MapleQuest.loadAllQuest();
System.out.println("Quest loaded in " + ((System.currentTimeMillis() - timeToTake) / 1000.0) + " seconds\r\n");
try {
Integer worldCount = Math.min(ServerConstants.WORLD_NAMES.length, Integer.parseInt(p.getProperty("worlds")));

View File

@@ -149,7 +149,7 @@ public final class ChangeMapHandler extends AbstractMaplePacketHandler {
} else {
c.announce(MaplePacketCreator.enableActions());
}
//chr.setRates();
//chr.setWorldRates();
} catch (Exception e) {
e.printStackTrace();
}

View File

@@ -50,7 +50,7 @@ public final class DistributeSPHandler extends AbstractMaplePacketHandler {
AutobanFactory.PACKET_EDIT.alert(player, "tried to packet edit in distributing sp.");
FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use skill " + skillid + " without it being in their job.\r\n");
c.disconnect(true, false);
return;
return;
}
if (skillid % 10000000 > 999 && skillid % 10000000 < 1003) {
int total = 0;

View File

@@ -100,7 +100,8 @@ public final class DueyHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
if (!ServerConstants.USE_DUEY){
return;
c.announce(MaplePacketCreator.enableActions());
return;
}
byte operation = slea.readByte();
@@ -196,6 +197,7 @@ public final class DueyHandler extends AbstractMaplePacketHandler {
dp = dueypack;
if(dp == null) {
System.out.println("Error: Null Duey package!");
c.announce(MaplePacketCreator.enableActions());
return;
}
@@ -203,7 +205,6 @@ public final class DueyHandler extends AbstractMaplePacketHandler {
if (!MapleInventoryManipulator.checkSpace(c, dp.getItem().getItemId(), dp.getItem().getQuantity(), dp.getItem().getOwner())) {
c.getPlayer().dropMessage(1, "Your inventory is full");
c.announce(MaplePacketCreator.enableActions());
return;
} else {
MapleInventoryManipulator.addFromDrop(c, dp.getItem(), false);

View File

@@ -35,33 +35,32 @@ import tools.data.input.SeekableLittleEndianAccessor;
*/
public class EnterCashShopHandler extends AbstractMaplePacketHandler {
@Override
public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
try {
MapleCharacter mc = c.getPlayer();
MapleCharacter mc = c.getPlayer();
if (mc.getCashShop().isOpened()) {
return;
}
mc.closePlayerInteractions();
Server.getInstance().getPlayerBuffStorage().addBuffsToStorage(mc.getId(), mc.getAllBuffs());
mc.cancelBuffEffects();
mc.cancelExpirationTask();
c.announce(MaplePacketCreator.openCashShop(c, false));
c.announce(MaplePacketCreator.showCashInventory(c));
c.announce(MaplePacketCreator.showGifts(mc.getCashShop().loadGifts()));
c.announce(MaplePacketCreator.showWishList(mc, false));
c.announce(MaplePacketCreator.showCash(mc));
c.getChannelServer().removePlayer(mc);
mc.getMap().removePlayer(mc);
mc.getCashShop().open(true);
mc.saveToDB();
if (mc.getCashShop().isOpened()) {
return;
}
mc.closePlayerInteractions();
Server.getInstance().getPlayerBuffStorage().addBuffsToStorage(mc.getId(), mc.getAllBuffs());
mc.cancelBuffEffects();
mc.cancelExpirationTask();
c.announce(MaplePacketCreator.openCashShop(c, false));
c.announce(MaplePacketCreator.showCashInventory(c));
c.announce(MaplePacketCreator.showGifts(mc.getCashShop().loadGifts()));
c.announce(MaplePacketCreator.showWishList(mc, false));
c.announce(MaplePacketCreator.showCash(mc));
c.getChannelServer().removePlayer(mc);
mc.getMap().removePlayer(mc);
mc.getCashShop().open(true);
mc.saveToDB();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

View File

@@ -249,7 +249,7 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
player.changeSkillLevel(SkillFactory.getSkill(10000000 * player.getJobType() + 12), (byte) (player.getLinkedLevel() / 10), 20, -1);
player.checkBerserk();
player.expirationTask();
//player.setRates();
//player.setWorldRates();
if (GameConstants.hasSPTable(player.getJob()) && player.getJob().getId() != 2001) {
player.createDragon();
}

View File

@@ -113,11 +113,11 @@ 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()) {
chr.revertRates(false);
chr.revertWorldRates();
}
this.exprate = exp;
for(MapleCharacter chr : getPlayerStorage().getAllCharacters()) {
chr.setRates();
chr.setWorldRates();
}
}
@@ -127,11 +127,11 @@ public class World {
public void setDropRate(int drop) {
for(MapleCharacter chr : getPlayerStorage().getAllCharacters()) {
chr.revertRates(false);
chr.revertWorldRates();
}
this.droprate = drop;
for(MapleCharacter chr : getPlayerStorage().getAllCharacters()) {
chr.setRates();
chr.setWorldRates();
}
}
@@ -141,11 +141,11 @@ public class World {
public void setMesoRate(int meso) {
for(MapleCharacter chr : getPlayerStorage().getAllCharacters()) {
chr.revertRates(false);
chr.revertWorldRates();
}
this.mesorate = meso;
for(MapleCharacter chr : getPlayerStorage().getAllCharacters()) {
chr.setRates();
chr.setWorldRates();
}
}
@@ -155,11 +155,11 @@ public class World {
public void setBossDropRate(int bossdrop) {
for(MapleCharacter chr : getPlayerStorage().getAllCharacters()) {
chr.revertRates(false);
chr.revertWorldRates();
}
this.bossdroprate = bossdrop;
for(MapleCharacter chr : getPlayerStorage().getAllCharacters()) {
chr.setRates();
chr.setWorldRates();
}
}