/*
OrpheusMS: MapleStory Private Server based on OdinMS
Copyright (C) 2012 Aaron Weiss <aaron@deviant-core.net>
Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
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 client.command;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import paranoia.BlacklistHandler;
import paranoia.ParanoiaInformation;
import paranoia.ParanoiaInformationHandler;
import constants.ParanoiaConstants;
import constants.ServerConstants;
import server.TimerManager;
import server.life.MapleLifeFactory;
import server.life.MapleMonster;
import server.life.MapleNPC;
import tools.DatabaseConnection;
import tools.MapleLogger;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
import net.server.Channel;
import net.server.Server;
import client.MapleCharacter;
import client.MapleClient;
/**
* @author Aaron Weiss
*/
public class DeveloperCommands extends EnumeratedCommands {
private static SeekableLittleEndianAccessor slea;
private static final int gmLevel = 4;
private static final char heading = '!';
@SuppressWarnings("unused")
public static boolean execute(MapleClient c, String[] sub, char heading) {
MapleCharacter chr = c.getPlayer();
Channel cserv = c.getChannelServer();
MapleCharacter victim; // For commands with targets.
ResultSet rs; // For commands with MySQL results.
MapleNPC npc;
int npcId = 0;
int mobTime = 0;
int xpos = 0;
int ypos = 0;
int fh = 0;
try {
Command command = Command.valueOf(sub[0]);
switch (command) {
default:
// chr.yellowMessage("Command: " + heading + sub[0] + ": does not exist.");
return false;
case coords:
xpos = chr.getPosition().x;
ypos = chr.getPosition().y;
fh = chr.getMap().getFootholds().findBelow(chr.getPosition()).getId();
chr.dropMessage("Position: (" + xpos + ", " + ypos + ")");
chr.dropMessage("Foothold ID: " + fh);
break;
case droprate:
c.getWorldServer().setDropRate(Integer.parseInt(sub[1]));
for (MapleCharacter mc : c.getWorldServer().getPlayerStorage().getAllCharacters()) {
mc.setRates();
}
Server.getInstance().broadcastMessage(chr.getWorld(), MaplePacketCreator.serverNotice(1, "[Notice] The drop rate has changed to " + sub[1] + "."));
chr.message("Done.");
break;
case exprate:
c.getWorldServer().setExpRate(Integer.parseInt(sub[1]));
for (MapleCharacter mc : c.getWorldServer().getPlayerStorage().getAllCharacters()) {
mc.setRates();
}
Server.getInstance().broadcastMessage(chr.getWorld(), MaplePacketCreator.serverNotice(1, "[Notice] The experience rate has changed to " + sub[1] + "."));
chr.message("Done.");
break;
case gc:
System.gc();
break;
case help:
if (sub.length > 1) {
if (sub[1].equalsIgnoreCase("dev")) {
if (sub.length > 2 && ServerConstants.PAGINATE_HELP) {
getHelp(Integer.parseInt(sub[2]), chr);
} else {
getHelp(chr);
}
break;
} else {
return false;
}
} else {
return false;
}
case horntail:
chr.getMap().spawnMonsterOnGroudBelow(MapleLifeFactory.getMonster(8810026), chr.getPosition());
break;
case mesorate:
c.getWorldServer().setMesoRate(Integer.parseInt(sub[1]));
for (MapleCharacter mc : c.getWorldServer().getPlayerStorage().getAllCharacters()) {
mc.setRates();
}
Server.getInstance().broadcastMessage(chr.getWorld(), MaplePacketCreator.serverNotice(1, "[Notice] The meso rate has changed to " + sub[1] + "."));
chr.message("Done.");
break;
case npc:
npc = MapleLifeFactory.getNPC(Integer.parseInt(sub[1]));
if (npc != null) {
npc.setPosition(chr.getPosition());
npc.setCy(chr.getPosition().y);
npc.setRx0(chr.getPosition().x + 50);
npc.setRx1(chr.getPosition().x - 50);
npc.setFh(chr.getMap().getFootholds().findBelow(c.getPlayer().getPosition()).getId());
chr.getMap().addMapObject(npc);
chr.getMap().broadcastMessage(MaplePacketCreator.spawnNPC(npc));
}
break;
case packet:
chr.getMap().broadcastMessage(MaplePacketCreator.customPacket(joinStringFrom(sub, 1)));
break;
case paranoia:
if (ParanoiaConstants.ALLOW_QUERY_COMMAND) {
if (sub.length > 1) {
if (sub[1].equalsIgnoreCase("help")) {
chr.dropMessage("Paranoia Information Querying Help");
for (ParanoiaInformation pi : ParanoiaInformation.values()) {
chr.dropMessage(pi.name() + " - " + pi.explain());
}
} else {
chr.dropMessage(ParanoiaInformationHandler.getFormattedValue(sub[1]));
}
} else {
chr.dropMessage("Usage: !paranoia value || !paranoia help");
}
} else {
chr.dropMessage("Paranoia Information Querying is forbidden by the server.");
}
break;
case pinkbean:
chr.getMap().spawnMonsterOnGroudBelow(MapleLifeFactory.getMonster(8820009), chr.getPosition());
break;
case playernpc:
if (sub.length > 2) {
chr.playerNPC(c.getChannelServer().getPlayerStorage().getCharacterByName(sub[1]), Integer.parseInt(sub[2]));
} else if (sub.length == 2) {
chr.playerNPC(chr, Integer.parseInt(sub[1]));
} else {
chr.dropMessage("Usage: !playernpc characterName scriptId || !playernpc scriptId");
}
case pmob:
npcId = Integer.parseInt(sub[1]);
mobTime = Integer.parseInt(sub[2]);
xpos = chr.getPosition().x;
ypos = chr.getPosition().y;
fh = chr.getMap().getFootholds().findBelow(chr.getPosition()).getId();
if (sub[2] == null) {
mobTime = 0;
}
MapleMonster mob = MapleLifeFactory.getMonster(npcId);
if (mob != null && !mob.getName().equals("MISSINGNO")) {
mob.setPosition(chr.getPosition());
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 spawns ( idd, f, fh, cy, rx0, rx1, type, x, y, mid, mobtime ) 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, "m");
ps.setInt(8, xpos);
ps.setInt(9, ypos);
ps.setInt(10, chr.getMapId());
ps.setInt(11, mobTime);
ps.executeUpdate();
} catch (SQLException e) {
chr.dropMessage("Failed to save mob to the database.");
}
chr.getMap().addMonsterSpawn(mob, mobTime, 0);
} else {
chr.dropMessage("You have entered an invalid mob ID.");
}
break;
case pnpc:
npcId = Integer.parseInt(sub[1]);
npc = MapleLifeFactory.getNPC(npcId);
xpos = chr.getPosition().x;
ypos = chr.getPosition().y;
fh = chr.getMap().getFootholds().findBelow(chr.getPosition()).getId();
if (npc != null && !npc.getName().equals("MISSINGNO")) {
npc.setPosition(chr.getPosition());
npc.setCy(ypos);
npc.setRx0(xpos + 50);
npc.setRx1(xpos - 50);
npc.setFh(fh);
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("INSERT INTO spawns ( idd, f, fh, cy, rx0, rx1, type, x, y, mid ) 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, chr.getMapId());
ps.executeUpdate();
} catch (SQLException e) {
chr.dropMessage("Failed to save NPC to the database.");
}
chr.getMap().addMapObject(npc);
chr.getMap().broadcastMessage(MaplePacketCreator.spawnNPC(npc));
} else {
chr.dropMessage("You have entered an invalid NPC id.");
}
break;
case reloadblacklist:
if (ServerConstants.USE_PARANOIA && ParanoiaConstants.ENABLE_BLACKLISTING && ParanoiaConstants.ALLOW_RELOADBLACKLIST_COMMAND) {
BlacklistHandler.reloadBlacklist();
chr.message("Done.");
} else if (!ParanoiaConstants.ENABLE_BLACKLISTING) {
chr.dropMessage("Blacklisting is disabled on the server.");
} else if (!ParanoiaConstants.ALLOW_RELOADBLACKLIST_COMMAND) {
chr.dropMessage("Reloading blacklist is forbidden by the server.");
}
break;
case say:
if (sub.length > 2) {
victim = cserv.getPlayerStorage().getCharacterByName(sub[1]);
final String s = joinStringFrom(sub, 2);
victim.getMap().broadcastMessage(MaplePacketCreator.getChatText(victim.getId(), s, victim.isGM(), slea.readByte()));
} else {
chr.message("Usage: !say playerName multi-word message");
}
break;
case shutdown:
if (sub.length == 2) {
int time = 60000;
if (sub[1].equalsIgnoreCase("now")) {
time = 1;
} else {
time *= Integer.parseInt(sub[1]);
}
TimerManager.getInstance().schedule(Server.getInstance().shutdown(false), time);
} else {
chr.message("Usage: !shutdown time || !shutdown now");
}
break;
case sql:
if (sub[1] == "true") {
String name = sub[1];
final String query = joinStringFrom(sub, 2);
try {
PreparedStatement ps = DatabaseConnection.getConnection().prepareStatement(query);
rs = ps.executeQuery();
while (rs.next()) {
chr.dropMessage(String.valueOf(rs.getObject(name)));
}
rs.close();
ps.close();
} catch (SQLException e) {
chr.message("Query Failed: " + query);
}
} else {
final String query = joinStringFrom(sub, 1);
try {
PreparedStatement ps = DatabaseConnection.getConnection().prepareStatement(query);
ps.executeUpdate();
ps.close();
chr.message("Completed: " + query);
} catch (SQLException e) {
chr.message("Query Failed: " + query);
}
}
break;
case updaterankings:
updateRankings();
break;
case zakum:
chr.getMap().spawnFakeMonsterOnGroundBelow(MapleLifeFactory.getMonster(8800000), chr.getPosition());
for (int x = 8800003; x < 8800011; x++) {
chr.getMap().spawnMonsterOnGroudBelow(MapleLifeFactory.getMonster(x), chr.getPosition());
}
break;
}
if (ServerConstants.USE_PARANOIA && ParanoiaConstants.PARANOIA_COMMAND_LOGGER && ParanoiaConstants.LOG_DEVELOPER_COMMANDS) {
MapleLogger.printFormatted(MapleLogger.PARANOIA_COMMAND, "[" + c.getPlayer().getName() + "] Used " + heading + sub[0] + ((sub.length > 1) ? " with parameters: " + joinStringFrom(sub, 1) : "."));
}
return true;
} catch (IllegalArgumentException e) {
return false;
}
}
public static void setSLEA(SeekableLittleEndianAccessor slea) {
DeveloperCommands.slea = slea;
}
public static void updateRankings() {
try {
Connection con = (Connection) DatabaseConnection.getConnection();
PreparedStatement ps;
ResultSet rs;
ps = (PreparedStatement) con.prepareStatement("SELECT id, rank, rankMove FROM characters WHERE gm < 2 ORDER BY rebirths DESC, level DESC, name DESC");
rs = ps.executeQuery();
int n = 1;
while (rs.next()) {
ps = (PreparedStatement) con.prepareStatement("UPDATE characters SET rank = ?, rankMove = ? WHERE id = ?");
ps.setInt(1, n);
ps.setInt(2, rs.getInt("rank") - n);
ps.setInt(3, rs.getInt("id"));
ps.executeUpdate();
n++;
}
} catch (SQLException e) {
MapleLogger.print(MapleLogger.EXCEPTION_CAUGHT, e);
}
}
protected static void getHelp(MapleCharacter chr) {
DeveloperCommands.getHelp(-1, chr);
}
protected static void getHelp(int page, MapleCharacter chr) {
int pageNumber = (int) (Command.values().length / ServerConstants.ENTRIES_PER_PAGE);
if (Command.values().length % ServerConstants.ENTRIES_PER_PAGE > 0) {
pageNumber++;
}
if (page <= 0 || pageNumber == 1) {
chr.dropMessage(ServerConstants.SERVER_NAME + "'s DeveloperCommands Help");
for (Command cmd : Command.values()) {
chr.dropMessage(heading + cmd.name() + " - " + cmd.getDescription());
}
} else {
if (page > pageNumber) {
page = pageNumber;
}
int lastPageEntry = (Command.values().length - Math.max(0, Command.values().length - (page * ServerConstants.ENTRIES_PER_PAGE)));
lastPageEntry -= 1;
chr.dropMessage(ServerConstants.SERVER_NAME + "'s DeveloperCommands Help (Page " + page + " / " + pageNumber + ")");
for (int i = lastPageEntry; i < lastPageEntry + ServerConstants.ENTRIES_PER_PAGE; i++) {
chr.dropMessage(heading + Command.values()[i].name() + " - " + Command.values()[i].getDescription());
}
}
}
public static int getRequiredStaffRank() {
return gmLevel;
}
public static char getHeading() {
return heading;
}
private static enum Command {
coords("Prints your current coordinates."),
droprate("Sets the server-wide drop rate."),
exprate("Sets the server-wide experience rate."),
gc("Runs the garbage collector."),
help("Displays this help message."),
horntail("Summons Horntail at your position."),
mesorate("Sets the server-wide meso rate."),
npc("Spawns an NPC at your position."),
packet("Executes a custom packet."),
paranoia("Gathers information about Paranoia."),
playernpc("Adds an NPC that mimics you or another player."),
pmob("Permanently spawns a mob at your position."),
pnpc("Permanently spawns an NPC at your position."),
pinkbean("Summons Pinkbean at your position."),
reloadblacklist("Reloads the Paranoia blacklist."),
say("Forces a victim to say something."),
shutdown("Shutdowns the server."),
sql("Executes an SQL query."),
updaterankings("Forces an update of the rankings."),
zakum("Summons Zakum at your position.");
private final String description;
private Command(String description){
this.description = description;
}
public String getDescription() {
return this.description;
}
}
}