Added Puppets on Aggro + Guild/Alliance packet info patch + Pet flags

Added pet flags recognition, such as the "pet speed same as owner" mechanic.
Fixed repeatable quest "Remnants of HT..." not rewarding Dragon Stones.
Refactored character object's check slots method, now using the same algorithm from the API class.
Fixed a vunerability from before reviving/respawning players to towns, on where players would get their HP restored in area map before sending them back to safety.
Fixed some NPC gates in Mushking Empire, allowing players to access bosses free of quest requirements.
Improved dialog of NPCs for "permission to attempt Zakum expeds".
Implemented (or rather improved) item scripts, now using NPC conversation methods as it was intended originally.
Rehauled mob-skill "summon mobs" limit, now using the placeholders found in the wz to determine limit of concurrent underlings spawned, and "limit" now refers to total of underlings spawned from a mob.
Revised IoSession closing mechanics in several login classes.
Implemented automated loggedin account shutdown if another player has gained a pass from the DB to login.
Improved the server-side check for teleport rocks.
Fixed removeAfter from mobs being procced after they were disposed.
Reviewed EIM start references on several scripts, minding the latest EIM setup implementations.
Fixed equipments that were reused Scissors of Karma not not being able to sell on pshops/merchants.
Fixed a bug where entering alliances would lead a player to see information from other guilds of past alliances until an update takes place.
Padronized a few skillbook names.
Reviewed player puppets not gaining priority properly on the recent aggro system.
Improved "item sold" message of merchants, now displaying also quantity left of an item on store.
Fixed a bug with itemid limits of weapons on server.
This commit is contained in:
ronancpl
2019-02-17 16:08:53 -03:00
parent 4beea39615
commit 46fec675f1
127 changed files with 1760 additions and 608 deletions
+44 -36
View File
@@ -66,6 +66,7 @@ import net.server.world.World;
import scripting.event.EventInstanceManager;
import server.CashShop;
import server.MapleItemInformationProvider;
import server.MapleItemInformationProvider.ScriptedItem;
import server.MaplePortal;
import server.MapleShop;
import server.MapleStatEffect;
@@ -287,6 +288,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
private AutobanManager autoban;
private boolean isbanned = false;
private boolean blockCashShop = false;
private boolean allowExpGain = false;
private byte pendantExp = 0, lastmobcount = 0, doorSlot = -1;
private List<Integer> trockmaps = new ArrayList<>();
private List<Integer> viptrockmaps = new ArrayList<>();
@@ -649,6 +651,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
public void addSummon(int id, MapleSummon summon) {
summons.put(id, summon);
if (summon.isPuppet()) {
map.addPlayerPuppet(this);
}
}
public void addVisibleMapObject(MapleMapObject mo) {
@@ -805,6 +811,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
public void toggleBlockCashShop() {
blockCashShop = !blockCashShop;
}
public void toggleExpGain() {
allowExpGain = !allowExpGain;
}
public void setClient(MapleClient c) {
this.client = c;
@@ -1728,6 +1738,15 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
}
public Collection<MapleMonster> getControlledMonsters() {
cpnLock.lock();
try {
return new ArrayList<>(controlled);
} finally {
cpnLock.unlock();
}
}
public void releaseControlledMonsters() {
Collection<MapleMonster> controlledMonsters;
@@ -1799,7 +1818,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
mpcs = getPartyMembersOnSameMap();
}
String itemScriptName = null;
ScriptedItem itemScript = null;
mapitem.lockItem();
try {
if (mapitem.isPickedUp()) {
@@ -1872,9 +1891,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
this.gainMeso(mapitem.getMeso(), true, true, false);
}
} else if (mItem.getItemId() / 10000 == 243) {
MapleItemInformationProvider.scriptedItem info = ii.getScriptedItemInfo(mItem.getItemId());
if (info.runOnPickup()) {
itemScriptName = info.getScript();
ScriptedItem info = ii.getScriptedItemInfo(mItem.getItemId());
if (info != null && info.runOnPickup()) {
itemScript = info;
} else {
if (!MapleInventoryManipulator.addFromDrop(client, mItem, true)) {
client.announce(MaplePacketCreator.enableActions());
@@ -1908,11 +1927,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
mapitem.unlockItem();
}
if (itemScriptName != null) {
if (itemScript != null) {
ItemScriptManager ism = ItemScriptManager.getInstance();
if (ism.scriptExists(itemScriptName)) {
ism.runItemScript(client, itemScriptName);
}
ism.runItemScript(client, itemScript);
}
}
client.announce(MaplePacketCreator.enableActions());
@@ -1927,14 +1944,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
public boolean canHold(int itemid, int quantity) {
int hold = getCleanItemQuantity(itemid, false);
if(hold > 0) {
if(hold + quantity <= ii.getSlotMax(client, itemid))
return true;
}
return getInventory(ItemConstants.getInventoryType(itemid)).getNextFreeSlot() > -1;
return client.getAbstractPlayerInteraction().canHold(itemid, quantity);
}
public boolean isRidingBattleship() {
@@ -2907,7 +2917,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
private synchronized void gainExpInternal(long gain, int equip, int party, boolean show, boolean inChat, boolean white) { // need of method synchonization here detected thanks to MedicOP
long total = Math.max(gain, -exp.get());
if (level < getMaxLevel()) {
if (level < getMaxLevel() && (allowExpGain || this.getEventInstance() != null)) {
long leftover = 0;
long nextExp = exp.get() + total;
@@ -3464,9 +3474,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
getMap().broadcastMessage(MaplePacketCreator.removeSummon(summon, true), summon.getPosition());
getMap().removeMapObject(summon);
removeVisibleMapObject(summon);
summons.remove(summonId);
if (summon.getSkill() == DarkKnight.BEHOLDER) {
if (summon.isPuppet()) {
map.removePlayerPuppet(this);
} else if (summon.getSkill() == DarkKnight.BEHOLDER) {
if (beholderHealingSchedule != null) {
beholderHealingSchedule.cancel(false);
beholderHealingSchedule = null;
@@ -4602,19 +4614,19 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
public int getItemQuantity(int itemid, boolean checkEquipped) {
int possesed = inventory[ItemConstants.getInventoryType(itemid).ordinal()].countById(itemid);
int count = inventory[ItemConstants.getInventoryType(itemid).ordinal()].countById(itemid);
if (checkEquipped) {
possesed += inventory[MapleInventoryType.EQUIPPED.ordinal()].countById(itemid);
count += inventory[MapleInventoryType.EQUIPPED.ordinal()].countById(itemid);
}
return possesed;
return count;
}
public int getCleanItemQuantity(int itemid, boolean checkEquipped) {
int possesed = inventory[ItemConstants.getInventoryType(itemid).ordinal()].countNotOwnedById(itemid);
int count = inventory[ItemConstants.getInventoryType(itemid).ordinal()].countNotOwnedById(itemid);
if (checkEquipped) {
possesed += inventory[MapleInventoryType.EQUIPPED.ordinal()].countNotOwnedById(itemid);
count += inventory[MapleInventoryType.EQUIPPED.ordinal()].countNotOwnedById(itemid);
}
return possesed;
return count;
}
public MapleJob getJob() {
@@ -6600,13 +6612,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
rs = ps.executeQuery();
while (rs.next()) {
String name = rs.getString("name");
if (rs.getString("name").equals("rescueGaga")) {
if (rs.getString("name").contentEquals("rescueGaga")) {
ret.events.put(name, new RescueGaga(rs.getInt("info")));
}
//ret.events = new MapleEvents(new RescueGaga(rs.getInt("rescuegaga")), new ArtifactHunt(rs.getInt("artifacthunt")));
}
if (!ret.events.containsKey("rescueGaga")) {
ret.events.put("rescueGaga", new RescueGaga(0));
}
rs.close();
ps.close();
@@ -6988,13 +6996,12 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
public void respawn(EventInstanceManager eim, int returnMap) {
cancelAllBuffs(false);
updateHp(50);
setStance(0);
if (eim != null) eim.unregisterPlayer(this); // some event scripts uses this...
changeMap(returnMap);
cancelAllBuffs(false); // thanks Oblivium91 for finding out players still could revive in area and take damage before returning to town
updateHp(50);
setStance(0);
}
private void prepareDragonBlood(final MapleStatEffect bloodEffect) {
@@ -7514,6 +7521,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
this.getInventory(itEntry.getRight()).addItem(itEntry.getLeft());
}
this.events.put("rescueGaga", new RescueGaga(0));
Connection con = null;
PreparedStatement ps = null;
@@ -9733,7 +9742,6 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
events = null;
mpc = null;
mgc = null;
events = null;
party = null;
family = null;
+16 -10
View File
@@ -517,11 +517,15 @@ public class MapleClient {
}
public int login(String login, String pwd, String nibbleHwid) {
loginattempt++;
if (loginattempt > 4) {
MapleSessionCoordinator.getInstance().closeSession(session, false);
}
int loginok = 5;
loginattempt++;
if (loginattempt > 4) {
loggedIn = false;
MapleSessionCoordinator.getInstance().closeSession(session, false);
return loginok;
}
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
@@ -748,7 +752,12 @@ public class MapleClient {
return accId;
}
public void updateLoginState(int newstate) {
public void updateLoginState(int newstate) {
// rules out possibility of multiple account entries
if (newstate == LOGIN_LOGGEDIN) {
MapleSessionCoordinator.getInstance().updateOnlineSession(this.getSession());
}
try {
Connection con = DatabaseConnection.getConnection();
try (PreparedStatement ps = con.prepareStatement("UPDATE accounts SET loggedin = ?, lastlogin = ? WHERE id = ?")) {
@@ -763,6 +772,7 @@ public class MapleClient {
} catch (SQLException e) {
e.printStackTrace();
}
if (newstate == LOGIN_NOTLOGGEDIN) {
loggedIn = false;
serverTransition = false;
@@ -797,9 +807,6 @@ public class MapleClient {
MapleSessionCoordinator.getInstance().closeSession(session, null);
updateLoginState(LOGIN_NOTLOGGEDIN);
}
} else if (state == LOGIN_LOGGEDIN && player == null) {
state = LOGIN_LOGGEDIN;
updateLoginState(LOGIN_LOGGEDIN);
}
rs.close();
ps.close();
@@ -1006,8 +1013,7 @@ public class MapleClient {
Server.getInstance().unregisterLoginState(this);
this.session.setAttribute(MapleClient.CLIENT_KEY, null);
this.accountName = null;
this.accountName = null;
this.macs = null;
this.hwid = null;
this.birthday = null;
+1
View File
@@ -193,6 +193,7 @@ public class CommandsExecutor {
addCommand("int", StatIntCommand.class);
addCommand("luk", StatLukCommand.class);
addCommand("enableauth", EnableAuthCommand.class);
addCommand("toggleexp", ToggleExpCommand.class);
commandsNameDesc.add(levelCommandsCursor);
}
@@ -0,0 +1,44 @@
/*
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: Ronan
*/
package client.command.commands.gm0;
import client.command.Command;
import client.MapleClient;
public class ToggleExpCommand extends Command {
{
setDescription("");
}
@Override
public void execute(MapleClient c, String[] params) {
if (c.tryacquireClient()) {
try {
c.getPlayer().toggleExpGain(); // Vcoc's idea
} finally {
c.releaseClient();
}
}
}
}
@@ -46,12 +46,19 @@ public class GotoCommand extends Command {
player.yellowMessage("Syntax: @goto <map name>");
return;
}
if (player.getEventInstance() != null || MapleMiniDungeonInfo.isDungeonMap(player.getMapId()) || FieldLimit.CANNOTMIGRATE.check(player.getMap().getFieldLimit()) || !player.isAlive()) {
player.dropMessage(1, "This command can not be used in this map.");
if (!player.isAlive()) {
player.dropMessage(1, "This command cannot be used when you're dead.");
return;
}
if (!player.isGM()) {
if (player.getEventInstance() != null || MapleMiniDungeonInfo.isDungeonMap(player.getMapId()) || FieldLimit.CANNOTMIGRATE.check(player.getMap().getFieldLimit())) {
player.dropMessage(1, "This command can not be used in this map.");
return;
}
}
HashMap<String, Integer> gotomaps;
if (player.isGM()) {
gotomaps = new HashMap<>(GameConstants.GOTO_AREAS); // distinct map registry for GM/users suggested thanks to Vcoc
@@ -50,11 +50,18 @@ public class WarpCommand extends Command {
return;
}
if (player.getEventInstance() != null || MapleMiniDungeonInfo.isDungeonMap(player.getMapId()) || FieldLimit.CANNOTMIGRATE.check(player.getMap().getFieldLimit()) || !player.isAlive()) {
player.dropMessage(1, "This command cannot be used in this map.");
if (!player.isAlive()) {
player.dropMessage(1, "This command cannot be used when you're dead.");
return;
}
if (!player.isGM()) {
if (player.getEventInstance() != null || MapleMiniDungeonInfo.isDungeonMap(player.getMapId()) || FieldLimit.CANNOTMIGRATE.check(player.getMap().getFieldLimit())) {
player.dropMessage(1, "This command cannot be used in this map.");
return;
}
}
// expedition issue with this command detected thanks to Masterrulax
player.saveLocationOnWarp();
player.changeMap(target, target.getRandomPlayerSpawnpoint());
@@ -27,6 +27,7 @@ import client.MapleStat;
import client.command.Command;
import client.MapleClient;
import client.MapleCharacter;
import constants.ItemConstants;
import server.MapleItemInformationProvider;
public class FaceCommand extends Command {
@@ -45,7 +46,7 @@ public class FaceCommand extends Command {
try {
if (params.length == 1) {
int itemId = Integer.parseInt(params[0]);
if (!(itemId >= 20000 && itemId < 22000) || MapleItemInformationProvider.getInstance().getName(itemId) == null) {
if (!ItemConstants.isFace(itemId) || MapleItemInformationProvider.getInstance().getName(itemId) == null) {
player.yellowMessage("Face id '" + params[0] + "' does not exist.");
return;
}
@@ -55,7 +56,7 @@ public class FaceCommand extends Command {
player.equipChanged();
} else {
int itemId = Integer.parseInt(params[1]);
if (!(itemId >= 20000 && itemId < 22000) || MapleItemInformationProvider.getInstance().getName(itemId) == null) {
if (!ItemConstants.isFace(itemId) || MapleItemInformationProvider.getInstance().getName(itemId) == null) {
player.yellowMessage("Face id '" + params[1] + "' does not exist.");
}
@@ -27,6 +27,7 @@ import client.MapleStat;
import client.command.Command;
import client.MapleClient;
import client.MapleCharacter;
import constants.ItemConstants;
import server.MapleItemInformationProvider;
public class HairCommand extends Command {
@@ -45,7 +46,7 @@ public class HairCommand extends Command {
try {
if (params.length == 1) {
int itemId = Integer.parseInt(params[0]);
if (!(itemId >= 30000 && itemId < 35000) || MapleItemInformationProvider.getInstance().getName(itemId) == null) {
if (!ItemConstants.isHair(itemId) || MapleItemInformationProvider.getInstance().getName(itemId) == null) {
player.yellowMessage("Hair id '" + params[0] + "' does not exist.");
return;
}
@@ -55,7 +56,7 @@ public class HairCommand extends Command {
player.equipChanged();
} else {
int itemId = Integer.parseInt(params[1]);
if (!(itemId >= 30000 && itemId < 35000) || MapleItemInformationProvider.getInstance().getName(itemId) == null) {
if (!ItemConstants.isHair(itemId) || MapleItemInformationProvider.getInstance().getName(itemId) == null) {
player.yellowMessage("Hair id '" + params[1] + "' does not exist.");
return;
}
+48 -5
View File
@@ -52,6 +52,21 @@ public class MaplePet extends Item {
private Point pos;
private int stance;
private boolean summoned;
private int petFlag = 0;
public enum PetFlag {
OWNER_SPEED(0x01);
private int i;
private PetFlag(int i) {
this.i = i;
}
public int getValue() {
return i;
}
}
private MaplePet(int id, short position, int uniqueid) {
super(id, position, (short) 1);
@@ -63,7 +78,7 @@ public class MaplePet extends Item {
try {
MaplePet ret = new MaplePet(itemid, position, petid);
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT name, level, closeness, fullness, summoned FROM pets WHERE petid = ?"); // Get pet details..
PreparedStatement ps = con.prepareStatement("SELECT name, level, closeness, fullness, summoned, flag FROM pets WHERE petid = ?"); // Get pet details..
ps.setInt(1, petid);
ResultSet rs = ps.executeQuery();
rs.next();
@@ -72,6 +87,7 @@ public class MaplePet extends Item {
ret.setLevel((byte) Math.min(rs.getByte("level"), 30));
ret.setFullness(Math.min(rs.getInt("fullness"), 100));
ret.setSummoned(rs.getInt("summoned") == 1);
ret.setPetFlag(rs.getInt("flag"));
rs.close();
ps.close();
con.close();
@@ -108,13 +124,14 @@ public class MaplePet extends Item {
public void saveToDb() {
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("UPDATE pets SET name = ?, level = ?, closeness = ?, fullness = ?, summoned = ? WHERE petid = ?");
PreparedStatement ps = con.prepareStatement("UPDATE pets SET name = ?, level = ?, closeness = ?, fullness = ?, summoned = ?, flag = ? WHERE petid = ?");
ps.setString(1, getName());
ps.setInt(2, getLevel());
ps.setInt(3, getCloseness());
ps.setInt(4, getFullness());
ps.setInt(5, isSummoned() ? 1 : 0);
ps.setInt(6, getUniqueId());
ps.setInt(6, getPetFlag());
ps.setInt(7, getUniqueId());
ps.executeUpdate();
ps.close();
con.close();
@@ -126,7 +143,7 @@ public class MaplePet extends Item {
public static int createPet(int itemid) {
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("INSERT INTO pets (petid, name, level, closeness, fullness, summoned) VALUES (?, ?, 1, 0, 100, 0)");
PreparedStatement ps = con.prepareStatement("INSERT INTO pets (petid, name, level, closeness, fullness, summoned, flag) VALUES (?, ?, 1, 0, 100, 0, 0)");
int ret = MapleCashidGenerator.generateCashId();
ps.setInt(1, ret);
ps.setString(2, MapleItemInformationProvider.getInstance().getName(itemid));
@@ -143,7 +160,7 @@ public class MaplePet extends Item {
public static int createPet(int itemid, byte level, int closeness, int fullness) {
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("INSERT INTO pets (petid, name, level, closeness, fullness, summoned) VALUES (?, ?, ?, ?, ?, 0)");
PreparedStatement ps = con.prepareStatement("INSERT INTO pets (petid, name, level, closeness, fullness, summoned, flag) VALUES (?, ?, ?, ?, ?, 0, 0)");
int ret = MapleCashidGenerator.generateCashId();
ps.setInt(1, ret);
ps.setString(2, MapleItemInformationProvider.getInstance().getName(itemid));
@@ -274,6 +291,32 @@ public class MaplePet extends Item {
public void setSummoned(boolean yes) {
this.summoned = yes;
}
public int getPetFlag() {
return this.petFlag;
}
private void setPetFlag(int flag) {
this.petFlag = flag;
}
public void addPetFlag(MapleCharacter owner, PetFlag flag) {
this.petFlag |= flag.getValue();
saveToDb();
Item petz = owner.getInventory(MapleInventoryType.CASH).getItem(getPosition());
if (petz != null)
owner.forceUpdateItem(petz);
}
public void removePetFlag(MapleCharacter owner, PetFlag flag) {
this.petFlag &= 0xFFFFFFFF ^ flag.getValue();
saveToDb();
Item petz = owner.getInventory(MapleInventoryType.CASH).getItem(getPosition());
if (petz != null)
owner.forceUpdateItem(petz);
}
public Pair<Integer, Boolean> canConsume(int itemId) {
return MapleItemInformationProvider.getInstance().canPetConsume(this.getItemId(), itemId);
@@ -53,6 +53,7 @@ public class MapleKarmaManipulator {
int flag = item.getFlag();
flag |= karmaFlag;
flag &= (0xFFFFFFFF ^ ItemConstants.UNTRADEABLE);
item.setFlag((byte) flag);
}
}
+1 -1
View File
@@ -218,7 +218,7 @@ public class MakerProcessor {
Map<Integer, Integer> reagentType = new LinkedHashMap<>();
List<Integer> toRemove = new LinkedList<>();
boolean isWeapon = ItemConstants.isWeapon(toCreate) || ServerConstants.USE_MAKER_PERMISSIVE_ATKUP;
boolean isWeapon = ItemConstants.isWeapon(toCreate) || ServerConstants.USE_MAKER_PERMISSIVE_ATKUP; // thanks Vcoc for finding a case where a weapon wouldn't be counted as such due to a bounding on isWeapon
for(Map.Entry<Integer, Short> r : reagentids.entrySet()) {
int curRid = r.getKey();
+9 -1
View File
@@ -217,7 +217,7 @@ public final class ItemConstants {
}
public static boolean isWeapon(int itemId) {
return itemId >= 1302000 && itemId < 1492024;
return itemId >= 1302000 && itemId < 1493000;
}
public static boolean isEquipment(int itemId) {
@@ -235,4 +235,12 @@ public final class ItemConstants {
public static boolean isWeddingToken(int itemId) {
return itemId >= 4031357 && itemId <= 4031364;
}
public static boolean isFace(int itemId) {
return itemId >= 20000 && itemId < 22000;
}
public static boolean isHair(int itemId) {
return itemId >= 30000 && itemId < 35000;
}
}
+1 -1
View File
@@ -149,7 +149,7 @@ public class ServerConstants {
public static boolean USE_DISPLAY_NUMBERS_WITH_COMMA = true; //Enforce comma on displayed strings (use this when USE_UNITPRICE_WITH_COMMA is active and you still want to display comma-separated values).
public static boolean USE_UNITPRICE_WITH_COMMA = true; //Set this accordingly with the layout of the unitPrices on Item.wz XML's, whether it's using commas or dots to represent fractions.
public static final byte MIN_UNDERLEVEL_TO_EXP_GAIN = 20; //Characters are unable to get EXP from a mob if their level are under this threshold, only if "USE_ENFORCE_MOB_LEVEL_RANGE" is enabled. For bosses, this attribute is doubled.
public static final byte MIN_UNDERLEVEL_TO_EXP_LEECH = 40; //Characters are unable to leech EXP from party member kills whose level difference are past this limit.
public static final byte MIN_RANGELEVEL_TO_EXP_LEECH = 40; //Characters are unable to leech EXP from party member kills whose level difference are past this limit.
public static final byte MAX_MONITORED_BUFFSTATS = 5; //Limits accounting for "dormant" buff effects, that should take place when stronger stat buffs expires.
public static final int MAX_AP = 32767; //Max AP allotted on the auto-assigner.
public static final int MAX_EVENT_LEVELS = 8; //Event has different levels of rewarding system.
+9 -5
View File
@@ -88,8 +88,8 @@ public class MapleServerHandler extends IoHandlerAdapter {
@Override
public void exceptionCaught(IoSession session, Throwable cause) {
if (cause instanceof IOException) { // mina closes session automatically and does not call sessionClosed on IOException
session.closeNow(); // calls sessionClosed
if (cause instanceof IOException) {
closeMapleSession(session);
} else {
MapleClient client = (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY);
@@ -137,8 +137,7 @@ public class MapleServerHandler extends IoHandlerAdapter {
session.setAttribute(MapleClient.CLIENT_KEY, client);
}
@Override
public void sessionClosed(IoSession session) throws Exception {
private void closeMapleSession(IoSession session) {
if (isLoginServerHandler()) {
MapleSessionCoordinator.getInstance().closeLoginSession(session);
} else {
@@ -157,10 +156,15 @@ public class MapleServerHandler extends IoHandlerAdapter {
FilePrinter.printError(FilePrinter.ACCOUNT_STUCK, t);
} finally {
session.close();
session.removeAttribute(MapleClient.CLIENT_KEY);
session.removeAttribute(MapleClient.CLIENT_KEY);
//client.empty();
}
}
}
@Override
public void sessionClosed(IoSession session) throws Exception {
closeMapleSession(session);
super.sessionClosed(session);
}
+9 -3
View File
@@ -86,6 +86,7 @@ import constants.ItemConstants;
import constants.GameConstants;
import constants.ServerConstants;
import java.util.TimeZone;
import net.server.coordinator.MapleSessionCoordinator;
import server.CashShop.CashItemFactory;
import server.MapleSkillbookInformationProvider;
import server.ThreadManager;
@@ -1693,7 +1694,7 @@ public class Server {
if(c.isLoggedIn()) {
c.disconnect(false, false);
} else {
c.getSession().close(true);
MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true);
}
}
}
@@ -1766,8 +1767,13 @@ public class Server {
System.out.println("Worlds + Channels are offline.");
acceptor.unbind();
acceptor = null;
if (!restart) {
System.exit(0);
if (!restart) { // shutdown hook deadlocks if System.exit() method is used within its body chores, thanks MIKE for pointing that out
new Thread(new Runnable() {
@Override
public void run() {
System.exit(0);
}
}).start();
} else {
System.out.println("\r\nRestarting the server....\r\n");
try {
@@ -130,7 +130,7 @@ public final class AllianceOperationHandler extends AbstractMaplePacketHandler {
chr.saveGuildStatus();
Server.getInstance().allianceMessage(alliance.getId(), MaplePacketCreator.addGuildToAlliance(alliance, guildid, c), -1, -1);
Server.getInstance().allianceMessage(alliance.getId(), MaplePacketCreator.updateAllianceInfo(alliance, 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.");
@@ -29,6 +29,8 @@ import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
public final class CharInfoRequestHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
slea.skip(4);
int cid = slea.readInt();
@@ -24,7 +24,6 @@ package net.server.channel.handlers;
import client.MapleBuffStat;
import client.MapleCharacter;
import client.MapleClient;
import client.SkillFactory;
import net.AbstractMaplePacketHandler;
import server.maps.MapleSummon;
import server.maps.MapleMapObject;
@@ -25,6 +25,7 @@ import client.MapleCharacter;
import client.MapleClient;
import client.autoban.AutobanFactory;
import client.inventory.Item;
import client.inventory.MapleInventory;
import client.inventory.MapleInventoryType;
import client.inventory.manipulator.MapleInventoryManipulator;
import client.inventory.manipulator.MapleKarmaManipulator;
@@ -465,22 +466,41 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
} else if (mode == Action.SET_ITEMS.getCode()) {
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
MapleInventoryType ivType = MapleInventoryType.getByType(slea.readByte());
Item item = chr.getInventory(ivType).getItem(slea.readShort());
short pos = slea.readShort();
Item item = chr.getInventory(ivType).getItem(pos);
short quantity = slea.readShort();
byte targetSlot = slea.readByte();
if (targetSlot < 1 || targetSlot > 9) {
System.out.println("[h4x] " + chr.getName() + " Trying to dupe on trade slot.");
c.announce(MaplePacketCreator.enableActions());
return;
}
if (item == null) {
c.announce(MaplePacketCreator.serverNotice(1, "Invalid item description."));
c.announce(MaplePacketCreator.enableActions());
return;
}
if(ServerConstants.USE_ENFORCE_UNMERCHABLE_CASH && ii.isCash(item.getItemId())) {
c.announce(MaplePacketCreator.serverNotice(1, "Cash items are not allowed to be traded."));
return;
}
if (ServerConstants.USE_ENFORCE_UNMERCHABLE_PET && ItemConstants.isPet(item.getItemId())) {
c.announce(MaplePacketCreator.serverNotice(1, "Pets are not allowed to be traded."));
return;
}
if (quantity < 1 || quantity > item.getQuantity()) {
c.announce(MaplePacketCreator.serverNotice(1, "You don't have enough quantity of the item."));
c.announce(MaplePacketCreator.enableActions());
return;
}
if (chr.getTrade() != null) {
MapleTrade trade = chr.getTrade();
if (trade != null) {
if ((quantity <= item.getQuantity() && quantity >= 0) || ItemConstants.isRechargeable(item.getItemId())) {
if (ii.isDropRestricted(item.getItemId())) { // ensure that undroppable items do not make it to the trade window
if (!MapleKarmaManipulator.hasKarmaFlag(item)) {
@@ -489,16 +509,38 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
return;
}
}
Item tradeItem = item.copy();
if (ItemConstants.isRechargeable(item.getItemId())) {
tradeItem.setQuantity(item.getQuantity());
MapleInventoryManipulator.removeFromSlot(c, ivType, item.getPosition(), item.getQuantity(), true);
} else {
MapleInventory inv = chr.getInventory(ivType);
inv.lockInventory();
try {
Item checkItem = chr.getInventory(ivType).getItem(pos);
if (checkItem != item || checkItem.getPosition() != item.getPosition()) {
c.announce(MaplePacketCreator.serverNotice(1, "Invalid item description."));
c.announce(MaplePacketCreator.enableActions());
return;
}
Item tradeItem = item.copy();
if (ItemConstants.isRechargeable(item.getItemId())) {
quantity = item.getQuantity();
}
tradeItem.setQuantity(quantity);
MapleInventoryManipulator.removeFromSlot(c, ivType, item.getPosition(), quantity, true);
tradeItem.setPosition(targetSlot);
if (trade.addItem(tradeItem)) {
MapleInventoryManipulator.removeFromSlot(c, ivType, item.getPosition(), quantity, true);
trade.getChr().announce(MaplePacketCreator.getTradeItemAdd((byte) 0, tradeItem));
if (trade.getPartner() != null) {
trade.getPartner().getChr().announce(MaplePacketCreator.getTradeItemAdd((byte) 1, tradeItem));
}
}
} catch (Exception e) {
FilePrinter.printError(FilePrinter.TRADE_EXCEPTION, e, "Player '" + chr + "' tried to add " + ii.getName(item.getItemId()) + " qty. " + item.getQuantity() + " in trade (slot " + targetSlot + ") then exception occurred.");
} finally {
inv.unlockInventory();
}
tradeItem.setPosition(targetSlot);
chr.getTrade().addItem(tradeItem);
}
}
} else if (mode == Action.CONFIRM.getCode()) {
@@ -289,7 +289,7 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
}
}
if (newAlliance != null) {
c.announce(MaplePacketCreator.updateAllianceInfo(newAlliance, c));
c.announce(MaplePacketCreator.updateAllianceInfo(newAlliance, c.getWorld()));
c.announce(MaplePacketCreator.allianceNotice(newAlliance.getId(), newAlliance.getNotice()));
if (newcomer) {
@@ -27,8 +27,7 @@ import constants.ItemConstants;
import net.AbstractMaplePacketHandler;
import scripting.item.ItemScriptManager;
import server.MapleItemInformationProvider;
import server.MapleItemInformationProvider.scriptedItem;
import tools.MaplePacketCreator;
import server.MapleItemInformationProvider.ScriptedItem;
import tools.data.input.SeekableLittleEndianAccessor;
/**
@@ -38,19 +37,20 @@ import tools.data.input.SeekableLittleEndianAccessor;
public final class ScriptedItemHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
slea.readInt(); // trash stamp, thanks RMZero213
short itemSlot = slea.readShort(); // item slot, thanks RMZero213
int itemId = slea.readInt();
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
slea.readInt(); // trash stamp (thanks rmzero)
short itemSlot = slea.readShort(); // item slot (thanks rmzero)
int itemId = slea.readInt(); // itemId
scriptedItem info = ii.getScriptedItemInfo(itemId);
ScriptedItem info = ii.getScriptedItemInfo(itemId);
if (info == null) return;
ItemScriptManager ism = ItemScriptManager.getInstance();
Item item = c.getPlayer().getInventory(ItemConstants.getInventoryType(itemId)).getItem(itemSlot);
if (item == null || item.getItemId() != itemId || item.getQuantity() < 1 || !ism.scriptExists(info.getScript())) {
if (item == null || item.getItemId() != itemId || item.getQuantity() < 1) {
return;
}
ism.runItemScript(c, info.getScript());
c.announce(MaplePacketCreator.enableActions());
//NPCScriptManager.getInstance().start(c, info.getNpc(), null, null);
ItemScriptManager ism = ItemScriptManager.getInstance();
ism.runItemScript(c, info);
}
}
@@ -33,6 +33,8 @@ import tools.data.input.SeekableLittleEndianAccessor;
* @author kevintjuh93
*/
public final class TrockAddMapHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
MapleCharacter chr = c.getPlayer();
byte type = slea.readByte();
@@ -53,6 +53,7 @@ import server.MapleShop;
import server.MapleShopFactory;
import server.TimerManager;
import server.maps.AbstractMapleMapObject;
import server.maps.FieldLimit;
import server.maps.MaplePlayerShopItem;
import server.maps.MapleKite;
import server.maps.MapleMap;
@@ -106,31 +107,32 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
if (itemType == 504) { // vip teleport rock
String error1 = "Either the player could not be found or you were trying to teleport to an illegal location.";
boolean vip = slea.readByte() == 1;
boolean vip = slea.readByte() == 1 && itemId / 1000 >= 5041;
remove(c, position, itemId);
boolean success = false;
if (!vip) {
int mapId = slea.readInt();
if (c.getChannelServer().getMapFactory().getMap(mapId).getForcedReturnId() == 999999999) {
player.changeMap(c.getChannelServer().getMapFactory().getMap(mapId));
if (itemId / 1000 >= 5041 || mapId / 100000000 == player.getMapId() / 100000000) { //check vip or same continent
MapleMap targetMap = c.getChannelServer().getMapFactory().getMap(mapId);
if (!FieldLimit.CANNOTVIPROCK.check(targetMap.getFieldLimit()) && (targetMap.getForcedReturnId() == 999999999 || mapId < 100000000)) {
player.forceChangeMap(targetMap, targetMap.getRandomPlayerSpawnpoint());
success = true;
} else {
player.dropMessage(1, error1);
}
} else {
MapleInventoryManipulator.addById(c, itemId, (short) 1);
player.dropMessage(1, error1);
c.announce(MaplePacketCreator.enableActions());
player.dropMessage(1, "You cannot teleport between continents with this teleport rock.");
}
} else {
String name = slea.readMapleAsciiString();
MapleCharacter victim = c.getChannelServer().getPlayerStorage().getCharacterByName(name);
boolean success = false;
if (victim != null) {
MapleMap target = victim.getMap();
if (c.getChannelServer().getMapFactory().getMap(victim.getMapId()).getForcedReturnId() == 999999999 || victim.getMapId() < 100000000) {
MapleMap targetMap = victim.getMap();
if (!FieldLimit.CANNOTVIPROCK.check(targetMap.getFieldLimit()) && (targetMap.getForcedReturnId() == 999999999 || targetMap.getId() < 100000000)) {
if (victim.gmLevel() <= player.gmLevel()) {
if (itemId == 5041000 || victim.getMapId() / player.getMapId() == 1) { //viprock & same continent
player.changeMap(target, target.findClosestPlayerSpawnpoint(victim.getPosition()));
success = true;
} else {
player.dropMessage(1, "You cannot teleport between continents with this teleport rock.");
}
player.forceChangeMap(targetMap, targetMap.findClosestPlayerSpawnpoint(victim.getPosition()));
success = true;
} else {
player.dropMessage(1, error1);
}
@@ -140,10 +142,11 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
} else {
player.dropMessage(1, "Player could not be found in this channel.");
}
if (!success) {
MapleInventoryManipulator.addById(c, itemId, (short) 1);
c.announce(MaplePacketCreator.enableActions());
}
}
if (!success) {
MapleInventoryManipulator.addById(c, itemId, (short) 1);
c.announce(MaplePacketCreator.enableActions());
}
} else if (itemType == 505) { // AP/SP reset
if(!player.isAlive()) {
@@ -25,8 +25,10 @@ import java.util.Comparator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import constants.ServerConstants;
import client.MapleCharacter;
@@ -56,6 +58,8 @@ public class MapleMonsterAggroCoordinator {
private Map<MapleMonster, Map<Integer, PlayerAggroEntry>> mobAggroEntries = new HashMap<>();
private Map<MapleMonster, List<PlayerAggroEntry>> mobSortedAggros = new HashMap<>();
private Set<Integer> mapPuppetEntries = new HashSet<>();
private class PlayerAggroEntry {
protected int cid;
protected int averageDamage = 0;
@@ -291,6 +295,12 @@ public class MapleMonsterAggroCoordinator {
}
public boolean isLeadingCharacterAggro(MapleMonster mob, MapleCharacter player) {
if (mob.isLeadingPuppetInVicinity()) {
return false;
} else if (mob.isCharacterPuppetInVicinity(player)) {
return true;
}
// by assuming the quasi-sorted nature of "mobAggroList", this method
// returns whether the player given as parameter can be elected as next aggro leader
@@ -342,6 +352,24 @@ public class MapleMonsterAggroCoordinator {
}
}
public void addPuppetAggro(MapleCharacter player) {
synchronized (mapPuppetEntries) {
mapPuppetEntries.add(player.getId());
}
}
public void removePuppetAggro(Integer cid) {
synchronized (mapPuppetEntries) {
mapPuppetEntries.remove(cid);
}
}
public List<Integer> getPuppetAggroList() {
synchronized (mapPuppetEntries) {
return new ArrayList<>(mapPuppetEntries);
}
}
public void dispose() {
stopAggroCoordinator();
@@ -68,6 +68,7 @@ public class MapleSessionCoordinator {
}
private final LoginStorage loginStorage = new LoginStorage();
private final Map<Integer, MapleClient> onlineClients = new HashMap<>();
private final Set<String> onlineRemoteHwids = new HashSet<>();
private final Map<String, Set<IoSession>> loginRemoteHosts = new HashMap<>();
private final Set<String> pooledRemoteHosts = new HashSet<>();
@@ -221,6 +222,24 @@ public class MapleSessionCoordinator {
return ((InetSocketAddress) session.getRemoteAddress()).getAddress().getHostAddress();
}
private static MapleClient getSessionClient(IoSession session) {
return (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY);
}
public void updateOnlineSession(IoSession session) {
MapleClient client = getSessionClient(session);
if (client != null) {
int accountId = client.getAccID();
MapleClient ingameClient = onlineClients.get(accountId);
if (ingameClient != null) { // thanks MedicOP for finding out a loss of loggedin account uniqueness when using the CMS "Unstuck" feature
ingameClient.forceDisconnect();
}
onlineClients.put(accountId, client);
}
}
public boolean canStartLoginSession(IoSession session) {
if (!ServerConstants.DETERRED_MULTICLIENT) return true;
@@ -296,6 +315,11 @@ public class MapleSessionCoordinator {
String nibbleHwid = (String) session.removeAttribute(MapleClient.CLIENT_NIBBLEHWID);
if (nibbleHwid != null) {
onlineRemoteHwids.remove(nibbleHwid);
MapleClient client = getSessionClient(session);
if (client != null) {
onlineClients.remove(client.getAccID());
}
}
}
@@ -448,6 +472,11 @@ public class MapleSessionCoordinator {
hwid = (String) session.removeAttribute(MapleClient.CLIENT_NIBBLEHWID); // making sure to clean up calls to this function on login phase
onlineRemoteHwids.remove(hwid);
MapleClient client = getSessionClient(session);
if (client != null) {
onlineClients.remove(client.getAccID());
}
if (immediately != null) {
session.close(immediately);
}
+4 -2
View File
@@ -130,7 +130,9 @@ public class MapleAlliance {
Server.getInstance().addAlliance(id, alliance);
Server.getInstance().allianceMessage(id, MaplePacketCreator.updateAllianceInfo(alliance, guildMasters.get(0).getClient()), -1, -1);
int worldid = guildMasters.get(0).getWorld();
Server.getInstance().allianceMessage(id, MaplePacketCreator.updateAllianceInfo(alliance, worldid), -1, -1);
Server.getInstance().allianceMessage(id, MaplePacketCreator.getGuildAlliances(alliance, worldid), -1, -1); // thanks Vcoc for noticing guilds from other alliances being visually stacked here due to this not being updated
} catch (Exception e) {
e.printStackTrace();
return null;
@@ -347,7 +349,7 @@ public class MapleAlliance {
public void updateAlliancePackets(MapleCharacter chr) {
if (allianceId > 0) {
this.broadcastMessage(MaplePacketCreator.updateAllianceInfo(this, chr.getClient()));
this.broadcastMessage(MaplePacketCreator.updateAllianceInfo(this, chr.getWorld()));
this.broadcastMessage(MaplePacketCreator.allianceNotice(this.getId(), this.getNotice()));
}
}
@@ -78,13 +78,13 @@ public final class CharSelectedHandler extends AbstractMaplePacketHandler {
}
if (c.hasBannedMac() || c.hasBannedHWID()) {
session.close(true);
MapleSessionCoordinator.getInstance().closeSession(session, true);
return;
}
Server server = Server.getInstance();
if(!server.haveCharacterEntry(c.getAccID(), charId)) {
session.close(true);
MapleSessionCoordinator.getInstance().closeSession(session, true);
return;
}
@@ -59,13 +59,13 @@ public class CharSelectedWithPicHandler extends AbstractMaplePacketHandler {
}
if (c.hasBannedMac() || c.hasBannedHWID()) {
c.getSession().close(true);
MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true);
return;
}
Server server = Server.getInstance();
if(!server.haveCharacterEntry(c.getAccID(), charId)) {
c.getSession().close(true);
MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true);
return;
}
@@ -83,6 +83,7 @@ public final class LoginPasswordHandler implements MaplePacketHandler {
c.setAccID(rs.getInt(1));
rs.close();
} catch (SQLException | NoSuchAlgorithmException | UnsupportedEncodingException e) {
c.setAccID(-1);
e.printStackTrace();
} finally {
disposeSql(con, ps);
@@ -59,13 +59,13 @@ public final class RegisterPicHandler extends AbstractMaplePacketHandler {
}
if (c.hasBannedMac() || c.hasBannedHWID()) {
c.getSession().close(true);
MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true);
return;
}
Server server = Server.getInstance();
if(!server.haveCharacterEntry(c.getAccID(), charId)) {
c.getSession().close(true);
MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true);
return;
}
@@ -96,7 +96,7 @@ public final class RegisterPicHandler extends AbstractMaplePacketHandler {
e.printStackTrace();
}
} else {
c.getSession().close(true);
MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true);
}
}
}
@@ -52,7 +52,7 @@ public final class ViewAllCharRegisterPicHandler extends AbstractMaplePacketHand
c.updateHWID(hwid);
if (c.hasBannedMac() || c.hasBannedHWID()) {
c.getSession().close(true);
MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true);
return;
}
@@ -65,7 +65,7 @@ public final class ViewAllCharRegisterPicHandler extends AbstractMaplePacketHand
Server server = Server.getInstance();
if(!server.haveCharacterEntry(c.getAccID(), charId)) {
c.getSession().close(true);
MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true);
return;
}
@@ -72,7 +72,7 @@ public final class ViewAllCharSelectedHandler extends AbstractMaplePacketHandler
c.updateHWID(hwid);
if (c.hasBannedMac() || c.hasBannedHWID()) {
c.getSession().close(true);
MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true);
return;
}
@@ -85,7 +85,7 @@ public final class ViewAllCharSelectedHandler extends AbstractMaplePacketHandler
Server server = Server.getInstance();
if(!server.haveCharacterEntry(c.getAccID(), charId)) {
c.getSession().close(true);
MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true);
return;
}
@@ -54,7 +54,7 @@ public class ViewAllCharSelectedWithPicHandler extends AbstractMaplePacketHandle
c.updateHWID(hwid);
if (c.hasBannedMac() || c.hasBannedHWID()) {
c.getSession().close(true);
MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true);
return;
}
@@ -67,7 +67,7 @@ public class ViewAllCharSelectedWithPicHandler extends AbstractMaplePacketHandle
Server server = Server.getInstance();
if(!server.haveCharacterEntry(c.getAccID(), charId)) {
c.getSession().close(true);
MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true);
return;
}
+5 -5
View File
@@ -228,7 +228,11 @@ public class AbstractPlayerInteraction {
}
public boolean canHold(int itemid, int quantity) {
return getPlayer().canHold(itemid, quantity);
return canHoldAll(Collections.singletonList(itemid), Collections.singletonList(quantity), true);
}
public boolean canHold(int itemid, int quantity, int removeItemid, int removeQuantity) {
return canHoldAllAfterRemoving(Collections.singletonList(itemid), Collections.singletonList(quantity), Collections.singletonList(removeItemid), Collections.singletonList(removeQuantity));
}
private static List<Integer> convertToIntegerArray(List<Double> list) {
@@ -263,10 +267,6 @@ public class AbstractPlayerInteraction {
return MapleInventory.checkSpots(c.getPlayer(), addedItems, false);
}
public boolean canHold(int itemid, int quantity, int removeItemid, int removeQuantity) {
return canHoldAllAfterRemoving(Collections.singletonList(itemid), Collections.singletonList(quantity), Collections.singletonList(removeItemid), Collections.singletonList(removeQuantity));
}
private static List<Pair<Item, MapleInventoryType>> prepareProofInventoryItems(List<Pair<Integer, Integer>> items) {
List<Pair<Item, MapleInventoryType>> addedItems = new LinkedList<>();
for(Pair<Integer, Integer> p : items) {
@@ -1,8 +1,6 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This 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
@@ -19,17 +17,19 @@
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 scripting.item;
package scripting.event;
import client.MapleClient;
import scripting.AbstractPlayerInteraction;
/**
*
* @author kevintjuh93
* @author Ronan
*/
public class ItemScriptMethods extends AbstractPlayerInteraction {
public ItemScriptMethods(MapleClient c) {
super(c);
public class EventInstanceInProgressException extends Exception {
public static String EIIP_KEY = "Event instance ";
public EventInstanceInProgressException(String eventName, String eventInstance) {
super(EIIP_KEY + "already in progress - " + eventName + ", EM: " + eventInstance);
}
}
+63 -15
View File
@@ -260,7 +260,7 @@ public class EventManager {
}
}
public EventInstanceManager newInstance(String name) {
public EventInstanceManager newInstance(String name) throws EventInstanceInProgressException {
EventInstanceManager ret = getReadyInstance();
if(ret == null) {
@@ -271,7 +271,7 @@ public class EventManager {
synchronized (instances) {
if (instances.containsKey(name)) {
return null;
throw new EventInstanceInProgressException(name, this.getName());
}
instances.put(name, ret);
@@ -365,6 +365,34 @@ public class EventManager {
return -1;
}
private String getInternalScriptExceptionMessage(Throwable a) {
if (!(a instanceof ScriptException)) {
return null;
}
while(true) {
Throwable t = a;
a = a.getCause();
if (a == null) {
return t.getMessage();
}
}
}
private EventInstanceManager createInstance(String name, Object... args) throws ScriptException, NoSuchMethodException {
return (EventInstanceManager) iv.invokeFunction(name, args);
}
private void registerEventInstance(String eventName, int lobbyId) {
Integer oldLobby = instanceLocks.get(eventName);
if (oldLobby != null) {
setLockLobby(oldLobby, false);
}
instanceLocks.put(eventName, lobbyId);
}
public boolean startInstance(MapleExpedition exped) {
return startInstance(-1, exped);
}
@@ -394,9 +422,14 @@ public class EventManager {
EventInstanceManager eim;
try {
eim = (EventInstanceManager) (iv.invokeFunction("setup", leader.getClient().getChannel()));
instanceLocks.put(eim.getName(), lobbyId);
} catch (NullPointerException npe) {
eim = createInstance("setup", leader.getClient().getChannel());
registerEventInstance(eim.getName(), lobbyId);
} catch (ScriptException | NullPointerException e) {
String message = getInternalScriptExceptionMessage(e);
if (message != null && !message.startsWith(EventInstanceInProgressException.EIIP_KEY)) {
throw e;
}
if(lobbyId > -1) {
setLockLobby(lobbyId, false);
}
@@ -460,9 +493,14 @@ public class EventManager {
EventInstanceManager eim;
try {
eim = (EventInstanceManager) (iv.invokeFunction("setup", difficulty, (lobbyId > -1) ? lobbyId : leader.getId()));
instanceLocks.put(eim.getName(), lobbyId);
} catch (NullPointerException npe) {
eim = createInstance("setup", difficulty, (lobbyId > -1) ? lobbyId : leader.getId());
registerEventInstance(eim.getName(), lobbyId);
} catch (ScriptException | NullPointerException e) {
String message = getInternalScriptExceptionMessage(e);
if (message != null && !message.startsWith(EventInstanceInProgressException.EIIP_KEY)) {
throw e;
}
if(lobbyId > -1) {
setLockLobby(lobbyId, false);
}
@@ -520,9 +558,14 @@ public class EventManager {
EventInstanceManager eim;
try {
eim = (EventInstanceManager) (iv.invokeFunction("setup", (Object) null));
instanceLocks.put(eim.getName(), lobbyId);
} catch (NullPointerException npe) {
eim = createInstance("setup", (Object) null);
registerEventInstance(eim.getName(), lobbyId);
} catch (ScriptException | NullPointerException e) {
String message = getInternalScriptExceptionMessage(e);
if (message != null && !message.startsWith(EventInstanceInProgressException.EIIP_KEY)) {
throw e;
}
if(lobbyId > -1) {
setLockLobby(lobbyId, false);
}
@@ -582,9 +625,14 @@ public class EventManager {
EventInstanceManager eim;
try {
eim = (EventInstanceManager) (iv.invokeFunction("setup", difficulty, (lobbyId > -1) ? lobbyId : party.getLeaderId()));
instanceLocks.put(eim.getName(), lobbyId);
} catch (NullPointerException npe) {
eim = createInstance("setup", difficulty, (lobbyId > -1) ? lobbyId : party.getLeaderId());
registerEventInstance(eim.getName(), lobbyId);
} catch (ScriptException | NullPointerException e) {
String message = getInternalScriptExceptionMessage(e);
if (message != null && !message.startsWith(EventInstanceInProgressException.EIIP_KEY)) {
throw e;
}
if(lobbyId > -1) {
setLockLobby(lobbyId, false);
}
@@ -652,7 +700,7 @@ public class EventManager {
}
return false;
}
instanceLocks.put(eim.getName(), lobbyId);
registerEventInstance(eim.getName(), lobbyId);
eim.setLeader(leader);
iv.invokeFunction("setup", eim);
+4 -70
View File
@@ -22,21 +22,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package scripting.item;
import client.MapleClient;
import constants.ServerConstants;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.HashMap;
import java.util.Map;
import javax.script.Compilable;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import tools.FilePrinter;
import tools.MaplePacketCreator;
import scripting.npc.NPCScriptManager;
import server.MapleItemInformationProvider.ScriptedItem;
public class ItemScriptManager {
@@ -46,60 +33,7 @@ public class ItemScriptManager {
return instance;
}
private Map<String, Invocable> scripts = new HashMap<>();
private ScriptEngineFactory sef;
private ItemScriptManager() {
ScriptEngineManager sem = new ScriptEngineManager();
sef = sem.getEngineByName("javascript").getFactory();
}
public boolean scriptExists(String scriptName) {
File scriptFile = new File("scripts/item/" + scriptName + ".js");
return scriptFile.exists();
}
public void runItemScript(MapleClient c, String scriptName) {
if (scripts.containsKey(scriptName)) {
try {
scripts.get(scriptName).invokeFunction("start", new ItemScriptMethods(c));
} catch (ScriptException | NoSuchMethodException ex) {
FilePrinter.printError(FilePrinter.ITEM + scriptName + ".txt", ex);
}
return;
}
File scriptFile = new File("scripts/item/" + scriptName + ".js");
if (!scriptFile.exists()) {
c.announce(MaplePacketCreator.enableActions());
return;
}
FileReader fr = null;
ScriptEngine portal = sef.getScriptEngine();
try {
fr = new FileReader(scriptFile);
// java 8 support here thanks to Arufonsu
if (ServerConstants.JAVA_8){
portal.eval("load('nashorn:mozilla_compat.js');" + System.lineSeparator());
}
((Compilable) portal).compile(fr).eval();
final Invocable script = ((Invocable) portal);
scripts.put(scriptName, script);
script.invokeFunction("start", new ItemScriptMethods(c));
} catch (final UndeclaredThrowableException | ScriptException ute) {
FilePrinter.printError(FilePrinter.ITEM + scriptName + ".txt", ute);
} catch (final Exception e) {
FilePrinter.printError(FilePrinter.ITEM + scriptName + ".txt", e);
} finally {
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void runItemScript(MapleClient c, ScriptedItem scriptItem) {
NPCScriptManager.getInstance().start(c, scriptItem, null);
}
}
+4 -4
View File
@@ -78,18 +78,18 @@ public class MapScriptManager {
return;
}
FileReader fr = null;
ScriptEngine portal = sef.getScriptEngine();
ScriptEngine se = sef.getScriptEngine();
try {
fr = new FileReader(scriptFile);
// java 8 support here thanks to Arufonsu
if (ServerConstants.JAVA_8){
portal.eval("load('nashorn:mozilla_compat.js');" + System.lineSeparator());
se.eval("load('nashorn:mozilla_compat.js');" + System.lineSeparator());
}
((Compilable) portal).compile(fr).eval();
((Compilable) se).compile(fr).eval();
final Invocable script = ((Invocable) portal);
final Invocable script = ((Invocable) se);
scripts.put(scriptName, script);
script.invokeFunction("start", new MapScriptMethods(c));
} catch (final UndeclaredThrowableException | ScriptException ute) {
+12 -2
View File
@@ -74,16 +74,18 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
private int npcOid;
private String scriptName;
private String getText;
private boolean itemScript;
public NPCConversationManager(MapleClient c, int npc, String scriptName) {
this(c, npc, -1, scriptName);
this(c, npc, -1, scriptName, false);
}
public NPCConversationManager(MapleClient c, int npc, int oid, String scriptName) {
public NPCConversationManager(MapleClient c, int npc, int oid, String scriptName, boolean itemScript) {
super(c);
this.npc = npc;
this.npcOid = oid;
this.scriptName = scriptName;
this.itemScript = itemScript;
}
public int getNpc() {
@@ -97,6 +99,14 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
public String getScriptName() {
return scriptName;
}
public boolean isItemScript() {
return itemScript;
}
public void resetItemScript() {
this.itemScript = false;
}
public void dispose() {
NPCScriptManager.getInstance().dispose(this);
+25 -8
View File
@@ -32,6 +32,7 @@ import javax.script.Invocable;
import javax.script.ScriptException;
import scripting.AbstractScriptManager;
import server.MapleItemInformationProvider.ScriptedItem;
import tools.FilePrinter;
import tools.MaplePacketCreator;
@@ -60,7 +61,7 @@ public class NPCScriptManager extends AbstractScriptManager {
}
public boolean start(MapleClient c, int npc, MapleCharacter chr) {
return start(c, npc, null, chr);
return start(c, npc, -1, chr);
}
public boolean start(MapleClient c, int npc, int oid, MapleCharacter chr) {
@@ -70,27 +71,42 @@ public class NPCScriptManager extends AbstractScriptManager {
public boolean start(MapleClient c, int npc, String fileName, MapleCharacter chr) {
return start(c, npc, -1, fileName, chr);
}
public boolean start(MapleClient c, int npc, int oid, String fileName, MapleCharacter chr) {
return start(c, npc, oid, fileName, chr, false, "cm");
}
public boolean start(MapleClient c, ScriptedItem scriptItem, MapleCharacter chr) {
return start(c, scriptItem.getNpc(), -1, scriptItem.getScript(), chr, true, "im");
}
private boolean start(MapleClient c, int npc, int oid, String fileName, MapleCharacter chr, boolean itemScript, String engineName) {
try {
NPCConversationManager cm = new NPCConversationManager(c, npc, oid, fileName);
NPCConversationManager cm = new NPCConversationManager(c, npc, oid, fileName, itemScript);
if (cms.containsKey(c)) {
dispose(c);
}
if (c.canClickNPC()) {
cms.put(c, cm);
Invocable iv = null;
if (fileName != null) {
iv = getInvocable("npc/" + fileName + ".js", c);
if (!itemScript) {
if (fileName != null) {
iv = getInvocable("npc/" + fileName + ".js", c);
}
} else {
if (fileName != null) { // thanks MiLin for drafting NPC-based item scripts
iv = getInvocable("item/" + fileName + ".js", c);
}
}
if (iv == null) {
iv = getInvocable("npc/" + npc + ".js", c);
cm.resetItemScript();
}
if (iv == null || NPCScriptManager.getInstance() == null) {
dispose(c);
return false;
}
engine.put("cm", cm);
engine.put(engineName, cm);
scripts.put(c, iv);
c.setClickedNPC();
try {
@@ -142,10 +158,11 @@ public class NPCScriptManager extends AbstractScriptManager {
cms.remove(c);
scripts.remove(c);
String scriptFolder = (cm.isItemScript() ? "item" : "npc");
if(cm.getScriptName() != null) {
resetContext("npc/" + cm.getScriptName() + ".js", c);
resetContext(scriptFolder + "/" + cm.getScriptName() + ".js", c);
} else {
resetContext("npc/" + cm.getNpc() + ".js", c);
resetContext(scriptFolder + "/" + cm.getNpc() + ".js", c);
}
}
+5 -5
View File
@@ -112,7 +112,7 @@ public class MapleItemInformationProvider {
protected Map<Integer, Integer> monsterBookID = new HashMap<>();
protected Map<Integer, Boolean> untradeableCache = new HashMap<>();
protected Map<Integer, Boolean> onEquipUntradeableCache = new HashMap<>();
protected Map<Integer, scriptedItem> scriptedItemCache = new HashMap<>();
protected Map<Integer, ScriptedItem> scriptedItemCache = new HashMap<>();
protected Map<Integer, Boolean> karmaCache = new HashMap<>();
protected Map<Integer, Integer> triggerItemCache = new HashMap<>();
protected Map<Integer, Integer> expCache = new HashMap<>();
@@ -1447,14 +1447,14 @@ public class MapleItemInformationProvider {
return untradeableOnEquip;
}
public scriptedItem getScriptedItemInfo(int itemId) {
public ScriptedItem getScriptedItemInfo(int itemId) {
if (scriptedItemCache.containsKey(itemId)) {
return scriptedItemCache.get(itemId);
}
if ((itemId / 10000) != 243) {
return null;
}
scriptedItem script = new scriptedItem(MapleDataTool.getInt("spec/npc", getItemData(itemId), 0),
ScriptedItem script = new ScriptedItem(MapleDataTool.getInt("spec/npc", getItemData(itemId), 0),
MapleDataTool.getString("spec/script", getItemData(itemId), ""),
MapleDataTool.getInt("spec/runOnPickup", getItemData(itemId), 0) == 1);
scriptedItemCache.put(itemId, script);
@@ -2100,13 +2100,13 @@ public class MapleItemInformationProvider {
return skillbook;
}
public class scriptedItem {
public class ScriptedItem {
private boolean runOnPickup;
private int npc;
private String script;
public scriptedItem(int npc, String script, boolean rop) {
public ScriptedItem(int npc, String script, boolean rop) {
this.npc = npc;
this.script = script;
this.runOnPickup = rop;
+88 -23
View File
@@ -82,16 +82,16 @@ public class MapleTrade {
partner.getChr().getClient().announce(MaplePacketCreator.getTradeConfirmation());
}
private void complete1() {
private void fetchExchangedItems() {
exchangeItems = partner.getItems();
exchangeMeso = partner.getMeso();
}
private void complete2() {
private void completeTrade() {
boolean show = ServerConstants.USE_DEBUG;
items.clear();
meso = 0;
for (Item item : exchangeItems) {
MapleKarmaManipulator.toggleKarmaFlagToUntradeable(item);
MapleInventoryManipulator.addFromDrop(chr.getClient(), item, show);
@@ -164,12 +164,21 @@ public class MapleTrade {
}
}
public void addItem(Item item) {
items.add(item);
chr.getClient().announce(MaplePacketCreator.getTradeItemAdd((byte) 0, item));
if (partner != null) {
partner.getChr().getClient().announce(MaplePacketCreator.getTradeItemAdd((byte) 1, item));
public boolean addItem(Item item) {
synchronized (items) {
if (items.size() > 9) {
return false;
}
for (Item it : items) {
if (it.getPosition() == item.getPosition()) {
return false;
}
}
items.add(item);
}
return true;
}
public void chat(String message) {
@@ -214,38 +223,65 @@ public class MapleTrade {
return MapleInventory.checkSpotsAndOwnership(chr, tradeItems);
}
private synchronized boolean checkTradeCompleteHandshake(boolean updateSelf) {
MapleTrade self, other;
if (updateSelf) {
self = this;
other = this.getPartner();
} else {
self = this.getPartner();
other = this;
}
if (self.isLocked()) {
return false;
}
self.lockTrade();
return other.isLocked();
}
private boolean checkCompleteHandshake() { // handshake checkout thanks to Ronan
if (this.getChr().getId() < this.getPartner().getChr().getId()) {
return this.checkTradeCompleteHandshake(true);
} else {
return this.getPartner().checkTradeCompleteHandshake(false);
}
}
public static void completeTrade(MapleCharacter c) {
c.getTrade().lockTrade();
MapleTrade local = c.getTrade();
MapleTrade partner = local.getPartner();
if (partner.isLocked()) {
local.complete1();
partner.complete1();
if (local.checkCompleteHandshake()) {
local.fetchExchangedItems();
partner.fetchExchangedItems();
if (!local.fitsMeso()) {
cancelTrade(c);
c.message("There is not enough meso inventory space to complete the trade.");
partner.getChr().message("Partner does not have enough meso inventory space to complete the trade.");
return;
}
else if (!partner.fitsMeso()) {
} else if (!partner.fitsMeso()) {
cancelTrade(c);
c.message("Partner does not have enough meso inventory space to complete the trade.");
partner.getChr().message("There is not enough meso inventory space to complete the trade.");
return;
}
if (!local.fitsInInventory()) {
cancelTrade(c);
c.message("There is not enough inventory space to complete the trade.");
partner.getChr().message("Partner does not have enough inventory space to complete the trade.");
return;
}
else if (!partner.fitsInInventory()) {
} else if (!partner.fitsInInventory()) {
cancelTrade(c);
c.message("Partner does not have enough inventory space to complete the trade.");
partner.getChr().message("There is not enough inventory space to complete the trade.");
return;
}
if (local.getChr().getLevel() < 15) {
if (local.getChr().getMesosTraded() + local.exchangeMeso > 1000000) {
cancelTrade(c);
@@ -263,16 +299,18 @@ public class MapleTrade {
partner.getChr().addMesosTraded(partner.exchangeMeso);
}
}
LogHelper.logTrade(local, partner);
local.complete2();
partner.complete2();
local.completeTrade();
partner.completeTrade();
partner.getChr().setTrade(null);
c.setTrade(null);
}
}
public static void cancelTrade(MapleCharacter c) {
MapleTrade trade = c.getTrade();
private static void cancelTradeInternal(MapleCharacter chr) {
MapleTrade trade = chr.getTrade();
if(trade == null) return;
trade.cancel();
@@ -280,9 +318,36 @@ public class MapleTrade {
trade.getPartner().cancel();
trade.getPartner().getChr().setTrade(null);
}
c.setTrade(null);
chr.setTrade(null);
}
private synchronized void tradeCancelHandshake(boolean updateSelf) {
MapleTrade self;
if (updateSelf) {
self = this;
} else {
self = this.getPartner();
}
cancelTradeInternal(self.getChr());
}
private void cancelHandshake() { // handshake checkout thanks to Ronan
if (this.getChr().getId() < this.getPartner().getChr().getId()) {
this.tradeCancelHandshake(true);
} else {
this.getPartner().tradeCancelHandshake(false);
}
}
public static void cancelTrade(MapleCharacter chr) {
MapleTrade trade = chr.getTrade();
if(trade == null) return;
trade.cancelHandshake();
}
public static void startTrade(MapleCharacter c) {
if (c.getTrade() == null) {
c.setTrade(new MapleTrade((byte) 0, c));
+269 -40
View File
@@ -63,7 +63,6 @@ import scripting.event.EventInstanceManager;
import server.TimerManager;
import server.life.MapleLifeFactory.BanishInfo;
import server.maps.MapleMap;
import server.maps.MapleMapObject;
import server.maps.MapleMapObjectType;
import tools.MaplePacketCreator;
import tools.Pair;
@@ -72,6 +71,8 @@ import net.server.audit.LockCollector;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import net.server.coordinator.MapleMonsterAggroCoordinator;
import server.MapleStatEffect;
import server.maps.MapleSummon;
public class MapleMonster extends AbstractLoadedMapleLife {
private ChangeableStats ostats = null; //unused, v83 WZs offers no support for changeable stats.
@@ -80,7 +81,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
private AtomicLong maxHpPlusHeal = new AtomicLong(1);
private int mp;
private WeakReference<MapleCharacter> controller = new WeakReference<>(null);
private boolean controllerHasAggro, controllerKnowsAboutAggro;
private boolean controllerHasAggro, controllerKnowsAboutAggro, controllerHasPuppet;
private Collection<MonsterListener> listeners = new LinkedList<>();
private EnumMap<MonsterStatus, MonsterStatusEffect> stati = new EnumMap<>(MonsterStatus.class);
private ArrayList<MonsterStatus> alreadyBuffed = new ArrayList<>();
@@ -91,10 +92,15 @@ public class MapleMonster extends AbstractLoadedMapleLife {
private List<Pair<Integer, Integer>> usedSkills = new ArrayList<>();
private Map<Pair<Integer, Integer>, Integer> skillsUsed = new HashMap<>();
private Set<Integer> usedAttacks = new HashSet<>();
private Set<Integer> calledMobOids = null;
private int calledMobCount = 0;
private WeakReference<MapleMonster> callerMob = new WeakReference<>(null);
private List<Integer> stolenItems = new ArrayList<>();
private int team;
private int parentMobOid = 0;
private final HashMap<Integer, AtomicInteger> takenDamage = new HashMap<>();
private Runnable removeAfterAction = null;
private boolean availablePuppetUpdate = true;
private MonitoredReentrantLock externalLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MOB_EXT);
private MonitoredReentrantLock monsterLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MOB, true);
@@ -152,7 +158,59 @@ public class MapleMonster extends AbstractLoadedMapleLife {
public void setParentMobOid(int parentMobId) {
this.parentMobOid = parentMobId;
}
public int countAvailableMobSummons(int limit, int skillLimit) { // limit prop for summons has another conotation, found thanks to MedicOP
Set<Integer> calledOids = this.calledMobOids;
if(calledOids != null) {
limit -= calledOids.size();
}
return Math.min(limit, skillLimit - this.calledMobCount);
}
public void addSummonedMob(MapleMonster mob) {
Set<Integer> calledOids = this.calledMobOids;
if (calledOids == null) {
calledOids = Collections.synchronizedSet(new HashSet<Integer>());
this.calledMobOids = calledOids;
}
calledOids.add(mob.getObjectId());
mob.setSummonerMob(this);
this.calledMobCount += 1;
}
private void removeSummonedMob(int mobOid) {
Set<Integer> calledOids = this.calledMobOids;
if (calledOids != null) {
calledOids.remove(mobOid);
}
}
private void setSummonerMob(MapleMonster mob) {
this.callerMob = new WeakReference<>(mob);
}
private void dispatchClearSummons() {
MapleMonster caller = this.callerMob.get();
if (caller != null) {
caller.removeSummonedMob(this.getObjectId());
}
this.calledMobOids = null;
}
public void pushRemoveAfterAction(Runnable run) {
this.removeAfterAction = run;
}
public Runnable popRemoveAfterAction() {
Runnable r = this.removeAfterAction;
this.removeAfterAction = null;
return r;
}
public int getHp() {
return hp.get();
}
@@ -421,7 +479,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
int expSharersLevel = 0;
for (MapleCharacter mc : members) {
if (mc.getLevel() >= minThresholdLevel) { //NO EXP WILL BE GIVEN for those who are underleveled!
if (Math.abs(killerLevel - mc.getLevel()) < ServerConstants.MIN_UNDERLEVEL_TO_EXP_LEECH) {
if (Math.abs(killerLevel - mc.getLevel()) < ServerConstants.MIN_RANGELEVEL_TO_EXP_LEECH) {
// thanks Thora for pointing out leech level limitation
expSharersLevel += mc.getLevel();
@@ -710,6 +768,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
}
this.aggroClearDamages();
this.dispatchClearSummons();
MonsterListener[] listenersList;
statiLock.lock();
@@ -809,6 +868,10 @@ public class MapleMonster extends AbstractLoadedMapleLife {
private void setControllerKnowsAboutAggro(boolean controllerKnowsAboutAggro) {
if (!fake) this.controllerKnowsAboutAggro = controllerKnowsAboutAggro;
}
private void setControllerHasPuppet(boolean controllerHasPuppet) {
this.controllerHasPuppet = controllerHasPuppet;
}
public byte[] makeBossHPBarPacket() {
return MaplePacketCreator.showBossHP(getId(), getHp(), getMaxHp(), getTagColor(), getTagBgColor());
@@ -1233,39 +1296,13 @@ public class MapleMonster extends AbstractLoadedMapleLife {
return false;
}
int useLimit = toUse.getLimit();
int useSkillid = toUse.getSkillId();
if (useSkillid == 200) {
int i = 0;
for (MapleMapObject mo : getMap().getMapObjects()) {
if (mo.getType() == MapleMapObjectType.MONSTER) {
i++;
}
}
if (i > 100) {
return false;
}
if (map.isDojoMap()) { // spawns in dojo should be unlimited
useLimit = 0;
}
} else if (useSkillid >= 143 && useSkillid <= 145) {
if (useSkillid >= 143 && useSkillid <= 145) {
if (this.isBuffed(MonsterStatus.WEAPON_REFLECT) || this.isBuffed(MonsterStatus.MAGIC_REFLECT)) {
return false;
}
}
if (useLimit > 0) {
monsterLock.lock();
try {
Integer times = this.skillsUsed.get(new Pair<>(useSkillid, toUse.getSkillLevel()));
if (times != null && times >= useLimit) {
return false;
}
} finally {
monsterLock.unlock();
}
}
monsterLock.lock();
try {
/*
@@ -1303,13 +1340,14 @@ public class MapleMonster extends AbstractLoadedMapleLife {
try {
mp -= skill.getMpCon();
this.usedSkills.add(new Pair<>(skillId, level));
if (this.skillsUsed.containsKey(new Pair<>(skillId, level))) {
int times = this.skillsUsed.get(new Pair<>(skillId, level)) + 1;
this.skillsUsed.remove(new Pair<>(skillId, level));
this.skillsUsed.put(new Pair<>(skillId, level), times);
Pair<Integer, Integer> skillKey = new Pair<>(skillId, level);
this.usedSkills.add(skillKey);
Integer useCount = this.skillsUsed.remove(skillKey);
if (useCount != null) {
this.skillsUsed.put(skillKey, useCount + 1);
} else {
this.skillsUsed.put(new Pair<>(skillId, level), 1);
this.skillsUsed.put(skillKey, 1);
}
} finally {
monsterLock.unlock();
@@ -1605,6 +1643,40 @@ public class MapleMonster extends AbstractLoadedMapleLife {
changeLevelByDifficulty(difficulty, pqMob);
}
private boolean isPuppetInVicinity(MapleSummon summon) {
return summon.getPosition().distanceSq(this.getPosition()) < 177777;
}
public boolean isCharacterPuppetInVicinity(MapleCharacter chr) {
MapleStatEffect mse = chr.getBuffEffect(MapleBuffStat.PUPPET);
if (mse != null) {
MapleSummon summon = chr.getSummonByKey(mse.getSourceId());
// check whether mob is currently under a puppet's field of action or not
if (summon != null) {
if (isPuppetInVicinity(summon)) {
return true;
}
} else {
map.getAggroCoordinator().removePuppetAggro(chr.getId());
}
}
return false;
}
public boolean isLeadingPuppetInVicinity() {
MapleCharacter chrController = this.getActiveController();
if (chrController != null) {
if (this.isCharacterPuppetInVicinity(chrController)) {
return true;
}
}
return false;
}
private MapleCharacter getNextControllerCandidate() {
int mincontrolled = Integer.MAX_VALUE;
MapleCharacter newController = null;
@@ -1612,11 +1684,16 @@ public class MapleMonster extends AbstractLoadedMapleLife {
int mincontrolleddead = Integer.MAX_VALUE;
MapleCharacter newControllerDead = null;
MapleCharacter newControllerWithPuppet = null;
for (MapleCharacter chr : getMap().getAllPlayers()) {
if (!chr.isHidden()) {
int ctrlMonsSize = chr.getNumControlledMonsters();
if (chr.isAlive()) {
if (isCharacterPuppetInVicinity(chr)) {
newControllerWithPuppet = chr;
break;
} else if (chr.isAlive()) {
if (ctrlMonsSize < mincontrolled) {
mincontrolled = ctrlMonsSize;
newController = chr;
@@ -1630,7 +1707,13 @@ public class MapleMonster extends AbstractLoadedMapleLife {
}
}
return newController != null ? newController : newControllerDead;
if (newControllerWithPuppet != null) {
return newControllerWithPuppet;
} else if (newController != null) {
return newController;
} else {
return newControllerDead;
}
}
/**
@@ -1682,15 +1765,39 @@ public class MapleMonster extends AbstractLoadedMapleLife {
this.setController(newController);
this.setControllerHasAggro(immediateAggro);
this.setControllerKnowsAboutAggro(false);
this.setControllerHasPuppet(false);
} finally {
aggroUpdateLock.unlock();
}
this.aggroUpdatePuppetVisibility();
newController.announce(MaplePacketCreator.controlMonster(this, false, immediateAggro));
newController.controlMonster(this);
}
}
public void aggroAddPuppet(MapleCharacter player) {
MapleMonsterAggroCoordinator mmac = map.getAggroCoordinator();
mmac.addPuppetAggro(player);
aggroUpdatePuppetController(player);
if (this.isControllerHasAggro()) {
this.aggroUpdatePuppetVisibility();
}
}
public void aggroRemovePuppet(MapleCharacter player) {
MapleMonsterAggroCoordinator mmac = map.getAggroCoordinator();
mmac.removePuppetAggro(player.getId());
aggroUpdatePuppetController(null);
if (this.isControllerHasAggro()) {
this.aggroUpdatePuppetVisibility();
}
}
/**
* Automagically finds a new controller for the given monster from the chars
* on the map it is from...
@@ -1710,6 +1817,60 @@ public class MapleMonster extends AbstractLoadedMapleLife {
this.aggroSwitchController(newController, false);
}
/**
* Finds a new controller for the given monster from the chars with deployed puppet
* nearby on the map it is from...
*
*/
private void aggroUpdatePuppetController(MapleCharacter newController) {
MapleCharacter chrController = this.getActiveController();
boolean updateController = false;
if (chrController != null && chrController.isAlive()) {
if (isCharacterPuppetInVicinity(chrController)) {
return;
}
} else {
updateController = true;
}
if (newController == null || !isCharacterPuppetInVicinity(newController)) {
MapleMonsterAggroCoordinator mmac = map.getAggroCoordinator();
List<Integer> puppetOwners = mmac.getPuppetAggroList();
List<Integer> toRemovePuppets = new LinkedList<>();
for (Integer cid : puppetOwners) {
MapleCharacter chr = map.getCharacterById(cid);
if (chr != null) {
if (isCharacterPuppetInVicinity(chr)) {
newController = chr;
break;
}
} else {
toRemovePuppets.add(cid);
}
}
for (Integer cid : toRemovePuppets) {
mmac.removePuppetAggro(cid);
}
if (newController == null) { // was a new controller found? (if not there's no puppet nearby)
if (updateController) {
aggroUpdateController();
}
return;
}
} else if (chrController == newController) {
this.aggroUpdatePuppetVisibility();
}
this.aggroSwitchController(newController, this.isControllerHasAggro());
}
/**
* Ensures controllability removal of the current player controller, and
* fetches for any player on the map to start controlling in place.
@@ -1767,6 +1928,9 @@ public class MapleMonster extends AbstractLoadedMapleLife {
if (chrController != attacker) {
if (this.getMapAggroCoordinator().isLeadingCharacterAggro(this, attacker)) {
this.aggroSwitchController(attacker, true);
} else {
this.setControllerHasAggro(true);
this.aggroUpdatePuppetVisibility();
}
/*
@@ -1780,9 +1944,69 @@ public class MapleMonster extends AbstractLoadedMapleLife {
*/
} else {
this.setControllerHasAggro(true);
this.aggroUpdatePuppetVisibility();
}
}
private void aggroRefreshPuppetVisibility(MapleCharacter chrController, MapleSummon puppet) {
// lame patch for client to redirect all aggro to the puppet
List<MapleMonster> puppetControlled = new LinkedList<>();
for (MapleMonster mob : chrController.getControlledMonsters()) {
if (mob.isPuppetInVicinity(puppet)) {
puppetControlled.add(mob);
}
}
for (MapleMonster mob : puppetControlled) {
chrController.announce(MaplePacketCreator.stopControllingMonster(mob.getObjectId()));
}
chrController.announce(MaplePacketCreator.removeSummon(puppet, false));
for (MapleMonster mob : puppetControlled) {
chrController.announce(MaplePacketCreator.controlMonster(mob, false, mob.isControllerHasAggro()));
}
chrController.announce(MaplePacketCreator.spawnSummon(puppet, false));
}
public void aggroUpdatePuppetVisibility() {
if (!availablePuppetUpdate) return;
availablePuppetUpdate = false;
Runnable r = new Runnable() {
@Override
public void run() {
try {
MapleCharacter chrController = MapleMonster.this.getActiveController();
if (chrController == null) return;
MapleStatEffect puppetEffect = chrController.getBuffEffect(MapleBuffStat.PUPPET);
if (puppetEffect != null) {
MapleSummon puppet = chrController.getSummonByKey(puppetEffect.getSourceId());
if (puppet != null && isPuppetInVicinity(puppet)) {
controllerHasPuppet = true;
aggroRefreshPuppetVisibility(chrController, puppet);
return;
}
}
if (controllerHasPuppet) {
controllerHasPuppet = false;
chrController.announce(MaplePacketCreator.stopControllingMonster(MapleMonster.this.getObjectId()));
chrController.announce(MaplePacketCreator.controlMonster(MapleMonster.this, false, MapleMonster.this.isControllerHasAggro()));
}
} finally {
availablePuppetUpdate = true;
}
}
};
// had to schedule this since mob wouldn't stick to puppet aggro who knows why
this.getMap().getChannelServer().registerOverallAction(this.getMap().getId(), r, ServerConstants.UPDATE_INTERVAL);
}
/**
* Clears all applied damage input for this mob, doesn't refresh target aggro.
*
@@ -1805,7 +2029,12 @@ public class MapleMonster extends AbstractLoadedMapleLife {
}
}
public final void disposeLocks() {
public void dispose() {
this.getMap().dismissRemoveAfter(this);
disposeLocks();
}
private void disposeLocks() {
LockCollector.getInstance().registerDisposeAction(new Runnable() {
@Override
public void run() {
+67 -51
View File
@@ -33,6 +33,7 @@ import constants.GameConstants;
import java.util.LinkedList;
import java.util.Map;
import tools.Randomizer;
import server.maps.MapleMap;
import server.maps.MapleMapObject;
import server.maps.MapleMapObjectType;
import server.maps.MapleMist;
@@ -235,57 +236,72 @@ public class MobSkill {
case 156: // speed up
break;
case 200: // summon
if (monster.getMap().getSpawnedMonstersOnMap() < 80) {
for (Integer mobId : getSummons()) {
MapleMonster toSpawn = MapleLifeFactory.getMonster(mobId);
if(toSpawn != null) {
if(GameConstants.isBossRush(monster.getMap().getId())) toSpawn.disableDrops(); // no littering on BRPQ pls
toSpawn.setPosition(monster.getPosition());
int ypos, xpos;
xpos = (int) monster.getPosition().getX();
ypos = (int) monster.getPosition().getY();
switch (mobId) {
case 8500003: // Pap bomb high
toSpawn.setFh((int) Math.ceil(Math.random() * 19.0));
ypos = -590;
break;
case 8500004: // Pap bomb
xpos = (int) (monster.getPosition().getX() + Randomizer.nextInt(1000) - 500);
if (ypos != -590) {
ypos = (int) monster.getPosition().getY();
}
break;
case 8510100: //Pianus bomb
if (Math.ceil(Math.random() * 5) == 1) {
ypos = 78;
xpos = (int) Randomizer.nextInt(5) + (Randomizer.nextInt(2) == 1 ? 180 : 0);
} else {
int skillLimit = this.getLimit();
MapleMap map = monster.getMap();
if (map.isDojoMap()) { // spawns in dojo should be unlimited
skillLimit = Integer.MAX_VALUE;
}
if (map.getSpawnedMonstersOnMap() < 80) {
List<Integer> summons = getSummons();
int summonLimit = monster.countAvailableMobSummons(summons.size(), skillLimit);
if (summonLimit >= 1) {
Collections.shuffle(summons);
boolean bossRushMap = GameConstants.isBossRush(map.getId());
for (Integer mobId : summons.subList(0, summonLimit)) {
MapleMonster toSpawn = MapleLifeFactory.getMonster(mobId);
if(toSpawn != null) {
if(bossRushMap) toSpawn.disableDrops(); // no littering on BRPQ pls
toSpawn.setPosition(monster.getPosition());
int ypos, xpos;
xpos = (int) monster.getPosition().getX();
ypos = (int) monster.getPosition().getY();
switch (mobId) {
case 8500003: // Pap bomb high
toSpawn.setFh((int) Math.ceil(Math.random() * 19.0));
ypos = -590;
break;
case 8500004: // Pap bomb
xpos = (int) (monster.getPosition().getX() + Randomizer.nextInt(1000) - 500);
}
break;
}
switch (monster.getMap().getId()) {
case 220080001: //Pap map
if (xpos < -890) {
xpos = (int) (Math.ceil(Math.random() * 150) - 890);
} else if (xpos > 230) {
xpos = (int) (230 - Math.ceil(Math.random() * 150));
}
break;
case 230040420: // Pianus map
if (xpos < -239) {
xpos = (int) (Math.ceil(Math.random() * 150) - 239);
} else if (xpos > 371) {
xpos = (int) (371 - Math.ceil(Math.random() * 150));
}
break;
}
toSpawn.setPosition(new Point(xpos, ypos));
if (toSpawn.getId() == 8500004) {
monster.getMap().spawnFakeMonster(toSpawn);
} else {
monster.getMap().spawnMonsterWithEffect(toSpawn, getSpawnEffect(), toSpawn.getPosition());
if (ypos != -590) {
ypos = (int) monster.getPosition().getY();
}
break;
case 8510100: //Pianus bomb
if (Math.ceil(Math.random() * 5) == 1) {
ypos = 78;
xpos = (int) Randomizer.nextInt(5) + (Randomizer.nextInt(2) == 1 ? 180 : 0);
} else {
xpos = (int) (monster.getPosition().getX() + Randomizer.nextInt(1000) - 500);
}
break;
}
switch (map.getId()) {
case 220080001: //Pap map
if (xpos < -890) {
xpos = (int) (Math.ceil(Math.random() * 150) - 890);
} else if (xpos > 230) {
xpos = (int) (230 - Math.ceil(Math.random() * 150));
}
break;
case 230040420: // Pianus map
if (xpos < -239) {
xpos = (int) (Math.ceil(Math.random() * 150) - 239);
} else if (xpos > 371) {
xpos = (int) (371 - Math.ceil(Math.random() * 150));
}
break;
}
toSpawn.setPosition(new Point(xpos, ypos));
if (toSpawn.getId() == 8500004) {
map.spawnFakeMonster(toSpawn);
} else {
map.spawnMonsterWithEffect(toSpawn, getSpawnEffect(), toSpawn.getPosition());
}
monster.addSummonedMob(toSpawn);
}
}
}
@@ -342,7 +358,7 @@ public class MobSkill {
}
public List<Integer> getSummons() {
return Collections.unmodifiableList(toSummon);
return new ArrayList<>(toSummon);
}
public int getSpawnEffect() {
+8 -1
View File
@@ -44,12 +44,19 @@ public class MapMonitor {
}
private void cancelAction() {
monitorSchedule.cancel(false);
if (monitorSchedule != null) { // thanks Thora for pointing a NPE occurring here
monitorSchedule.cancel(false);
monitorSchedule = null;
}
map.killAllMonsters();
map.clearDrops();
if (portal != null) {
portal.setPortalStatus(MaplePortal.OPEN);
}
map.resetReactors();
map = null;
portal = null;
}
}
+20 -4
View File
@@ -227,6 +227,18 @@ public class MapleHiredMerchant extends AbstractMapleMapObject {
return MapleInventoryManipulator.checkSpace(c, newItem.getItemId(), newItem.getQuantity(), newItem.getOwner()) && MapleInventoryManipulator.addFromDrop(c, newItem, false);
}
private int getQuantityLeft(int itemid) {
int count = 0;
for (MaplePlayerShopItem mpsi : items) {
if (mpsi.getItem().getItemId() == itemid) {
count += (mpsi.getBundles() * mpsi.getItem().getQuantity());
}
}
return count;
}
public void buy(MapleClient c, int item, short quantity) {
synchronized (items) {
MaplePlayerShopItem pItem = items.get(item);
@@ -247,7 +259,6 @@ public class MapleHiredMerchant extends AbstractMapleMapObject {
if (c.getPlayer().getMeso() >= price) {
if (canBuy(c, newItem)) {
c.getPlayer().gainMeso(-price, false);
if(ServerConstants.USE_ANNOUNCE_SHOPITEMSOLD) announceItemSold(newItem, price); // idea thanks to Vcoc
synchronized (sold) {
sold.add(new SoldItem(c.getPlayer().getName(), pItem.getItem().getItemId(), newItem.getQuantity(), price));
@@ -257,6 +268,11 @@ public class MapleHiredMerchant extends AbstractMapleMapObject {
if (pItem.getBundles() < 1) {
pItem.setDoesExist(false);
}
if(ServerConstants.USE_ANNOUNCE_SHOPITEMSOLD) { // idea thanks to Vcoc
announceItemSold(newItem, price, getQuantityLeft(pItem.getItem().getItemId()));
}
MapleCharacter owner = Server.getInstance().getWorld(world).getPlayerStorage().getCharacterByName(ownerName);
if (owner != null) {
owner.addMerchantMesos(price);
@@ -288,12 +304,12 @@ public class MapleHiredMerchant extends AbstractMapleMapObject {
}
}
private void announceItemSold(Item item, int mesos) {
String qtyStr = (item.getQuantity() > 1) ? " (qty. " + item.getQuantity() + ")" : "";
private void announceItemSold(Item item, int mesos, int inStore) {
String qtyStr = (item.getQuantity() > 1) ? " x " + item.getQuantity() : "";
MapleCharacter player = Server.getInstance().getWorld(world).getPlayerStorage().getCharacterById(ownerId);
if(player != null && player.isLoggedinWorld()) {
player.dropMessage(6, "[HIRED MERCHANT] Item '" + MapleItemInformationProvider.getInstance().getName(item.getItemId()) + "'" + qtyStr + " has been sold for " + mesos + " mesos.");
player.dropMessage(6, "[Hired Merchant] Item '" + MapleItemInformationProvider.getInstance().getName(item.getItemId()) + "'" + qtyStr + " has been sold for " + mesos + " mesos. (" + inStore + " left)");
}
}
+37 -7
View File
@@ -1796,21 +1796,36 @@ public class MapleMap {
private void applyRemoveAfter(final MapleMonster monster) {
final selfDestruction selfDestruction = monster.getStats().selfDestruction();
if (monster.getStats().removeAfter() > 0 || selfDestruction != null && selfDestruction.getHp() < 0) {
Runnable removeAfterAction;
if (selfDestruction == null) {
registerMapSchedule(new Runnable() {
removeAfterAction = new Runnable() {
@Override
public void run() {
killMonster(monster, null, false);
}
}, monster.getStats().removeAfter() * 1000);
};
registerMapSchedule(removeAfterAction, monster.getStats().removeAfter() * 1000);
} else {
registerMapSchedule(new Runnable() {
removeAfterAction = new Runnable() {
@Override
public void run() {
killMonster(monster, null, false, selfDestruction.getAction());
}
}, selfDestruction.removeAfter() * 1000);
};
registerMapSchedule(removeAfterAction, selfDestruction.removeAfter() * 1000);
}
monster.pushRemoveAfterAction(removeAfterAction);
}
}
public void dismissRemoveAfter(final MapleMonster monster) {
Runnable removeAfterAction = monster.popRemoveAfterAction();
if (removeAfterAction != null) {
this.getChannelServer().forceRunOverallAction(mapid, removeAfterAction);
}
}
@@ -2583,6 +2598,18 @@ public class MapleMap {
return Collections.unmodifiableCollection(portals.values());
}
*/
public void addPlayerPuppet(MapleCharacter player) {
for (MapleMonster mm : this.getMonsters()) {
mm.aggroAddPuppet(player);
}
}
public void removePlayerPuppet(MapleCharacter player) {
for (MapleMonster mm : this.getMonsters()) {
mm.aggroRemovePuppet(player);
}
}
public void removePlayer(MapleCharacter chr) {
Channel cserv = chr.getClient().getChannelServer();
@@ -3699,8 +3726,11 @@ public class MapleMap {
if (mapid >= 922240100 && mapid <= 922240119) {
toggleHiddenNPC(9001108);
}
mapMonitor.cancel(true);
mapMonitor = null;
if (mapMonitor != null) {
mapMonitor.cancel(true);
mapMonitor = null;
}
}
}
}, 1000);
@@ -3993,7 +4023,7 @@ public class MapleMap {
public void dispose() {
for(MapleMonster mm : this.getMonsters()) {
mm.disposeLocks();
mm.dispose();
}
clearMapObjects();
+3
View File
@@ -536,6 +536,9 @@ public class MapleQuest {
case PETTAMENESS:
ret = new PetTamenessAction(this, data);
break;
case PETSPEED:
ret = new PetSpeedAction(this, data);
break;
default:
//FilePrinter.printError(FilePrinter.EXCEPTION_CAUGHT, "Unhandled Action Type: " + type.toString() + " QuestID: " + this.getId());
break;
+3 -1
View File
@@ -26,7 +26,7 @@ package server.quest;
* @author Matze
*/
public enum MapleQuestActionType {
UNDEFINED(-1), EXP(0), ITEM(1), NEXTQUEST(2), MESO(3), QUEST(4), SKILL(5), FAME(6), BUFF(7), PETSKILL(8), YES(9), NO(10), NPC(11), MIN_LEVEL(12), NORMAL_AUTO_START(13), PETTAMENESS(14), ZERO(15);
UNDEFINED(-1), EXP(0), ITEM(1), NEXTQUEST(2), MESO(3), QUEST(4), SKILL(5), FAME(6), BUFF(7), PETSKILL(8), YES(9), NO(10), NPC(11), MIN_LEVEL(12), NORMAL_AUTO_START(13), PETTAMENESS(14), PETSPEED(15), ZERO(16);
final byte type;
private MapleQuestActionType(int type) {
@@ -62,6 +62,8 @@ public enum MapleQuestActionType {
return NORMAL_AUTO_START;
} else if (name.equals("pettameness")) {
return PETTAMENESS;
} else if (name.equals("petspeed")) {
return PETSPEED;
} else if (name.equals("0")) {
return ZERO;
} else {
@@ -0,0 +1,60 @@
/*
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 server.quest.actions;
import client.MapleClient;
import client.MapleCharacter;
import client.inventory.MaplePet;
import provider.MapleData;
import provider.MapleDataTool;
import server.quest.MapleQuest;
import server.quest.MapleQuestActionType;
/**
*
* @author Ronan
*/
public class PetSpeedAction extends MapleQuestAction {
public PetSpeedAction(MapleQuest quest, MapleData data) {
super(MapleQuestActionType.PETTAMENESS, quest);
questID = quest.getId();
}
@Override
public void processData(MapleData data) {}
@Override
public void run(MapleCharacter chr, Integer extSelection) {
MapleClient c = chr.getClient();
MaplePet pet = chr.getPet(0); // assuming here only the pet leader will gain owner speed
if(pet == null) return;
c.lockClient();
try {
pet.addPetFlag(c.getPlayer(), MaplePet.PetFlag.OWNER_SPEED);
} finally {
c.unlockClient();
}
}
}
+1
View File
@@ -28,6 +28,7 @@ public class FilePrinter {
PACKET_LOG = "game/Log.txt",
CASHITEM_BOUGHT = "interactions/CashLog.txt",
EXCEPTION = "game/Exceptions.txt",
TRADE_EXCEPTION = "game/TradeExceptions.txt",
SQL_EXCEPTION = "game/SqlExceptions.txt",
PACKET_HANDLER = "game/packethandler/",
PORTAL = "game/portals/",
+20 -15
View File
@@ -406,7 +406,7 @@ public class MaplePacketCreator {
mplew.writeShort(pet.getCloseness());
mplew.write(pet.getFullness());
addExpirationTime(mplew, item.getExpiration());
mplew.writeInt(0);
mplew.writeInt(pet.getPetFlag()); /* pet flags found by -- Irenex & Spoon */
mplew.write(new byte[]{(byte) 0x50, (byte) 0x46}); //wonder what this is
mplew.writeInt(0);
return;
@@ -1065,7 +1065,7 @@ public class MaplePacketCreator {
mplew.writeShort(chr.getHp());
mplew.writeBool(false);
mplew.writeLong(getTime(Server.getInstance().getCurrentTime()));
mplew.skip(10);
mplew.skip(14);
return mplew.getPacket();
}
@@ -1082,7 +1082,7 @@ public class MaplePacketCreator {
mplew.writeInt(spawnPosition.x); // spawn position placement thanks to Arnah (Vertisy)
mplew.writeInt(spawnPosition.y);
mplew.writeLong(getTime(Server.getInstance().getCurrentTime()));
mplew.skip(10);
mplew.skip(14);
return mplew.getPacket();
}
@@ -1158,7 +1158,8 @@ public class MaplePacketCreator {
mplew.write(0x0A); //v83
mplew.write(summon.getSkillLevel());
mplew.writePos(summon.getPosition());
mplew.skip(3);
mplew.write(summon.getStance()); //bMoveAction & foothold, found thanks to Rien dev team
mplew.writeShort(0);
mplew.write(summon.getMovementType().getValue()); // 0 = don't move, 1 = follow (4th mage summons?), 2/4 = only tele follow, 3 = bird follow
mplew.write(summon.isPuppet() ? 0 : 1); // 0 and the summon can't attack - but puppets don't attack with 1 either ^.-
mplew.write(animated ? 0 : 1);
@@ -1828,8 +1829,7 @@ public class MaplePacketCreator {
mplew.writeInt(!drop.isFFADrop() ? (recvrInParty ? drop.getPartyOwnerId() : drop.getOwnerId()) : 0); // owner charid/partyid :)
mplew.write(drop.getDropType()); // 0 = timeout for non-owner, 1 = timeout for non-owner's party, 2 = FFA, 3 = explosive/FFA
mplew.writePos(dropto);
// its not charId, but dropper's oid, this error will occur only if a monster's oid in map equals charId, (the item will drop from the error monster)
mplew.writeInt(drop.getDropper().getObjectId());
mplew.writeInt(drop.getDropper().getObjectId()); // dropper oid, found thanks to Li Jixue
if (mod != 2) {
mplew.writePos(dropfrom);
@@ -2736,9 +2736,9 @@ public class MaplePacketCreator {
}
}
mplew.writeMapleAsciiString(guildName);
mplew.writeMapleAsciiString(allianceName); // does not seems to work
mplew.writeMapleAsciiString(allianceName); // does not seem to work
mplew.write(0); // pMedalInfo, thanks to Arnah (Vertisy)
mplew.write(0);
MaplePet[] pets = chr.getPets();
Item inv = chr.getInventory(MapleInventoryType.EQUIPPED).getItem((short) -114);
for (int i = 0; i < 3; i++) {
@@ -3778,11 +3778,16 @@ public class MaplePacketCreator {
}
/**
* 10: A beginner can't create a party. 1/11/14/19: Your request for a party
* didn't work due to an unexpected error. 13: You have yet to join a party.
* 10: A beginner can't create a party. 1/5/6/11/14/19: Your request for a
* party didn't work due to an unexpected error. 12: Quit as leader of the
* party. 13: You have yet to join a party.
* 16: Already have joined a party. 17: The party you're trying to join is
* already in full capacity. 19: Unable to find the requested character in
* this channel.
* this channel. 21: Player is blocking any party invitations. 22: Player
* is taking care of another invitation. 23: Player denied request.
* 25: Cannot kick another user in this map. 28/29: Leadership can only be
* given to a party member in the vicinity. 30: Change leadership only on
* same channel.
*
* @param message
* @return
@@ -4065,7 +4070,7 @@ public class MaplePacketCreator {
mplew.writeInt(cid);
mplew.writeInt(oid);
mplew.write(12);
mplew.writeInt(damage); // damage display doesn't seems to work...
mplew.writeInt(damage); // damage display doesn't seem to work...
mplew.writeInt(monsterIdFrom);
mplew.write(0);
return mplew.getPacket();
@@ -6746,7 +6751,7 @@ public class MaplePacketCreator {
return mplew.getPacket();
}
public static byte[] updateAllianceInfo(MapleAlliance alliance, MapleClient c) {
public static byte[] updateAllianceInfo(MapleAlliance alliance, int world) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.ALLIANCE_OPERATION.getValue());
mplew.write(0x0F);
@@ -6761,8 +6766,8 @@ public class MaplePacketCreator {
}
mplew.writeInt(alliance.getCapacity()); // probably capacity
mplew.writeShort(0);
for (Integer guildd : alliance.getGuilds()) {
getGuildInfo(mplew, Server.getInstance().getGuild(guildd, c.getWorld()));
for (Integer guildid : alliance.getGuilds()) {
getGuildInfo(mplew, Server.getInstance().getGuild(guildid, world));
}
return mplew.getPacket();
}