Several PQ platform patches + Quest complete count + Fast meso drop

Implemented CPQ challenges using the matching system.
Fixed LanguageConstants statically acting for all players.
Fixed OPQ's <On the Way Up> stage sometimes leading players to unexpected platforms.
Fixed EllinPQ fountain not giving Altaire Fragment to players.
Fixed "Lab - Unit" stage on RnJPQ, now using correlated sequences between the units.
Fixed Fredrick handing out negative values of mesos to players.
Improved "goto" command info.
Implemented quest complete count.
Fixed mobs still being "controlled" by players even though it's already dead.
Concurrently protected adding items into inventory.
Concurrently protected EXP gain through Writs of Solomon.
Adjusted smoothly respawn rate of mobs in map (solo players in a map now experiences 75% of mobs spawned).
Fixed mesos not being able to drop so frequently (prior 200ms threshold between drops).
Tweaked matchchecking so that match checking doesn't outright dispose matching members on dismissal (match still sticks to the player until they answer or timeout).
Fixed a dupe case within storage's item store.
Added any-NPC scriptable to the source.
This commit is contained in:
ronancpl
2019-05-02 11:02:07 -03:00
parent b6a91c523f
commit c2fa7883fe
90 changed files with 1454 additions and 611 deletions
+26 -21
View File
@@ -64,6 +64,7 @@ import net.server.world.MapleParty;
import net.server.world.MaplePartyCharacter;
import net.server.world.PartyOperation;
import net.server.world.World;
import scripting.AbstractPlayerInteraction;
import scripting.event.EventInstanceManager;
import server.CashShop;
import server.MapleItemInformationProvider;
@@ -199,7 +200,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
private int expRate = 1, mesoRate = 1, dropRate = 1, expCoupon = 1, mesoCoupon = 1, dropCoupon = 1;
private int omokwins, omokties, omoklosses, matchcardwins, matchcardties, matchcardlosses;
private int owlSearch;
private long lastfametime, lastUsedCashItem, lastExpression = 0, lastHealed, lastBuyback = 0, lastDeathtime, lastMesoDrop = -1, jailExpiration = -1;
private long lastfametime, lastUsedCashItem, lastExpression = 0, lastHealed, lastBuyback = 0, lastDeathtime, jailExpiration = -1;
private transient int localstr, localdex, localluk, localint_, localmagic, localwatk;
private transient int equipmaxhp, equipmaxmp, equipstr, equipdex, equipluk, equipint_, equipmagic, equipwatk, localchairhp, localchairmp;
private int localchairrate;
@@ -2954,22 +2955,24 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
public void gainGachaExp() {
int expgain = 0;
int currentgexp = gachaexp.get();
long currentgexp = gachaexp.get();
if ((currentgexp + exp.get()) >= ExpTable.getExpNeededForLevel(level)) {
expgain += ExpTable.getExpNeededForLevel(level) - exp.get();
int nextneed = ExpTable.getExpNeededForLevel(level + 1);
if ((currentgexp - expgain) >= nextneed) {
if (currentgexp - expgain >= nextneed) {
expgain += nextneed;
}
this.gachaexp.set(currentgexp - expgain);
this.gachaexp.set((int) (currentgexp - expgain));
} else {
expgain = this.gachaexp.getAndSet(0);
}
gainExp(expgain, false, false);
gainExp(expgain, false, true);
updateSingleStat(MapleStat.GACHAEXP, this.gachaexp.get());
}
public void gainGachaExp(int gain) {
public void addGachaExp(int gain) {
updateSingleStat(MapleStat.GACHAEXP, gachaexp.addAndGet(gain));
}
@@ -4296,6 +4299,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
return client;
}
public AbstractPlayerInteraction getAbstractPlayerInteraction() {
return client.getAbstractPlayerInteraction();
}
public final List<MapleQuestStatus> getCompletedQuests() {
synchronized (quests) {
List<MapleQuestStatus> ret = new LinkedList<>();
@@ -4930,8 +4937,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
if (elapsedDays > 100) elapsedDays = 100;
int netMeso = (merchantmeso * (100 - elapsedDays)) / 100;
return netMeso;
long netMeso = (long) merchantmeso; // negative mesos issues found thanks to Flash, Vcoc
netMeso = (netMeso * (100 - elapsedDays)) / 100;
return (int) netMeso;
}
public int getMesosTraded() {
@@ -6906,6 +6914,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
status.setForfeited(rs.getInt("forfeited"));
status.setCompleted(rs.getInt("completed"));
ret.quests.put(q.getId(), status);
loadedQuestStatus.put(rs.getInt("queststatusid"), status);
}
@@ -7665,7 +7674,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
public void resetBattleshipHp() {
this.battleshipHp = 400 * getSkillLevel(SkillFactory.getSkill(Corsair.BATTLE_SHIP)) + ((getLevel() - 120) * 200);
int bshipLevel = Math.max(getLevel() - 120, 0); // thanks alex12 for noticing battleship HP issues for low-level players
this.battleshipHp = 400 * getSkillLevel(SkillFactory.getSkill(Corsair.BATTLE_SHIP)) + (bshipLevel * 200);
}
public void resetEnteredScript() {
@@ -8254,7 +8264,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
deleteQuestProgressWhereCharacterId(con, id);
ps = con.prepareStatement("INSERT INTO queststatus (`queststatusid`, `characterid`, `quest`, `status`, `time`, `expires`, `forfeited`) VALUES (DEFAULT, ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
ps = con.prepareStatement("INSERT INTO queststatus (`queststatusid`, `characterid`, `quest`, `status`, `time`, `expires`, `forfeited`, `completed`) VALUES (DEFAULT, ?, ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
PreparedStatement psf;
try (PreparedStatement pse = con.prepareStatement("INSERT INTO questprogress VALUES (DEFAULT, ?, ?, ?, ?)")) {
psf = con.prepareStatement("INSERT INTO medalmaps VALUES (DEFAULT, ?, ?, ?)");
@@ -8267,6 +8277,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
ps.setInt(4, (int) (q.getCompletionTime() / 1000));
ps.setLong(5, q.getExpirationTime());
ps.setInt(6, q.getForfeited());
ps.setInt(7, q.getCompleted());
ps.executeUpdate();
try (ResultSet rs = ps.getGeneratedKeys()) {
rs.next();
@@ -8549,8 +8560,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
public synchronized void withdrawMerchantMesos() {
int merchantMeso = this.getMerchantNetMeso();
int playerMeso = this.getMeso();
if (merchantMeso > 0) {
int possible = Integer.MAX_VALUE - merchantMeso;
int possible = Integer.MAX_VALUE - playerMeso;
if (possible > 0) {
if (possible < merchantMeso) {
@@ -8562,7 +8575,6 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
}
} else {
int playerMeso = this.getMeso();
int nextMeso = playerMeso + merchantMeso;
if (nextMeso < 0) {
@@ -9401,9 +9413,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
} else if (quest.getStatus().equals(MapleQuestStatus.Status.COMPLETED)) {
MapleQuest mquest = quest.getQuest();
short questid = mquest.getId();
if(!mquest.isSameDayRepeatable() && !MapleQuest.isExploitableQuest(questid)) {
if (!mquest.isSameDayRepeatable() && !MapleQuest.isExploitableQuest(questid)) {
awardQuestPoint(ServerConstants.QUEST_POINT_PER_QUEST_COMPLETE);
}
quest.setCompleted(quest.getCompleted() + 1); // count quest completed Jayd's idea
announce(MaplePacketCreator.completeQuest(questid, quest.getCompletionTime()));
} else if (quest.getStatus().equals(MapleQuestStatus.Status.NOT_STARTED)) {
@@ -10040,14 +10053,6 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
whiteChat = !whiteChat;
}
public boolean canDropMeso() {
if (System.currentTimeMillis() - lastMesoDrop >= 200 || lastMesoDrop == -1) { //About 200 meso drops a minute
lastMesoDrop = System.currentTimeMillis();
return true;
}
return false;
}
// These need to be renamed, but I am too lazy right now to go through the scripts and rename them...
public String getPartyQuestItems() {
return dataString;
+3 -13
View File
@@ -888,7 +888,6 @@ public class MapleClient {
player.cancelAllBuffs(true);
player.closePlayerInteractions();
QuestScriptManager.getInstance().dispose(this);
if (!serverTransition) { // thanks MedicOP for detecting an issue with party leader change on changing channels
removePartyPlayer(wserv);
@@ -1463,18 +1462,8 @@ public class MapleClient {
return;
}
if (player.getTrade() != null) {
MapleTrade.cancelTrade(getPlayer(), MapleTrade.TradeResult.PARTNER_CANCEL);
}
MapleHiredMerchant merchant = player.getHiredMerchant();
if (merchant != null) {
if (merchant.isOwner(getPlayer())) {
merchant.setOpen(true);
} else {
merchant.removeVisitor(getPlayer());
}
}
player.closePlayerInteractions();
player.unregisterChairBuff();
server.getPlayerBuffStorage().addBuffsToStorage(player.getId(), player.getAllBuffs());
server.getPlayerBuffStorage().addDiseasesToStorage(player.getId(), player.getAllDiseases());
@@ -1540,6 +1529,7 @@ public class MapleClient {
public void closePlayerScriptInteractions() {
this.removeClickedNPC();
NPCScriptManager.getInstance().dispose(this);
QuestScriptManager.getInstance().dispose(this);
}
public boolean attemptCsCoupon() {
+13 -1
View File
@@ -65,7 +65,7 @@ public class MapleQuestStatus {
private final List<Integer> medalProgress = new LinkedList<Integer>();
private int npc;
private long completionTime, expirationTime;
private int forfeited = 0;
private int forfeited = 0, completed = 0;
private String customData;
public MapleQuestStatus(MapleQuest quest, Status status) {
@@ -214,6 +214,10 @@ public class MapleQuestStatus {
return forfeited;
}
public int getCompleted() {
return completed;
}
public String getInfo() {
if(!progress.containsKey(0) && !getMedalMaps().isEmpty()) {
return Integer.toString(getMedalProgress());
@@ -233,6 +237,14 @@ public class MapleQuestStatus {
throw new IllegalArgumentException("Can't set forfeits to something lower than before.");
}
}
public void setCompleted(int completed) {
if (completed >= this.completed) {
this.completed = completed;
} else {
throw new IllegalArgumentException("Can't set completes to something lower than before.");
}
}
public final void setCustomData(final String customData) {
this.customData = customData;
+3 -4
View File
@@ -204,6 +204,8 @@ public class CommandsExecutor {
addCommand("enableauth", EnableAuthCommand.class);
addCommand("toggleexp", ToggleExpCommand.class);
addCommand("mylawn", MapOwnerClaimCommand.class);
addCommand("bosshp", BossHpCommand.class);
addCommand("mobhp", MobHpCommand.class);
commandsNameDesc.add(levelCommandsCursor);
}
@@ -212,8 +214,6 @@ public class CommandsExecutor {
private void registerLv1Commands() {
levelCommandsCursor = new Pair<>((List<String>) new ArrayList<String>(), (List<String>) new ArrayList<String>());
addCommand("bosshp", 1, BossHpCommand.class);
addCommand("mobhp", 1, MobHpCommand.class);
addCommand("whatdropsfrom", 1, WhatDropsFromCommand.class);
addCommand("whodrops", 1, WhoDropsCommand.class);
addCommand("buffme", 1, BuffMeCommand.class);
@@ -291,7 +291,6 @@ public class CommandsExecutor {
addCommand("givevp", 3, GiveVpCommand.class);
addCommand("givems", 3, GiveMesosCommand.class);
addCommand("giverp", 3, GiveRpCommand.class);
addCommand("id", 3, IdCommand.class);
addCommand("expeds", 3, ExpedsCommand.class);
addCommand("kill", 3, KillCommand.class);
addCommand("seed", 3, SeedCommand.class);
@@ -307,7 +306,6 @@ public class CommandsExecutor {
addCommand("startmapevent", 3, StartMapEventCommand.class);
addCommand("stopmapevent", 3, StopMapEventCommand.class);
addCommand("online2", 3, OnlineTwoCommand.class);
addCommand("warpsnowball", 3, WarpSnowBallCommand.class);
addCommand("ban", 3, BanCommand.class);
addCommand("unban", 3, UnBanCommand.class);
addCommand("healmap", 3, HealMapCommand.class);
@@ -339,6 +337,7 @@ public class CommandsExecutor {
addCommand("exprate", 4, ExpRateCommand.class);
addCommand("mesorate", 4, MesoRateCommand.class);
addCommand("droprate", 4, DropRateCommand.class);
addCommand("bossdroprate", 4, BossDropRateCommand.class);
addCommand("questrate", 4, QuestRateCommand.class);
addCommand("travelrate", 4, TravelRateCommand.class);
addCommand("fishrate", 4, FishingRateCommand.class);
@@ -27,23 +27,64 @@ import client.MapleCharacter;
import client.command.Command;
import client.MapleClient;
import constants.GameConstants;
import java.util.ArrayList;
import java.util.Collections;
import net.server.Server;
import server.MaplePortal;
import server.maps.FieldLimit;
import server.maps.MapleMap;
import server.maps.MapleMapFactory;
import server.maps.MapleMiniDungeonInfo;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class GotoCommand extends Command {
{
setDescription("");
MapleMapFactory mapFactory = Server.getInstance().getWorlds().get(0).getChannels().get(0).getMapFactory();
List<Entry<String, Integer>> towns = new ArrayList<>(GameConstants.GOTO_TOWNS.entrySet());
sortGotoEntries(towns);
for (Map.Entry<String, Integer> e : towns) {
GOTO_TOWNS_INFO += ("'" + e.getKey() + "' - #b" + (mapFactory.getMap(e.getValue()).getMapName()) + "#k\r\n");
}
List<Entry<String, Integer>> areas = new ArrayList<>(GameConstants.GOTO_AREAS.entrySet());
sortGotoEntries(areas);
for (Map.Entry<String, Integer> e : areas) {
GOTO_AREAS_INFO += ("'" + e.getKey() + "' - #b" + (mapFactory.getMap(e.getValue()).getMapName()) + "#k\r\n");
}
}
public static String GOTO_TOWNS_INFO = "";
public static String GOTO_AREAS_INFO = "";
private static void sortGotoEntries(List<Entry<String, Integer>> listEntries) {
Collections.sort(listEntries, new Comparator<Entry<String, Integer>>() {
@Override
public int compare(Entry<String, Integer> e1, Entry<String, Integer> e2)
{
return e1.getValue().compareTo(e2.getValue());
}
});
}
@Override
public void execute(MapleClient c, String[] params) {
MapleCharacter player = c.getPlayer();
if (params.length < 1){
player.yellowMessage("Syntax: @goto <map name>");
String sendStr = "Syntax: #b@goto <map name>#k. Available areas:\r\n\r\n#rTowns:#k\r\n" + GOTO_TOWNS_INFO;
if (player.isGM()) {
sendStr += ("\r\n#rAreas:#k\r\n" + GOTO_AREAS_INFO);
}
player.getAbstractPlayerInteraction().npcTalk(9000020, sendStr);
return;
}
@@ -74,7 +115,13 @@ public class GotoCommand extends Command {
player.saveLocationOnWarp();
player.changeMap(target, targetPortal);
} else {
player.dropMessage(5, "Area '" + params[0] + "' is not registered.");
// detailed info on goto available areas suggested thanks to Vcoc
String sendStr = "Area '#r" + params[0] + "#k' is not available. Available areas:\r\n\r\n#rTowns:#k" + GOTO_TOWNS_INFO;
if (player.isGM()) {
sendStr += ("\r\n#rAreas:#k\r\n" + GOTO_AREAS_INFO);
}
player.getAbstractPlayerInteraction().npcTalk(9000020, sendStr);
}
}
}
@@ -62,7 +62,7 @@ public class WhatDropsFromCommand extends Command {
if (name == null || name.equals("null") || drop.chance == 0){
continue;
}
float chance = 1000000 / drop.chance / (!MapleMonsterInformationProvider.getInstance().isBoss(mobId) ? player.getDropRate() : player.getBossDropRate());
float chance = Math.max(1000000 / drop.chance / (!MapleMonsterInformationProvider.getInstance().isBoss(mobId) ? player.getDropRate() : player.getBossDropRate()), 1);
output += "- " + name + " (1/" + (int) chance + ")\r\n";
} catch (Exception ex){
ex.printStackTrace();
@@ -1,57 +0,0 @@
/*
This file is part of the HeavenMS MapleStory Server, commands OdinMS-based
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/>.
*/
/*
@Author: Arthur L - Refactored command content into modules
*/
package client.command.commands.gm3;
import client.command.Command;
import client.MapleClient;
import client.MapleCharacter;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
public class IdCommand extends Command {
{
setDescription("");
}
@Override
public void execute(MapleClient c, String[] params) {
MapleCharacter player = c.getPlayer();
if (params.length < 1) {
player.yellowMessage("Syntax: !id <id>");
return;
}
try {
try (BufferedReader dis = new BufferedReader(new InputStreamReader(new URL("http://www.mapletip.com/search_java.php?search_value=" + params[0] + "&check=true").openConnection().getInputStream()))) {
String s;
while ((s = dis.readLine()) != null) {
player.dropMessage(s);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
@@ -1,5 +1,5 @@
/*
This file is part of the HeavenMS MapleStory Server, commands OdinMS-based
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
@@ -17,19 +17,18 @@
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/>.
*/
/*
@Author: Arthur L - Refactored command content into modules
*/
package client.command.commands.gm3;
package client.command.commands.gm4;
import client.command.Command;
import client.MapleClient;
import client.MapleCharacter;
import tools.MaplePacketCreator;
import java.util.List;
public class WarpSnowBallCommand extends Command {
/**
*
* @author Ronan
*/
public class BossDropRateCommand extends Command {
{
setDescription("");
}
@@ -37,10 +36,13 @@ public class WarpSnowBallCommand extends Command {
@Override
public void execute(MapleClient c, String[] params) {
MapleCharacter player = c.getPlayer();
List<MapleCharacter> chars = player.getMap().getAllPlayers();
for (MapleCharacter chr : chars) {
chr.saveLocationOnWarp();
chr.changeMap(109060000, chr.getTeam());
if (params.length < 1) {
player.yellowMessage("Syntax: !bossdroprate <newrate>");
return;
}
int bossdroprate = Math.max(Integer.parseInt(params[0]), 1);
c.getWorldServer().setBossDropRate(bossdroprate);
c.getWorldServer().broadcastPacket(MaplePacketCreator.serverNotice(6, "[Rate] Boss Drop Rate has been changed to " + bossdroprate + "x."));
}
}
@@ -44,6 +44,5 @@ public class MesoRateCommand extends Command {
int mesorate = Math.max(Integer.parseInt(params[0]), 1);
c.getWorldServer().setMesoRate(mesorate);
c.getWorldServer().broadcastPacket(MaplePacketCreator.serverNotice(6, "[Rate] Meso Rate has been changed to " + mesorate + "x."));
}
}
+25 -1
View File
@@ -24,6 +24,7 @@ package client.inventory;
import client.MapleClient;
import constants.ServerConstants;
import constants.ExpTable;
import constants.ItemConstants;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@@ -302,10 +303,33 @@ public class Equip extends Item {
return stat;
}
private static boolean isPhysicalWeapon(int itemid) {
Equip eqp = (Equip) MapleItemInformationProvider.getInstance().getEquipById(itemid);
return eqp.getWatk() >= eqp.getMatk();
}
private boolean isNotWeaponAffinity(StatUpgrade name) {
// WATK/MATK expected gains lessens outside of weapon affinity (physical/magic): Vcoc's idea
if (ItemConstants.isWeapon(this.getItemId())) {
if (name.equals(StatUpgrade.incPAD)) {
if (!isPhysicalWeapon(this.getItemId())) {
return true;
}
} else if (name.equals(StatUpgrade.incMAD)) {
if (isPhysicalWeapon(this.getItemId())) {
return true;
}
}
}
return false;
}
private void getUnitStatUpgrade(List<Pair<StatUpgrade, Integer>> stats, StatUpgrade name, int curStat, boolean isAttribute) {
isUpgradeable = true;
int maxUpgrade = randomizeStatUpgrade((int)(1 + (curStat / getStatModifier(isAttribute))));
int maxUpgrade = randomizeStatUpgrade((int)(1 + (curStat / (getStatModifier(isAttribute) * (isNotWeaponAffinity(name) ? 2.7 : 1)))));
if(maxUpgrade == 0) return;
stats.add(new Pair<>(name, maxUpgrade));
@@ -69,10 +69,29 @@ public class MapleInventoryManipulator {
}
public static boolean addById(MapleClient c, int itemId, short quantity, String owner, int petid, byte flag, long expiration) {
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
MapleInventoryType type = ItemConstants.getInventoryType(itemId);
MapleCharacter chr = c.getPlayer();
MapleInventory inv = chr.getInventory(type);
MapleInventoryType type = ItemConstants.getInventoryType(itemId);
if (c.tryacquireClient()) {
try {
MapleInventory inv = chr.getInventory(type);
inv.lockInventory();
try {
return addByIdInternal(c, chr, type, inv, itemId, quantity, owner, petid, flag, expiration);
} finally {
inv.unlockInventory();
}
} finally {
c.releaseClient();
}
} else {
c.announce(MaplePacketCreator.enableActions());
return false;
}
}
private static boolean addByIdInternal(MapleClient c, MapleCharacter chr, MapleInventoryType type, MapleInventory inv, int itemId, short quantity, String owner, int petid, byte flag, long expiration) {
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
if (!type.equals(MapleInventoryType.EQUIP)) {
short slotMax = ii.getSlotMax(c, itemId);
List<Item> existing = inv.listById(itemId);
@@ -166,15 +185,36 @@ public class MapleInventoryManipulator {
public static boolean addFromDrop(MapleClient c, Item item, boolean show, int petId) {
MapleCharacter chr = c.getPlayer();
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
MapleInventoryType type = item.getInventoryType();
if (c.tryacquireClient()) {
try {
MapleInventory inv = chr.getInventory(type);
inv.lockInventory();
try {
return addFromDropInternal(c, chr, type, inv, item, show, petId);
} finally {
inv.unlockInventory();
}
} finally {
c.releaseClient();
}
} else {
c.announce(MaplePacketCreator.enableActions());
return false;
}
}
private static boolean addFromDropInternal(MapleClient c, MapleCharacter chr, MapleInventoryType type, MapleInventory inv, Item item, boolean show, int petId) {
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
if (ii.isPickupRestricted(item.getItemId()) && chr.haveItemWithId(item.getItemId(), true)) {
c.announce(MaplePacketCreator.getInventoryFull());
c.announce(MaplePacketCreator.showItemUnavailable());
return false;
}
short quantity = item.getQuantity();
MapleInventory inv = chr.getInventory(type);
if (!type.equals(MapleInventoryType.EQUIP)) {
short slotMax = ii.getSlotMax(c, item.getItemId());
List<Item> existing = inv.listById(item.getItemId());
@@ -214,13 +254,13 @@ public class MapleInventoryManipulator {
nItem.setPosition(newSlot);
item.setPosition(newSlot);
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, nItem))));
if(MapleInventoryManipulator.isSandboxItem(nItem)) chr.setHasSandboxItem();
if (MapleInventoryManipulator.isSandboxItem(nItem)) chr.setHasSandboxItem();
}
} else {
Item nItem = new Item(item.getItemId(), (short) 0, quantity, petId);
nItem.setExpiration(item.getExpiration());
nItem.setFlag(item.getFlag());
short newSlot = inv.addItem(nItem);
if (newSlot == -1) {
c.announce(MaplePacketCreator.getInventoryFull());
@@ -230,7 +270,7 @@ public class MapleInventoryManipulator {
nItem.setPosition(newSlot);
item.setPosition(newSlot);
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, nItem))));
if(MapleInventoryManipulator.isSandboxItem(nItem)) chr.setHasSandboxItem();
if (MapleInventoryManipulator.isSandboxItem(nItem)) chr.setHasSandboxItem();
c.announce(MaplePacketCreator.enableActions());
}
} else if (quantity == 1) {
@@ -242,7 +282,7 @@ public class MapleInventoryManipulator {
}
item.setPosition(newSlot);
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, item))));
if(MapleInventoryManipulator.isSandboxItem(item)) chr.setHasSandboxItem();
if (MapleInventoryManipulator.isSandboxItem(item)) chr.setHasSandboxItem();
} else {
FilePrinter.printError(FilePrinter.ITEM, "Tried to pickup Equip id " + item.getItemId() + " containing more than 1 quantity --> " + quantity);
c.announce(MaplePacketCreator.getInventoryFull());
+34 -22
View File
@@ -40,6 +40,7 @@ import tools.data.input.SeekableLittleEndianAccessor;
/**
*
* @author Matze
* @author Ronan - inventory concurrency protection on storing items
*/
public class StorageProcessor {
@@ -97,15 +98,15 @@ public class StorageProcessor {
short slot = slea.readShort();
int itemId = slea.readInt();
short quantity = slea.readShort();
MapleInventoryType slotType = ItemConstants.getInventoryType(itemId);
MapleInventory Inv = chr.getInventory(slotType);
if (slot < 1 || slot > Inv.getSlotLimit()) { //player inv starts at one
MapleInventoryType invType = ItemConstants.getInventoryType(itemId);
MapleInventory inv = chr.getInventory(invType);
if (slot < 1 || slot > inv.getSlotLimit()) { //player inv starts at one
AutobanFactory.PACKET_EDIT.alert(c.getPlayer(), c.getPlayer().getName() + " tried to packet edit with storage.");
FilePrinter.print(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to store item at slot " + slot);
c.disconnect(true, false);
return;
}
if (quantity < 1 || chr.getItemQuantity(itemId, false) < quantity) {
if (quantity < 1) {
c.announce(MaplePacketCreator.enableActions());
return;
}
@@ -118,28 +119,39 @@ public class StorageProcessor {
if (chr.getMeso() < storeFee) {
c.announce(MaplePacketCreator.getStorageError((byte) 0x0B));
} else {
MapleInventoryType invType = ItemConstants.getInventoryType(itemId);
Item item = chr.getInventory(invType).getItem(slot).copy();
if (item != null && item.getItemId() == itemId && (item.getQuantity() >= quantity || ItemConstants.isRechargeable(itemId))) {
if (ItemConstants.isWeddingRing(itemId) || ItemConstants.isWeddingToken(itemId)) {
Item item;
inv.lockInventory(); // thanks imbee for pointing a dupe within storage
try {
item = inv.getItem(slot);
if (item != null && item.getItemId() == itemId && (item.getQuantity() >= quantity || ItemConstants.isRechargeable(itemId))) {
if (ItemConstants.isWeddingRing(itemId) || ItemConstants.isWeddingToken(itemId)) {
c.announce(MaplePacketCreator.enableActions());
return;
}
if (ItemConstants.isRechargeable(itemId)) {
quantity = item.getQuantity();
}
MapleInventoryManipulator.removeFromSlot(c, invType, slot, quantity, false);
} else {
c.announce(MaplePacketCreator.enableActions());
return;
}
if (ItemConstants.isRechargeable(itemId)) {
quantity = item.getQuantity();
}
chr.gainMeso(-storeFee, false, true, false);
MapleKarmaManipulator.toggleKarmaFlagToUntradeable(item);
MapleInventoryManipulator.removeFromSlot(c, invType, slot, quantity, false);
item.setQuantity(quantity);
storage.store(item);
storage.sendStored(c, ItemConstants.getInventoryType(itemId));
String itemName = MapleItemInformationProvider.getInstance().getName(item.getItemId());
FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " stored " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")");
chr.setUsedStorage();
} finally {
inv.unlockInventory();
}
chr.gainMeso(-storeFee, false, true, false);
MapleKarmaManipulator.toggleKarmaFlagToUntradeable(item);
item.setQuantity(quantity);
storage.store(item);
storage.sendStored(c, ItemConstants.getInventoryType(itemId));
String itemName = MapleItemInformationProvider.getInstance().getName(item.getItemId());
FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " stored " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")");
chr.setUsedStorage();
}
} else if (mode == 6) { // arrange items
if(ServerConstants.USE_STORAGE_ITEM_SORT) storage.arrangeItems(c);
+83 -60
View File
@@ -8,66 +8,89 @@ import client.MapleCharacter;
*/
public class LanguageConstants {
public static String CPQBlue;
public static String CPQError;
public static String CPQEntry;
public static String CPQFindError;
public static String CPQRed;
public static String CPQPlayerExit;
public static String CPQEntryLobby;
public static String CPQPickRoom;
public static String CPQExtendTime;
public static String CPQLeaderNotFound;
public static String CPQChallengeRoomAnswer;
public static String CPQChallengeRoomSent;
public static LanguageConstants Languages(MapleCharacter chr) {
if (chr.getLanguage() == 0) {
LanguageConstants.CPQBlue = "Maple Azul";
LanguageConstants.CPQRed = "Maple Vermelho";
LanguageConstants.CPQExtendTime = "O tempo foi estendido.";
LanguageConstants.CPQPlayerExit = " deixou o Carnaval de Monstros.";
LanguageConstants.CPQError = "Ocorreu um problema. Favor recriar a sala.";
LanguageConstants.CPQLeaderNotFound = "Nao foi possivel encontrar o Lider.";
LanguageConstants.CPQPickRoom = "Inscreva-se no Festival de Monstros!\r\n";
LanguageConstants.CPQChallengeRoomAnswer = "O grupo esta respondendo um desafio no momento.";
LanguageConstants.CPQChallengeRoomSent = "Um desafio foi enviado para o grupo na sala. Aguarde um momento.";
LanguageConstants.CPQFindError = "Nao foi possivel encontrar um grupo nesta sala.\r\nProvavelmente o grupo foi desfeito dentro da sala!";
LanguageConstants.CPQEntryLobby = "Agora voce ira receber desafios de outros grupos. Se voce nao aceitar um desafio em 3 minutos, voce sera levado para fora.";
LanguageConstants.CPQEntry = "Voce pode selecionar \"Invocar Monstros\", \"Habilidade\", ou \"Protetor\" como sua tatica durante o Carnaval dos Monstros. Use Tab a F1~F12 para acesso rapido!";
} else if (chr.getLanguage() == 1) {
LanguageConstants.CPQBlue = "Maple Azul";
LanguageConstants.CPQRed = "Maple Rojo";
LanguageConstants.CPQExtendTime = "El tiempo se ha ampliado.";
LanguageConstants.CPQPlayerExit = " ha dejado el Carnaval de Monstruos.";
LanguageConstants.CPQLeaderNotFound = "No se pudo encontrar el Lider.";
LanguageConstants.CPQPickRoom = "!Inscribete en el Festival de Monstruos!\r\n";
LanguageConstants.CPQError = "Se ha producido un problema. Por favor, volver a crear una sala.";
LanguageConstants.CPQChallengeRoomAnswer = "El grupo esta respondiendo un desafio en el momento.";
LanguageConstants.CPQChallengeRoomSent = "Un desafio fue enviado al grupo en la sala. Espera un momento.";
LanguageConstants.CPQFindError = "No se pudo encontrar un grupo en esta sala.\r\nProbablemente el grupo fue deshecho dentro de la sala!";
LanguageConstants.CPQEntryLobby = "Ahora usted recibira los retos de otros grupos. Si usted no acepta un desafio en 3 minutos, usted sera llevado hacia fuera.";
LanguageConstants.CPQEntry = "Usted puede seleccionar \"Invocar Monstruos \", \"Habilidad \", o \"Protector \" como su tactica durante el Carnaval de los Monstruos. Utilice Tab y F1 ~ F12 para acceso rapido!";
} else if (chr.getLanguage() == 2) {
LanguageConstants.CPQBlue = "Maple Blue";
LanguageConstants.CPQRed = "Maple Red";
LanguageConstants.CPQPlayerExit = " left the Carnival of Monsters.";
LanguageConstants.CPQExtendTime = "The time has been extended.";
LanguageConstants.CPQLeaderNotFound = "Could not find the Leader.";
LanguageConstants.CPQError = "There was a problem. Please re-create a room.";
LanguageConstants.CPQPickRoom = "Sign up for the Monster Festival!\r\n";
LanguageConstants.CPQChallengeRoomAnswer = "The group is currently facing a challenge.";
LanguageConstants.CPQChallengeRoomSent = "A challenge has been sent to the group in the room. Please wait a while.";
LanguageConstants.CPQFindError = "We could not find a group in this room.\r\nProbably the group was scrapped inside the room!";
LanguageConstants.CPQEntryLobby = "You will now receive challenges from other groups. If you do not accept a challenge within 3 minutes, you will be taken out.";
LanguageConstants.CPQEntry = "You can select \"Summon Monsters \", \"Ability \", or \"Protector \" as your tactic during the Monster Carnival. Use Tab and F1 ~ F12 for quick access!";
enum Language {
LANG_PRT(0),
LANG_ESP(1),
LANG_ENG(2);
int lang;
private Language(int lang) {
this.lang = lang;
}
return null;
private int getValue() {
return this.lang;
}
}
public static String CPQBlue[] = new String[3];
public static String CPQError[] = new String[3];
public static String CPQEntry[] = new String[3];
public static String CPQFindError[] = new String[3];
public static String CPQRed[] = new String[3];
public static String CPQPlayerExit[] = new String[3];
public static String CPQEntryLobby[] = new String[3];
public static String CPQPickRoom[] = new String[3];
public static String CPQExtendTime[] = new String[3];
public static String CPQLeaderNotFound[] = new String[3];
public static String CPQChallengeRoomAnswer[] = new String[3];
public static String CPQChallengeRoomSent[] = new String[3];
public static String CPQChallengeRoomDenied[] = new String[3];
static {
int lang;
lang = Language.LANG_PRT.getValue();
LanguageConstants.CPQBlue[lang] = "Maple Azul";
LanguageConstants.CPQRed[lang] = "Maple Vermelho";
LanguageConstants.CPQExtendTime[lang] = "O tempo foi estendido.";
LanguageConstants.CPQPlayerExit[lang] = " deixou o Carnaval de Monstros.";
LanguageConstants.CPQError[lang] = "Ocorreu um problema. Favor recriar a sala.";
LanguageConstants.CPQLeaderNotFound[lang] = "Nao foi possivel encontrar o Lider.";
LanguageConstants.CPQPickRoom[lang] = "Inscreva-se no Festival de Monstros!\r\n";
LanguageConstants.CPQChallengeRoomAnswer[lang] = "O grupo esta respondendo um desafio no momento.";
LanguageConstants.CPQChallengeRoomSent[lang] = "Um desafio foi enviado para o grupo na sala. Aguarde um momento.";
LanguageConstants.CPQChallengeRoomDenied[lang] = "O grupo na sala cancelou seu desafio.";
LanguageConstants.CPQFindError[lang] = "Nao foi possivel encontrar um grupo nesta sala.\r\nProvavelmente o grupo foi desfeito dentro da sala!";
LanguageConstants.CPQEntryLobby[lang] = "Agora voce ira receber desafios de outros grupos. Se voce nao aceitar um desafio em 3 minutos, voce sera levado para fora.";
LanguageConstants.CPQEntry[lang] = "Voce pode selecionar \"Invocar Monstros\", \"Habilidade\", ou \"Protetor\" como sua tatica durante o Carnaval dos Monstros. Use Tab a F1~F12 para acesso rapido!";
lang = Language.LANG_ESP.getValue();
LanguageConstants.CPQBlue[lang] = "Maple Azul";
LanguageConstants.CPQRed[lang] = "Maple Rojo";
LanguageConstants.CPQExtendTime[lang] = "El tiempo se ha ampliado.";
LanguageConstants.CPQPlayerExit[lang] = " ha dejado el Carnaval de Monstruos.";
LanguageConstants.CPQLeaderNotFound[lang] = "No se pudo encontrar el Lider.";
LanguageConstants.CPQPickRoom[lang] = "!Inscribete en el Festival de Monstruos!\r\n";
LanguageConstants.CPQError[lang] = "Se ha producido un problema. Por favor, volver a crear una sala.";
LanguageConstants.CPQChallengeRoomAnswer[lang] = "El grupo esta respondiendo un desafio en el momento.";
LanguageConstants.CPQChallengeRoomSent[lang] = "Un desafio fue enviado al grupo en la sala. Espera un momento.";
LanguageConstants.CPQChallengeRoomDenied[lang] = "El grupo en la sala cancelo su desafio.";
LanguageConstants.CPQFindError[lang] = "No se pudo encontrar un grupo en esta sala.\r\nProbablemente el grupo fue deshecho dentro de la sala!";
LanguageConstants.CPQEntryLobby[lang] = "Ahora usted recibira los retos de otros grupos. Si usted no acepta un desafio en 3 minutos, usted sera llevado hacia fuera.";
LanguageConstants.CPQEntry[lang] = "Usted puede seleccionar \"Invocar Monstruos\", \"Habilidad\", o \"Protector\" como su tactica durante el Carnaval de los Monstruos. Utilice Tab y F1 ~ F12 para acceso rapido!";
lang = Language.LANG_ENG.getValue();
LanguageConstants.CPQBlue[lang] = "Maple Blue";
LanguageConstants.CPQRed[lang] = "Maple Red";
LanguageConstants.CPQPlayerExit[lang] = " left the Carnival of Monsters.";
LanguageConstants.CPQExtendTime[lang] = "The time has been extended.";
LanguageConstants.CPQLeaderNotFound[lang] = "Could not find the Leader.";
LanguageConstants.CPQError[lang] = "There was a problem. Please re-create a room.";
LanguageConstants.CPQPickRoom[lang] = "Sign up for the Monster Festival!\r\n";
LanguageConstants.CPQChallengeRoomAnswer[lang] = "The group is currently facing a challenge.";
LanguageConstants.CPQChallengeRoomSent[lang] = "A challenge has been sent to the group in the room. Please wait a while.";
LanguageConstants.CPQChallengeRoomDenied[lang] = "The group in the room canceled your challenge.";
LanguageConstants.CPQFindError[lang] = "We could not find a group in this room.\r\nProbably the group was scrapped inside the room!";
LanguageConstants.CPQEntryLobby[lang] = "You will now receive challenges from other groups. If you do not accept a challenge within 3 minutes, you will be taken out.";
LanguageConstants.CPQEntry[lang] = "You can select \"Summon Monsters\", \"Ability\", or \"Protector\" as your tactic during the Monster Carnival. Use Tab and F1 ~ F12 for quick access!";
}
public static String getMessage(MapleCharacter chr, String[] message) {
return message[chr.getLanguage()];
}
}
+23
View File
@@ -0,0 +1,23 @@
package constants;
/**
* @brief ScriptableNPCConstants
* @author GabrielSin <gabrielsin@playellin.net>
* @date 16/09/2018
*
* Adaptations to use Pair and Set, in order to suit a one-packet marshall,
* by Ronan
*/
import java.util.HashSet;
import java.util.Set;
import tools.Pair;
public class ScriptableNPCConstants {
public static final Set<Pair<Integer, String>> SCRIPTABLE_NPCS = new HashSet<Pair<Integer, String>>(){{
add(new Pair<>(9200000, "Cody"));
}};
}
+3
View File
@@ -64,6 +64,7 @@ public class ServerConstants {
public static boolean USE_DEBUG_SHOW_RCVD_MVLIFE = false; //Prints on the cmd all received move life content.
public static final boolean USE_DEBUG_SHOW_PACKET = false;
public static boolean USE_SUPPLY_RATE_COUPONS = true; //Allows rate coupons to be sold through the Cash Shop.
public static final boolean USE_IP_VALIDATION = true; //Enables IP checking when logging in.
public static final boolean USE_MAXRANGE = true; //Will send and receive packets from all events on a map, rather than those of only view range.
public static final boolean USE_MAXRANGE_ECHO_OF_HERO = true;
@@ -112,6 +113,7 @@ public class ServerConstants {
public static final boolean USE_REBIRTH_SYSTEM = false; //Flag to enable/disable rebirth system
public static final boolean USE_MAP_OWNERSHIP_SYSTEM = true; //Flag to enable/disable map ownership system
public static final boolean USE_FISHING_SYSTEM = true; //Flag to enable/disable fishing system
public static final boolean USE_NPCS_SCRIPTABLE = true; //Flag to enable/disable serverside predefined script NPCs.
//Events/PQs Configuration
public static final boolean USE_OLD_GMS_STYLED_PQ_NPCS = true; //Enables PQ NPCs with similar behaviour to old GMS style, that skips info about the PQs and immediately tries to register the party in.
@@ -164,6 +166,7 @@ public class ServerConstants {
public static final long PET_LOOT_UPON_ATTACK = (long)(0.7 * 1000); //Time the pet must wait before trying to pick items up.
public static final int TOT_MOB_QUEST_REQUIREMENT = 77; //Overwrites old 999-mobs requirement for the ToT questline with new requirement value, set 0 for default.
public static final int MOB_REACTOR_REFRESH_TIME = 30 * 1000; //Overwrites refresh time for those reactors oriented to inflict damage to bosses (Ice Queen, Riche), set 0 for default.
public static final int PARTY_SEARCH_REENTRY_LIMIT = 10; //Max amount of times a party leader is allowed to persist on the Party Search before entry expiration (thus needing to manually restart the Party Search to be able to search for members).
//Dangling Items/Locks Configuration
public static final int ITEM_EXPIRE_TIME = 3 * 60 * 1000; //Time before items start disappearing. Recommended to be set up to 3 minutes.
+1
View File
@@ -266,6 +266,7 @@ public enum SendOpcode {
REMOVE_NPC(0x102),
SPAWN_NPC_REQUEST_CONTROLLER(0x103),
NPC_ACTION(0x104),
SET_NPC_SCRIPTABLE(0x107),
SPAWN_HIRED_MERCHANT(0x109),
DESTROY_HIRED_MERCHANT(0x10A),
UPDATE_HIRED_MERCHANT(0x10B),
+12
View File
@@ -1648,6 +1648,10 @@ public class Server {
}
public boolean validateCharacteridInTransition(IoSession session, int charId) {
if (!ServerConstants.USE_IP_VALIDATION) {
return true;
}
String remoteIp = getRemoteIp(session);
lgnWLock.lock();
@@ -1660,6 +1664,10 @@ public class Server {
}
public Integer freeCharacteridInTransition(IoSession session) {
if (!ServerConstants.USE_IP_VALIDATION) {
return null;
}
String remoteIp = getRemoteIp(session);
lgnWLock.lock();
@@ -1671,6 +1679,10 @@ public class Server {
}
public boolean hasCharacteridInTransition(IoSession session) {
if (!ServerConstants.USE_IP_VALIDATION) {
return true;
}
String remoteIp = getRemoteIp(session);
lgnRLock.lock();
@@ -64,6 +64,8 @@ public enum MonitoredLockType {
GUILD,
PARTY,
WORLD_PARTY,
WORLD_PARTY_SEARCH_ECHELON,
WORLD_PARTY_SEARCH_STORAGE,
WORLD_SRVMESSAGES,
WORLD_PETS,
WORLD_CHARS,
+13 -8
View File
@@ -296,17 +296,22 @@ public final class Channel {
}
private void closeAllMerchants() {
merchWlock.lock();
try {
final Iterator<MapleHiredMerchant> hmit = hiredMerchants.values().iterator();
while (hmit.hasNext()) {
hmit.next().forceClose();
hmit.remove();
List<MapleHiredMerchant> merchs;
merchWlock.lock();
try {
merchs = new ArrayList<>(hiredMerchants.values());
hiredMerchants.clear();
} finally {
merchWlock.unlock();
}
for (MapleHiredMerchant merch : merchs) {
merch.forceClose();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
merchWlock.unlock();
e.printStackTrace();
}
}
@@ -273,6 +273,17 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
int totDamageToOneMonster = 0;
List<Integer> onedList = attack.allDamage.get(oned);
if (attack.magic) { // thanks BHB, Alex (CanIGetaPR) for noticing no immunity status check here
if (monster.isBuffed(MonsterStatus.MAGIC_IMMUNITY)) {
Collections.fill(onedList, 1);
}
} else {
if (monster.isBuffed(MonsterStatus.WEAPON_IMMUNITY)) {
Collections.fill(onedList, 1);
}
}
for (Integer eachd : onedList) {
if(eachd < 0) eachd += Integer.MAX_VALUE;
totDamageToOneMonster += eachd;
@@ -453,7 +464,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
int skillLv = player.getSkillLevel(threeSnailsId);
if(skillLv > 0) {
AbstractPlayerInteraction api = player.getClient().getAbstractPlayerInteraction();
AbstractPlayerInteraction api = player.getAbstractPlayerInteraction();
int shellId;
switch(skillLv) {
@@ -77,7 +77,13 @@ public final class GuildOperationHandler extends AbstractMaplePacketHandler {
Set<MapleCharacter> eligibleMembers = new HashSet<>(MapleGuild.getEligiblePlayersForGuild(mc));
if (eligibleMembers.size() < ServerConstants.CREATE_GUILD_MIN_PARTNERS) {
mc.dropMessage(1, "The Guild you are trying to create don't meet the minimum criteria of number of founders.");
if (mc.getMap().getAllPlayers().size() < ServerConstants.CREATE_GUILD_MIN_PARTNERS) {
mc.dropMessage(1, "The Guild you are trying to create don't meet the minimum criteria of number of founders.");
} else {
// players may be unaware of not belonging on a party in order to become eligible, thanks Hair (Legalize) for pointing this out
mc.dropMessage(1, "Please make sure everyone you are trying to invite is neither on a guild nor on a party.");
}
return;
}
@@ -91,7 +97,7 @@ public final class GuildOperationHandler extends AbstractMaplePacketHandler {
eligibleCids.add(chr.getId());
}
c.getWorldServer().getMatchCheckerCoordinator().createMatchConfirmation(MatchCheckerType.GUILD_CREATION, c.getWorld(), mc.getId(), eligibleCids, guildName);
c.getWorldServer().getMatchCheckerCoordinator().createMatchConfirmation(MatchCheckerType.GUILD_CREATION, c.getWorld(), mc.getId(), eligibleCids, guildName);
break;
case 0x05:
if (mc.getGuildId() <= 0 || mc.getGuildRank() > 2) {
@@ -246,12 +252,12 @@ public final class GuildOperationHandler extends AbstractMaplePacketHandler {
int leaderid = wserv.getMatchCheckerCoordinator().getMatchConfirmationLeaderid(mc.getId());
if (leaderid != -1) {
boolean result = slea.readByte() != 0;
if (result) {
if (result && wserv.getMatchCheckerCoordinator().isMatchConfirmationActive(mc.getId())) {
MapleCharacter leader = wserv.getPlayerStorage().getCharacterById(leaderid);
if (leader != null) {
int partyid = leader.getPartyId();
if (partyid != -1) {
MapleParty.joinParty(mc, partyid, true);
MapleParty.joinParty(mc, partyid, true); // GMS gimmick "party to form guild" recalled thanks to Vcoc
}
}
}
@@ -30,29 +30,39 @@ import client.MapleClient;
/**
*
* @author Matze
* @author Ronan - concurrency protection
*/
public final class MesoDropHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
MapleCharacter player = c.getPlayer();
if (!player.isAlive()) {
MapleCharacter player = c.getPlayer();
if (!player.isAlive()) {
c.announce(MaplePacketCreator.enableActions());
return;
}
slea.skip(4);
int meso = slea.readInt();
if (c.tryacquireClient()) { // thanks imbee for noticing players not being able to throw mesos too fast, dampening gameplay of some classes
try {
if (meso <= player.getMeso() && meso > 9 && meso < 50001) {
player.gainMeso(-meso, false, true, false);
} else {
c.announce(MaplePacketCreator.enableActions());
return;
}
} finally {
c.releaseClient();
}
if (!player.canDropMeso()){
player.announce(MaplePacketCreator.serverNotice(5, "Fast meso drop has been patched, cut that out. ;)"));
return;
}
slea.skip(4);
int meso = slea.readInt();
if (meso <= player.getMeso() && meso > 9 && meso < 50001) {
player.gainMeso(-meso, false, true, false);
if (player.attemptCatchFish(meso)) {
player.getMap().disappearingMesoDrop(meso, player, player, player.getPosition());
} else {
player.getMap().spawnMesoDrop(meso, player.getPosition(), player, player, true, (byte) 2);
}
}
} else {
c.announce(MaplePacketCreator.enableActions());
return;
}
if (player.attemptCatchFish(meso)) {
player.getMap().disappearingMesoDrop(meso, player, player, player.getPosition());
} else {
player.getMap().spawnMesoDrop(meso, player.getPosition(), player, player, true, (byte) 2);
}
}
}
@@ -67,8 +67,8 @@ public final class NewYearCardHandler extends AbstractMaplePacketHandler {
NewYearCardRecord.saveNewYearCard(newyear);
player.addNewYearRecord(newyear);
player.getClient().getAbstractPlayerInteraction().gainItem(2160101, (short)-1);
player.getClient().getAbstractPlayerInteraction().gainItem(4300000, (short) 1);
player.getAbstractPlayerInteraction().gainItem(2160101, (short)-1);
player.getAbstractPlayerInteraction().gainItem(4300000, (short) 1);
Server.getInstance().setNewYearCard(newyear);
newyear.startNewYearCardTask();
@@ -99,7 +99,7 @@ public final class NewYearCardHandler extends AbstractMaplePacketHandler {
newyear.stopNewYearCardTask();
NewYearCardRecord.updateNewYearCard(newyear);
player.getClient().getAbstractPlayerInteraction().gainItem(4301000, (short)1);
player.getAbstractPlayerInteraction().gainItem(4301000, (short)1);
if(!newyear.getMessage().isEmpty()) player.dropMessage(6, "[New Year] " + newyear.getSenderName() + ": " + newyear.getMessage());
player.addNewYearRecord(newyear);
@@ -34,14 +34,9 @@ import constants.ServerConstants;
import net.server.coordinator.MapleInviteCoordinator;
import net.server.coordinator.MapleInviteCoordinator.InviteResult;
import net.server.coordinator.MapleInviteCoordinator.InviteType;
import net.server.coordinator.MapleMatchCheckerCoordinator;
import net.server.coordinator.matchchecker.MatchCheckerListenerFactory.MatchCheckerType;
import scripting.event.EventInstanceManager;
import server.maps.MapleMap;
import tools.Pair;
import java.util.List;
import server.partyquest.MonsterCarnival;
public final class PartyOperationHandler extends AbstractMaplePacketHandler {
@@ -572,11 +572,11 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
}
short perBundle = slea.readShort();
if (ItemConstants.isRechargeable(ivItem.getItemId())) {
perBundle = 1;
bundles = 1;
} else if (chr.getItemQuantity(ivItem.getItemId(), false) < perBundle * bundles) {
} else if (ivItem.getQuantity() < (bundles * perBundle)) { // thanks GabrielSin for finding a dupe here
c.announce(MaplePacketCreator.serverNotice(1, "Could not perform shop operation with that item."));
c.announce(MaplePacketCreator.enableActions());
return;
@@ -58,6 +58,7 @@ import client.inventory.MapleInventory;
import client.inventory.MapleInventoryType;
import client.inventory.MaplePet;
import constants.GameConstants;
import constants.ScriptableNPCConstants;
import constants.ServerConstants;
import java.util.Collections;
import java.util.Comparator;
@@ -404,6 +405,10 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
eim.registerPlayer(player);
}
}
if (ServerConstants.USE_NPCS_SCRIPTABLE) {
c.announce(MaplePacketCreator.setNPCScriptable(ScriptableNPCConstants.SCRIPTABLE_NPCS));
}
} finally {
c.releaseClient();
}
@@ -212,21 +212,30 @@ public final class TakeDamageHandler extends AbstractMaplePacketHandler {
chr.getAutobanManager().resetMisses();
}
if (damage > 0 && !chr.isHidden()) {
if (attacker != null && damagefrom == -1 && chr.getBuffedValue(MapleBuffStat.POWERGUARD) != null) { // PG works on bosses, but only at half of the rate.
int bouncedamage = (int) (damage * (chr.getBuffedValue(MapleBuffStat.POWERGUARD).doubleValue() / (attacker.isBoss() ? 200 : 100)));
bouncedamage = Math.min(bouncedamage, attacker.getMaxHp() / 10);
damage -= bouncedamage;
map.damageMonster(chr, attacker, bouncedamage);
map.broadcastMessage(chr, MaplePacketCreator.damageMonster(oid, bouncedamage), false, true);
attacker.aggroMonsterDamage(chr, bouncedamage);
}
if (attacker != null && damagefrom == -1 && chr.getBuffedValue(MapleBuffStat.BODY_PRESSURE) != null) {
Skill skill = SkillFactory.getSkill(Aran.BODY_PRESSURE);
final MapleStatEffect eff = skill.getEffect(chr.getSkillLevel(skill));
if (!attacker.alreadyBuffedStats().contains(MonsterStatus.NEUTRALISE)) {
if (!attacker.isBoss() && eff.makeChanceResult()) {
attacker.applyStatus(chr, new MonsterStatusEffect(Collections.singletonMap(MonsterStatus.NEUTRALISE, 1), skill, null, false), false, (eff.getDuration()/10) * 2, false);
if (attacker != null) {
if (damagefrom == -1) {
if (chr.getBuffedValue(MapleBuffStat.POWERGUARD) != null) { // PG works on bosses, but only at half of the rate.
int bouncedamage = (int) (damage * (chr.getBuffedValue(MapleBuffStat.POWERGUARD).doubleValue() / (attacker.isBoss() ? 200 : 100)));
bouncedamage = Math.min(bouncedamage, attacker.getMaxHp() / 10);
damage -= bouncedamage;
map.damageMonster(chr, attacker, bouncedamage);
map.broadcastMessage(chr, MaplePacketCreator.damageMonster(oid, bouncedamage), false, true);
attacker.aggroMonsterDamage(chr, bouncedamage);
}
MapleStatEffect bPressure = chr.getBuffEffect(MapleBuffStat.COMBO_BARRIER);
if (bPressure != null) {
Skill skill = SkillFactory.getSkill(Aran.BODY_PRESSURE);
if (!attacker.alreadyBuffedStats().contains(MonsterStatus.NEUTRALISE)) {
if (!attacker.isBoss() && bPressure.makeChanceResult()) {
attacker.applyStatus(chr, new MonsterStatusEffect(Collections.singletonMap(MonsterStatus.NEUTRALISE, 1), skill, null, false), false, (bPressure.getDuration() / 10) * 2, false);
}
}
}
}
MapleStatEffect cBarrier = chr.getBuffEffect(MapleBuffStat.COMBO_BARRIER); // thanks BHB for noticing Combo Barrier buff not working
if (cBarrier != null) {
damage *= (cBarrier.getX() / 1000.0);
}
}
if (damagefrom != -3 && damagefrom != -4) {
@@ -30,14 +30,24 @@ import tools.data.input.SeekableLittleEndianAccessor;
/**
*
* @author kevintjuh93
* @author kevintjuh93; modified by Ronan
*/
public class UseGachaExpHandler extends AbstractMaplePacketHandler {
@Override
public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
if (c.getPlayer().getGachaExp() == 0) {
AutobanFactory.GACHA_EXP.autoban(c.getPlayer(), "Player tried to redeem GachaEXP, but had none to redeem.");
if (c.tryacquireClient()) {
try {
if (c.getPlayer().getGachaExp() <= 0) {
AutobanFactory.GACHA_EXP.autoban(c.getPlayer(), "Player tried to redeem GachaEXP, but had none to redeem.");
}
c.getPlayer().gainGachaExp();
} finally {
c.releaseClient();
}
}
c.getPlayer().gainGachaExp();
c.announce(MaplePacketCreator.enableActions());
}
}
@@ -31,6 +31,7 @@ import constants.ExpTable;
import net.AbstractMaplePacketHandler;
import client.inventory.manipulator.MapleInventoryManipulator;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.data.input.SeekableLittleEndianAccessor;
/**
@@ -50,6 +51,8 @@ public final class UseMountFoodHandler extends AbstractMaplePacketHandler {
if (c.tryacquireClient()) {
try {
Boolean mountLevelup = null;
useInv.lockInventory();
try {
Item item = useInv.getItem(pos);
@@ -67,7 +70,8 @@ public final class UseMountFoodHandler extends AbstractMaplePacketHandler {
if (levelup) {
mount.setLevel(level + 1);
}
chr.getMap().broadcastMessage(MaplePacketCreator.updateMount(chr.getId(), mount, levelup));
mountLevelup = levelup;
}
MapleInventoryManipulator.removeById(c, MapleInventoryType.USE, itemid, 1, true, false);
@@ -75,6 +79,10 @@ public final class UseMountFoodHandler extends AbstractMaplePacketHandler {
} finally {
useInv.unlockInventory();
}
if (mountLevelup != null) {
chr.getMap().broadcastMessage(MaplePacketCreator.updateMount(chr.getId(), mount, mountLevelup));
}
} finally {
c.releaseClient();
}
@@ -21,7 +21,9 @@
*/
package net.server.channel.handlers;
import client.MapleCharacter;
import client.MapleClient;
import client.inventory.MapleInventory;
import client.inventory.Item;
import client.inventory.MapleInventoryType;
import net.AbstractMaplePacketHandler;
@@ -32,7 +34,7 @@ import tools.data.input.SeekableLittleEndianAccessor;
/**
*
* @author XoticStory; modified by kevintjuh93
* @author XoticStory; modified by kevintjuh93, Ronan
*/
public final class UseSolomonHandler extends AbstractMaplePacketHandler {
@@ -42,16 +44,35 @@ public final class UseSolomonHandler extends AbstractMaplePacketHandler {
short slot = slea.readShort();
int itemId = slea.readInt();
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
Item slotItem = c.getPlayer().getInventory(MapleInventoryType.USE).getItem(slot);
int gachaexp = ii.getExpById(itemId);
if (c.getPlayer().getInventory(MapleInventoryType.USE).countById(itemId) <= 0 || slotItem.getItemId() != itemId || c.getPlayer().getLevel() > ii.getMaxLevelById(itemId)) {
return;
if (c.tryacquireClient()) {
try {
MapleCharacter chr = c.getPlayer();
MapleInventory inv = chr.getInventory(MapleInventoryType.USE);
inv.lockInventory();
try {
Item slotItem = inv.getItem(slot);
if (slotItem == null) {
return;
}
long gachaexp = ii.getExpById(itemId);
if (slotItem.getItemId() != itemId || slotItem.getQuantity() <= 0 || chr.getLevel() > ii.getMaxLevelById(itemId)) {
return;
}
if (gachaexp + chr.getGachaExp() > Integer.MAX_VALUE) {
return;
}
chr.addGachaExp((int) gachaexp);
MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.USE, slot, (short) 1, false);
} finally {
inv.unlockInventory();
}
} finally {
c.releaseClient();
}
}
if ((c.getPlayer().getGachaExp() + gachaexp) > Integer.MAX_VALUE) {
return;
}
c.getPlayer().gainGachaExp(gachaexp);
MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.USE, slot, (short) 1, false);
c.announce(MaplePacketCreator.enableActions());
}
}
@@ -29,6 +29,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.Semaphore;
@@ -76,6 +77,7 @@ public class MapleMatchCheckerCoordinator {
private Map<Integer, MapleMatchCheckingEntry> confirmingMembers = new HashMap<>();
private int confirmCount;
private boolean active = true;
private String message;
@@ -108,10 +110,30 @@ public class MapleMatchCheckerCoordinator {
return false;
}
private boolean isMatchActive() {
return active;
}
private void setMatchActive(boolean a) {
active = a;
}
private Set<Integer> getMatchPlayers() {
return confirmingMembers.keySet();
}
private Set<Integer> getAcceptedMatchPlayers() {
Set<Integer> s = new HashSet<>();
for (Entry<Integer, MapleMatchCheckingEntry> e : confirmingMembers.entrySet()) {
if (e.getValue().getAccept()) {
s.add(e.getKey());
}
}
return s;
}
private Set<MapleCharacter> getMatchCharacters() {
Set<MapleCharacter> players = new HashSet<>();
@@ -197,6 +219,20 @@ public class MapleMatchCheckerCoordinator {
return true;
}
private void reenablePlayerMatching(Set<Integer> matchPlayers) {
for (Integer cid : matchPlayers) {
MapleMatchCheckingElement mmce = matchEntries.get(cid);
if (mmce != null) {
synchronized (mmce) {
if (!mmce.isMatchActive()) {
matchEntries.remove(cid);
}
}
}
}
}
public int getMatchConfirmationLeaderid(int cid) {
MapleMatchCheckingElement mmce = matchEntries.get(cid);
if (mmce != null) {
@@ -215,6 +251,15 @@ public class MapleMatchCheckerCoordinator {
}
}
public boolean isMatchConfirmationActive(int cid) {
MapleMatchCheckingElement mmce = matchEntries.get(cid);
if (mmce != null) {
return mmce.active;
} else {
return false;
}
}
private void createMatchConfirmationInternal(MatchCheckerType matchType, int world, int leaderCid, AbstractMatchCheckerListener leaderListener, Set<Integer> players, String message) {
MapleMatchCheckingElement mmce = new MapleMatchCheckingElement(matchType, leaderCid, world, leaderListener, players, message);
@@ -224,9 +269,7 @@ public class MapleMatchCheckerCoordinator {
mmce.dispatchMatchCreated();
if (mmce.acceptEntry(leaderCid)) {
acceptMatchElement(mmce, leaderCid);
}
acceptMatchElement(mmce, leaderCid);
}
public boolean createMatchConfirmation(MatchCheckerType matchType, int world, int leaderCid, Set<Integer> players, String message) {
@@ -239,6 +282,8 @@ public class MapleMatchCheckerCoordinator {
AbstractMatchCheckerListener leaderListener = matchType.getListener();
createMatchConfirmationInternal(matchType, world, leaderCid, leaderListener, players, message);
return true;
} else {
reenablePlayerMatching(players);
}
} finally {
unpoolMatchPlayers(players);
@@ -255,7 +300,7 @@ public class MapleMatchCheckerCoordinator {
}
private void disposeMatchElement(MapleMatchCheckingElement mmce) {
Set<Integer> matchPlayers = mmce.getMatchPlayers();
Set<Integer> matchPlayers = mmce.getAcceptedMatchPlayers();
while (!poolMatchPlayers(matchPlayers)) {
try {
Thread.sleep(1000);
@@ -288,6 +333,8 @@ public class MapleMatchCheckerCoordinator {
}
private void dismissMatchElement(MapleMatchCheckingElement mmce, int cid) {
mmce.setMatchActive(false);
unpoolMatchPlayer(cid);
disposeMatchElement(mmce);
@@ -302,14 +349,22 @@ public class MapleMatchCheckerCoordinator {
if (poolMatchPlayer(cid)) {
try {
MapleMatchCheckingElement mmce = matchEntries.get(cid);
if (mmce != null) {
if (accept) {
acceptMatchElement(mmce, cid);
} else {
denyMatchElement(mmce, cid);
synchronized (mmce) {
if (!mmce.isMatchActive()) { // thanks Alex (CanIGetaPR) for noticing that exploiters could stall on match checking
matchEntries.remove(cid);
return false;
}
if (accept) {
acceptMatchElement(mmce, cid);
} else {
denyMatchElement(mmce, cid);
matchEntries.remove(cid);
}
}
return true;
}
} finally {
@@ -337,8 +392,14 @@ public class MapleMatchCheckerCoordinator {
MapleMatchCheckingElement mmce = matchEntries.get(cid);
if (mmce != null) {
dismissMatchElement(mmce, cid);
return true;
synchronized (mmce) {
if (!mmce.isMatchActive()) {
return false;
}
dismissMatchElement(mmce, cid);
return true;
}
}
} finally {
unpoolMatchPlayer(cid);
@@ -305,12 +305,12 @@ public class MapleSessionCoordinator {
}
public void closeLoginSession(IoSession session) {
String remoteIp = getSessionRemoteAddress(session);
Set<IoSession> lrh = loginRemoteHosts.get(remoteIp);
String remoteHost = getSessionRemoteAddress(session);
Set<IoSession> lrh = loginRemoteHosts.get(remoteHost);
if (lrh != null) {
lrh.remove(session);
if (lrh.isEmpty()) {
loginRemoteHosts.remove(remoteIp);
loginRemoteHosts.remove(remoteHost);
}
}
@@ -20,6 +20,7 @@
package net.server.coordinator.matchchecker;
import net.server.coordinator.matchchecker.listener.MatchCheckerGuildCreation;
import net.server.coordinator.matchchecker.listener.MatchCheckerCPQChallenge;
/**
*
@@ -29,7 +30,8 @@ public class MatchCheckerListenerFactory {
public enum MatchCheckerType {
GUILD_CREATION(MatchCheckerGuildCreation.loadListener());
GUILD_CREATION(MatchCheckerGuildCreation.loadListener()),
CPQ_CHALLENGE(MatchCheckerCPQChallenge.loadListener());
private final AbstractMatchCheckerListener listener;
@@ -0,0 +1,119 @@
/*
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.matchchecker.listener;
import client.MapleCharacter;
import constants.LanguageConstants;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import net.server.coordinator.matchchecker.AbstractMatchCheckerListener;
import net.server.coordinator.matchchecker.MatchCheckerListenerRecipe;
import net.server.world.MaplePartyCharacter;
import scripting.npc.NPCConversationManager;
import scripting.npc.NPCScriptManager;
/**
*
* @author Ronan
*/
public class MatchCheckerCPQChallenge implements MatchCheckerListenerRecipe {
public static AbstractMatchCheckerListener loadListener() {
return (new MatchCheckerCPQChallenge()).getListener();
}
private static MapleCharacter getChallenger(int leaderid, Set<MapleCharacter> matchPlayers) {
MapleCharacter leader = null;
for (MapleCharacter chr : matchPlayers) {
if (chr.getId() == leaderid && chr.getClient() != null) {
leader = chr;
break;
}
}
return leader;
}
@Override
public AbstractMatchCheckerListener getListener() {
return new AbstractMatchCheckerListener() {
@Override
public void onMatchCreated(MapleCharacter leader, Set<MapleCharacter> nonLeaderMatchPlayers, String message) {
NPCConversationManager cm = leader.getClient().getCM();
int npcid = cm.getNpc();
MapleCharacter ldr = null;
for (MapleCharacter chr : nonLeaderMatchPlayers) {
ldr = chr;
break;
}
MapleCharacter chr = leader;
List<MaplePartyCharacter> chrMembers = new LinkedList<>();
for (MaplePartyCharacter mpc : chr.getParty().getMembers()) {
chrMembers.add(mpc);
}
if (message.contentEquals("cpq1")) {
NPCScriptManager.getInstance().start("cpqchallenge", ldr.getClient(), npcid, chrMembers);
} else {
NPCScriptManager.getInstance().start("cpqchallenge2", ldr.getClient(), npcid, chrMembers);
}
cm.sendOk(LanguageConstants.getMessage(chr, LanguageConstants.CPQChallengeRoomSent));
}
@Override
public void onMatchAccepted(int leaderid, Set<MapleCharacter> matchPlayers, String message) {
MapleCharacter chr = getChallenger(leaderid, matchPlayers);
MapleCharacter ldr = null;
for (MapleCharacter ch : matchPlayers) {
if (ch != chr) {
ldr = ch;
break;
}
}
if (message.contentEquals("cpq1")) {
ldr.getClient().getCM().startCPQ(chr, ldr.getMapId() + 1);
} else {
ldr.getClient().getCM().startCPQ2(chr, ldr.getMapId() + 1);
}
ldr.getParty().setEnemy(chr.getParty());
chr.getParty().setEnemy(ldr.getParty());
chr.setChallenged(false);
}
@Override
public void onMatchDeclined(int leaderid, Set<MapleCharacter> matchPlayers, String message) {
MapleCharacter chr = getChallenger(leaderid, matchPlayers);
chr.dropMessage(5, LanguageConstants.getMessage(chr, LanguageConstants.CPQChallengeRoomDenied));
}
@Override
public void onMatchDismissed(int leaderid, Set<MapleCharacter> matchPlayers, String message) {}
};
}
}
@@ -166,13 +166,29 @@ public class MatchCheckerGuildCreation implements MatchCheckerListenerRecipe {
@Override
public void onMatchDismissed(int leaderid, Set<MapleCharacter> matchPlayers, String message) {
MapleCharacter leader = null;
for (MapleCharacter chr : matchPlayers) {
if (chr.getId() == leaderid) {
leader = chr;
break;
}
}
String msg;
if (leader != null && leader.getParty() == null) {
msg = "The Guild creation has been dismissed since the leader left the founding party.";
} else {
msg = "The Guild creation has been dismissed since a member was already in a party when they answered.";
}
for (MapleCharacter chr : matchPlayers) {
if (chr.getId() == leaderid && chr.getClient() != null) {
MapleParty.leaveParty(chr.getParty(), chr.getClient());
}
if (chr.isLoggedinWorld()) {
chr.message("All cofounders must not be in a party when performing the Guild creation.");
chr.message(msg);
}
}
}
+4 -2
View File
@@ -50,6 +50,7 @@ import net.server.audit.locks.MonitoredLockType;
import net.server.coordinator.MapleInviteCoordinator;
import net.server.coordinator.MapleInviteCoordinator.InviteType;
import net.server.coordinator.MapleInviteCoordinator.InviteResult;
import net.server.coordinator.MapleMatchCheckerCoordinator;
public class MapleGuild {
@@ -752,8 +753,9 @@ public class MapleGuild {
Set<MapleCharacter> guildMembers = new HashSet<>();
guildMembers.add(guildLeader);
MapleMatchCheckerCoordinator mmce = guildLeader.getWorldServer().getMatchCheckerCoordinator();
for (MapleCharacter chr : guildLeader.getMap().getAllPlayers()) {
if (chr.getParty() == null && chr.getGuild() == null) {
if (chr.getParty() == null && chr.getGuild() == null && mmce.getMatchConfirmationLeaderid(chr.getId()) == -1) {
guildMembers.add(chr);
}
}
@@ -765,7 +767,7 @@ public class MapleGuild {
try {
ResultSet rs;
Connection con = DatabaseConnection.getConnection();
try (PreparedStatement ps = con.prepareStatement("SELECT `name`, `GP`, `logoBG`, `logoBGColor`, `logo`, `logoColor` FROM guilds WHERE NOT `guildid` = '1' ORDER BY `GP` DESC LIMIT 50")) {
try (PreparedStatement ps = con.prepareStatement("SELECT `name`, `GP`, `logoBG`, `logoBGColor`, `logo`, `logoColor` FROM guilds ORDER BY `GP` DESC LIMIT 50")) {
rs = ps.executeQuery();
c.announce(MaplePacketCreator.showGuildRanks(npcid, rs));
}
@@ -64,15 +64,17 @@ public final class LoginPasswordHandler implements MaplePacketHandler {
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
String remoteHost = getRemoteIp(c.getSession());
if (!remoteHost.contentEquals("null")) {
if (remoteHost.startsWith("127.")) {
if (!ServerConstants.LOCALSERVER) { // thanks Mills for noting HOST can also have a field named "localhost"
c.announce(MaplePacketCreator.getLoginFailed(13)); // cannot login as localhost if it's not a local server
return;
}
} else {
if (ServerConstants.LOCALSERVER) {
c.announce(MaplePacketCreator.getLoginFailed(13)); // cannot login as non-localhost if it's a local server
return;
if (ServerConstants.USE_IP_VALIDATION) { // thanks Alex (CanIGetaPR) for suggesting IP validation as a server flag
if (remoteHost.startsWith("127.")) {
if (!ServerConstants.LOCALSERVER) { // thanks Mills for noting HOST can also have a field named "localhost"
c.announce(MaplePacketCreator.getLoginFailed(13)); // cannot login as localhost if it's not a local server
return;
}
} else {
if (ServerConstants.LOCALSERVER) {
c.announce(MaplePacketCreator.getLoginFailed(13)); // cannot login as non-localhost if it's a local server
return;
}
}
}
} else {
+3 -2
View File
@@ -334,7 +334,7 @@ public class MapleParty {
player.dropMessage(5, "You cannot request a party creation while participating the Ariant Battle Arena.");
return false;
}
MaplePartyCharacter partyplayer = new MaplePartyCharacter(player);
party = player.getWorldServer().createParty(partyplayer);
player.setParty(party);
@@ -343,6 +343,7 @@ public class MapleParty {
player.silentPartyUpdate();
player.partyOperationUpdate(party, null);
player.announce(MaplePacketCreator.partyCreated(party, partyplayer.getId()));
return true;
@@ -365,7 +366,7 @@ public class MapleParty {
if (party.getMembers().size() < 6) {
MaplePartyCharacter partyplayer = new MaplePartyCharacter(player);
player.getMap().addPartyMember(player);
world.updateParty(party.getId(), PartyOperation.JOIN, partyplayer);
player.receivePartyMemberHP();
player.updatePartyMemberHP();
@@ -943,7 +943,7 @@ public class EventInstanceManager {
}
private void dropExclusiveItems(MapleCharacter chr) {
AbstractPlayerInteraction api = chr.getClient().getAbstractPlayerInteraction();
AbstractPlayerInteraction api = chr.getAbstractPlayerInteraction();
for(Integer item: exclusiveItems) {
api.removeAll(item);
@@ -1051,7 +1051,7 @@ public class EventInstanceManager {
if(!hasRewardSlot(player, eventLevel)) return false;
AbstractPlayerInteraction api = player.getClient().getAbstractPlayerInteraction();
AbstractPlayerInteraction api = player.getAbstractPlayerInteraction();
int rnd = (int)Math.floor(Math.random() * rewardsSet.size());
api.gainItem(rewardsSet.get(rnd), rewardsQty.get(rnd).shortValue());
+35 -27
View File
@@ -62,6 +62,7 @@ import constants.ItemConstants;
import constants.LanguageConstants;
import net.server.PlayerStorage;
import net.server.channel.Channel;
import net.server.coordinator.matchchecker.MatchCheckerListenerFactory.MatchCheckerType;
import server.MapleSkillbookInformationProvider;
import server.MapleSkillbookInformationProvider.SkillBookEntry;
import server.TimerManager;
@@ -76,8 +77,9 @@ import tools.packets.Wedding;
import java.awt.Point;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import server.MapleMarriage;
/**
@@ -633,7 +635,7 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
}
public boolean sendCPQMapLists() {
String msg = LanguageConstants.Languages(getPlayer()).CPQPickRoom;
String msg = LanguageConstants.getMessage(getPlayer(), LanguageConstants.CPQPickRoom);
int msgLen = msg.length();
for (int i = 0; i < 6; i++) {
if (fieldTaken(i)) {
@@ -696,7 +698,7 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
mc = ps.getCharacterById(mpc.getId());
if (mc != null) {
mc.changeMap(map, map.getPortal(0));
mc.announce(MaplePacketCreator.serverNotice(6, LanguageConstants.Languages(mc).CPQEntryLobby));
mc.announce(MaplePacketCreator.serverNotice(6, LanguageConstants.getMessage(mc, LanguageConstants.CPQEntryLobby)));
TimerManager tMan = TimerManager.getInstance();
tMan.schedule(new Runnable() {
@Override
@@ -850,7 +852,7 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
}
public boolean sendCPQMapLists2() {
String msg = LanguageConstants.Languages(getPlayer()).CPQPickRoom;
String msg = LanguageConstants.getMessage(getPlayer(), LanguageConstants.CPQPickRoom);
int msgLen = msg.length();
for (int i = 0; i < 3; i++) {
if (fieldTaken2(i)) {
@@ -913,7 +915,7 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
mc = ps.getCharacterById(mpc.getId());
if (mc != null) {
mc.changeMap(map, map.getPortal(0));
mc.announce(MaplePacketCreator.serverNotice(6, LanguageConstants.Languages(mc).CPQEntryLobby));
mc.announce(MaplePacketCreator.serverNotice(6, LanguageConstants.getMessage(mc, LanguageConstants.CPQEntryLobby)));
TimerManager tMan = TimerManager.getInstance();
tMan.schedule(new Runnable() {
@Override
@@ -934,14 +936,30 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
ex.printStackTrace();
}
}
public void mapClock(int time) {
getPlayer().getMap().broadcastMessage(MaplePacketCreator.getClock(time));
}
private boolean sendCPQChallenge(String cpqType, int leaderid) {
Set<Integer> cpqLeaders = new HashSet<>();
cpqLeaders.add(leaderid);
cpqLeaders.add(getPlayer().getId());
return c.getWorldServer().getMatchCheckerCoordinator().createMatchConfirmation(MatchCheckerType.CPQ_CHALLENGE, c.getWorld(), getPlayer().getId(), cpqLeaders, cpqType);
}
public void answerCPQChallenge(boolean accept) {
c.getWorldServer().getMatchCheckerCoordinator().answerMatchConfirmation(getPlayer().getId(), accept);
}
public void challengeParty2(int field) {
MapleCharacter leader = null;
MapleMap map = c.getChannelServer().getMapFactory().getMap(980031000 + 1000 * field);
for (MapleMapObject mmo : map.getAllPlayer()) {
MapleCharacter mc = (MapleCharacter) mmo;
if (mc.getParty() == null) {
sendOk(LanguageConstants.Languages(mc).CPQFindError);
sendOk(LanguageConstants.getMessage(mc, LanguageConstants.CPQFindError));
return;
}
if (mc.getParty().getLeader().getId() == mc.getId()) {
@@ -951,23 +969,17 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
}
if (leader != null) {
if (!leader.isChallenged()) {
List<MaplePartyCharacter> members = new LinkedList<>();
for (MaplePartyCharacter fucker : c.getPlayer().getParty().getMembers()) {
members.add(fucker);
if (!sendCPQChallenge("cpq2", leader.getId())) {
sendOk(LanguageConstants.getMessage(leader, LanguageConstants.CPQChallengeRoomAnswer));
}
NPCScriptManager.getInstance().start("cpqchallenge2", leader.getClient(), npc, members);
} else {
sendOk(LanguageConstants.Languages(leader).CPQChallengeRoomAnswer);
sendOk(LanguageConstants.getMessage(leader, LanguageConstants.CPQChallengeRoomAnswer));
}
} else {
sendOk(LanguageConstants.Languages(leader).CPQLeaderNotFound);
sendOk(LanguageConstants.getMessage(leader, LanguageConstants.CPQLeaderNotFound));
}
}
public void mapClock(int time) {
getPlayer().getMap().broadcastMessage(MaplePacketCreator.getClock(time));
}
public void challengeParty(int field) {
MapleCharacter leader = null;
MapleMap map = c.getChannelServer().getMapFactory().getMap(980000100 + 100 * field);
@@ -978,7 +990,7 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
for (MapleMapObject mmo : map.getAllPlayer()) {
MapleCharacter mc = (MapleCharacter) mmo;
if (mc.getParty() == null) {
sendOk(LanguageConstants.Languages(mc).CPQFindError);
sendOk(LanguageConstants.getMessage(mc, LanguageConstants.CPQFindError));
return;
}
if (mc.getParty().getLeader().getId() == mc.getId()) {
@@ -988,18 +1000,14 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
}
if (leader != null) {
if (!leader.isChallenged()) {
List<MaplePartyCharacter> members = new LinkedList<>();
for (MaplePartyCharacter mpc : c.getPlayer().getParty().getMembers()) {
members.add(mpc);
if (!sendCPQChallenge("cpq1", leader.getId())) {
sendOk(LanguageConstants.getMessage(leader, LanguageConstants.CPQChallengeRoomAnswer));
}
NPCScriptManager.getInstance().start("cpqchallenge", leader.getClient(), npc, members);
sendOk(LanguageConstants.Languages(leader).CPQChallengeRoomSent);
} else {
sendOk(LanguageConstants.Languages(leader).CPQChallengeRoomAnswer);
sendOk(LanguageConstants.getMessage(leader, LanguageConstants.CPQChallengeRoomAnswer));
}
} else {
sendOk(LanguageConstants.Languages(leader).CPQLeaderNotFound);
sendOk(LanguageConstants.getMessage(leader, LanguageConstants.CPQLeaderNotFound));
}
}
@@ -141,7 +141,7 @@ public class MapleSkillbookInformationProvider {
if(token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
else if(token.contains("imgdir") && !token.endsWith("/>")) { // '\>' XML node description not being accounted, issue found thanks to Robin Schulz, CanIGetaPR
status += 1;
}
}
@@ -182,7 +182,7 @@ public class MapleSkillbookInformationProvider {
currentItemid = 0;
currentCount = 0;
}
else if(token.contains("imgdir")) {
else if(token.contains("imgdir") && !token.endsWith("/>")) {
status += 1;
}
else {
@@ -203,7 +203,7 @@ public class MapleSkillbookInformationProvider {
if(token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
else if(token.contains("imgdir") && !token.endsWith("/>")) {
if(status == 1) { //getting QuestId
d = getName(token);
questId = Integer.parseInt(d);
@@ -228,19 +228,22 @@ public class MapleSkillbookInformationProvider {
}
private static void fetchSkillbooksFromQuests() {
String line;
String line = "";
int lineNumber = 0; // add line number, thanks to Alex (CanIGetaPR)
try {
fileReader = new InputStreamReader(new FileInputStream(wzPath + "/Quest.wz/Act.img.xml"), "UTF-8");
bufferedReader = new BufferedReader(fileReader);
while((line = bufferedReader.readLine()) != null) {
lineNumber++;
translateActToken(line);
}
bufferedReader.close();
fileReader.close();
} catch(IOException ioe) {
System.out.println("Failed to read Quest.wz file.");
System.out.println("Failed to read Quest.wz file. Line " + lineNumber + ": " + line);
ioe.printStackTrace();
}
}
+1 -1
View File
@@ -805,7 +805,7 @@ public class MapleStatEffect {
int mpchange = calcMPChange(applyfrom, primary);
if (primary) {
if (itemConNo != 0) {
if (!applyto.getClient().getAbstractPlayerInteraction().hasItem(itemCon, itemConNo)) {
if (!applyto.getAbstractPlayerInteraction().hasItem(itemCon, itemConNo)) {
applyto.announce(MaplePacketCreator.enableActions());
return false;
}
+2
View File
@@ -1750,6 +1750,8 @@ public class MapleMonster extends AbstractLoadedMapleLife {
changeLevelByDifficulty(difficulty, pqMob);
}
// ---------------------------------------------------------------------------------
private boolean isPuppetInVicinity(MapleSummon summon) {
return summon.getPosition().distanceSq(this.getPosition()) < 177777;
}
+14 -1
View File
@@ -34,6 +34,7 @@ import com.mysql.jdbc.Statement;
import constants.ServerConstants;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
@@ -297,8 +298,20 @@ public class MapleHiredMerchant extends AbstractMapleMapObject {
try {
Connection con = DatabaseConnection.getConnection();
try (PreparedStatement ps = con.prepareStatement("UPDATE characters SET MerchantMesos = MerchantMesos + " + price + " WHERE id = ?", Statement.RETURN_GENERATED_KEYS)) {
long merchantMesos = 0;
try (PreparedStatement ps = con.prepareStatement("SELECT MerchantMesos FROM characters WHERE id = ?")) {
ps.setInt(1, ownerId);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
merchantMesos = rs.getInt(1);
}
}
}
merchantMesos += price;
try (PreparedStatement ps = con.prepareStatement("UPDATE characters SET MerchantMesos = ? WHERE id = ?", Statement.RETURN_GENERATED_KEYS)) {
ps.setInt(1, (int) Math.min(merchantMesos, Integer.MAX_VALUE));
ps.setInt(2, ownerId);
ps.executeUpdate();
}
+4 -3
View File
@@ -1380,12 +1380,13 @@ public class MapleMap {
}
if (chr == null) {
if(removeKilledMonsterObject(monster)) {
if (removeKilledMonsterObject(monster)) {
monster.dispatchMonsterKilled(false);
broadcastMessage(MaplePacketCreator.killMonster(monster.getObjectId(), animation), monster.getPosition());
monster.aggroSwitchController(null, false);
}
} else {
if(removeKilledMonsterObject(monster)) {
if (removeKilledMonsterObject(monster)) {
if (monster.getStats().getLevel() >= chr.getLevel() + 30 && !chr.isGM()) {
AutobanFactory.GENERAL.alert(chr, " for killing a " + monster.getName() + " which is over 30 levels higher.");
}
@@ -3676,7 +3677,7 @@ public class MapleMap {
}
private static double getCurrentSpawnRate(int numPlayers) {
return 0.550 + (0.075 * Math.min(6, numPlayers));
return 0.70 + (0.05 * Math.min(6, numPlayers));
}
private int getNumShouldSpawn(int numPlayers) {
+1 -1
View File
@@ -110,7 +110,7 @@ public class MapleMapFactory {
int y = MapleDataTool.getInt(life.getChildByPath("y"));
int hide = MapleDataTool.getInt("hide", life, 0);
int mobTime = MapleDataTool.getInt("mobTime", life, 0);
loadLifeRaw(map, Integer.parseInt(id), type, cy, f, fh, rx0, rx1, x, y, hide, mobTime, team);
}
}
+1 -1
View File
@@ -193,7 +193,7 @@ public class AriantColiseum {
chr.setAriantColiseum(null);
int shards = chr.countItem(4031868);
chr.getClient().getAbstractPlayerInteraction().removeAll(4031868);
chr.getAbstractPlayerInteraction().removeAll(4031868);
chr.updateAriantScore(shards);
}
}
+8 -8
View File
@@ -59,7 +59,7 @@ public class MonsterCarnival {
mc.setTeam(0);
mc.setFestivalPoints(0);
mc.forceChangeMap(map, map.getPortal(redPortal));
mc.dropMessage(6, LanguageConstants.Languages(mc).CPQEntry);
mc.dropMessage(6, LanguageConstants.getMessage(mc, LanguageConstants.CPQEntry));
if (p1.getLeader().getId() == mc.getId()) {
leader1 = mc;
}
@@ -73,7 +73,7 @@ public class MonsterCarnival {
mc.setTeam(1);
mc.setFestivalPoints(0);
mc.forceChangeMap(map, map.getPortal(bluePortal));
mc.dropMessage(6, LanguageConstants.Languages(mc).CPQEntry);
mc.dropMessage(6, LanguageConstants.getMessage(mc, LanguageConstants.CPQEntry));
if (p2.getLeader().getId() == mc.getId()) {
leader2 = mc;
}
@@ -82,10 +82,10 @@ public class MonsterCarnival {
}
if (Grupo1 == null || Grupo2 == null) {
for (MaplePartyCharacter mpc : p2.getMembers()) {
mpc.getPlayer().dropMessage(5, LanguageConstants.Languages(mpc.getPlayer()).CPQError);
mpc.getPlayer().dropMessage(5, LanguageConstants.getMessage(mpc.getPlayer(), LanguageConstants.CPQError));
}
for (MaplePartyCharacter mpc : p2.getMembers()) {
mpc.getPlayer().dropMessage(5, LanguageConstants.Languages(mpc.getPlayer()).CPQError);
mpc.getPlayer().dropMessage(5, LanguageConstants.getMessage(mpc.getPlayer(), LanguageConstants.CPQError));
}
return;
}
@@ -140,13 +140,13 @@ public class MonsterCarnival {
String teamS = "";
switch (team) {
case 0:
teamS = LanguageConstants.Languages(chrMap).CPQRed;
teamS = LanguageConstants.getMessage(chrMap, LanguageConstants.CPQRed);
break;
case 1:
teamS = LanguageConstants.Languages(chrMap).CPQBlue;
teamS = LanguageConstants.getMessage(chrMap, LanguageConstants.CPQBlue);
break;
}
chrMap.dropMessage(5, teamS + LanguageConstants.Languages(chrMap).CPQPlayerExit);
chrMap.dropMessage(5, teamS + LanguageConstants.getMessage(chrMap, LanguageConstants.CPQPlayerExit));
}
earlyFinish();
}
@@ -353,7 +353,7 @@ public class MonsterCarnival {
private void extendTime() {
for (MapleCharacter chrMap : map.getAllPlayers()) {
chrMap.dropMessage(5, LanguageConstants.Languages(chrMap).CPQExtendTime);
chrMap.dropMessage(5, LanguageConstants.getMessage(chrMap, LanguageConstants.CPQExtendTime));
}
startTime = System.currentTimeMillis() + 3 * 60 * 1000;
+2
View File
@@ -321,6 +321,7 @@ public class MapleQuest {
public boolean forceStart(MapleCharacter c, int npc) {
MapleQuestStatus newStatus = new MapleQuestStatus(this, MapleQuestStatus.Status.STARTED, npc);
newStatus.setForfeited(c.getQuest(this).getForfeited());
newStatus.setCompleted(c.getQuest(this).getCompleted());
if (timeLimit > 0) {
newStatus.setExpirationTime(System.currentTimeMillis() + (timeLimit * 1000));
@@ -355,6 +356,7 @@ public class MapleQuest {
MapleQuestStatus newStatus = new MapleQuestStatus(this, MapleQuestStatus.Status.COMPLETED, npc);
newStatus.setForfeited(c.getQuest(this).getForfeited());
newStatus.setCompleted(c.getQuest(this).getCompleted());
newStatus.setCompletionTime(System.currentTimeMillis());
c.updateQuest(newStatus);
+14
View File
@@ -7902,4 +7902,18 @@ public class MaplePacketCreator {
mplew.writeInt(transition);
return mplew.getPacket();
}
public static byte[] setNPCScriptable(Set<Pair<Integer, String>> scriptNpcDescriptions) { // thanks to GabrielSin
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.SET_NPC_SCRIPTABLE.getValue());
mplew.write(scriptNpcDescriptions.size());
for (Pair<Integer, String> p : scriptNpcDescriptions) {
mplew.writeInt(p.getLeft());
mplew.writeMapleAsciiString(p.getRight());
mplew.writeInt(0); // start time
mplew.writeInt(Integer.MAX_VALUE); // end time
}
return mplew.getPacket();
}
}
+1 -1
View File
@@ -108,7 +108,7 @@ public class Fishing {
rewardStr = "a(n) " + MapleItemInformationProvider.getInstance().getName(itemid) + ".";
if (chr.canHold(itemid)) {
chr.getClient().getAbstractPlayerInteraction().gainItem(itemid, true);
chr.getAbstractPlayerInteraction().gainItem(itemid, true);
} else {
chr.showHint("Couldn't catch a(n) #r" + MapleItemInformationProvider.getInstance().getName(itemid) + "#k due to #e#b" + ItemConstants.getInventoryType(itemid) + "#k#n inventory limit.");
rewardStr += ".. but has goofed up due to full inventory.";