Trade results + Map-Ownership & Fishing + P. EXP distribution rework

Fixed an issue where players could lose priority over recently dropped items after switching maps.
Adjusted EXP bonus buffs to also cover party bonus gains.
Fixed items taken back from merchants not properly checking for stacking opportunities in player inventory.
Fixed some merchant references in visitors' player object not being properly cleared when owner closes shop.
Adjusted merchants to automatically close as soon as the merchant owner finishes maintenance process with it having no items in store.
Added trade result opcodes. Trade results now should work almost as intended originally.
Implemented server-side check for portal distance when deploying player shops and merchants.
Implemented server-side check for whether local or remote IP is being used when logging in a local/remote server (this should mitigate a few of the issues people may find when trying to log in game world).
Implemented commands designed for management of opened IO sessions.
Fixed chalkboard not showing up for owner player when changing maps.
Added "time left" functionality for merchant owners managing the opened store.
Fixed skillbooks not showing properly for other players in the map.
Fixed commands using lowercased-version of content inputted by player.
Implemented the Fredrick expected fee on using the Store Bank service.
Implemented "exclusive invitation management" in the system. Inviters are notified the invited players are already managing an invite, should it be visually "in-progress" for that one.
Implemented "map ownership". Non-map owners are unable to farm in an area if they are not party members with the owner or until the ownership rescinds.
Adjusted inventory sort feature, now sorting projectile items in such a fashion that commonly stronger versions comes before the basic ones.
Added a visual effect that shows up when obtaining Aran skills.
Revised party EXP gain system. Party bonuses now accounts a fraction of the accumulated EXP gained by members when defeating a mob, and raw EXP gained by a player is kept the same regardless of him/her being in a party or not (thus a bonus being REALLY a bonus).
Implemented a custom fishing system in the source, on which during "seasonal" times (that gets arbitrarily defined by both day-of-year and time-of-day) fishes are more likely to be hooked. Such likelihood also improved depending on the amount of mesos spent as lure.
This commit is contained in:
ronancpl
2019-03-10 01:30:22 -03:00
parent 799870df63
commit 9538c415e1
86 changed files with 2992 additions and 887 deletions

View File

@@ -231,6 +231,7 @@ public final class PacketProcessor {
registerHandler(RecvOpcode.ADMIN_COMMAND, new AdminCommandHandler());
registerHandler(RecvOpcode.ADMIN_LOG, new AdminLogHandler());
registerHandler(RecvOpcode.ALLIANCE_OPERATION, new AllianceOperationHandler());
registerHandler(RecvOpcode.DENY_ALLIANCE_REQUEST, new DenyAllianceRequestHandler());
registerHandler(RecvOpcode.USE_SOLOMON_ITEM, new UseSolomonHandler());
registerHandler(RecvOpcode.USE_GACHA_EXP, new UseGachaExpHandler());
registerHandler(RecvOpcode.NEW_YEAR_CARD_REQUEST, new NewYearCardHandler());

View File

@@ -144,6 +144,7 @@ public enum RecvOpcode {
WEDDING_TALK(0x8B),
WEDDING_TALK_MORE(0x8B),
ALLIANCE_OPERATION(0x8F),
DENY_ALLIANCE_REQUEST(0x90),
OPEN_FAMILY(0x92),
ADD_FAMILY(0x93),
ACCEPT_FAMILY(0x96),

View File

@@ -60,6 +60,8 @@ import net.server.guild.MapleGuildCharacter;
import net.server.worker.CharacterDiseaseWorker;
import net.server.worker.CouponWorker;
import net.server.worker.EventRecallCoordinatorWorker;
import net.server.worker.FredrickWorker;
import net.server.worker.InvitationWorker;
import net.server.worker.LoginCoordinatorWorker;
import net.server.worker.LoginStorageWorker;
import net.server.worker.RankingCommandWorker;
@@ -398,11 +400,12 @@ public class Server {
int bossdroprate = getWorldProperty(p, "bossdroprate", i, ServerConstants.BOSS_DROP_RATE);
int questrate = getWorldProperty(p, "questrate", i, ServerConstants.QUEST_RATE);
int travelrate = getWorldProperty(p, "travelrate", i, ServerConstants.TRAVEL_RATE);
int fishingrate = getWorldProperty(p, "fishrate", i, ServerConstants.FISHING_RATE);
World world = new World(i,
Integer.parseInt(p.getProperty("flag" + i)),
p.getProperty("eventmessage" + i),
exprate, droprate, bossdroprate, mesorate, questrate, travelrate);
exprate, droprate, bossdroprate, mesorate, questrate, travelrate, fishingrate);
worldRecommendedList.add(new Pair<>(i, p.getProperty("whyamirecommended" + i)));
worlds.add(world);
@@ -915,6 +918,8 @@ public class Server {
tMan.register(new LoginCoordinatorWorker(), 60 * 60 * 1000, timeLeft);
tMan.register(new EventRecallCoordinatorWorker(), 60 * 60 * 1000, timeLeft);
tMan.register(new LoginStorageWorker(), 2 * 60 * 1000, 2 * 60 * 1000);
tMan.register(new FredrickWorker(), 60 * 60 * 1000, 60 * 60 * 1000);
tMan.register(new InvitationWorker(), 30 * 1000, 30 * 1000);
long timeToTake = System.currentTimeMillis();
SkillFactory.loadAllSkills();

View File

@@ -33,6 +33,7 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
@@ -89,18 +90,19 @@ public final class Channel {
private String ip, serverMessage;
private MapleMapFactory mapFactory;
private EventScriptManager eventSM;
private MobStatusScheduler mobStatusSchedulers[] = new MobStatusScheduler[4];
private MobAnimationScheduler mobAnimationSchedulers[] = new MobAnimationScheduler[4];
private MobClearSkillScheduler mobClearSkillSchedulers[] = new MobClearSkillScheduler[4];
private MobMistScheduler mobMistSchedulers[] = new MobMistScheduler[4];
private FaceExpressionScheduler faceExpressionSchedulers[] = new FaceExpressionScheduler[4];
private EventScheduler eventSchedulers[] = new EventScheduler[4];
private OverallScheduler channelSchedulers[] = new OverallScheduler[4];
private MobStatusScheduler mobStatusSchedulers[] = new MobStatusScheduler[ServerConstants.CHANNEL_LOCKS];
private MobAnimationScheduler mobAnimationSchedulers[] = new MobAnimationScheduler[ServerConstants.CHANNEL_LOCKS];
private MobClearSkillScheduler mobClearSkillSchedulers[] = new MobClearSkillScheduler[ServerConstants.CHANNEL_LOCKS];
private MobMistScheduler mobMistSchedulers[] = new MobMistScheduler[ServerConstants.CHANNEL_LOCKS];
private FaceExpressionScheduler faceExpressionSchedulers[] = new FaceExpressionScheduler[ServerConstants.CHANNEL_LOCKS];
private EventScheduler eventSchedulers[] = new EventScheduler[ServerConstants.CHANNEL_LOCKS];
private OverallScheduler channelSchedulers[] = new OverallScheduler[ServerConstants.CHANNEL_LOCKS];
private Map<Integer, MapleHiredMerchant> hiredMerchants = new HashMap<>();
private final Map<Integer, Integer> storedVars = new HashMap<>();
private Set<Integer> playersAway = new HashSet<>();
private List<MapleExpedition> expeditions = new ArrayList<>();
private List<MapleExpeditionType> expedType = new ArrayList<>();
private Set<MapleMap> ownedMaps = Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<MapleMap, Boolean>()));
private MapleEvent event;
private boolean finishedShutdown = false;
private int usedDojo = 0;
@@ -130,7 +132,7 @@ public final class Channel {
private ReadLock merchRlock = merchantLock.readLock();
private WriteLock merchWlock = merchantLock.writeLock();
private MonitoredReentrantLock faceLock[] = new MonitoredReentrantLock[4];
private MonitoredReentrantLock faceLock[] = new MonitoredReentrantLock[ServerConstants.CHANNEL_LOCKS];
private MonitoredReentrantLock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHANNEL, true);
@@ -168,7 +170,7 @@ public final class Channel {
dojoTask[i] = null;
}
for(int i = 0; i < 4; i++) {
for(int i = 0; i < ServerConstants.CHANNEL_LOCKS; i++) {
faceLock[i] = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHANNEL_FACEEXPRS, true);
mobStatusSchedulers[i] = new MobStatusScheduler();
@@ -235,7 +237,7 @@ public final class Channel {
}
}
for(int i = 0; i < 4; i++) {
for(int i = 0; i < ServerConstants.CHANNEL_LOCKS; i++) {
if(mobStatusSchedulers[i] != null) {
mobStatusSchedulers[i].dispose();
mobStatusSchedulers[i] = null;
@@ -285,7 +287,7 @@ public final class Channel {
}
private void emptyLocks() {
for(int i = 0; i < 4; i++) {
for(int i = 0; i < ServerConstants.CHANNEL_LOCKS; i++) {
faceLock[i] = faceLock[i].dispose();
}
@@ -968,22 +970,33 @@ public final class Channel {
}
}
private static int getChannelSchedulerIndex(int mapid) {
if(mapid >= 250000000) {
if(mapid >= 900000000) {
return 3;
} else {
return 2;
public void registerOwnedMap(MapleMap map) {
ownedMaps.add(map);
}
public void unregisterOwnedMap(MapleMap map) {
ownedMaps.remove(map);
}
public void runCheckOwnedMapsSchedule() {
if (!ownedMaps.isEmpty()) {
List<MapleMap> ownedMapsList;
synchronized (ownedMaps) {
ownedMapsList = new ArrayList<>(ownedMaps);
}
} else {
if(mapid >= 200000000) {
return 1;
} else {
return 0;
for (MapleMap map : ownedMapsList) {
map.checkMapOwnerActivity();
}
}
}
private static int getChannelSchedulerIndex(int mapid) {
int section = 1000000000 / ServerConstants.CHANNEL_LOCKS;
return mapid / section;
}
public void registerMobStatus(int mapid, MonsterStatusEffect mse, Runnable cancelAction, long duration) {
registerMobStatus(mapid, mse, cancelAction, duration, null, -1);
}

View File

@@ -136,6 +136,10 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
}
protected synchronized void applyAttack(AttackInfo attack, final MapleCharacter player, int attackCount) {
if (player.getMap().isOwnershipRestricted(player)) {
return;
}
Skill theSkill = null;
MapleStatEffect attackEffect = null;
final int job = player.getJob().getId();

View File

@@ -132,7 +132,6 @@ public final class AdminCommandHandler extends AbstractMaplePacketHandler {
MapleMonster monster = (MapleMonster) monsterx.get(x);
if (monster.getId() == mobToKill) {
c.getPlayer().getMap().killMonster(monster, c.getPlayer(), true);
//monster.giveExpToCharacter(c.getPlayer(), monster.getExp(), true, 1); already being done
}
}
break;

View File

@@ -61,7 +61,7 @@ public final class AllianceOperationHandler extends AbstractMaplePacketHandler {
}
} else {
if (b == 4) {
chr.dropMessage("Your guild is already registered on a Guild Alliance.");
chr.dropMessage(5, "Your guild is already registered on a guild alliance.");
c.announce(MaplePacketCreator.enableActions());
return;
}
@@ -88,27 +88,16 @@ public final class AllianceOperationHandler extends AbstractMaplePacketHandler {
case 0x03: // Send Invite
String guildName = slea.readMapleAsciiString();
if(alliance.getGuilds().size() == alliance.getCapacity()) {
chr.dropMessage("Your alliance cannot comport any more guilds at the moment.");
if (alliance.getGuilds().size() == alliance.getCapacity()) {
chr.dropMessage(5, "Your alliance cannot comport any more guilds at the moment.");
} else {
MapleGuild mg = Server.getInstance().getGuildByName(guildName);
if(mg == null) {
chr.dropMessage("The entered guild does not exist.");
}
else {
MapleCharacter victim = mg.getMGC(mg.getLeaderId()).getCharacter();
if (victim == null) {
chr.dropMessage("The master of the guild that you offered an invitation is currently not online.");
} else {
victim.getClient().announce(MaplePacketCreator.sendAllianceInvitation(alliance.getId(), chr));
}
}
MapleAlliance.sendInvitation(c, guildName, alliance.getId());
}
break;
case 0x04: { // Accept Invite
if (chr.getGuild().getAllianceId() != 0 || chr.getGuildRank() != 1 || chr.getGuildId() < 1) {
MapleGuild guild = chr.getGuild();
if (guild.getAllianceId() != 0 || chr.getGuildRank() != 1 || chr.getGuildId() < 1) {
return;
}
@@ -120,6 +109,15 @@ public final class AllianceOperationHandler extends AbstractMaplePacketHandler {
return;
}
if (!MapleAlliance.answerInvitation(c.getPlayer().getId(), guild.getName(), alliance.getId(), true)) {
return;
}
if (alliance.getGuilds().size() == alliance.getCapacity()) {
chr.dropMessage(5, "Your alliance cannot comport any more guilds at the moment.");
return;
}
int guildid = chr.getGuildId();
Server.getInstance().addGuildtoAlliance(alliance.getId(), guildid);
@@ -132,7 +130,7 @@ public final class AllianceOperationHandler extends AbstractMaplePacketHandler {
Server.getInstance().allianceMessage(alliance.getId(), MaplePacketCreator.addGuildToAlliance(alliance, guildid, c), -1, -1);
Server.getInstance().allianceMessage(alliance.getId(), MaplePacketCreator.updateAllianceInfo(alliance, c.getWorld()), -1, -1);
Server.getInstance().allianceMessage(alliance.getId(), MaplePacketCreator.allianceNotice(alliance.getId(), alliance.getNotice()), -1, -1);
chr.getGuild().dropMessage("Your guild has joined the [" + alliance.getName() + "] union.");
guild.dropMessage("Your guild has joined the [" + alliance.getName() + "] union.");
break;
}

View File

@@ -57,317 +57,325 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
return;
}
final int action = slea.readByte();
if (action == 0x03 || action == 0x1E) {
slea.readByte();
final int useNX = slea.readInt();
final int snCS = slea.readInt();
CashItem cItem = CashItemFactory.getItem(snCS);
if (!canBuy(chr, cItem, cs.getCash(useNX))) {
FilePrinter.printError(FilePrinter.ITEM, "Denied to sell cash item with SN " + snCS); // preventing NPE here thanks to MedicOP
c.enableCSActions();
return;
}
if (action == 0x03) { // Item
if (ItemConstants.isCashStore(cItem.getItemId()) && chr.getLevel() < 16) {
c.enableCSActions();
return;
} else if (ItemConstants.isRateCoupon(cItem.getItemId()) && !ServerConstants.USE_SUPPLY_RATE_COUPONS) {
chr.dropMessage(1, "Rate coupons are currently unavailable to purchase.");
c.enableCSActions();
return;
} else if (ItemConstants.isMapleLife(cItem.getItemId()) && chr.getLevel() < 30) {
c.enableCSActions();
return;
}
Item item = cItem.toItem();
cs.addToInventory(item);
c.announce(MaplePacketCreator.showBoughtCashItem(item, c.getAccID()));
} else { // Package
List<Item> cashPackage = CashItemFactory.getPackage(cItem.getItemId());
for (Item item : cashPackage) {
cs.addToInventory(item);
}
c.announce(MaplePacketCreator.showBoughtCashPackage(cashPackage, c.getAccID()));
}
cs.gainCash(useNX, cItem, chr.getWorld());
c.announce(MaplePacketCreator.showCash(chr));
} else if (action == 0x04) {//TODO check for gender
int birthday = slea.readInt();
CashItem cItem = CashItemFactory.getItem(slea.readInt());
Map<String, String> recipient = MapleCharacter.getCharacterFromDatabase(slea.readMapleAsciiString());
String message = slea.readMapleAsciiString();
if (!canBuy(chr, cItem, cs.getCash(4)) || message.length() < 1 || message.length() > 73) {
c.enableCSActions();
return;
}
if (!checkBirthday(c, birthday)) {
c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xC4));
return;
} else if (recipient == null) {
c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xA9));
return;
} else if (recipient.get("accountid").equals(String.valueOf(c.getAccID()))) {
c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xA8));
return;
}
cs.gift(Integer.parseInt(recipient.get("id")), chr.getName(), message, cItem.getSN());
c.announce(MaplePacketCreator.showGiftSucceed(recipient.get("name"), cItem));
cs.gainCash(4, cItem, chr.getWorld());
c.announce(MaplePacketCreator.showCash(chr));
if (c.tryacquireClient()) { // thanks Thora for finding out an exploit within cash operations
try {
chr.sendNote(recipient.get("name"), chr.getName() + " has sent you a gift! Go check out the Cash Shop.", (byte) 0); //fame or not
} catch (SQLException ex) {
ex.printStackTrace();
}
MapleCharacter receiver = c.getChannelServer().getPlayerStorage().getCharacterByName(recipient.get("name"));
if (receiver != null) receiver.showNote();
} else if (action == 0x05) { // Modify wish list
cs.clearWishList();
for (byte i = 0; i < 10; i++) {
int sn = slea.readInt();
CashItem cItem = CashItemFactory.getItem(sn);
if (cItem != null && cItem.isOnSale() && sn != 0) {
cs.addToWishList(sn);
}
}
c.announce(MaplePacketCreator.showWishList(chr, true));
} else if (action == 0x06) { // Increase Inventory Slots
slea.skip(1);
int cash = slea.readInt();
byte mode = slea.readByte();
if (mode == 0) {
byte type = slea.readByte();
if (cs.getCash(cash) < 4000) {
c.enableCSActions();
return;
}
if (chr.gainSlots(type, 4, false)) {
c.announce(MaplePacketCreator.showBoughtInventorySlots(type, chr.getSlots(type)));
cs.gainCash(cash, -4000);
c.announce(MaplePacketCreator.showCash(chr));
}
} else {
CashItem cItem = CashItemFactory.getItem(slea.readInt());
int type = (cItem.getItemId() - 9110000) / 1000;
if (!canBuy(chr, cItem, cs.getCash(cash))) {
c.enableCSActions();
return;
}
if (chr.gainSlots(type, 8, false)) {
c.announce(MaplePacketCreator.showBoughtInventorySlots(type, chr.getSlots(type)));
cs.gainCash(cash, cItem, chr.getWorld());
c.announce(MaplePacketCreator.showCash(chr));
}
}
} else if (action == 0x07) { // Increase Storage Slots
slea.skip(1);
int cash = slea.readInt();
byte mode = slea.readByte();
if (mode == 0) {
if (cs.getCash(cash) < 4000) {
c.enableCSActions();
return;
}
if (chr.getStorage().gainSlots(4)) {
FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " bought 4 slots to their account storage.");
chr.setUsedStorage();
c.announce(MaplePacketCreator.showBoughtStorageSlots(chr.getStorage().getSlots()));
cs.gainCash(cash, -4000);
c.announce(MaplePacketCreator.showCash(chr));
}
} else {
CashItem cItem = CashItemFactory.getItem(slea.readInt());
if (!canBuy(chr, cItem, cs.getCash(cash))) {
c.enableCSActions();
return;
}
if (chr.getStorage().gainSlots(8)) { // thanks ABaldParrot & Thora for detecting storage issues here
FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " bought 8 slots to their account storage.");
chr.setUsedStorage();
c.announce(MaplePacketCreator.showBoughtStorageSlots(chr.getStorage().getSlots()));
cs.gainCash(cash, cItem, chr.getWorld());
c.announce(MaplePacketCreator.showCash(chr));
}
}
} else if (action == 0x08) { // Increase Character Slots
slea.skip(1);
int cash = slea.readInt();
CashItem cItem = CashItemFactory.getItem(slea.readInt());
if (!canBuy(chr, cItem, cs.getCash(cash))) {
c.enableCSActions();
return;
}
if (c.gainCharacterSlot()) {
c.announce(MaplePacketCreator.showBoughtCharacterSlot(c.getCharacterSlots()));
cs.gainCash(cash, cItem, chr.getWorld());
c.announce(MaplePacketCreator.showCash(chr));
} else {
chr.dropMessage(1, "You have already used up all 12 extra character slots.");
c.enableCSActions();
return;
}
} else if (action == 0x0D) { // Take from Cash Inventory
Item item = cs.findByCashId(slea.readInt());
if (item == null) {
c.enableCSActions();
return;
}
if (chr.getInventory(item.getInventoryType()).addItem(item) != -1) {
cs.removeFromInventory(item);
c.announce(MaplePacketCreator.takeFromCashInventory(item));
if(item instanceof Equip) {
Equip equip = (Equip) item;
if(equip.getRingId() >= 0) {
MapleRing ring = MapleRing.loadFromDb(equip.getRingId());
chr.addPlayerRing(ring);
}
}
}
} else if (action == 0x0E) { // Put into Cash Inventory
int cashId = slea.readInt();
slea.skip(4);
byte invType = slea.readByte();
if (invType < 1 || invType > 5) {
c.disconnect(false, false);
return;
}
MapleInventory mi = chr.getInventory(MapleInventoryType.getByType(invType));
Item item = mi.findByCashId(cashId);
if (item == null) {
c.enableCSActions();
return;
} else if (c.getPlayer().getPetIndex(item.getPetId()) > -1) {
chr.getClient().announce(MaplePacketCreator.serverNotice(1, "You cannot put the pet you currently equip into the Cash Shop inventory."));
c.enableCSActions();
return;
} else if (ItemConstants.isWeddingRing(item.getItemId()) || ItemConstants.isWeddingToken(item.getItemId())) {
chr.getClient().announce(MaplePacketCreator.serverNotice(1, "You cannot put relationship items into the Cash Shop inventory."));
c.enableCSActions();
return;
}
cs.addToInventory(item);
mi.removeSlot(item.getPosition());
c.announce(MaplePacketCreator.putIntoCashInventory(item, c.getAccID()));
} else if (action == 0x1D) { //crush ring (action 28)
int birthday = slea.readInt();
if (checkBirthday(c, birthday)) {
int toCharge = slea.readInt();
int SN = slea.readInt();
String recipientName = slea.readMapleAsciiString();
String text = slea.readMapleAsciiString();
CashItem itemRing = CashItemFactory.getItem(SN);
MapleCharacter partner = c.getChannelServer().getPlayerStorage().getCharacterByName(recipientName);
if (partner == null) {
chr.getClient().announce(MaplePacketCreator.serverNotice(1, "The partner you specified cannot be found.\r\nPlease make sure your partner is online and in the same channel."));
} else {
/* if (partner.getGender() == chr.getGender()) {
chr.dropMessage("You and your partner are the same gender, please buy a friendship ring.");
final int action = slea.readByte();
if (action == 0x03 || action == 0x1E) {
slea.readByte();
final int useNX = slea.readInt();
final int snCS = slea.readInt();
CashItem cItem = CashItemFactory.getItem(snCS);
if (!canBuy(chr, cItem, cs.getCash(useNX))) {
FilePrinter.printError(FilePrinter.ITEM, "Denied to sell cash item with SN " + snCS); // preventing NPE here thanks to MedicOP
c.enableCSActions();
return;
}*/ //Gotta let them faggots marry too, hence why this is commented out <3
if(itemRing.toItem() instanceof Equip) {
Equip eqp = (Equip) itemRing.toItem();
Pair<Integer, Integer> rings = MapleRing.createRing(itemRing.getItemId(), chr, partner);
eqp.setRingId(rings.getLeft());
cs.addToInventory(eqp);
c.announce(MaplePacketCreator.showBoughtCashItem(eqp, c.getAccID()));
cs.gift(partner.getId(), chr.getName(), text, eqp.getSN(), rings.getRight());
cs.gainCash(toCharge, itemRing, chr.getWorld());
chr.addCrushRing(MapleRing.loadFromDb(rings.getLeft()));
try {
chr.sendNote(partner.getName(), text, (byte) 1);
} catch (SQLException ex) {
ex.printStackTrace();
}
partner.showNote();
}
}
} else {
c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xC4));
}
c.announce(MaplePacketCreator.showCash(c.getPlayer()));
} else if (action == 0x20) {
int serialNumber = slea.readInt(); // thanks GabrielSin for detecting a potential exploit with 1 meso cash items.
if (serialNumber / 10000000 != 8) {
c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xC0));
return;
}
CashItem item = CashItemFactory.getItem(serialNumber);
if (item == null || !item.isOnSale()) {
c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xC0));
return;
}
int itemId = item.getItemId();
int itemPrice = item.getPrice();
if (itemPrice <= 0) {
c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xC0));
return;
}
if (chr.getMeso() >= itemPrice) {
if (chr.canHold(itemId)) {
chr.gainMeso(-itemPrice, false);
MapleInventoryManipulator.addById(c, itemId, (short) 1, "", -1);
c.announce(MaplePacketCreator.showBoughtQuestItem(itemId));
}
}
c.announce(MaplePacketCreator.showCash(c.getPlayer()));
} else if (action == 0x23) { //Friendship :3
int birthday = slea.readInt();
if (checkBirthday(c, birthday)) {
int payment = slea.readByte();
slea.skip(3); //0s
int snID = slea.readInt();
CashItem itemRing = CashItemFactory.getItem(snID);
String sentTo = slea.readMapleAsciiString();
int available = slea.readShort() - 1;
String text = slea.readAsciiString(available);
slea.readByte();
MapleCharacter partner = c.getChannelServer().getPlayerStorage().getCharacterByName(sentTo);
if (partner == null) {
chr.dropMessage("The partner you specified cannot be found.\r\nPlease make sure your partner is online and in the same channel.");
} else {
// Need to check to make sure its actually an equip and the right SN...
if(itemRing.toItem() instanceof Equip) {
Equip eqp = (Equip) itemRing.toItem();
Pair<Integer, Integer> rings = MapleRing.createRing(itemRing.getItemId(), chr, partner);
eqp.setRingId(rings.getLeft());
cs.addToInventory(eqp);
c.announce(MaplePacketCreator.showBoughtCashItem(eqp, c.getAccID()));
cs.gift(partner.getId(), chr.getName(), text, eqp.getSN(), rings.getRight());
cs.gainCash(payment, -itemRing.getPrice());
chr.addFriendshipRing(MapleRing.loadFromDb(rings.getLeft()));
try {
chr.sendNote(partner.getName(), text, (byte) 1);
} catch (SQLException ex) {
ex.printStackTrace();
}
partner.showNote();
}
if (action == 0x03) { // Item
if (ItemConstants.isCashStore(cItem.getItemId()) && chr.getLevel() < 16) {
c.enableCSActions();
return;
} else if (ItemConstants.isRateCoupon(cItem.getItemId()) && !ServerConstants.USE_SUPPLY_RATE_COUPONS) {
chr.dropMessage(1, "Rate coupons are currently unavailable to purchase.");
c.enableCSActions();
return;
} else if (ItemConstants.isMapleLife(cItem.getItemId()) && chr.getLevel() < 30) {
c.enableCSActions();
return;
}
Item item = cItem.toItem();
cs.addToInventory(item);
c.announce(MaplePacketCreator.showBoughtCashItem(item, c.getAccID()));
} else { // Package
List<Item> cashPackage = CashItemFactory.getPackage(cItem.getItemId());
for (Item item : cashPackage) {
cs.addToInventory(item);
}
c.announce(MaplePacketCreator.showBoughtCashPackage(cashPackage, c.getAccID()));
}
cs.gainCash(useNX, cItem, chr.getWorld());
c.announce(MaplePacketCreator.showCash(chr));
} else if (action == 0x04) {//TODO check for gender
int birthday = slea.readInt();
CashItem cItem = CashItemFactory.getItem(slea.readInt());
Map<String, String> recipient = MapleCharacter.getCharacterFromDatabase(slea.readMapleAsciiString());
String message = slea.readMapleAsciiString();
if (!canBuy(chr, cItem, cs.getCash(4)) || message.length() < 1 || message.length() > 73) {
c.enableCSActions();
return;
}
if (!checkBirthday(c, birthday)) {
c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xC4));
return;
} else if (recipient == null) {
c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xA9));
return;
} else if (recipient.get("accountid").equals(String.valueOf(c.getAccID()))) {
c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xA8));
return;
}
cs.gift(Integer.parseInt(recipient.get("id")), chr.getName(), message, cItem.getSN());
c.announce(MaplePacketCreator.showGiftSucceed(recipient.get("name"), cItem));
cs.gainCash(4, cItem, chr.getWorld());
c.announce(MaplePacketCreator.showCash(chr));
try {
chr.sendNote(recipient.get("name"), chr.getName() + " has sent you a gift! Go check out the Cash Shop.", (byte) 0); //fame or not
} catch (SQLException ex) {
ex.printStackTrace();
}
MapleCharacter receiver = c.getChannelServer().getPlayerStorage().getCharacterByName(recipient.get("name"));
if (receiver != null) receiver.showNote();
} else if (action == 0x05) { // Modify wish list
cs.clearWishList();
for (byte i = 0; i < 10; i++) {
int sn = slea.readInt();
CashItem cItem = CashItemFactory.getItem(sn);
if (cItem != null && cItem.isOnSale() && sn != 0) {
cs.addToWishList(sn);
}
}
c.announce(MaplePacketCreator.showWishList(chr, true));
} else if (action == 0x06) { // Increase Inventory Slots
slea.skip(1);
int cash = slea.readInt();
byte mode = slea.readByte();
if (mode == 0) {
byte type = slea.readByte();
if (cs.getCash(cash) < 4000) {
c.enableCSActions();
return;
}
if (chr.gainSlots(type, 4, false)) {
c.announce(MaplePacketCreator.showBoughtInventorySlots(type, chr.getSlots(type)));
cs.gainCash(cash, -4000);
c.announce(MaplePacketCreator.showCash(chr));
}
} else {
CashItem cItem = CashItemFactory.getItem(slea.readInt());
int type = (cItem.getItemId() - 9110000) / 1000;
if (!canBuy(chr, cItem, cs.getCash(cash))) {
c.enableCSActions();
return;
}
if (chr.gainSlots(type, 8, false)) {
c.announce(MaplePacketCreator.showBoughtInventorySlots(type, chr.getSlots(type)));
cs.gainCash(cash, cItem, chr.getWorld());
c.announce(MaplePacketCreator.showCash(chr));
}
}
} else if (action == 0x07) { // Increase Storage Slots
slea.skip(1);
int cash = slea.readInt();
byte mode = slea.readByte();
if (mode == 0) {
if (cs.getCash(cash) < 4000) {
c.enableCSActions();
return;
}
if (chr.getStorage().gainSlots(4)) {
FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " bought 4 slots to their account storage.");
chr.setUsedStorage();
c.announce(MaplePacketCreator.showBoughtStorageSlots(chr.getStorage().getSlots()));
cs.gainCash(cash, -4000);
c.announce(MaplePacketCreator.showCash(chr));
}
} else {
CashItem cItem = CashItemFactory.getItem(slea.readInt());
if (!canBuy(chr, cItem, cs.getCash(cash))) {
c.enableCSActions();
return;
}
if (chr.getStorage().gainSlots(8)) { // thanks ABaldParrot & Thora for detecting storage issues here
FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " bought 8 slots to their account storage.");
chr.setUsedStorage();
c.announce(MaplePacketCreator.showBoughtStorageSlots(chr.getStorage().getSlots()));
cs.gainCash(cash, cItem, chr.getWorld());
c.announce(MaplePacketCreator.showCash(chr));
}
}
} else if (action == 0x08) { // Increase Character Slots
slea.skip(1);
int cash = slea.readInt();
CashItem cItem = CashItemFactory.getItem(slea.readInt());
if (!canBuy(chr, cItem, cs.getCash(cash))) {
c.enableCSActions();
return;
}
if (c.gainCharacterSlot()) {
c.announce(MaplePacketCreator.showBoughtCharacterSlot(c.getCharacterSlots()));
cs.gainCash(cash, cItem, chr.getWorld());
c.announce(MaplePacketCreator.showCash(chr));
} else {
chr.dropMessage(1, "You have already used up all 12 extra character slots.");
c.enableCSActions();
return;
}
} else if (action == 0x0D) { // Take from Cash Inventory
Item item = cs.findByCashId(slea.readInt());
if (item == null) {
c.enableCSActions();
return;
}
if (chr.getInventory(item.getInventoryType()).addItem(item) != -1) {
cs.removeFromInventory(item);
c.announce(MaplePacketCreator.takeFromCashInventory(item));
if(item instanceof Equip) {
Equip equip = (Equip) item;
if(equip.getRingId() >= 0) {
MapleRing ring = MapleRing.loadFromDb(equip.getRingId());
chr.addPlayerRing(ring);
}
}
}
} else if (action == 0x0E) { // Put into Cash Inventory
int cashId = slea.readInt();
slea.skip(4);
byte invType = slea.readByte();
if (invType < 1 || invType > 5) {
c.disconnect(false, false);
return;
}
MapleInventory mi = chr.getInventory(MapleInventoryType.getByType(invType));
Item item = mi.findByCashId(cashId);
if (item == null) {
c.enableCSActions();
return;
} else if (c.getPlayer().getPetIndex(item.getPetId()) > -1) {
chr.getClient().announce(MaplePacketCreator.serverNotice(1, "You cannot put the pet you currently equip into the Cash Shop inventory."));
c.enableCSActions();
return;
} else if (ItemConstants.isWeddingRing(item.getItemId()) || ItemConstants.isWeddingToken(item.getItemId())) {
chr.getClient().announce(MaplePacketCreator.serverNotice(1, "You cannot put relationship items into the Cash Shop inventory."));
c.enableCSActions();
return;
}
cs.addToInventory(item);
mi.removeSlot(item.getPosition());
c.announce(MaplePacketCreator.putIntoCashInventory(item, c.getAccID()));
} else if (action == 0x1D) { //crush ring (action 28)
int birthday = slea.readInt();
if (checkBirthday(c, birthday)) {
int toCharge = slea.readInt();
int SN = slea.readInt();
String recipientName = slea.readMapleAsciiString();
String text = slea.readMapleAsciiString();
CashItem itemRing = CashItemFactory.getItem(SN);
MapleCharacter partner = c.getChannelServer().getPlayerStorage().getCharacterByName(recipientName);
if (partner == null) {
chr.getClient().announce(MaplePacketCreator.serverNotice(1, "The partner you specified cannot be found.\r\nPlease make sure your partner is online and in the same channel."));
} else {
/* if (partner.getGender() == chr.getGender()) {
chr.dropMessage(5, "You and your partner are the same gender, please buy a friendship ring.");
c.enableCSActions();
return;
}*/ //Gotta let them faggots marry too, hence why this is commented out <3
if(itemRing.toItem() instanceof Equip) {
Equip eqp = (Equip) itemRing.toItem();
Pair<Integer, Integer> rings = MapleRing.createRing(itemRing.getItemId(), chr, partner);
eqp.setRingId(rings.getLeft());
cs.addToInventory(eqp);
c.announce(MaplePacketCreator.showBoughtCashItem(eqp, c.getAccID()));
cs.gift(partner.getId(), chr.getName(), text, eqp.getSN(), rings.getRight());
cs.gainCash(toCharge, itemRing, chr.getWorld());
chr.addCrushRing(MapleRing.loadFromDb(rings.getLeft()));
try {
chr.sendNote(partner.getName(), text, (byte) 1);
} catch (SQLException ex) {
ex.printStackTrace();
}
partner.showNote();
}
}
} else {
c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xC4));
}
c.announce(MaplePacketCreator.showCash(c.getPlayer()));
} else if (action == 0x20) {
int serialNumber = slea.readInt(); // thanks GabrielSin for detecting a potential exploit with 1 meso cash items.
if (serialNumber / 10000000 != 8) {
c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xC0));
return;
}
CashItem item = CashItemFactory.getItem(serialNumber);
if (item == null || !item.isOnSale()) {
c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xC0));
return;
}
int itemId = item.getItemId();
int itemPrice = item.getPrice();
if (itemPrice <= 0) {
c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xC0));
return;
}
if (chr.getMeso() >= itemPrice) {
if (chr.canHold(itemId)) {
chr.gainMeso(-itemPrice, false);
MapleInventoryManipulator.addById(c, itemId, (short) 1, "", -1);
c.announce(MaplePacketCreator.showBoughtQuestItem(itemId));
}
}
c.announce(MaplePacketCreator.showCash(c.getPlayer()));
} else if (action == 0x23) { //Friendship :3
int birthday = slea.readInt();
if (checkBirthday(c, birthday)) {
int payment = slea.readByte();
slea.skip(3); //0s
int snID = slea.readInt();
CashItem itemRing = CashItemFactory.getItem(snID);
String sentTo = slea.readMapleAsciiString();
int available = slea.readShort() - 1;
String text = slea.readAsciiString(available);
slea.readByte();
MapleCharacter partner = c.getChannelServer().getPlayerStorage().getCharacterByName(sentTo);
if (partner == null) {
chr.dropMessage(5, "The partner you specified cannot be found. Please make sure your partner is online and in the same channel.");
} else {
// Need to check to make sure its actually an equip and the right SN...
if(itemRing.toItem() instanceof Equip) {
Equip eqp = (Equip) itemRing.toItem();
Pair<Integer, Integer> rings = MapleRing.createRing(itemRing.getItemId(), chr, partner);
eqp.setRingId(rings.getLeft());
cs.addToInventory(eqp);
c.announce(MaplePacketCreator.showBoughtCashItem(eqp, c.getAccID()));
cs.gift(partner.getId(), chr.getName(), text, eqp.getSN(), rings.getRight());
cs.gainCash(payment, -itemRing.getPrice());
chr.addFriendshipRing(MapleRing.loadFromDb(rings.getLeft()));
try {
chr.sendNote(partner.getName(), text, (byte) 1);
} catch (SQLException ex) {
ex.printStackTrace();
}
partner.showNote();
}
}
} else {
c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xC4));
}
c.announce(MaplePacketCreator.showCash(c.getPlayer()));
} else {
System.out.println("Unhandled action: " + action + "\n" + slea);
}
} else {
c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xC4));
} finally {
c.releaseClient();
}
c.announce(MaplePacketCreator.showCash(c.getPlayer()));
} else {
System.out.println("Unhandled action: " + action + "\n" + slea);
c.announce(MaplePacketCreator.enableActions());
}
}

View File

@@ -52,7 +52,7 @@ public final class ChangeMapHandler extends AbstractMaplePacketHandler {
return;
}
if (chr.getTrade() != null) {
MapleTrade.cancelTrade(chr);
MapleTrade.cancelTrade(chr, MapleTrade.TradeResult.UNSUCCESSFUL_ANOTHER_MAP);
}
if (slea.available() == 0) { //Cash Shop :)
if(!chr.getCashShop().isOpened()) {
@@ -79,66 +79,70 @@ public final class ChangeMapHandler extends AbstractMaplePacketHandler {
MaplePortal portal = chr.getMap().getPortal(startwp);
slea.readByte();
boolean wheel = slea.readShort() > 0;
if (targetid != -1 && !chr.isAlive()) {
MapleMap map = chr.getMap();
if (wheel && chr.haveItemWithId(5510000, false)) {
// thanks lucasziron for showing revivePlayer() also being triggered by Wheel
MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, 5510000, 1, true, false);
chr.announce(MaplePacketCreator.showWheelsLeft(chr.getItemQuantity(5510000, false)));
if (targetid != -1) {
if (!chr.isAlive()) {
MapleMap map = chr.getMap();
if (wheel && chr.haveItemWithId(5510000, false)) {
// thanks lucasziron for showing revivePlayer() also being triggered by Wheel
chr.updateHp(50);
chr.changeMap(map, map.findClosestPlayerSpawnpoint(chr.getPosition()));
MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, 5510000, 1, true, false);
chr.announce(MaplePacketCreator.showWheelsLeft(chr.getItemQuantity(5510000, false)));
chr.updateHp(50);
chr.changeMap(map, map.findClosestPlayerSpawnpoint(chr.getPosition()));
} else {
boolean executeStandardPath = true;
if (chr.getEventInstance() != null) {
executeStandardPath = chr.getEventInstance().revivePlayer(chr);
}
if (executeStandardPath) {
chr.respawn(map.getReturnMapId());
}
}
} else {
boolean executeStandardPath = true;
if (chr.getEventInstance() != null) {
executeStandardPath = chr.getEventInstance().revivePlayer(chr);
}
if (executeStandardPath) {
chr.respawn(map.getReturnMapId());
}
}
} else if (targetid != -1) {
if(chr.isGM()) {
MapleMap to = chr.getWarpMap(targetid);
chr.changeMap(to, to.getPortal(0));
}
else {
final int divi = chr.getMapId() / 100;
boolean warp = false;
if (divi == 0) {
if (targetid == 10000) {
warp = true;
}
} else if (divi == 20100) {
if (targetid == 104000000) {
c.announce(MaplePacketCreator.lockUI(false));
c.announce(MaplePacketCreator.disableUI(false));
warp = true;
}
} else if (divi == 9130401) { // Only allow warp if player is already in Intro map, or else = hack
if (targetid == 130000000 || targetid / 100 == 9130401) { // Cygnus introduction
warp = true;
}
} else if (divi == 9140900) { // Aran Introduction
if (targetid == 914090011 || targetid == 914090012 || targetid == 914090013 || targetid == 140090000) {
warp = true;
}
} else if (divi / 10 == 1020) { // Adventurer movie clip Intro
if (targetid == 1020000) {
warp = true;
}
} else if(divi / 10 >= 980040 && divi / 10 <= 980045) {
if(targetid == 980040000) {
warp = true;
}
}
if (warp) {
final MapleMap to = chr.getWarpMap(targetid);
if(chr.isGM()) {
MapleMap to = chr.getWarpMap(targetid);
chr.changeMap(to, to.getPortal(0));
}
else {
final int divi = chr.getMapId() / 100;
boolean warp = false;
if (divi == 0) {
if (targetid == 10000) {
warp = true;
}
} else if (divi == 20100) {
if (targetid == 104000000) {
c.announce(MaplePacketCreator.lockUI(false));
c.announce(MaplePacketCreator.disableUI(false));
warp = true;
}
} else if (divi == 9130401) { // Only allow warp if player is already in Intro map, or else = hack
if (targetid == 130000000 || targetid / 100 == 9130401) { // Cygnus introduction
warp = true;
}
} else if (divi == 9140900) { // Aran Introduction
if (targetid == 914090011 || targetid == 914090012 || targetid == 914090013 || targetid == 140090000) {
warp = true;
}
} else if (divi / 10 == 1020) { // Adventurer movie clip Intro
if (targetid == 1020000) {
warp = true;
}
} else if(divi / 10 >= 980040 && divi / 10 <= 980045) {
if(targetid == 980040000) {
warp = true;
}
}
if (warp) {
final MapleMap to = chr.getWarpMap(targetid);
chr.changeMap(to, to.getPortal(0));
}
}
}
}
}
if (portal != null && !portal.getPortalStatus()) {
c.announce(MaplePacketCreator.blockedMessage(1));
c.announce(MaplePacketCreator.enableActions());

View File

@@ -25,6 +25,7 @@ import client.MapleClient;
import net.AbstractMaplePacketHandler;
import server.MaplePortal;
import server.MapleTrade;
import server.MapleTrade.TradeResult;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
@@ -44,7 +45,7 @@ public final class ChangeMapSpecialHandler extends AbstractMaplePacketHandler {
return;
}
if (c.getPlayer().getTrade() != null) {
MapleTrade.cancelTrade(c.getPlayer());
MapleTrade.cancelTrade(c.getPlayer(), TradeResult.UNSUCCESSFUL_ANOTHER_MAP);
}
portal.enterPortal(c);
}

View File

@@ -31,6 +31,8 @@ import tools.data.input.SeekableLittleEndianAccessor;
* @author Xterminator
*/
public final class CloseChalkboardHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
c.getPlayer().setChalkboard(null);
c.getPlayer().getMap().broadcastMessage(MaplePacketCreator.useChalkboard(c.getPlayer(), true));

View File

@@ -0,0 +1,47 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2018 RonanLana
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.channel.handlers;
import client.MapleClient;
import client.MapleCharacter;
import net.AbstractMaplePacketHandler;
import net.server.guild.MapleAlliance;
import tools.data.input.SeekableLittleEndianAccessor;
/**
* @author Ronan
*/
public final class DenyAllianceRequestHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
slea.readByte();
String inviterName = slea.readMapleAsciiString();
String guildName = slea.readMapleAsciiString();
MapleCharacter chr = c.getWorldServer().getPlayerStorage().getCharacterByName(inviterName);
if (chr != null) {
MapleAlliance alliance = chr.getAlliance();
if (alliance != null) {
MapleAlliance.answerInvitation(c.getPlayer().getId(), guildName, alliance.getId(), false);
}
}
}
}

View File

@@ -24,7 +24,7 @@ package net.server.channel.handlers;
import client.MapleCharacter;
import client.MapleClient;
import net.AbstractMaplePacketHandler;
import tools.MaplePacketCreator;
import net.server.guild.MapleGuild;
import tools.data.input.SeekableLittleEndianAccessor;
/**
@@ -32,11 +32,13 @@ import tools.data.input.SeekableLittleEndianAccessor;
* @author Xterminator
*/
public final class DenyGuildRequestHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
slea.readByte();
MapleCharacter cfrom = c.getChannelServer().getPlayerStorage().getCharacterByName(slea.readMapleAsciiString());
MapleCharacter cfrom = c.getWorldServer().getPlayerStorage().getCharacterByName(slea.readMapleAsciiString());
if (cfrom != null) {
cfrom.getClient().announce(MaplePacketCreator.denyGuildInvitation(c.getPlayer().getName()));
MapleGuild.answerInvitation(c.getPlayer().getId(), c.getPlayer().getName(), cfrom.getGuildId(), false);
}
}
}

View File

@@ -24,15 +24,22 @@ package net.server.channel.handlers;
import client.MapleCharacter;
import client.MapleClient;
import net.AbstractMaplePacketHandler;
import net.server.coordinator.MapleInviteCoordinator;
import net.server.coordinator.MapleInviteCoordinator.InviteResult;
import net.server.coordinator.MapleInviteCoordinator.InviteType;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
public final class DenyPartyRequestHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
slea.readByte();
MapleCharacter cfrom = c.getChannelServer().getPlayerStorage().getCharacterByName(slea.readMapleAsciiString());
if (cfrom != null) {
cfrom.getClient().announce(MaplePacketCreator.partyStatusMessage(23, c.getPlayer().getName()));
if (MapleInviteCoordinator.answerInvite(InviteType.PARTY, c.getPlayer().getId(), cfrom.getPartyId(), false).getLeft() == InviteResult.DENIED) {
cfrom.getClient().announce(MaplePacketCreator.partyStatusMessage(23, c.getPlayer().getName()));
}
}
}
}

View File

@@ -68,6 +68,7 @@ public class EnterCashShopHandler extends AbstractMaplePacketHandler {
Server.getInstance().getPlayerBuffStorage().addDiseasesToStorage(mc.getId(), mc.getAllDiseases());
mc.setAwayFromChannelWorld();
mc.notifyMapTransferToPartner(-1);
mc.removeIncomingInvites();
mc.cancelAllBuffs(true);
mc.cancelAllDebuffs();
mc.cancelBuffExpireTask();

View File

@@ -93,6 +93,7 @@ public final class EnterMTSHandler extends AbstractMaplePacketHandler {
Server.getInstance().getPlayerBuffStorage().addDiseasesToStorage(chr.getId(), chr.getAllDiseases());
chr.setAwayFromChannelWorld();
chr.notifyMapTransferToPartner(-1);
chr.removeIncomingInvites();
chr.cancelAllBuffs(true);
chr.cancelAllDebuffs();
chr.cancelBuffExpireTask();

View File

@@ -28,7 +28,6 @@ import constants.ServerConstants;
import client.MapleClient;
import net.AbstractMaplePacketHandler;
import tools.data.input.SeekableLittleEndianAccessor;
import java.util.Iterator;
import tools.MaplePacketCreator;
import client.MapleCharacter;
import net.server.Server;
@@ -47,50 +46,8 @@ public final class GuildOperationHandler extends AbstractMaplePacketHandler {
return true;
}
private class Invited {
public String name;
public int gid;
public long expiration;
public Invited(String n, int id) {
name = n.toLowerCase();
gid = id;
expiration = currentServerTime() + 60 * 60 * 1000;
}
@Override
public boolean equals(Object other) {
if (!(other instanceof Invited)) {
return false;
}
Invited oth = (Invited) other;
return (gid == oth.gid && name.equals(oth));
}
@Override
public int hashCode() {
int hash = 3;
hash = 83 * hash + (this.name != null ? this.name.hashCode() : 0);
hash = 83 * hash + this.gid;
return hash;
}
}
private java.util.List<Invited> invited = new java.util.LinkedList<Invited>();
private long nextPruneTime = currentServerTime() + 20 * 60 * 1000;
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
if (currentServerTime() >= nextPruneTime) {
Iterator<Invited> itr = invited.iterator();
Invited inv;
while (itr.hasNext()) {
inv = itr.next();
if (currentServerTime() >= inv.expiration) {
itr.remove();
}
}
nextPruneTime = currentServerTime() + 20 * 60 * 1000;
}
MapleCharacter mc = c.getPlayer();
byte type = slea.readByte();
int allianceId = -1;
@@ -134,16 +91,13 @@ public final class GuildOperationHandler extends AbstractMaplePacketHandler {
if (mc.getGuildId() <= 0 || mc.getGuildRank() > 2) {
return;
}
String name = slea.readMapleAsciiString();
MapleGuildResponse mgr = MapleGuild.sendInvite(c, name);
String targetName = slea.readMapleAsciiString();
MapleGuildResponse mgr = MapleGuild.sendInvitation(c, targetName);
if (mgr != null) {
c.announce(mgr.getPacket());
} else {
Invited inv = new Invited(name, mc.getGuildId());
if (!invited.contains(inv)) {
invited.add(inv);
}
}
c.announce(mgr.getPacket(targetName));
} else {} // already sent invitation, do nothing
break;
case 0x06:
if (mc.getGuildId() > 0) {
@@ -156,28 +110,18 @@ public final class GuildOperationHandler extends AbstractMaplePacketHandler {
System.out.println("[hax] " + mc.getName() + " attempted to join a guild with a different character id.");
return;
}
name = mc.getName().toLowerCase();
Iterator<Invited> itr = invited.iterator();
boolean bOnList = false;
while (itr.hasNext()) {
Invited inv = itr.next();
if (gid == inv.gid && name.equals(inv.name)) {
bOnList = true;
itr.remove();
break;
}
}
if (!bOnList) {
System.out.println("[hax] " + mc.getName() + " is trying to join a guild that never invited him/her (or that the invitation has expired)");
if (!MapleGuild.answerInvitation(cid, mc.getName(), gid, true)) {
return;
}
mc.getMGC().setGuildId(gid); // joins the guild
mc.getMGC().setGuildRank(5); // start at lowest rank
mc.getMGC().setAllianceRank(5);
int s = Server.getInstance().addGuildMember(mc.getMGC(), mc);
if (s == 0) {
c.getPlayer().dropMessage(1, "The Guild you are trying to join is already full.");
c.getPlayer().dropMessage(1, "The guild you are trying to join is already full.");
mc.getMGC().setGuildId(0);
return;
}
@@ -193,7 +137,7 @@ public final class GuildOperationHandler extends AbstractMaplePacketHandler {
break;
case 0x07:
cid = slea.readInt();
name = slea.readMapleAsciiString();
String name = slea.readMapleAsciiString();
if (cid != mc.getId() || !name.equals(mc.getName()) || mc.getGuildId() <= 0) {
System.out.println("[hax] " + mc.getName() + " tried to quit guild under the name \"" + name + "\" and current guild id of " + mc.getGuildId() + ".");
return;

View File

@@ -72,6 +72,28 @@ class PairedQuicksort {
} while (i <= j);
}
private void PartitionByItemIdReverse(int Esq, int Dir, ArrayList<Item> A) {
Item x, w;
i = Esq;
j = Dir;
x = A.get((i + j) / 2);
do {
while (x.getItemId() < A.get(i).getItemId()) i++;
while (x.getItemId() > A.get(j).getItemId()) j--;
if (i <= j) {
w = A.get(i);
A.set(i, A.get(j));
A.set(j, w);
i++;
j--;
}
} while (i <= j);
}
private void PartitionByName(int Esq, int Dir, ArrayList<Item> A) {
Item x, w;
@@ -163,10 +185,64 @@ class PairedQuicksort {
if (i < Dir) MapleQuicksort(i, Dir, A, sort);
}
private static int getItemSubtype(Item it) {
return it.getItemId() / 10000;
}
private int[] BinarySearchElement(ArrayList<Item> A, int rangeId) {
int st = 0, en = A.size() - 1;
int mid = -1, idx = -1;
while (en >= st) {
idx = (st + en) / 2;
mid = getItemSubtype(A.get(idx));
if (mid == rangeId) {
break;
} else if (mid < rangeId) {
st = idx + 1;
} else {
en = idx - 1;
}
}
if (en < st) {
return null;
}
st = idx - 1;
en = idx + 1;
while (st >= 0 && getItemSubtype(A.get(st)) == rangeId) {
st -= 1;
}
st += 1;
while (en < A.size() && getItemSubtype(A.get(en)) == rangeId) {
en += 1;
}
en -= 1;
return new int[]{st, en};
}
public void reverseSortSublist(ArrayList<Item> A, int[] range) {
if (range != null) {
PartitionByItemIdReverse(range[0], range[1], A);
}
}
public PairedQuicksort(ArrayList<Item> A, int primarySort, int secondarySort) {
intersect = new ArrayList<>();
if(A.size() > 0) MapleQuicksort(0, A.size() - 1, A, primarySort);
if(A.size() > 0) {
MapleQuicksort(0, A.size() - 1, A, primarySort);
if (A.get(0).getInventoryType().equals(MapleInventoryType.USE)) { // thanks KDA & Vcoc for suggesting stronger projectiles coming before weaker ones
reverseSortSublist(A, BinarySearchElement(A, 206)); // arrows
reverseSortSublist(A, BinarySearchElement(A, 207)); // stars
reverseSortSublist(A, BinarySearchElement(A, 233)); // bullets
}
}
intersect.add(0);
for(int ind = 1; ind < A.size(); ind++) {

View File

@@ -47,7 +47,12 @@ public final class MesoDropHandler extends AbstractMaplePacketHandler {
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.attemptCatchFish(meso)) {
player.getMap().disappearingMesoDrop(meso, player, player, player.getPosition());
} else {
player.getMap().spawnMesoDrop(meso, player.getPosition(), player, player, true, (byte) 2);
}
}
}
}

View File

@@ -24,84 +24,104 @@ package net.server.channel.handlers;
import client.MapleCharacter;
import client.MapleClient;
import net.AbstractMaplePacketHandler;
import net.server.coordinator.MapleInviteCoordinator;
import net.server.coordinator.MapleInviteCoordinator.InviteResult;
import net.server.coordinator.MapleInviteCoordinator.InviteType;
import net.server.world.MapleMessenger;
import net.server.world.MapleMessengerCharacter;
import net.server.world.World;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.data.input.SeekableLittleEndianAccessor;
public final class MessengerHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
String input;
byte mode = slea.readByte();
MapleCharacter player = c.getPlayer();
World world = c.getWorldServer();
MapleMessenger messenger = player.getMessenger();
switch (mode) {
case 0x00:
if (messenger == null) {
c.tryacquireClient();
try {
String input;
byte mode = slea.readByte();
MapleCharacter player = c.getPlayer();
World world = c.getWorldServer();
MapleMessenger messenger = player.getMessenger();
switch (mode) {
case 0x00:
int messengerid = slea.readInt();
if (messengerid == 0) {
MapleMessengerCharacter messengerplayer = new MapleMessengerCharacter(player, 0);
messenger = world.createMessenger(messengerplayer);
player.setMessenger(messenger);
player.setMessengerPosition(0);
} else {
messenger = world.getMessenger(messengerid);
int position = messenger.getLowestPosition();
MapleMessengerCharacter messengerplayer = new MapleMessengerCharacter(player, position);
if (messenger.getMembers().size() < 3) {
if (messenger == null) {
if (messengerid == 0) {
MapleInviteCoordinator.removeInvite(InviteType.MESSENGER, player.getId());
MapleMessengerCharacter messengerplayer = new MapleMessengerCharacter(player, 0);
messenger = world.createMessenger(messengerplayer);
player.setMessenger(messenger);
player.setMessengerPosition(position);
world.joinMessenger(messenger.getId(), messengerplayer, player.getName(), messengerplayer.getChannel());
}
}
}
break;
case 0x02:
player.closePlayerMessenger();
break;
case 0x03:
if (messenger.getMembers().size() < 3) {
input = slea.readMapleAsciiString();
MapleCharacter target = c.getChannelServer().getPlayerStorage().getCharacterByName(input);
if (target != null) {
if (target.getMessenger() == null) {
target.getClient().announce(MaplePacketCreator.messengerInvite(c.getPlayer().getName(), messenger.getId()));
c.announce(MaplePacketCreator.messengerNote(input, 4, 1));
player.setMessengerPosition(0);
} else {
c.announce(MaplePacketCreator.messengerChat(player.getName() + " : " + input + " is already using Maple Messenger"));
messenger = world.getMessenger(messengerid);
if (messenger != null) {
Pair<InviteResult, MapleCharacter> inviteRes = MapleInviteCoordinator.answerInvite(InviteType.MESSENGER, player.getId(), messengerid, true);
InviteResult res = inviteRes.getLeft();
if (res == InviteResult.ACCEPTED) {
int position = messenger.getLowestPosition();
MapleMessengerCharacter messengerplayer = new MapleMessengerCharacter(player, position);
if (messenger.getMembers().size() < 3) {
player.setMessenger(messenger);
player.setMessengerPosition(position);
world.joinMessenger(messenger.getId(), messengerplayer, player.getName(), messengerplayer.getChannel());
}
} else {
player.message("Could not verify your Maple Messenger accept since the invitation rescinded.");
}
}
}
} else {
if (world.find(input) > -1) {
world.messengerInvite(c.getPlayer().getName(), messenger.getId(), input, c.getChannel());
MapleInviteCoordinator.answerInvite(InviteType.MESSENGER, player.getId(), messengerid, false);
}
break;
case 0x02:
player.closePlayerMessenger();
break;
case 0x03:
if (messenger == null) {
c.announce(MaplePacketCreator.messengerChat(player.getName() + " : This Maple Messenger is currently unavailable. Please quit this chat."));
} else if (messenger.getMembers().size() < 3) {
input = slea.readMapleAsciiString();
MapleCharacter target = c.getChannelServer().getPlayerStorage().getCharacterByName(input);
if (target != null) {
if (target.getMessenger() == null) {
if (MapleInviteCoordinator.createInvite(InviteType.MESSENGER, c.getPlayer(), messenger.getId(), target.getId())) {
target.getClient().announce(MaplePacketCreator.messengerInvite(c.getPlayer().getName(), messenger.getId()));
c.announce(MaplePacketCreator.messengerNote(input, 4, 1));
} else {
c.announce(MaplePacketCreator.messengerChat(player.getName() + " : " + input + " is already managing a Maple Messenger invitation"));
}
} else {
c.announce(MaplePacketCreator.messengerChat(player.getName() + " : " + input + " is already using Maple Messenger"));
}
} else {
c.announce(MaplePacketCreator.messengerNote(input, 4, 0));
if (world.find(input) > -1) {
world.messengerInvite(c.getPlayer().getName(), messenger.getId(), input, c.getChannel());
} else {
c.announce(MaplePacketCreator.messengerNote(input, 4, 0));
}
}
} else {
c.announce(MaplePacketCreator.messengerChat(player.getName() + " : You cannot have more than 3 people in the Maple Messenger"));
}
} else {
c.announce(MaplePacketCreator.messengerChat(player.getName() + " : You cannot have more than 3 people in the Maple Messenger"));
}
break;
case 0x05:
String targeted = slea.readMapleAsciiString();
MapleCharacter target = c.getChannelServer().getPlayerStorage().getCharacterByName(targeted);
if (target != null) {
if (target.getMessenger() != null) {
target.getClient().announce(MaplePacketCreator.messengerNote(player.getName(), 5, 0));
break;
case 0x05:
String targeted = slea.readMapleAsciiString();
world.declineChat(targeted, player);
break;
case 0x06:
if (messenger != null) {
MapleMessengerCharacter messengerplayer = new MapleMessengerCharacter(player, player.getMessengerPosition());
input = slea.readMapleAsciiString();
world.messengerChat(messenger, input, messengerplayer.getName());
}
} else {
world.declineChat(targeted, player.getName());
}
break;
case 0x06:
if (messenger != null) {
MapleMessengerCharacter messengerplayer = new MapleMessengerCharacter(player, player.getMessengerPosition());
input = slea.readMapleAsciiString();
world.messengerChat(messenger, input, messengerplayer.getName());
}
break;
break;
}
} finally {
c.releaseClient();
}
}
}

View File

@@ -31,9 +31,14 @@ import tools.data.input.SeekableLittleEndianAccessor;
import client.MapleCharacter;
import client.MapleClient;
import constants.ServerConstants;
import java.util.List;
import net.server.coordinator.MapleInviteCoordinator;
import net.server.coordinator.MapleInviteCoordinator.InviteResult;
import net.server.coordinator.MapleInviteCoordinator.InviteType;
import scripting.event.EventInstanceManager;
import server.maps.MapleMap;
import tools.Pair;
import java.util.List;
public final class PartyOperationHandler extends AbstractMaplePacketHandler {
@@ -101,26 +106,33 @@ public final class PartyOperationHandler extends AbstractMaplePacketHandler {
}
case 3: { // join
int partyid = slea.readInt();
if (party == null) {
party = world.getParty(partyid);
if (party != null) {
if (party.getMembers().size() < 6) {
partyplayer = new MaplePartyCharacter(player);
player.getMap().addPartyMember(player);
Pair<InviteResult, MapleCharacter> inviteRes = MapleInviteCoordinator.answerInvite(InviteType.PARTY, player.getId(), partyid, true);
InviteResult res = inviteRes.getLeft();
if (res == InviteResult.ACCEPTED) {
if (party == null) {
party = world.getParty(partyid);
if (party != null) {
if (party.getMembers().size() < 6) {
partyplayer = new MaplePartyCharacter(player);
player.getMap().addPartyMember(player);
world.updateParty(party.getId(), PartyOperation.JOIN, partyplayer);
player.receivePartyMemberHP();
player.updatePartyMemberHP();
player.partyOperationUpdate(party, null);
world.updateParty(party.getId(), PartyOperation.JOIN, partyplayer);
player.receivePartyMemberHP();
player.updatePartyMemberHP();
player.partyOperationUpdate(party, null);
} else {
c.announce(MaplePacketCreator.partyStatusMessage(17));
}
} else {
c.announce(MaplePacketCreator.partyStatusMessage(17));
c.announce(MaplePacketCreator.serverNotice(5, "The person you have invited to the party is already in one."));
}
} else {
c.announce(MaplePacketCreator.serverNotice(5, "The person you have invited to the party is already in one."));
c.announce(MaplePacketCreator.serverNotice(5, "You can't join the party as you are already in one."));
}
} else {
c.announce(MaplePacketCreator.serverNotice(5, "You can't join the party as you are already in one."));
c.announce(MaplePacketCreator.serverNotice(5, "You couldn't join the party due to an expired invitation request."));
}
break;
}
@@ -154,7 +166,11 @@ public final class PartyOperationHandler extends AbstractMaplePacketHandler {
c.announce(MaplePacketCreator.partyCreated(party, partyplayer.getId()));
}
if (party.getMembers().size() < 6) {
invited.getClient().announce(MaplePacketCreator.partyInvite(player));
if (MapleInviteCoordinator.createInvite(InviteType.PARTY, player, party.getId(), invited.getId())) {
invited.getClient().announce(MaplePacketCreator.partyInvite(player));
} else {
c.announce(MaplePacketCreator.partyStatusMessage(22, invited.getName()));
}
} else {
c.announce(MaplePacketCreator.partyStatusMessage(17));
}

View File

@@ -36,7 +36,6 @@ import net.AbstractMaplePacketHandler;
import server.MapleItemInformationProvider;
import server.MapleTrade;
import constants.GameConstants;
import java.sql.SQLException;
import server.maps.FieldLimit;
import server.maps.MapleHiredMerchant;
import server.maps.MapleMapObject;
@@ -48,6 +47,9 @@ import tools.FilePrinter;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
import java.awt.Point;
import java.sql.SQLException;
/**
*
* @author Matze
@@ -146,7 +148,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
}
byte createType = slea.readByte();
if (createType == 3) {// trade
if (createType == 3) { // trade
MapleTrade.startTrade(chr);
} else if (createType == 1) { // omok mini game
int status = establishMiniroomStatus(chr, true);
@@ -234,7 +236,15 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
chr.getClient().announce(MaplePacketCreator.getMiniRoomError(status));
return;
}
try {
Point cpos = chr.getPosition();
if (chr.getMap().findClosestWarpPortal(cpos).getPosition().distance(cpos) < 120.0) {
chr.getClient().announce(MaplePacketCreator.getMiniRoomError(10));
return;
}
} catch (NullPointerException npe) {}
String desc = slea.readMapleAsciiString();
slea.skip(3);
int itemId = slea.readInt();
@@ -259,20 +269,22 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
}
}
} else if (mode == Action.INVITE.getCode()) {
int otherPlayer = slea.readInt();
if (chr.getId() == chr.getMap().getCharacterById(otherPlayer).getId()) {
int otherCid = slea.readInt();
MapleCharacter other = chr.getMap().getCharacterById(otherCid);
if (other == null || chr.getId() == other.getId()) {
return;
}
MapleTrade.inviteTrade(chr, chr.getMap().getCharacterById(otherPlayer));
MapleTrade.inviteTrade(chr, other);
} else if (mode == Action.DECLINE.getCode()) {
MapleTrade.declineTrade(chr);
} else if (mode == Action.VISIT.getCode()) {
if (chr.getTrade() != null && chr.getTrade().getPartner() != null) {
if (!chr.getTrade().isFullTrade() && !chr.getTrade().getPartner().isFullTrade()) {
MapleTrade.visitTrade(chr, chr.getTrade().getPartner().getChr());
MapleTrade.visitTrade(chr, chr.getTrade().getPartner().getChr());
} else {
chr.getClient().announce(MaplePacketCreator.getMiniRoomError(2));
return;
chr.getClient().announce(MaplePacketCreator.getMiniRoomError(2));
return;
}
} else {
if (isTradeOpen(chr)) return;
@@ -329,7 +341,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
}
} else if (mode == Action.EXIT.getCode()) {
if (chr.getTrade() != null) {
MapleTrade.cancelTrade(chr);
MapleTrade.cancelTrade(chr, MapleTrade.TradeResult.PARTNER_CANCEL);
} else {
chr.closePlayerShop();
chr.closeMiniGame();
@@ -627,6 +639,10 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
c.announce(MaplePacketCreator.updateHiredMerchant(merchant, chr));
if (ServerConstants.USE_ENFORCE_MERCHANT_SAVE) {
chr.saveCharToDB(false);
}
try {
merchant.saveItems(false); // thanks Masterrulax for realizing yet another dupe with merchants/Fredrick
} catch (SQLException ex) {
@@ -726,14 +742,14 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
MapleHiredMerchant merchant = chr.getHiredMerchant();
if(merchant != null) {
if (merchant.getItems().isEmpty() && merchant.isOwner(chr)) {
merchant.closeShop(c, false);
chr.setHasMerchant(false);
}
if (merchant.isOwner(chr)) {
merchant.clearMessages();
merchant.setOpen(true);
merchant.getMap().broadcastMessage(MaplePacketCreator.updateHiredMerchantBox(merchant));
if (merchant.getItems().isEmpty()) {
merchant.closeOwnerMerchant(chr);
} else {
merchant.clearMessages();
merchant.setOpen(true);
merchant.getMap().broadcastMessage(MaplePacketCreator.updateHiredMerchantBox(merchant));
}
}
}

View File

@@ -32,7 +32,6 @@ import net.AbstractMaplePacketHandler;
import client.inventory.manipulator.MapleInventoryManipulator;
import server.MapleItemInformationProvider;
import tools.MaplePacketCreator;
import tools.Randomizer;
import tools.data.input.SeekableLittleEndianAccessor;
public final class SkillBookHandler extends AbstractMaplePacketHandler {
@@ -75,7 +74,9 @@ public final class SkillBookHandler extends AbstractMaplePacketHandler {
} else {
canuse = false;
}
player.getClient().announce(MaplePacketCreator.skillBookSuccess(player, skill, maxlevel, canuse, success));
// thanks Vcoc for noting skill book result not showing for all in area
player.getMap().broadcastMessage(MaplePacketCreator.skillBookResult(player, skill, maxlevel, canuse, success));
}
}
}

View File

@@ -84,6 +84,9 @@ public final class SummonDamageHandler extends AbstractDealDamageHandler {
allDamage.add(new SummonAttackEntry(monsterOid, damage));
}
player.getMap().broadcastMessage(player, MaplePacketCreator.summonAttack(player.getId(), summon.getObjectId(), direction, allDamage), summon.getPosition());
if (player.getMap().isOwnershipRestricted(player)) {
return;
}
for (SummonAttackEntry attackEntry : allDamage) {
int damage = attackEntry.getDamage();
MapleMonster target = player.getMap().getMonsterByOid(attackEntry.getMonsterOid());

View File

@@ -400,9 +400,16 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
} else if (itemType == 533) {
NPCScriptManager.getInstance().start(c, 9010009, null);
} else if (itemType == 537) {
if (GameConstants.isFreeMarketRoom(player.getMapId())) {
player.dropMessage(5, "You cannot use the chalkboard here.");
player.getClient().announce(MaplePacketCreator.enableActions());
return;
}
player.setChalkboard(slea.readMapleAsciiString());
player.getMap().broadcastMessage(MaplePacketCreator.useChalkboard(player, false));
player.getClient().announce(MaplePacketCreator.enableActions());
remove(c, position, itemId);
} else if (itemType == 539) {
List<String> strLines = new LinkedList<>();
for (int i = 0; i < 4; i++) {

View File

@@ -0,0 +1,149 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2018 RonanLana
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.coordinator;
import client.MapleCharacter;
import tools.Pair;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.HashSet;
import java.util.concurrent.ConcurrentHashMap;
/**
*
* @author Ronan
*/
public class MapleInviteCoordinator {
public enum InviteResult {
ACCEPTED,
DENIED,
NOT_FOUND;
}
public enum InviteType {
//BUDDY, (not needed)
//FAMILY, (not implemented)
MESSENGER,
TRADE,
PARTY,
GUILD,
ALLIANCE;
final ConcurrentHashMap<Integer, Object> invites;
final ConcurrentHashMap<Integer, MapleCharacter> inviteFrom;
final ConcurrentHashMap<Integer, Integer> inviteTimeouts;
private InviteType() {
invites = new ConcurrentHashMap<>();
inviteTimeouts = new ConcurrentHashMap<>();
inviteFrom = new ConcurrentHashMap<>();
}
private Map<Integer, Object> getRequestsTable() {
return invites;
}
private Map<Integer, Integer> getRequestsTimeoutTable() {
return inviteTimeouts;
}
private MapleCharacter removeRequest(Integer target) {
invites.remove(target);
MapleCharacter from = inviteFrom.remove(target);
inviteTimeouts.remove(target);
return from;
}
private boolean addRequest(MapleCharacter from, Object referenceFrom, int targetCid) {
Object v = invites.putIfAbsent(targetCid, referenceFrom);
if (v != null) { // there was already an entry
return false;
}
inviteFrom.put(targetCid, from);
inviteTimeouts.put(targetCid, 0);
return true;
}
private boolean hasRequest(int targetCid) {
return invites.containsKey(targetCid);
}
}
// note: referenceFrom is a specific value that represents the "common association" created between the sender/recver parties
public static boolean createInvite(InviteType type, MapleCharacter from, Object referenceFrom, int targetCid) {
return type.addRequest(from, referenceFrom, targetCid);
}
public static boolean hasInvite(InviteType type, int targetCid) {
return type.hasRequest(targetCid);
}
public static Pair<InviteResult, MapleCharacter> answerInvite(InviteType type, int targetCid, Object referenceFrom, boolean answer) {
Map<Integer, Object> table = type.getRequestsTable();
MapleCharacter from = null;
InviteResult result = InviteResult.NOT_FOUND;
Object reference = table.get(targetCid);
if (referenceFrom.equals(reference)) {
from = type.removeRequest(targetCid);
if (from != null && !from.isLoggedinWorld()) from = null;
result = answer ? InviteResult.ACCEPTED : InviteResult.DENIED;
}
return new Pair<>(result, from);
}
public static void removeInvite(InviteType type, int targetCid) {
type.removeRequest(targetCid);
}
public static void removePlayerIncomingInvites(int cid) {
for (InviteType it : InviteType.values()) {
it.removeRequest(cid);
}
}
public static void runTimeoutSchedule() {
for (InviteType it : InviteType.values()) {
Map<Integer, Integer> timeoutTable = it.getRequestsTimeoutTable();
if (!timeoutTable.isEmpty()) {
Set<Entry<Integer, Integer>> entrySet = new HashSet<>(timeoutTable.entrySet());
for (Entry<Integer, Integer> e : entrySet) {
int eVal = e.getValue();
if (eVal > 5) { // 3min to expire
it.removeRequest(e.getKey());
} else {
timeoutTable.put(e.getKey(), eVal + 1);
}
}
}
}
}
}

View File

@@ -34,6 +34,8 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.HashMap;
@@ -215,7 +217,7 @@ public class MapleSessionCoordinator {
}
private Lock getCoodinatorLock(String remoteHost) {
return poolLock.get(remoteHost.hashCode() % 100);
return poolLock.get(Math.abs(remoteHost.hashCode()) % 100);
}
private static String getRemoteIp(IoSession session) {
@@ -318,7 +320,12 @@ public class MapleSessionCoordinator {
MapleClient client = getSessionClient(session);
if (client != null) {
onlineClients.remove(client.getAccID());
MapleClient loggedClient = onlineClients.get(client.getAccID());
// do not remove an online game session here, only login session
if (loggedClient != null && loggedClient.getSessionId() == client.getSessionId()) {
onlineClients.remove(client.getAccID());
}
}
}
}
@@ -466,15 +473,24 @@ public class MapleSessionCoordinator {
}
public void closeSession(IoSession session, Boolean immediately) {
String hwid = (String) session.removeAttribute(MapleClient.CLIENT_HWID);
String hwid = (String) session.removeAttribute(MapleClient.CLIENT_NIBBLEHWID); // making sure to clean up calls to this function on login phase
onlineRemoteHwids.remove(hwid);
hwid = (String) session.removeAttribute(MapleClient.CLIENT_NIBBLEHWID); // making sure to clean up calls to this function on login phase
hwid = (String) session.removeAttribute(MapleClient.CLIENT_HWID);
onlineRemoteHwids.remove(hwid);
MapleClient client = getSessionClient(session);
if (client != null) {
onlineClients.remove(client.getAccID());
if (hwid != null) { // is a game session
onlineClients.remove(client.getAccID());
} else {
MapleClient loggedClient = onlineClients.get(client.getAccID());
// do not remove an online game session here, only login session
if (loggedClient != null && loggedClient.getSessionId() == client.getSessionId()) {
onlineClients.remove(client.getAccID());
}
}
}
if (immediately != null) {
@@ -523,4 +539,94 @@ public class MapleSessionCoordinator {
public void runUpdateLoginHistory() {
loginStorage.updateLoginHistory();
}
public void printSessionTrace() {
if (!onlineClients.isEmpty()) {
List<Entry<Integer, MapleClient>> elist = new ArrayList<>(onlineClients.entrySet());
Collections.sort(elist, new Comparator<Entry<Integer, MapleClient>>() {
@Override
public int compare(Entry<Integer, MapleClient> e1, Entry<Integer, MapleClient> e2) {
return e1.getKey().compareTo(e2.getKey());
}
});
System.out.println("Current online clients: ");
for (Entry<Integer, MapleClient> e : elist) {
System.out.println(" " + e.getKey());
}
}
if (!onlineRemoteHwids.isEmpty()) {
List<String> slist = new ArrayList<>(onlineRemoteHwids);
Collections.sort(slist);
System.out.println("Current online HWIDs: ");
for (String s : slist) {
System.out.println(" " + s);
}
}
if (!loginRemoteHosts.isEmpty()) {
List<Entry<String, Set<IoSession>>> elist = new ArrayList<>(loginRemoteHosts.entrySet());
Collections.sort(elist, new Comparator<Entry<String, Set<IoSession>>>() {
@Override
public int compare(Entry<String, Set<IoSession>> e1, Entry<String, Set<IoSession>> e2) {
return e1.getKey().compareTo(e2.getKey());
}
});
System.out.println("Current login sessions: ");
for (Entry<String, Set<IoSession>> e : elist) {
System.out.println(" " + e.getKey() + ", size: " + e.getValue().size());
}
}
}
public void printSessionTrace(MapleClient c) {
String str = "Opened server sessions:\r\n\r\n";
if (!onlineClients.isEmpty()) {
List<Entry<Integer, MapleClient>> elist = new ArrayList<>(onlineClients.entrySet());
Collections.sort(elist, new Comparator<Entry<Integer, MapleClient>>() {
@Override
public int compare(Entry<Integer, MapleClient> e1, Entry<Integer, MapleClient> e2) {
return e1.getKey().compareTo(e2.getKey());
}
});
str += ("Current online clients:\r\n");
for (Entry<Integer, MapleClient> e : elist) {
str += (" " + e.getKey() + "\r\n");
}
}
if (!onlineRemoteHwids.isEmpty()) {
List<String> slist = new ArrayList<>(onlineRemoteHwids);
Collections.sort(slist);
str += ("Current online HWIDs:\r\n");
for (String s : slist) {
str += (" " + s + "\r\n");
}
}
if (!loginRemoteHosts.isEmpty()) {
List<Entry<String, Set<IoSession>>> elist = new ArrayList<>(loginRemoteHosts.entrySet());
Collections.sort(elist, new Comparator<Entry<String, Set<IoSession>>>() {
@Override
public int compare(Entry<String, Set<IoSession>> e1, Entry<String, Set<IoSession>> e2) {
return e1.getKey().compareTo(e2.getKey());
}
});
str += ("Current login sessions:\r\n");
for (Entry<String, Set<IoSession>> e : elist) {
str += (" " + e.getKey() + ", IP: " + e.getValue() + "\r\n");
}
}
c.getAbstractPlayerInteraction().npcTalk(2140000, str);
}
}

View File

@@ -29,11 +29,16 @@ import java.util.LinkedList;
import java.util.List;
import client.MapleCharacter;
import client.MapleClient;
import net.server.Server;
import net.server.coordinator.MapleInviteCoordinator;
import net.server.coordinator.MapleInviteCoordinator.InviteResult;
import net.server.coordinator.MapleInviteCoordinator.InviteType;
import net.server.world.MapleParty;
import net.server.world.MaplePartyCharacter;
import tools.DatabaseConnection;
import tools.MaplePacketCreator;
import tools.Pair;
/**
*
@@ -465,4 +470,50 @@ public class MapleAlliance {
public void broadcastMessage(byte[] packet) {
Server.getInstance().allianceMessage(allianceId, packet, -1, -1);
}
public static void sendInvitation(MapleClient c, String targetGuildName, int allianceId) {
MapleGuild mg = Server.getInstance().getGuildByName(targetGuildName);
if(mg == null) {
c.getPlayer().dropMessage(5, "The entered guild does not exist.");
} else {
if (mg.getAllianceId() > 0) {
c.getPlayer().dropMessage(5, "The entered guild is already registered on a guild alliance.");
} else {
MapleCharacter victim = mg.getMGC(mg.getLeaderId()).getCharacter();
if (victim == null) {
c.getPlayer().dropMessage(5, "The master of the guild that you offered an invitation is currently not online.");
} else {
if (MapleInviteCoordinator.createInvite(InviteType.ALLIANCE, c.getPlayer(), allianceId, victim.getId())) {
victim.getClient().announce(MaplePacketCreator.allianceInvite(allianceId, c.getPlayer()));
} else {
c.getPlayer().dropMessage(5, "The master of the guild that you offered an invitation is currently managing another invite.");
}
}
}
}
}
public static boolean answerInvitation(int targetId, String targetGuildName, int allianceId, boolean answer) {
Pair<InviteResult, MapleCharacter> res = MapleInviteCoordinator.answerInvite(InviteType.ALLIANCE, targetId, allianceId, answer);
String msg;
MapleCharacter sender = res.getRight();
switch (res.getLeft()) {
case ACCEPTED:
return true;
case DENIED:
msg = "[" + targetGuildName + "] guild has denied your guild alliance invitation.";
break;
default:
msg = "The guild alliance request has not been accepted, since the invitation expired.";
}
if (sender != null) {
sender.dropMessage(5, msg);
}
return false;
}
}

View File

@@ -44,7 +44,11 @@ import net.server.Server;
import net.server.channel.Channel;
import tools.DatabaseConnection;
import tools.MaplePacketCreator;
import tools.Pair;
import net.server.audit.locks.MonitoredLockType;
import net.server.coordinator.MapleInviteCoordinator;
import net.server.coordinator.MapleInviteCoordinator.InviteType;
import net.server.coordinator.MapleInviteCoordinator.InviteResult;
public class MapleGuild {
@@ -702,7 +706,7 @@ public class MapleGuild {
this.guildMessage(MaplePacketCreator.updateGP(this.id, this.gp));
}
public static MapleGuildResponse sendInvite(MapleClient c, String targetName) {
public static MapleGuildResponse sendInvitation(MapleClient c, String targetName) {
MapleCharacter mc = c.getChannelServer().getPlayerStorage().getCharacterByName(targetName);
if (mc == null) {
return MapleGuildResponse.NOT_IN_CHANNEL;
@@ -710,8 +714,37 @@ public class MapleGuild {
if (mc.getGuildId() > 0) {
return MapleGuildResponse.ALREADY_IN_GUILD;
}
mc.getClient().announce(MaplePacketCreator.guildInvite(c.getPlayer().getGuildId(), c.getPlayer().getName()));
return null;
MapleCharacter sender = c.getPlayer();
if (MapleInviteCoordinator.createInvite(InviteType.GUILD, sender, sender.getGuildId(), mc.getId())) {
mc.getClient().announce(MaplePacketCreator.guildInvite(sender.getGuildId(), sender.getName()));
return null;
} else {
return MapleGuildResponse.MANAGING_INVITE;
}
}
public static boolean answerInvitation(int targetId, String targetName, int guildId, boolean answer) {
Pair<InviteResult, MapleCharacter> res = MapleInviteCoordinator.answerInvite(InviteType.GUILD, targetId, guildId, answer);
MapleGuildResponse mgr;
MapleCharacter sender = res.getRight();
switch (res.getLeft()) {
case ACCEPTED:
return true;
case DENIED:
mgr = MapleGuildResponse.DENIED_INVITE;
break;
default:
mgr = MapleGuildResponse.NOT_FOUND_INVITE;
}
if (mgr != null && sender != null) {
sender.announce(mgr.getPacket(targetName));
}
return false;
}
public static void displayGuildRanks(MapleClient c, int npcid) {

View File

@@ -26,14 +26,22 @@ import tools.MaplePacketCreator;
public enum MapleGuildResponse {
NOT_IN_CHANNEL(0x2a),
ALREADY_IN_GUILD(0x28),
NOT_IN_GUILD(0x2d);
NOT_IN_GUILD(0x2d),
NOT_FOUND_INVITE(0x2e),
MANAGING_INVITE(0x36),
DENIED_INVITE(0x37);
private int value;
private MapleGuildResponse(int val) {
value = val;
}
public final byte[] getPacket() {
return MaplePacketCreator.genericGuildMessage((byte) value);
public final byte[] getPacket(String targetName) {
if (value >= MANAGING_INVITE.value) {
return MaplePacketCreator.responseGuildMessage((byte) value, targetName);
} else {
return MaplePacketCreator.genericGuildMessage((byte) value);
}
}
}

View File

@@ -28,9 +28,13 @@ import tools.FilePrinter;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
public final class CreateCharHandler extends AbstractMaplePacketHandler {
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
private static int[] IDs = {
public final class CreateCharHandler extends AbstractMaplePacketHandler {
private final static Set<Integer> IDs = new HashSet<>(Arrays.asList(new Integer[]{
1302000, 1312004, 1322005, 1442079,// weapons
1040002, 1040006, 1040010, 1041002, 1041006, 1041010, 1041011, 1042167,// bottom
1060002, 1060006, 1061002, 1061008, 1062115, // top
@@ -38,15 +42,10 @@ public final class CreateCharHandler extends AbstractMaplePacketHandler {
30000, 30010,30020, 30030, 31000, 31040, 31050,// hair
20000, 20001, 20002, 21000, 21001, 21002, 21201, 20401, 20402, 21700, 20100 //face
//#NeverTrustStevenCode
};
}));
private static boolean isLegal(int toCompare) {
for (int i = 0; i < IDs.length; i++) {
if (IDs[i] == toCompare) {
return true;
}
}
return false;
private static boolean isLegal(Integer toCompare) {
return IDs.contains(toCompare);
}

View File

@@ -38,8 +38,10 @@ import client.MapleClient;
import java.sql.ResultSet;
import java.sql.Statement;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.mina.core.session.IoSession;
public final class LoginPasswordHandler implements MaplePacketHandler {
@@ -54,9 +56,18 @@ public final class LoginPasswordHandler implements MaplePacketHandler {
return HexTool.toString(digester.digest()).replace(" ", "").toLowerCase();
}
private static String getRemoteIp(IoSession session) {
return ((InetSocketAddress) session.getRemoteAddress()).getAddress().getHostAddress();
}
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
String remoteHost = getRemoteIp(c.getSession());
if (remoteHost.startsWith("127.") && !ServerConstants.HOST.startsWith("127.")) {
c.announce(MaplePacketCreator.getLoginFailed(13)); // cannot login as localhost if it's not a test server
return;
}
String login = slea.readMapleAsciiString();
String pwd = slea.readMapleAsciiString();
c.setAccountName(login);

View File

@@ -0,0 +1,37 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2018 RonanLana
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.worker;
import net.server.world.World;
/**
* @author Ronan
*/
public class FishingWorker extends BaseWorker implements Runnable {
@Override
public void run() {
wserv.runCheckFishingSchedule();
}
public FishingWorker(World world) {
super(world);
}
}

View File

@@ -0,0 +1,33 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2018 RonanLana
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.worker;
import client.processor.FredrickProcessor;
/**
* @author Ronan
*/
public class FredrickWorker implements Runnable {
@Override
public void run() {
FredrickProcessor.runFredrickSchedule();
}
}

View File

@@ -0,0 +1,33 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2018 RonanLana
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.worker;
import net.server.coordinator.MapleInviteCoordinator;
/**
* @author Ronan
*/
public class InvitationWorker implements Runnable {
@Override
public void run() {
MapleInviteCoordinator.runTimeoutSchedule();
}
}

View File

@@ -0,0 +1,40 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2018 RonanLana
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.worker;
import net.server.world.World;
import net.server.channel.Channel;
/**
* @author Ronan
*/
public class MapOwnershipWorker extends BaseWorker implements Runnable {
@Override
public void run() {
for (Channel ch : wserv.getChannels()) {
ch.runCheckOwnedMapsSchedule();
}
}
public MapOwnershipWorker(World world) {
super(world);
}
}

View File

@@ -55,6 +55,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.Set;
import java.util.HashSet;
import java.util.PriorityQueue;
import java.util.WeakHashMap;
import java.util.concurrent.ScheduledFuture;
import scripting.event.EventInstanceManager;
@@ -65,7 +66,9 @@ import server.maps.MaplePlayerShop;
import server.maps.MaplePlayerShopItem;
import server.maps.AbstractMapleMapObject;
import net.server.worker.CharacterAutosaverWorker;
import net.server.worker.FishingWorker;
import net.server.worker.HiredMerchantWorker;
import net.server.worker.MapOwnershipWorker;
import net.server.worker.MountTirednessWorker;
import net.server.worker.PetFullnessWorker;
import net.server.worker.ServerMessageWorker;
@@ -86,6 +89,10 @@ import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReentrantLock;
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import net.server.coordinator.MapleInviteCoordinator;
import net.server.coordinator.MapleInviteCoordinator.InviteResult;
import net.server.coordinator.MapleInviteCoordinator.InviteType;
import tools.packets.Fishing;
/**
*
@@ -94,7 +101,7 @@ import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
*/
public class World {
private int id, flag, exprate, droprate, bossdroprate, mesorate, questrate, travelrate;
private int id, flag, exprate, droprate, bossdroprate, mesorate, questrate, travelrate, fishingrate;
private String eventmsg;
private List<Channel> channels = new ArrayList<>();
private Map<Integer, Byte> pnpcStep = new HashMap<>();
@@ -155,10 +162,14 @@ public class World {
private ScheduledFuture<?> timedMapObjectsSchedule;
private MonitoredReentrantLock timedMapObjectLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.WORLD_MAPOBJS, true);
private Map<MapleCharacter, Integer> fishingAttempters = Collections.synchronizedMap(new WeakHashMap<MapleCharacter, Integer>());
private ScheduledFuture<?> charactersSchedule;
private ScheduledFuture<?> marriagesSchedule;
private ScheduledFuture<?> mapOwnershipSchedule;
private ScheduledFuture<?> fishingSchedule;
public World(int world, int flag, String eventmsg, int exprate, int droprate, int bossdroprate, int mesorate, int questrate, int travelrate) {
public World(int world, int flag, String eventmsg, int exprate, int droprate, int bossdroprate, int mesorate, int questrate, int travelrate, int fishingrate) {
this.id = world;
this.flag = flag;
this.eventmsg = eventmsg;
@@ -168,7 +179,8 @@ public class World {
this.mesorate = mesorate;
this.questrate = questrate;
this.travelrate = travelrate;
runningPartyId.set(1);
this.fishingrate = fishingrate;
runningPartyId.set(1000000001); // partyid must not clash with charid to solve update item looting issues, found thanks to Vcoc
runningMessengerId.set(1);
petUpdate = Server.getInstance().getCurrentTime();
@@ -186,6 +198,8 @@ public class World {
timedMapObjectsSchedule = tman.register(new TimedMapObjectWorker(this), 60 * 1000, 60 * 1000);
charactersSchedule = tman.register(new CharacterAutosaverWorker(this), 60 * 60 * 1000, 60 * 60 * 1000);
marriagesSchedule = tman.register(new WeddingReservationWorker(this), ServerConstants.WEDDING_RESERVATION_INTERVAL * 60 * 1000, ServerConstants.WEDDING_RESERVATION_INTERVAL * 60 * 1000);
mapOwnershipSchedule = tman.register(new MapOwnershipWorker(this), 20 * 1000, 20 * 1000);
fishingSchedule = tman.register(new FishingWorker(this), 10 * 1000, 10 * 1000);
}
@@ -362,14 +376,22 @@ public class World {
return travelrate;
}
public void setTravelRate(int quest) {
this.travelrate = quest;
public void setTravelRate(int travel) {
this.travelrate = travel;
}
public int getTransportationTime(int travelTime) {
return (int) Math.ceil(travelTime / travelrate);
}
public int getFishingRate() {
return fishingrate;
}
public void setFishingRate(int quest) {
this.fishingrate = quest;
}
public void loadAccountCharactersView(Integer accountId, List<MapleCharacter> chars) {
SortedMap<Integer, MapleCharacter> charsMap = new TreeMap<>();
for(MapleCharacter chr : chars) {
@@ -985,14 +1007,23 @@ public class World {
public void messengerInvite(String sender, int messengerid, String target, int fromchannel) {
if (isConnected(target)) {
MapleMessenger messenger = getPlayerStorage().getCharacterByName(target).getMessenger();
if (messenger == null) {
getPlayerStorage().getCharacterByName(target).getClient().announce(MaplePacketCreator.messengerInvite(sender, messengerid));
MapleCharacter from = getChannel(fromchannel).getPlayerStorage().getCharacterByName(sender);
from.getClient().announce(MaplePacketCreator.messengerNote(target, 4, 1));
} else {
MapleCharacter from = getChannel(fromchannel).getPlayerStorage().getCharacterByName(sender);
from.getClient().announce(MaplePacketCreator.messengerChat(sender + " : " + target + " is already using Maple Messenger"));
MapleCharacter targetChr = getPlayerStorage().getCharacterByName(target);
if (targetChr != null) {
MapleMessenger messenger = targetChr.getMessenger();
if (messenger == null) {
MapleCharacter from = getChannel(fromchannel).getPlayerStorage().getCharacterByName(sender);
if (from != null) {
if (MapleInviteCoordinator.createInvite(InviteType.MESSENGER, from, messengerid, targetChr.getId())) {
targetChr.getClient().announce(MaplePacketCreator.messengerInvite(sender, messengerid));
from.getClient().announce(MaplePacketCreator.messengerNote(target, 4, 1));
} else {
from.announce(MaplePacketCreator.messengerChat(sender + " : " + target + " is already managing a Maple Messenger invitation"));
}
}
} else {
MapleCharacter from = getChannel(fromchannel).getPlayerStorage().getCharacterByName(sender);
from.getClient().announce(MaplePacketCreator.messengerChat(sender + " : " + target + " is already using Maple Messenger"));
}
}
}
}
@@ -1043,11 +1074,13 @@ public class World {
}
}
public void declineChat(String target, String namefrom) {
if (isConnected(target)) {
MapleCharacter chr = getPlayerStorage().getCharacterByName(target);
if (chr != null && chr.getMessenger() != null) {
chr.getClient().announce(MaplePacketCreator.messengerNote(namefrom, 5, 0));
public void declineChat(String sender, MapleCharacter player) {
if (isConnected(sender)) {
MapleCharacter senderChr = getPlayerStorage().getCharacterByName(sender);
if (senderChr != null && senderChr.getMessenger() != null) {
if (MapleInviteCoordinator.answerInvite(InviteType.MESSENGER, player.getId(), senderChr.getMessenger().getId(), false).getLeft() == InviteResult.DENIED) {
senderChr.getClient().announce(MaplePacketCreator.messengerNote(player.getName(), 5, 0));
}
}
}
}
@@ -1890,6 +1923,44 @@ public class World {
}
}
public boolean registerFisherPlayer(MapleCharacter chr, int baitLevel) {
synchronized (fishingAttempters) {
if (fishingAttempters.containsKey(chr)) {
return false;
}
fishingAttempters.put(chr, baitLevel);
return true;
}
}
public int unregisterFisherPlayer(MapleCharacter chr) {
Integer baitLevel = fishingAttempters.remove(chr);
if (baitLevel != null) {
return baitLevel;
} else {
return 0;
}
}
public void runCheckFishingSchedule() {
double[] fishingLikelihoods = Fishing.fetchFishingLikelihood();
double yearLikelihood = fishingLikelihoods[0], timeLikelihood = fishingLikelihoods[1];
if (!fishingAttempters.isEmpty()) {
List<MapleCharacter> fishingAttemptersList;
synchronized (fishingAttempters) {
fishingAttemptersList = new ArrayList<>(fishingAttempters.keySet());
}
for (MapleCharacter chr : fishingAttemptersList) {
int baitLevel = unregisterFisherPlayer(chr);
Fishing.doFishing(chr, baitLevel, yearLikelihood, timeLikelihood);
}
}
}
private void clearWorldData() {
List<MapleParty> pList;
partyLock.lock();
@@ -1966,6 +2037,16 @@ public class World {
marriagesSchedule = null;
}
if(mapOwnershipSchedule != null) {
mapOwnershipSchedule.cancel(false);
mapOwnershipSchedule = null;
}
if(fishingSchedule != null) {
fishingSchedule.cancel(false);
fishingSchedule = null;
}
players.disconnectAll();
players = null;