/* 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.io.File; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.Arrays; import java.util.List; import constants.ParanoiaConstants; import constants.ServerConstants; import paranoia.BlacklistHandler; import provider.MapleData; import provider.MapleDataProviderFactory; import net.server.Channel; import net.server.Server; import scripting.npc.NPCScriptManager; import server.MapleItemInformationProvider; import server.MapleShopFactory; import server.life.MapleLifeFactory; import server.life.MapleMonster; import server.maps.MapleMap; import server.maps.MapleMapObject; import server.maps.MapleMapObjectType; import tools.DatabaseConnection; import tools.MapleLogger; import tools.MaplePacketCreator; import client.IItem; import client.ISkill; import client.Item; import client.MapleCharacter; import client.MapleClient; import client.MapleInventoryType; import client.MapleJob; import client.MapleStat; import client.SkillFactory; /** * @author Aaron Weiss */ public class GMCommands extends EnumeratedCommands { private static final int gmLevel = 3; 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(); Server serv = Server.getInstance(); MapleCharacter victim; // For commands with targets. ResultSet rs; // For commands with MySQL results. try { Command command = Command.valueOf(sub[0]); switch (command) { default: // chr.yellowMessage("Command: " + heading + sub[0] + ": does not exist."); return false; case ap: if (sub.length > 2) { victim = cserv.getPlayerStorage().getCharacterByName(sub[1]); victim.setRemainingAp(Integer.parseInt(sub[2])); victim.updateSingleStat(MapleStat.AVAILABLEAP, Integer.parseInt(sub[2])); } else if (sub.length == 2) { chr.setRemainingAp(Integer.parseInt(sub[1])); chr.updateSingleStat(MapleStat.AVAILABLEAP, Integer.parseInt(sub[1])); } else { chr.message("Usage: !ap number || !ap playerName number"); } break; case ban: if (sub.length >= 3) { victim = cserv.getPlayerStorage().getCharacterByName(sub[1]); String reason = chr.getName() + " banned " + victim.getName() + ": " + joinStringFrom(sub, 2);; if (victim != null) { String ip = victim.getClient().getSession().getRemoteAddress().toString().split(":")[0]; chr.dropMessage(victim.getName() + "'s IP was " + ip + "."); // print the IP just for the banner. serv.broadcastMessage(chr.getWorld(), MaplePacketCreator.serverNotice(6, victim.getName() + " has been banned for " + reason + ".")); reason += " (IP: " + ip + ")"; // Add the IP afterward, no need to share that with everyone. victim.ban(reason); } else { if (MapleCharacter.ban(sub[1], reason, false)) { chr.dropMessage("Successfully banned " + sub[1] + "."); } else { chr.dropMessage("Failed to ban " + sub[1] + "."); } } } else { chr.dropMessage("Usage: !ban playerName reason"); } case blacklist: if (ParanoiaConstants.ALLOW_BLACKLIST_COMMAND && ParanoiaConstants.ENABLE_BLACKLISTING) { if (sub.length >= 2) { victim = cserv.getPlayerStorage().getCharacterByName(sub[1]); if (BlacklistHandler.isBlacklisted(victim.getAccountID())) { chr.dropMessage(victim.getName() + " is already blacklisted."); } else { BlacklistHandler.addToBlacklist(victim.getAccountID()); chr.message("Done."); } } else { chr.dropMessage("!blacklist playerName"); } } else if (!ParanoiaConstants.ENABLE_BLACKLISTING) { chr.dropMessage("Blacklisting is disabled on the server."); } else if (!ParanoiaConstants.ALLOW_BLACKLIST_COMMAND) { chr.dropMessage("Blacklisting users is forbidden by the server."); } break; case buff: if (sub.length == 2) { victim = cserv.getPlayerStorage().getCharacterByName(sub[1]); final int[] array = {9001000, 9101002, 9101003, 9101008, 2001002, 1101007, 1005, 2301003, 5121009, 1111002, 4111001, 4111002, 4211003, 4211005, 1321000, 2321004, 3121002}; for (int i : array) { SkillFactory.getSkill(i).getEffect(SkillFactory.getSkill(i).getMaxLevel()).applyTo(victim); } } else if (sub.length == 1) { final int[] array = {9001000, 9101002, 9101003, 9101008, 2001002, 1101007, 1005, 2301003, 5121009, 1111002, 4111001, 4111002, 4211003, 4211005, 1321000, 2321004, 3121002}; for (int i : array) { SkillFactory.getSkill(i).getEffect(SkillFactory.getSkill(i).getMaxLevel()).applyTo(chr); } } else { chr.message("Usage: !buff || !buff playerName"); } break; case dc: if (sub.length == 2) { victim = cserv.getPlayerStorage().getCharacterByName(sub[1]); } else { chr.message("Usage: !dc playerName"); } break; case dispose: if (sub.length == 2) { victim = cserv.getPlayerStorage().getCharacterByName(sub[1]); NPCScriptManager.getInstance().dispose(victim.getClient()); victim.getClient().announce(MaplePacketCreator.enableActions()); chr.message("Done."); } else { chr.message("Usage: !dispose playerName"); } break; case drop: int itemId = Integer.parseInt(sub[1]); short quantity = 1; try { quantity = Short.parseShort(sub[2]); } catch (Exception e) { } IItem toDrop; if (MapleItemInformationProvider.getInstance().getInventoryType(itemId) == MapleInventoryType.EQUIP) { toDrop = MapleItemInformationProvider.getInstance().getEquipById(itemId); } else { toDrop = new Item(itemId, (byte) 0, quantity); } c.getPlayer().getMap().spawnItemDrop(c.getPlayer(), c.getPlayer(), toDrop, c.getPlayer().getPosition(), true, true); break; case fame: if (sub.length > 2) { victim = cserv.getPlayerStorage().getCharacterByName(sub[1]); victim.setFame(Integer.parseInt(sub[2])); victim.updateSingleStat(MapleStat.FAME, Integer.parseInt(sub[2])); } else if (sub.length == 2) { chr.setFame(Integer.parseInt(sub[1])); chr.updateSingleStat(MapleStat.FAME, Integer.parseInt(sub[1])); } else { chr.message("Usage: !fame number || !fame playerName number"); } break; case gmshop: MapleShopFactory.getInstance().getShop(1337).sendShop(c); break; case heal: if (sub.length == 2) { victim = cserv.getPlayerStorage().getCharacterByName(sub[1]); victim.setHpMp(30000); } else if (sub.length == 1) { chr.setHpMp(30000); } else { chr.message("Usage: !heal || !heal playerName"); } break; case help: if (sub.length > 1) { if (sub[1].equalsIgnoreCase("gm")) { if (sub.length > 2 && ServerConstants.PAGINATE_HELP) { getHelp(Integer.parseInt(sub[2]), chr); } else { getHelp(chr); } break; } else { return false; } } else { return false; } case job: if (sub.length > 2) { victim = cserv.getPlayerStorage().getCharacterByName(sub[1]); victim.changeJob(MapleJob.getById(Integer.parseInt(sub[2]))); victim.equipChanged(); } else if (sub.length == 2) { chr.changeJob(MapleJob.getById(Integer.parseInt(sub[1]))); chr.equipChanged(); } else { chr.message("Usage: !job number || !job playerName number"); } break; case kill: if (sub.length == 2) { cserv.getPlayerStorage().getCharacterByName(sub[1]).setHpMp(0); } else { chr.message("Usage: !kill playerName"); } break; case killall: List<MapleMapObject> monsters = chr.getMap().getMapObjectsInRange(chr.getPosition(), Double.POSITIVE_INFINITY, Arrays.asList(MapleMapObjectType.MONSTER)); MapleMap map = chr.getMap(); for (MapleMapObject monstermo : monsters) { MapleMonster monster = (MapleMonster) monstermo; map.killMonster(monster, chr, true); monster.giveExpToCharacter(chr, monster.getExp() * c.getPlayer().getExpRate(), true, 1); } chr.message("Killed " + monsters.size() + " monsters."); break; case level: if (sub.length > 2) { victim = cserv.getPlayerStorage().getCharacterByName(sub[1]); victim.setLevel(Integer.parseInt(sub[2])); victim.setExp(0); victim.updateSingleStat(MapleStat.LEVEL, Integer.parseInt(sub[2])); victim.updateSingleStat(MapleStat.EXP, 0); } else if (sub.length == 2) { chr.setLevel(Integer.parseInt(sub[1])); chr.setExp(0); chr.updateSingleStat(MapleStat.LEVEL, Integer.parseInt(sub[1])); chr.updateSingleStat(MapleStat.EXP, 0); } else { chr.message("Usage: !level number || !level playerName number"); } break; case levelup: if (sub.length == 2) { victim = cserv.getPlayerStorage().getCharacterByName(sub[1]); victim.levelUp(true); } else if (sub.length == 1) { chr.levelUp(true); } else { chr.message("Usage: !levelup || !levelup playerName"); } break; case map: if (sub.length > 2) { victim = cserv.getPlayerStorage().getCharacterByName(sub[1]); victim.changeMap(Integer.parseInt(sub[2])); } else if (sub.length == 2) { chr.changeMap(Integer.parseInt(sub[1])); } else { chr.message("Usage: !map number || !map playerName number"); } break; case maxskills: if (sub.length == 2) { victim = cserv.getPlayerStorage().getCharacterByName(sub[1]); for (MapleData skill_ : MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "String.wz")).getData("Skill.img").getChildren()) { try { ISkill skill = SkillFactory.getSkill(Integer.parseInt(skill_.getName())); victim.changeSkillLevel(skill, (byte) skill.getMaxLevel(), skill.getMaxLevel(), -1); } catch (NumberFormatException nfe) { break; } catch (NullPointerException npe) { continue; } } } else if (sub.length == 1) { for (MapleData skill_ : MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "String.wz")).getData("Skill.img").getChildren()) { try { ISkill skill = SkillFactory.getSkill(Integer.parseInt(skill_.getName())); chr.changeSkillLevel(skill, (byte) skill.getMaxLevel(), skill.getMaxLevel(), -1); } catch (NumberFormatException nfe) { break; } catch (NullPointerException npe) { continue; } } } else { chr.message("Usage: !maxskills || !maxskills playerName"); } break; case maxstats: if (sub.length == 2) { final String[] s = {"setall", sub[1], String.valueOf(Short.MAX_VALUE)}; execute(c, s, heading); victim = cserv.getPlayerStorage().getCharacterByName(sub[1]); victim.setLevel(255); victim.setFame(Short.MAX_VALUE); victim.setMaxHp(30000); victim.setMaxMp(30000); victim.updateSingleStat(MapleStat.LEVEL, 255); victim.updateSingleStat(MapleStat.FAME, Short.MAX_VALUE); victim.updateSingleStat(MapleStat.MAXHP, 30000); victim.updateSingleStat(MapleStat.MAXMP, 30000); } else if (sub.length == 1) { final String[] s = {"setall", String.valueOf(Short.MAX_VALUE)}; execute(c, s, heading); chr.setLevel(255); chr.setFame(Short.MAX_VALUE); chr.setMaxHp(30000); chr.setMaxMp(30000); chr.updateSingleStat(MapleStat.LEVEL, 255); chr.updateSingleStat(MapleStat.FAME, Short.MAX_VALUE); chr.updateSingleStat(MapleStat.MAXHP, 30000); chr.updateSingleStat(MapleStat.MAXMP, 30000); } else { chr.message("Usage: !maxstats || !maxstats playerName"); } break; case mesos: if (sub.length > 2) { victim = cserv.getPlayerStorage().getCharacterByName(sub[1]); victim.gainMeso(Integer.parseInt(sub[2]), true); } else if (sub.length == 2) { chr.gainMeso(Integer.parseInt(sub[1]), true); } else { chr.message("Usage: !mesos number || !mesos playerName number"); } break; case pap: chr.getMap().spawnMonsterOnGroudBelow(MapleLifeFactory.getMonster(8500001), chr.getPosition()); break; case popup: if (sub.length >= 3) { victim = cserv.getPlayerStorage().getCharacterByName(sub[1]); victim.dropMessage(1, joinStringFrom(sub, 2)); } else { chr.message("Usage: !popup playerName message"); } break; case pianus: chr.getMap().spawnMonsterOnGroudBelow(MapleLifeFactory.getMonster(8510000), chr.getPosition()); break; case notice: Server.getInstance().broadcastMessage(chr.getWorld(), MaplePacketCreator.serverNotice(6, "[Notice] " + joinStringFrom(sub, 1))); break; case saveall: for (Channel chan : Server.getInstance().getAllChannels()) { for (MapleCharacter plyrs : chan.getPlayerStorage().getAllCharacters()) { plyrs.saveToDB(true); } } chr.message("Save Complete."); break; case servermessage: c.getWorldServer().setServerMessage(joinStringFrom(sub, 1)); break; case setall: if (sub.length > 2) { victim = cserv.getPlayerStorage().getCharacterByName(sub[1]); final int x = Short.parseShort(sub[2]); victim.setStr(x); victim.setDex(x); victim.setInt(x); victim.setLuk(x); victim.updateSingleStat(MapleStat.STR, x); victim.updateSingleStat(MapleStat.DEX, x); victim.updateSingleStat(MapleStat.INT, x); victim.updateSingleStat(MapleStat.LUK, x); } else if (sub.length == 2) { final int x = Short.parseShort(sub[1]); chr.setStr(x); chr.setDex(x); chr.setInt(x); chr.setLuk(x); chr.updateSingleStat(MapleStat.STR, x); chr.updateSingleStat(MapleStat.DEX, x); chr.updateSingleStat(MapleStat.INT, x); chr.updateSingleStat(MapleStat.LUK, x); } else { chr.message("Usage: !setall number || !setall playerName number"); } break; case shop: if (sub.length == 2) { MapleShopFactory.getInstance().getShop(Integer.parseInt(sub[1])).sendShop(c); } else { chr.message("Usage: !shop number"); } break; case sp: if (sub.length > 2) { victim = cserv.getPlayerStorage().getCharacterByName(sub[1]); victim.setRemainingSp(Integer.parseInt(sub[2])); victim.updateSingleStat(MapleStat.AVAILABLESP, Integer.parseInt(sub[2])); } else if (sub.length == 2) { chr.setRemainingSp(Integer.parseInt(sub[1])); chr.updateSingleStat(MapleStat.AVAILABLESP, Integer.parseInt(sub[1])); } else { chr.message("Usage: !ap number || !ap playerName number"); } break; case spawn: if (sub.length > 2) { for (int i = 0; i < Integer.parseInt(sub[2]); i++) { chr.getMap().spawnMonsterOnGroudBelow(MapleLifeFactory.getMonster(Integer.parseInt(sub[1])), chr.getPosition()); } } else { chr.getMap().spawnMonsterOnGroudBelow(MapleLifeFactory.getMonster(Integer.parseInt(sub[1])), chr.getPosition()); } break; case speak: if (sub.length == 2) { NPCScriptManager.getInstance().start(c, Integer.parseInt(sub[1]), null, null); } else { chr.message("Usage: !speak number"); } break; case texttype: chr.toggleGMText(); chr.message("Your chat messages are now " + (chr.getGMText() ? "white." : "black.")); break; case unban: try { PreparedStatement p = DatabaseConnection.getConnection().prepareStatement("UPDATE accounts SET banned = -1 WHERE id = " + MapleCharacter.getIdByName(sub[1])); p.executeUpdate(); p.close(); } catch (Exception e) { chr.message("Failed to unban " + sub[1]); } chr.message("Unbanned " + sub[1]); break; } if (ServerConstants.USE_PARANOIA && ParanoiaConstants.PARANOIA_COMMAND_LOGGER && ParanoiaConstants.LOG_GM_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; } } protected static void getHelp(MapleCharacter chr) { GMCommands.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 GMCommands 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 GMCommands 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 { ap("Sets the AP of you or a victim."), ban("Bans a user."), blacklist("Adds a user to the Paranoia blacklist."), buff("Applies buffs to you or a victim."), dc("Disconnects a victim."), dispose("Fixes NPC issues for you or a victim."), drop("Drops an item at your feet."), fame("Sets the fame of you or a victim."), gmshop("Opens up the GM Shop."), heal("Heals you or a victim."), help("Displays this help message."), job("Sets the job of you or a victim."), kill("Kills a victim."), killall("Kills all monsters on map."), level("Sets the level of you or a victim."), levelup("Causes you or a victim to level up."), map("Changes the map of you or a victim."), maxskills("Maxes the skills of you or a victim."), maxstats("Maxes the stats of you or a victim."), mesos("Gives mesos to you or a victim."), pap("Summons Papulatus at your position."), popup("Create a popup window for a victim."), pianus("Summons Pianus at your position."), notice("Makes a server-wide notice anonymously."), saveall("Saves all players online!"), servermessage("Sets the servermessage."), setall("Sets the values of all of the stats of you or a victim."), shop("Opens up a shop by ID."), sp("Sets the SP of you or a victim."), spawn("Spawns a mob at your position."), speak("Opens up a conversation with an NPC."), texttype("Toggles your text type."), unban("Unbans a user."); private final String description; private Command(String description){ this.description = description; } public String getDescription() { return this.description; } } }