PNPC & Pmob + Events Wheel & P. Leave patch + Proper cash use deplete

Reworked cash item consume. It now depletes from the selected slot rather than from the first slot it appears on inventory.
Implemented PNPC and PMOB commands.
Solved an issue with some events finishing abruptly when players quit a party.
Added missing mandatory script functions to the event scripts.
Solved an interaction issue with Wheel of Destiny activation in event instances.
Added interaction with NPC Mom & Dad in the Engagement prequest.
Solved an issue with event maps being disposed while reactor's spray items activity is still in effect.
Fixed modifier scrolls such as Spikes on Shoes depleting a upgrade slot.
Added a server flag for avoiding compulsory consuming by pet autopot.
Added a cache for fetching mob names.
Implemented boss drop rate.
Smart protected commands system against command requests in burst by the same user.
Revised login handler, bringing disconnection checks before checking login state and preventing new client reattribution to already logged-in character objects.
Botched login handler sessions now properly gets closed.
This commit is contained in:
ronancpl
2019-01-16 14:46:28 -02:00
parent 132a4ee989
commit 0203d45901
107 changed files with 2740 additions and 473 deletions

View File

@@ -81,6 +81,18 @@ public class CommandsExecutor {
}
public void handle(MapleClient client, String message){
if (client.tryacquireClient()) {
try {
handleInternal(client, message);
} finally {
client.releaseClient();
}
} else {
client.getPlayer().dropMessage(5, "Try again in a while... Latest commands are currently being processed.");
}
}
private void handleInternal(MapleClient client, String message){
final String[] spitedMessage = message.toLowerCase().substring(1).split("[ ]");
final String commandName = spitedMessage[0];
final RegisteredCommand command = registeredCommands.get(commandName);
@@ -321,8 +333,12 @@ public class CommandsExecutor {
addCommand("pap", 4, PapCommand.class);
addCommand("pianus", 4, PianusCommand.class);
addCommand("cake", 4, CakeCommand.class);
addCommand("playernpcremove", 4, PlayerNpcRemoveCommand.class);
addCommand("playernpc", 4, PlayerNpcCommand.class);
addCommand("playernpcremove", 4, PlayerNpcRemoveCommand.class);
addCommand("pnpc", 4, PnpcCommand.class);
addCommand("pnpcremove", 4, PnpcRemoveCommand.class);
addCommand("pmob", 4, PmobCommand.class);
addCommand("pmobremove", 4, PmobRemoveCommand.class);
commandsNameDesc.add(levelCommandsCursor);
}

View File

@@ -42,6 +42,7 @@ public class RatesCommand extends Command {
showMsg_ += "EXP Rate: #e#b" + player.getExpRate() + "x#k#n" + "\r\n";
showMsg_ += "MESO Rate: #e#b" + player.getMesoRate() + "x#k#n" + "\r\n";
showMsg_ += "DROP Rate: #e#b" + player.getDropRate() + "x#k#n" + "\r\n";
showMsg_ += "BOSS DROP Rate: #e#b" + player.getBossDropRate() + "x#k#n" + "\r\n";
if(ServerConstants.USE_QUEST_RATE) showMsg_ += "QUEST Rate: #e#b" + c.getWorldServer().getQuestRate() + "x#k#n" + "\r\n";
player.showHint(showMsg_, 300);

View File

@@ -37,30 +37,36 @@ public class ShowRatesCommand extends Command {
public void execute(MapleClient c, String[] params) {
MapleCharacter player = c.getPlayer();
String showMsg = "#eEXP RATE#n" + "\r\n";
showMsg += "Server EXP Rate: #k" + c.getWorldServer().getExpRate() + "x#k" + "\r\n";
showMsg += "World EXP Rate: #k" + c.getWorldServer().getExpRate() + "x#k" + "\r\n";
showMsg += "Player EXP Rate: #k" + player.getRawExpRate() + "x#k" + "\r\n";
if(player.getCouponExpRate() != 1) showMsg += "Coupon EXP Rate: #k" + player.getCouponExpRate() + "x#k" + "\r\n";
showMsg += "EXP Rate: #e#b" + player.getExpRate() + "x#k#n" + "\r\n";
showMsg += "\r\n" + "#eMESO RATE#n" + "\r\n";
showMsg += "Server MESO Rate: #k" + c.getWorldServer().getMesoRate() + "x#k" + "\r\n";
showMsg += "World MESO Rate: #k" + c.getWorldServer().getMesoRate() + "x#k" + "\r\n";
showMsg += "Player MESO Rate: #k" + player.getRawMesoRate() + "x#k" + "\r\n";
if(player.getCouponMesoRate() != 1) showMsg += "Coupon MESO Rate: #k" + player.getCouponMesoRate() + "x#k" + "\r\n";
showMsg += "MESO Rate: #e#b" + player.getMesoRate() + "x#k#n" + "\r\n";
showMsg += "\r\n" + "#eDROP RATE#n" + "\r\n";
showMsg += "Server DROP Rate: #k" + c.getWorldServer().getDropRate() + "x#k" + "\r\n";
showMsg += "World DROP Rate: #k" + c.getWorldServer().getDropRate() + "x#k" + "\r\n";
showMsg += "Player DROP Rate: #k" + player.getRawDropRate() + "x#k" + "\r\n";
if(player.getCouponDropRate() != 1) showMsg += "Coupon DROP Rate: #k" + player.getCouponDropRate() + "x#k" + "\r\n";
showMsg += "DROP Rate: #e#b" + player.getDropRate() + "x#k#n" + "\r\n";
showMsg += "\r\n" + "#eBOSS DROP RATE#n" + "\r\n";
showMsg += "World BOSS DROP Rate: #k" + c.getWorldServer().getBossDropRate() + "x#k" + "\r\n";
showMsg += "Player DROP Rate: #k" + player.getRawDropRate() + "x#k" + "\r\n";
if(player.getCouponDropRate() != 1) showMsg += "Coupon DROP Rate: #k" + player.getCouponDropRate() + "x#k" + "\r\n";
showMsg += "BOSS DROP Rate: #e#b" + player.getBossDropRate() + "x#k#n" + "\r\n";
if(ServerConstants.USE_QUEST_RATE) {
showMsg += "\r\n" + "#eQUEST RATE#n" + "\r\n";
showMsg += "Server QUEST Rate: #e#b" + c.getWorldServer().getQuestRate() + "x#k#n" + "\r\n";
showMsg += "World QUEST Rate: #e#b" + c.getWorldServer().getQuestRate() + "x#k#n" + "\r\n";
}
showMsg += "\r\n" + "#eTRAVEL RATE#n" + "\r\n";
showMsg += "Server TRAVEL Rate: #e#b" + c.getWorldServer().getTravelRate() + "x#k#n" + "\r\n";
showMsg += "\r\n";
showMsg += "World TRAVEL Rate: #e#b" + c.getWorldServer().getTravelRate() + "x#k#n" + "\r\nServer\r\nPlayer";
player.showHint(showMsg, 300);
}

View File

@@ -62,7 +62,7 @@ public class WhatDropsFromCommand extends Command {
if (name == null || name.equals("null") || drop.chance == 0){
continue;
}
float chance = 1000000 / drop.chance / player.getDropRate();
float chance = 1000000 / drop.chance / (!MapleMonsterInformationProvider.getInstance().isBoss(mobId) ? player.getDropRate() : player.getBossDropRate());
output += "- " + name + " (1/" + (int) chance + ")\r\n";
} catch (Exception ex){
ex.printStackTrace();

View File

@@ -29,7 +29,6 @@ import client.MapleClient;
import server.MapleItemInformationProvider;
import server.life.MapleMonsterInformationProvider;
import tools.DatabaseConnection;
import tools.MaplePacketCreator;
import tools.Pair;
import java.sql.Connection;
@@ -66,7 +65,7 @@ public class WhoDropsCommand extends Command {
ps.setInt(1, data.getLeft());
ResultSet rs = ps.executeQuery();
while(rs.next()) {
String resultName = MapleMonsterInformationProvider.getMobNameFromID(rs.getInt("dropperid"));
String resultName = MapleMonsterInformationProvider.getInstance().getMobNameFromId(rs.getInt("dropperid"));
if (resultName != null) {
output += resultName + ", ";
}

View File

@@ -0,0 +1,105 @@
/*
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.gm4;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import net.server.channel.Channel;
import server.life.MapleLifeFactory;
import server.maps.MapleMap;
import client.command.Command;
import client.MapleCharacter;
import client.MapleClient;
import java.awt.Point;
import server.life.MapleMonster;
import tools.DatabaseConnection;
public class PmobCommand extends Command {
{
setDescription("");
}
@Override
public void execute(MapleClient c, String[] params) {
MapleCharacter player = c.getPlayer();
if (params.length < 1) {
player.yellowMessage("Syntax: !pmob <mobid> [<mobtime>]");
return;
}
// command suggestion thanks to HighKey21, none, bibiko94 (TAYAMO), asafgb
int mapId = player.getMapId();
int mobId = Integer.parseInt(params[0]);
int mobTime = (params.length > 1) ? Integer.parseInt(params[1]) : -1;
Point checkpos = player.getMap().getGroundBelow(player.getPosition());
int xpos = checkpos.x;
int ypos = checkpos.y;
int fh = player.getMap().getFootholds().findBelow(checkpos).getId();
MapleMonster mob = MapleLifeFactory.getMonster(mobId);
if (mob != null && !mob.getName().equals("MISSINGNO")) {
mob.setPosition(checkpos);
mob.setCy(ypos);
mob.setRx0(xpos + 50);
mob.setRx1(xpos - 50);
mob.setFh(fh);
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("INSERT INTO plife ( life, f, fh, cy, rx0, rx1, type, x, y, world, map, mobtime, hide ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )");
ps.setInt(1, mobId);
ps.setInt(2, 0);
ps.setInt(3, fh);
ps.setInt(4, ypos);
ps.setInt(5, xpos + 50);
ps.setInt(6, xpos - 50);
ps.setString(7, "m");
ps.setInt(8, xpos);
ps.setInt(9, ypos);
ps.setInt(10, player.getWorld());
ps.setInt(11, mapId);
ps.setInt(12, mobTime);
ps.setInt(13, 0);
ps.executeUpdate();
ps.close();
con.close();
for (Channel ch: player.getWorldServer().getChannels()) {
MapleMap map = ch.getMapFactory().getMap(mapId);
map.addMonsterSpawn(mob, mobTime, -1);
map.addAllMonsterSpawn(mob, mobTime, -1);
}
player.yellowMessage("Pmob created.");
} catch (SQLException e) {
e.printStackTrace();
player.dropMessage(5, "Failed to store pmob in the database.");
}
} else {
player.dropMessage(5, "You have entered an invalid mob id.");
}
}
}

View File

@@ -0,0 +1,114 @@
/*
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.gm4;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;
import net.server.channel.Channel;
import server.maps.MapleMap;
import client.command.Command;
import client.MapleCharacter;
import client.MapleClient;
import java.awt.Point;
import tools.DatabaseConnection;
import tools.Pair;
public class PmobRemoveCommand extends Command {
{
setDescription("");
}
@Override
public void execute(MapleClient c, String[] params) {
MapleCharacter player = c.getPlayer();
int mapId = player.getMapId();
int mobId = params.length > 0 ? Integer.parseInt(params[0]) : -1;
Point pos = player.getPosition();
int xpos = pos.x;
int ypos = pos.y;
List<Pair<Integer, Pair<Integer, Integer>>> toRemove = new LinkedList<>();
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps;
if (mobId > -1) {
String select = "SELECT * FROM plife WHERE world = ? AND map = ? AND type LIKE ? AND life = ?";
ps = con.prepareStatement(select, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
ps.setInt(1, player.getWorld());
ps.setInt(2, mapId);
ps.setString(3, "m");
ps.setInt(4, mobId);
} else {
String select = "SELECT * FROM plife WHERE world = ? AND map = ? AND type LIKE ? AND x >= ? AND x <= ? AND y >= ? AND y <= ?";
ps = con.prepareStatement(select, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
ps.setInt(1, player.getWorld());
ps.setInt(2, mapId);
ps.setString(3, "m");
ps.setInt(4, xpos - 50);
ps.setInt(5, xpos + 50);
ps.setInt(6, ypos - 50);
ps.setInt(7, ypos + 50);
}
ResultSet rs = ps.executeQuery();
while (true) {
rs.beforeFirst();
if (!rs.next()) {
break;
}
toRemove.add(new Pair<>(rs.getInt("life"), new Pair<>(rs.getInt("x"), rs.getInt("y"))));
rs.deleteRow();
}
rs.close();
ps.close();
con.close();
} catch (SQLException e) {
e.printStackTrace();
player.dropMessage(5, "Failed to remove pmob from the database.");
}
if (!toRemove.isEmpty()) {
for (Channel ch: player.getWorldServer().getChannels()) {
MapleMap map = ch.getMapFactory().getMap(mapId);
for (Pair<Integer, Pair<Integer, Integer>> r : toRemove) {
map.removeMonsterSpawn(r.getLeft(), r.getRight().getLeft(), r.getRight().getRight());
map.removeAllMonsterSpawn(r.getLeft(), r.getRight().getLeft(), r.getRight().getRight());
}
}
}
player.yellowMessage("Cleared " + toRemove.size() + " pmob placements.");
}
}

View File

@@ -0,0 +1,112 @@
/*
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.gm4;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import net.server.channel.Channel;
import server.life.MapleLifeFactory;
import server.life.MapleNPC;
import client.command.Command;
import client.MapleCharacter;
import client.MapleClient;
import java.awt.Point;
import server.maps.MapleMap;
import tools.DatabaseConnection;
import tools.MaplePacketCreator;
public class PnpcCommand extends Command {
{
setDescription("");
}
@Override
public void execute(MapleClient c, String[] params) {
MapleCharacter player = c.getPlayer();
if (params.length < 1) {
player.yellowMessage("Syntax: !pnpc <npcid>");
return;
}
// command suggestion thanks to HighKey21, none, bibiko94 (TAYAMO), asafgb
int mapId = player.getMapId();
int npcId = Integer.parseInt(params[0]);
if (player.getMap().containsNPC(npcId)) {
player.dropMessage(5, "This map already contains the specified NPC.");
return;
}
MapleNPC npc = MapleLifeFactory.getNPC(npcId);
Point checkpos = player.getMap().getGroundBelow(player.getPosition());
int xpos = checkpos.x;
int ypos = checkpos.y;
int fh = player.getMap().getFootholds().findBelow(checkpos).getId();
if (npc != null && !npc.getName().equals("MISSINGNO")) {
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("INSERT INTO plife ( life, f, fh, cy, rx0, rx1, type, x, y, world, map, mobtime, hide ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )");
ps.setInt(1, npcId);
ps.setInt(2, 0);
ps.setInt(3, fh);
ps.setInt(4, ypos);
ps.setInt(5, xpos + 50);
ps.setInt(6, xpos - 50);
ps.setString(7, "n");
ps.setInt(8, xpos);
ps.setInt(9, ypos);
ps.setInt(10, player.getWorld());
ps.setInt(11, mapId);
ps.setInt(12, -1);
ps.setInt(13, 0);
ps.executeUpdate();
ps.close();
con.close();
for (Channel ch: player.getWorldServer().getChannels()) {
npc = MapleLifeFactory.getNPC(npcId);
npc.setPosition(checkpos);
npc.setCy(ypos);
npc.setRx0(xpos + 50);
npc.setRx1(xpos - 50);
npc.setFh(fh);
MapleMap map = ch.getMapFactory().getMap(mapId);
map.addMapObject(npc);
map.broadcastMessage(MaplePacketCreator.spawnNPC(npc));
}
player.yellowMessage("Pnpc created.");
} catch (SQLException e) {
e.printStackTrace();
player.dropMessage(5, "Failed to store pNPC in the database.");
}
} else {
player.dropMessage(5, "You have entered an invalid NPC id.");
}
}
}

View File

@@ -0,0 +1,113 @@
/*
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.gm4;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import net.server.channel.Channel;
import client.command.Command;
import client.MapleCharacter;
import client.MapleClient;
import java.awt.Point;
import java.sql.ResultSet;
import java.util.LinkedList;
import java.util.List;
import server.maps.MapleMap;
import tools.DatabaseConnection;
import tools.Pair;
public class PnpcRemoveCommand extends Command {
{
setDescription("");
}
@Override
public void execute(MapleClient c, String[] params) {
MapleCharacter player = c.getPlayer();
int mapId = player.getMapId();
int npcId = params.length > 0 ? Integer.parseInt(params[0]) : -1;
Point pos = player.getPosition();
int xpos = pos.x;
int ypos = pos.y;
List<Pair<Integer, Pair<Integer, Integer>>> toRemove = new LinkedList<>();
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps;
if (npcId > -1) {
String select = "SELECT * FROM plife WHERE world = ? AND map = ? AND type LIKE ? AND life = ?";
ps = con.prepareStatement(select, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
ps.setInt(1, player.getWorld());
ps.setInt(2, mapId);
ps.setString(3, "n");
ps.setInt(4, npcId);
} else {
String select = "SELECT * FROM plife WHERE world = ? AND map = ? AND type LIKE ? AND x >= ? AND x <= ? AND y >= ? AND y <= ?";
ps = con.prepareStatement(select, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
ps.setInt(1, player.getWorld());
ps.setInt(2, mapId);
ps.setString(3, "n");
ps.setInt(4, xpos - 50);
ps.setInt(5, xpos + 50);
ps.setInt(6, ypos - 50);
ps.setInt(7, ypos + 50);
}
ResultSet rs = ps.executeQuery();
while (true) {
rs.beforeFirst();
if (!rs.next()) {
break;
}
toRemove.add(new Pair<>(rs.getInt("life"), new Pair<>(rs.getInt("x"), rs.getInt("y"))));
rs.deleteRow();
}
rs.close();
ps.close();
con.close();
} catch (SQLException e) {
e.printStackTrace();
player.dropMessage(5, "Failed to remove pNPC from the database.");
}
if (!toRemove.isEmpty()) {
for (Channel ch: player.getWorldServer().getChannels()) {
MapleMap map = ch.getMapFactory().getMap(mapId);
for (Pair<Integer, Pair<Integer, Integer>> r : toRemove) {
map.destroyNPC(r.getLeft());
}
}
}
player.yellowMessage("Cleared " + toRemove.size() + " pNPC placements.");
}
}