MapleClient.java
上传用户:gwt600
上传日期:2021-06-03
资源大小:704k
文件大小:42k
源码类别:

游戏

开发平台:

Java

  1. /*
  2. This file is part of the OdinMS Maple Story Server
  3. Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc> 
  4. Matthias Butz <matze@odinms.de>
  5. Jan Christian Meyer <vimes@odinms.de>
  6. This program is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU Affero General Public License version 3
  8. as published by the Free Software Foundation. You may not use, modify
  9. or distribute this program under any other version of the
  10. GNU Affero General Public License.
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU Affero General Public License for more details.
  15. You should have received a copy of the GNU Affero General Public License
  16. along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17.  */
  18. package net.sf.odinms.client;
  19. import java.rmi.RemoteException;
  20. import java.sql.Connection;
  21. import java.sql.PreparedStatement;
  22. import java.sql.ResultSet;
  23. import java.sql.SQLException;
  24. import java.sql.Timestamp;
  25. import java.util.Calendar;
  26. import java.util.Collections;
  27. import java.net.InetAddress;
  28. import java.io.FileOutputStream;
  29. import java.text.SimpleDateFormat;
  30. import net.sf.odinms.net.channel.ChannelServer;
  31. import java.util.HashMap;
  32. import net.sf.odinms.net.MaplePacket;
  33. import java.util.HashSet;
  34. import java.util.Iterator;
  35. import java.util.LinkedList;
  36. import java.util.List;
  37. import java.util.Map;
  38. import java.util.Properties;
  39. import java.util.Set;
  40. import java.util.concurrent.ScheduledFuture;
  41. import javax.script.ScriptEngine;
  42. import net.sf.odinms.client.messages.MessageCallback;
  43. import net.sf.odinms.database.DatabaseConnection;
  44. import net.sf.odinms.database.DatabaseException;
  45. import net.sf.odinms.net.channel.ChannelServer;
  46. import net.sf.odinms.net.login.LoginServer;
  47. import net.sf.odinms.net.world.MapleMessengerCharacter;
  48. import net.sf.odinms.net.world.MaplePartyCharacter;
  49. import net.sf.odinms.net.world.PartyOperation;
  50. import net.sf.odinms.net.world.PlayerCoolDownValueHolder;
  51. import net.sf.odinms.net.world.guild.MapleGuildCharacter;
  52. import net.sf.odinms.net.world.remote.WorldChannelInterface;
  53. import net.sf.odinms.scripting.npc.NPCScriptManager;
  54. import net.sf.odinms.server.MapleInventoryManipulator;
  55. import net.sf.odinms.scripting.npc.NPCConversationManager;
  56. import net.sf.odinms.scripting.quest.QuestScriptManager;
  57. import net.sf.odinms.scripting.quest.QuestActionManager;
  58. import net.sf.odinms.server.PlayerInteraction.MaplePlayerShopItem;
  59. import net.sf.odinms.server.MapleTrade;
  60. import net.sf.odinms.server.PublicChatHandler;
  61. import net.sf.odinms.server.PlayerInteraction.HiredMerchant;
  62. import net.sf.odinms.server.PlayerInteraction.IPlayerInteractionManager;
  63. import net.sf.odinms.server.TimerManager;
  64. import net.sf.odinms.tools.IPAddressTool;
  65. import net.sf.odinms.tools.MapleAESOFB;
  66. import net.sf.odinms.tools.MaplePacketCreator;
  67. import net.sf.odinms.tools.Pair;
  68. import org.apache.mina.common.IoSession;
  69. import org.slf4j.Logger;
  70. import org.slf4j.LoggerFactory;
  71. public class MapleClient {
  72.     public static final int LOGIN_NOTLOGGEDIN = 0;
  73.     public static final int LOGIN_SERVER_TRANSITION = 1;
  74.     public static final int LOGIN_LOGGEDIN = 2;
  75.     public static final int LOGIN_WAITING = 3;
  76.     public static final String CLIENT_KEY = "CLIENT";
  77.     private static final Logger log = LoggerFactory.getLogger(MapleClient.class);
  78.     private MapleAESOFB send;
  79.     private MapleAESOFB receive;
  80.     private IoSession session;
  81.     private MapleCharacter player;
  82.     private int gmlevel;
  83.     private int channel = 1;
  84.     private int accId = 1;
  85.     private boolean loggedIn = false;
  86.     private boolean serverTransition = false;
  87.     private Calendar birthday = null;
  88.     private Calendar tempban = null;
  89.     private String accountName;
  90.     private String accountPass;
  91.     private int world;
  92.     private long lastPong;
  93.     private boolean gm = false;
  94.     private byte greason = 1;
  95.     private boolean guest;
  96.     private Map<Pair<MapleCharacter, Integer>, Integer> timesTalked = new HashMap<Pair<MapleCharacter, Integer>, Integer>(); //npcid, times
  97.     private Set<String> macs = new HashSet<String>();
  98.     private Map<String, ScriptEngine> engines = new HashMap<String, ScriptEngine>();
  99.     private ScheduledFuture<?> idleTask = null;
  100.     private int attemptedLogins = 0;
  101.     public MapleClient(MapleAESOFB send, MapleAESOFB receive, IoSession session) {
  102.         this.send = send;
  103.         this.receive = receive;
  104.         this.session = session;
  105.     }
  106.     public MapleAESOFB getReceiveCrypto() {
  107.         return receive;
  108.     }
  109.     public MapleAESOFB getSendCrypto() {
  110.         return send;
  111.     }
  112.     public synchronized IoSession getSession() {
  113.         return session;
  114.     }
  115.     public MapleCharacter getPlayer() {
  116.         return player;
  117.     }
  118.     public void setPlayer(MapleCharacter player) {
  119.         this.player = player;
  120.     }
  121.     public void sendCharList(int server) {
  122.         this.session.write(MaplePacketCreator.getCharList(this, server));
  123.     }
  124.     public List<MapleCharacter> loadCharacters(int serverId) { // TODO make this less costly zZz
  125.         List<MapleCharacter> chars = new LinkedList<MapleCharacter>();
  126.         for (CharNameAndId cni : loadCharactersInternal(serverId)) {
  127.             try {
  128.                 chars.add(MapleCharacter.loadCharFromDB(cni.id, this, false));
  129.             } catch (SQLException e) {
  130.                 log.error("载入角色失败", e);
  131.             }
  132.         }
  133.         return chars;
  134.     }
  135.     public List<String> loadCharacterNames(int serverId) {
  136.         List<String> chars = new LinkedList<String>();
  137.         for (CharNameAndId cni : loadCharactersInternal(serverId)) {
  138.             chars.add(cni.name);
  139.         }
  140.         return chars;
  141.     }
  142.     private List<CharNameAndId> loadCharactersInternal(int serverId) {
  143.         Connection con = DatabaseConnection.getConnection();
  144.         PreparedStatement ps;
  145.         List<CharNameAndId> chars = new LinkedList<CharNameAndId>();
  146.         try {
  147.             ps = con.prepareStatement("SELECT id, name FROM characters WHERE accountid = ? AND world = ?");
  148.             ps.setInt(1, this.accId);
  149.             ps.setInt(2, serverId);
  150.             ResultSet rs = ps.executeQuery();
  151.             while (rs.next()) {
  152.                 chars.add(new CharNameAndId(rs.getString("name"), rs.getInt("id")));
  153.             }
  154.             rs.close();
  155.             ps.close();
  156.         } catch (SQLException e) {
  157.             log.error("THROW", e);
  158.         }
  159.         return chars;
  160.     }
  161.     public boolean isLoggedIn() {
  162.         return loggedIn;
  163.     }
  164.     private Calendar getTempBanCalendar(ResultSet rs) throws SQLException {
  165.         Calendar lTempban = Calendar.getInstance();
  166.         long blubb = rs.getLong("tempban");
  167.         if (blubb == 0) { // basically if timestamp in db is 0000-00-00
  168.             lTempban.setTimeInMillis(0);
  169.             return lTempban;
  170.         }
  171.         Calendar today = Calendar.getInstance();
  172.         lTempban.setTimeInMillis(rs.getTimestamp("tempban").getTime());
  173.         if (today.getTimeInMillis() < lTempban.getTimeInMillis()) {
  174.             return lTempban;
  175.         }
  176.         lTempban.setTimeInMillis(0);
  177.         return lTempban;
  178.     }
  179.     public Calendar getTempBanCalendar() {
  180.         return tempban;
  181.     }
  182.     public byte getBanReason() {
  183.         return greason;
  184.     }
  185.     public int getGMLevel() {
  186.         return this.gmlevel;
  187.     }
  188.     public void changeChannel(int vipchannel) {
  189. int channel = vipchannel;
  190. if (!getPlayer().isAlive()) {
  191. getSession().write(MaplePacketCreator.enableActions());
  192. return;
  193. }
  194. String ip = ChannelServer.getInstance(getChannel()).getIP(channel);
  195. String[] socket = ip.split(":");
  196. if (getPlayer().getTrade() != null) {
  197. MapleTrade.cancelTrade(getPlayer());
  198. }
  199.         if (!getPlayer().getDiseases().isEmpty()) {
  200.  getPlayer().cancelAllDebuffs();
  201. }
  202. getPlayer().cancelMagicDoor();
  203. if (getPlayer().getBuffedValue(MapleBuffStat.MONSTER_RIDING) != null) {
  204. getPlayer().cancelEffectFromBuffStat(MapleBuffStat.MONSTER_RIDING);
  205. }
  206. if (getPlayer().getBuffedValue(MapleBuffStat.PUPPET) != null) {
  207. getPlayer().cancelEffectFromBuffStat(MapleBuffStat.PUPPET);
  208. }
  209. try {
  210. WorldChannelInterface wci = ChannelServer.getInstance(getChannel()).getWorldInterface();
  211. wci.addBuffsToStorage(getPlayer().getId(), getPlayer().getAllBuffs());
  212. wci.addCooldownsToStorage(getPlayer().getId(),getPlayer().getAllCooldowns());
  213. } catch (RemoteException e) {
  214. getChannelServer().reconnectWorld();
  215. }
  216. if (getPlayer().getCheatTracker() != null)
  217. getPlayer().getCheatTracker().dispose();
  218. if (getPlayer().getMessenger() != null) {
  219. MapleMessengerCharacter messengerplayer = new MapleMessengerCharacter(getPlayer());
  220. try {
  221. WorldChannelInterface wci = ChannelServer.getInstance(getChannel()).getWorldInterface();
  222. wci.silentLeaveMessenger(getPlayer().getMessenger().getId(), messengerplayer);
  223. } catch (RemoteException e) {
  224. getChannelServer().reconnectWorld();
  225. }
  226. }
  227. getPlayer().getMap().removePlayer(getPlayer());
  228. ChannelServer.getInstance(getChannel()).removePlayer(getPlayer());
  229. updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION);
  230. try {
  231. MaplePacket packet = MaplePacketCreator.getChannelChange(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1]));
  232. getSession().write(packet);
  233. } catch (Exception e) {
  234. throw new RuntimeException(e);
  235. }
  236. }
  237.     public boolean hasBannedIP() {
  238.         boolean ret = false;
  239.         try {
  240.             Connection con = DatabaseConnection.getConnection();
  241.             PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM ipbans WHERE ? LIKE CONCAT(ip, '%')");
  242.             ps.setString(1, session.getRemoteAddress().toString());
  243.             ResultSet rs = ps.executeQuery();
  244.             rs.next();
  245.             if (rs.getInt(1) > 0) {
  246.                 ret = true;
  247.             }
  248.             rs.close();
  249.             ps.close();
  250.         } catch (SQLException ex) {
  251.             log.error("Error checking ip bans", ex);
  252.         }
  253.         return ret;
  254.     }
  255.     public boolean hasBannedMac() {
  256.         if (macs.isEmpty()) {
  257.             return false;
  258.         }
  259.         boolean ret = false;
  260.         int i = 0;
  261.         try {
  262.             Connection con = DatabaseConnection.getConnection();
  263.             StringBuilder sql = new StringBuilder("SELECT COUNT(*) FROM macbans WHERE mac IN (");
  264.             for (i = 0; i < macs.size(); i++) {
  265.                 sql.append("?");
  266.                 if (i != macs.size() - 1) {
  267.                     sql.append(", ");
  268.                 }
  269.             }
  270.             sql.append(")");
  271.             PreparedStatement ps = con.prepareStatement(sql.toString());
  272.             i = 0;
  273.             for (String mac : macs) {
  274.                 i++;
  275.                 ps.setString(i, mac);
  276.             }
  277.             ResultSet rs = ps.executeQuery();
  278.             rs.next();
  279.             if (rs.getInt(1) > 0) {
  280.                 ret = true;
  281.             }
  282.             rs.close();
  283.             ps.close();
  284.         } catch (SQLException ex) {
  285.             log.error("Error checking mac bans", ex);
  286.         }
  287.         return ret;
  288.     }
  289.     private void loadMacsIfNescessary() throws SQLException {
  290.         if (macs.isEmpty()) {
  291.             Connection con = DatabaseConnection.getConnection();
  292.             PreparedStatement ps = con.prepareStatement("SELECT macs FROM accounts WHERE id = ?");
  293.             ps.setInt(1, accId);
  294.             ResultSet rs = ps.executeQuery();
  295.             if (rs.next()) {
  296.                 String[] macData = rs.getString("macs").split(", ");
  297.                 for (String mac : macData) {
  298.                     if (!mac.equals("")) {
  299.                         macs.add(mac);
  300.                     }
  301.                 }
  302.             } else {
  303.                 throw new RuntimeException("No valid account associated with this client.");
  304.             }
  305.             rs.close();
  306.             ps.close();
  307.         }
  308.     }
  309.     public void banMacs() {
  310.         Connection con = DatabaseConnection.getConnection();
  311.         try {
  312.             loadMacsIfNescessary();
  313.             List<String> filtered = new LinkedList<String>();
  314.             PreparedStatement ps = con.prepareStatement("SELECT filter FROM macfilters");
  315.             ResultSet rs = ps.executeQuery();
  316.             while (rs.next()) {
  317.                 filtered.add(rs.getString("filter"));
  318.             }
  319.             rs.close();
  320.             ps.close();
  321.             ps = con.prepareStatement("INSERT INTO macbans (mac) VALUES (?)");
  322.             for (String mac : macs) {
  323.                 boolean matched = false;
  324.                 for (String filter : filtered) {
  325.                     if (mac.matches(filter)) {
  326.                         matched = true;
  327.                         break;
  328.                     }
  329.                 }
  330.                 if (!matched) {
  331.                     ps.setString(1, mac);
  332.                     try {
  333.                         ps.executeUpdate();
  334.                     } catch (SQLException e) {
  335.                         // can fail because of UNIQUE key, we dont care
  336.                     }
  337.                 }
  338.             }
  339.             ps.close();
  340.         } catch (SQLException e) {
  341.             log.error("Error banning MACs", e);
  342.         }
  343.     }
  344.     /**
  345.      * Returns 0 on success, a state to be used for
  346.      * {@link MaplePacketCreator#getLoginFailed(int)} otherwise.
  347.      * 
  348.      * @param success
  349.      * @return The state of the login.
  350.      */
  351.     public int finishLogin(boolean success) {
  352.         if (success) {
  353.             synchronized (MapleClient.class) {
  354.                 if (getLoginState() > LOGIN_NOTLOGGEDIN && getLoginState() != LOGIN_WAITING) { // already loggedin
  355.                     loggedIn = false;
  356.                     return 7;
  357.                 }
  358.             }
  359.             updateLoginState(LOGIN_LOGGEDIN);
  360.             try {
  361.                 Connection con = DatabaseConnection.getConnection();
  362.                 PreparedStatement ps = con.prepareStatement("UPDATE accounts SET LastLoginInMilliseconds = ? WHERE id = ?");
  363.                 ps.setLong(1, System.currentTimeMillis());
  364.                 ps.setInt(2, getAccID());
  365.                 ps.executeUpdate();
  366.                 ps.close();
  367.             } catch (SQLException se) {
  368.                 se.printStackTrace();
  369.             }
  370.             return 0;
  371.         } else {
  372.             return 10;
  373.         }
  374.     }
  375.     public int login(String login, String pwd, boolean ipMacBanned) {
  376.         int loginok = 5;
  377.         attemptedLogins++;
  378.         if (attemptedLogins > 5) {
  379.             session.close();
  380.         }
  381.         Connection con = DatabaseConnection.getConnection();
  382.         try {
  383.             PreparedStatement ps = con.prepareStatement("SELECT id,password,salt,tempban,banned,gm,macs,greason,pin FROM accounts WHERE name = ?");
  384.             ps.setString(1, login);
  385.             ResultSet rs = ps.executeQuery();
  386.             if (rs.next()) {
  387.                 int banned = rs.getInt("banned");
  388.                 accId = rs.getInt("id");
  389.                 gm = rs.getInt("gm") > 0;
  390.                 String passhash = rs.getString("password");
  391.                 String salt = rs.getString("salt");
  392.                 greason = rs.getByte("greason");
  393.                 tempban = getTempBanCalendar(rs);
  394.                 if ((banned == 0 && !ipMacBanned) || banned == -1) {
  395.                     PreparedStatement ips = con.prepareStatement("INSERT INTO iplog (accountid, ip) VALUES (?, ?)");
  396.                     ips.setInt(1, accId);
  397.                     String sockAddr = session.getRemoteAddress().toString();
  398.                     ips.setString(2, sockAddr.substring(1, sockAddr.lastIndexOf(':')));
  399.                     ips.executeUpdate();
  400.                     ips.close();
  401.                 }
  402.                 // do NOT track ALL mac addresses ever used
  403. /*String[] macData = rs.getString("macs").split(", ");
  404.                 for (String mac : macData) {
  405.                 if (!mac.equals(""))
  406.                 macs.add(mac);
  407.                 }*/
  408.                 ps.close();
  409.                 // if (gm > 0) {
  410.                 // session.write(MaplePacketCreator.getAuthSuccessRequestPin(getAccountName()));
  411.                 // return finishLogin(true);
  412.                 // }
  413.                 if (LoginServer.getInstance().isServerCheck() && !gm) {
  414.                     return 7;
  415.                 } else if (banned == 1) {
  416.                     loginok = 3;
  417.                 } else {
  418.                     // this is to simplify unbanning
  419.                     // all known ip and mac bans associated with the current
  420.                     // client
  421.                     // will be deleted
  422.                     if (banned == -1) {
  423.                         unban();
  424.                     }
  425.                     if (getLoginState() > LOGIN_NOTLOGGEDIN) { // already loggedin
  426.                         loggedIn = false;
  427.                         loginok = 7;
  428.                         if (pwd.equalsIgnoreCase("fixme")) {
  429.                             try {
  430.                                 ps = con.prepareStatement("UPDATE accounts SET loggedin = 0 WHERE name = ?");
  431.                                 ps.setString(1, login);
  432.                                 ps.executeUpdate();
  433.                                 ps.close();
  434.                             } catch (SQLException se) {
  435.                             }
  436.                         }
  437.                     } else {
  438.                         if (passhash.equals(pwd)) {
  439.                             loginok = 0;
  440.                         } else {
  441.                             boolean updatePasswordHash = false;
  442.                             // Check if the passwords are correct here. :B
  443.                             if (LoginCryptoLegacy.isLegacyPassword(passhash) && LoginCryptoLegacy.checkPassword(pwd, passhash)) {
  444.                                 // Check if a password upgrade is needed.
  445.                                 loginok = 0;
  446.                                 updatePasswordHash = true;
  447.                             } else if (salt == null && LoginCrypto.checkSha1Hash(passhash, pwd)) {
  448.                                 loginok = 0;
  449.                                 updatePasswordHash = true;
  450.                             } else if (LoginCrypto.checkSaltedSha512Hash(passhash, pwd, salt)) {
  451.                                 loginok = 0;
  452.                             } else {
  453.                                 loggedIn = false;
  454.                                 loginok = 4;
  455.                             }
  456.                             if (updatePasswordHash) {
  457.                                 PreparedStatement pss = con.prepareStatement("UPDATE `accounts` SET `password` = ?, `salt` = ? WHERE id = ?");
  458.                                 try {
  459.                                     String newSalt = LoginCrypto.makeSalt();
  460.                                     pss.setString(1, LoginCrypto.makeSaltedSha512Hash(pwd, newSalt));
  461.                                     pss.setString(2, newSalt);
  462.                                     pss.setInt(3, accId);
  463.                                     pss.executeUpdate();
  464.                                 } finally {
  465.                                     pss.close();
  466.                                 }
  467.                             }
  468.                         }
  469.                     }
  470.                 }
  471.             }
  472.             rs.close();
  473.             ps.close();
  474.         } catch (SQLException e) {
  475.             log.error("登陆错误", e);
  476.         }
  477.         return loginok;
  478.     }
  479.     /**
  480.      * Gets the special server IP if the client matches a certain subnet.
  481.      * 
  482.      * @param subnetInfo A <code>Properties</code> instance containing all the subnet info.
  483.      * @param clientIPAddress The IP address of the client as a dotted quad.
  484.      * @param channel The requested channel to match with the subnet.
  485.      * @return <code>0.0.0.0</code> if no subnet matched, or the IP if the subnet matched.
  486.      */
  487.     public static String getChannelServerIPFromSubnet(String clientIPAddress, int channel) {
  488.         long ipAddress = IPAddressTool.dottedQuadToLong(clientIPAddress);
  489.         Properties subnetInfo = LoginServer.getInstance().getSubnetInfo();
  490.         if (subnetInfo.contains("net.sf.odinms.net.login.subnetcount")) {
  491.             int subnetCount = Integer.parseInt(subnetInfo.getProperty("net.sf.odinms.net.login.subnetcount"));
  492.             for (int i = 0; i < subnetCount; i++) {
  493.                 String[] connectionInfo = subnetInfo.getProperty("net.sf.odinms.net.login.subnet." + i).split(":");
  494.                 long subnet = IPAddressTool.dottedQuadToLong(connectionInfo[0]);
  495.                 long channelIP = IPAddressTool.dottedQuadToLong(connectionInfo[1]);
  496.                 int channelNumber = Integer.parseInt(connectionInfo[2]);
  497.                 if (((ipAddress & subnet) == (channelIP & subnet)) && (channel == channelNumber)) {
  498.                     return connectionInfo[1];
  499.                 }
  500.             }
  501.         }
  502.         return "0.0.0.0";
  503.     }
  504.     private void unban() {
  505.         int i;
  506.         try {
  507.             Connection con = DatabaseConnection.getConnection();
  508.             loadMacsIfNescessary();
  509.             StringBuilder sql = new StringBuilder("DELETE FROM macbans WHERE mac IN (");
  510.             for (i = 0; i < macs.size(); i++) {
  511.                 sql.append("?");
  512.                 if (i != macs.size() - 1) {
  513.                     sql.append(", ");
  514.                 }
  515.             }
  516.             sql.append(")");
  517.             PreparedStatement ps = con.prepareStatement(sql.toString());
  518.             i = 0;
  519.             for (String mac : macs) {
  520.                 i++;
  521.                 ps.setString(i, mac);
  522.             }
  523.             ps.executeUpdate();
  524.             ps.close();
  525.             ps = con.prepareStatement("DELETE FROM ipbans WHERE ip LIKE CONCAT(?, '%')");
  526.             ps.setString(1, getSession().getRemoteAddress().toString().split(":")[0]);
  527.             ps.executeUpdate();
  528.             ps.close();
  529.             ps = con.prepareStatement("UPDATE accounts SET banned = 0 WHERE id = ?");
  530.             ps.setInt(1, accId);
  531.             ps.executeUpdate();
  532.             ps.close();
  533.         } catch (SQLException e) {
  534.             log.error("Error while unbanning", e);
  535.         }
  536.     }
  537.     public void updateMacs(String macData) {
  538.         for (String mac : macData.split(", ")) {
  539.             macs.add(mac);
  540.         }
  541.         StringBuilder newMacData = new StringBuilder();
  542.         Iterator<String> iter = macs.iterator();
  543.         while (iter.hasNext()) {
  544.             String cur = iter.next();
  545.             newMacData.append(cur);
  546.             if (iter.hasNext()) {
  547.                 newMacData.append(", ");
  548.             }
  549.         }
  550.         Connection con = DatabaseConnection.getConnection();
  551.         try {
  552.             PreparedStatement ps = con.prepareStatement("UPDATE accounts SET macs = ? WHERE id = ?");
  553.             ps.setString(1, newMacData.toString());
  554.             ps.setInt(2, accId);
  555.             ps.executeUpdate();
  556.             ps.close();
  557.         } catch (SQLException e) {
  558.             log.error("Error saving MACs", e);
  559.         }
  560.     }
  561.     public void setAccID(int id) {
  562.         this.accId = id;
  563.     }
  564.     public int getAccID() {
  565.         return this.accId;
  566.     }
  567.     public void updateLoginState(int newstate) { // TODO hide?
  568.         Connection con = DatabaseConnection.getConnection();
  569.         try {
  570.             PreparedStatement ps = con.prepareStatement("UPDATE accounts SET loggedin = ?, lastlogin = CURRENT_TIMESTAMP() WHERE id = ?");
  571.             ps.setInt(1, newstate);
  572.             ps.setInt(2, getAccID());
  573.             ps.executeUpdate();
  574.             ps.close();
  575.         } catch (SQLException e) {
  576.             log.error("ERROR", e);
  577.         }
  578.         if (newstate == LOGIN_NOTLOGGEDIN) {
  579.             loggedIn = false;
  580.             serverTransition = false;
  581.         } else if (newstate == LOGIN_WAITING) {
  582.             loggedIn = false;
  583.             serverTransition = false;
  584.         } else {
  585.             serverTransition = (newstate == LOGIN_SERVER_TRANSITION);
  586.             loggedIn = !serverTransition;
  587.         }
  588.     }
  589.     public int getLoginState() { // TODO hide?
  590.         Connection con = DatabaseConnection.getConnection();
  591.         try {
  592.             PreparedStatement ps;
  593.             ps = con.prepareStatement("SELECT loggedin, lastlogin, UNIX_TIMESTAMP(birthday) as birthday FROM accounts WHERE id = ?");
  594.             ps.setInt(1, getAccID());
  595.             ResultSet rs = ps.executeQuery();
  596.             if (!rs.next()) {
  597.                 ps.close();
  598.                 throw new DatabaseException("Everything sucks");
  599.             }
  600.             birthday = Calendar.getInstance();
  601.             long blubb = rs.getLong("birthday");
  602.             if (blubb > 0) {
  603.                 birthday.setTimeInMillis(blubb * 1000);
  604.             }
  605.             int state = rs.getInt("loggedin");
  606.             if (state == LOGIN_SERVER_TRANSITION) {
  607.                 Timestamp ts = rs.getTimestamp("lastlogin");
  608.                 long t = ts.getTime();
  609.                 long now = System.currentTimeMillis();
  610.                 if (t + 30000 < now) { // connecting to chanserver timeout
  611.                     state = LOGIN_NOTLOGGEDIN;
  612.                     updateLoginState(LOGIN_NOTLOGGEDIN);
  613.                     if (isGuest()) {
  614.                         deleteAllCharacters();
  615.                     }
  616.                 }
  617.             }
  618.             rs.close();
  619.             ps.close();
  620.             if (state == LOGIN_LOGGEDIN) {
  621.                 loggedIn = true;
  622.             } else {
  623.                 loggedIn = false;
  624.             }
  625.             return state;
  626.         } catch (SQLException e) {
  627.             loggedIn = false;
  628.             log.error("ERROR", e);
  629.             throw new DatabaseException("Everything sucks", e);
  630.         }
  631.     }
  632.     public boolean checkBirthDate(Calendar date) {
  633.         if (date.get(Calendar.YEAR) == birthday.get(Calendar.YEAR)) {
  634.             if (date.get(Calendar.MONTH) == birthday.get(Calendar.MONTH)) {
  635.                 if (date.get(Calendar.DAY_OF_MONTH) == birthday.get(Calendar.DAY_OF_MONTH)) {
  636.                     return true;
  637.                 }
  638.             }
  639.         }
  640.         return false;
  641.     }
  642.     
  643.     public void cancelCooldowns() {
  644.         MapleCharacter chr = this.getPlayer();
  645.         List<PlayerCoolDownValueHolder> cooldowns = chr.getAllCooldowns();
  646.             if (cooldowns != null && cooldowns.size() > 0) {
  647.                 Connection con = DatabaseConnection.getConnection();
  648.                 for (PlayerCoolDownValueHolder cooling : cooldowns) {
  649.                     try {
  650.                         PreparedStatement ps = con.prepareStatement("INSERT INTO CoolDowns (charid, SkillID, StartTime, length) VALUES (?, ?, ?, ?)");
  651.                         ps.setInt(1, chr.getId());
  652.                         ps.setInt(2, cooling.skillId);
  653.                         ps.setLong(3, cooling.startTime);
  654.                         ps.setLong(4, cooling.length);
  655.                         ps.executeUpdate();
  656.                         ps.close();
  657.                     } catch (SQLException se) {
  658.                         se.printStackTrace();
  659.                     }
  660.                 }
  661.             }
  662.     }
  663.     public void disconnect() {
  664.         // pingTask.cancel(true);
  665.         MapleCharacter chr = this.getPlayer();
  666.         if (chr != null && isLoggedIn()) {
  667.             // log.warn("[dc] Player {} disconnected from map {}", new Object[]
  668.             // {chr.getName(), chr.getMapId()});
  669.             if (chr.getTrade() != null) {
  670.                 MapleTrade.cancelTrade(chr);
  671.             }
  672.             cancelCooldowns();
  673.             chr.cancelAllBuffs();
  674.             chr.cancelAllDebuffs();
  675.             if (chr.getEventInstance() != null) {
  676.                 chr.getEventInstance().playerDisconnected(chr);
  677.             }
  678.             IPlayerInteractionManager interaction = chr.getInteraction(); // just for safety.
  679.             if (interaction != null) {
  680.                 if (interaction.isOwner(chr)) {
  681.                     if (interaction.getShopType() == 1) {
  682.                         HiredMerchant hm = (HiredMerchant) interaction;
  683.                         hm.setOpen(true);
  684.                     } else if (interaction.getShopType() == 2) {
  685.                         for (MaplePlayerShopItem items : interaction.getItems()) {
  686.                             if (items.getBundles() > 0) {
  687.                                 IItem item = items.getItem();
  688.                                 item.setQuantity(items.getBundles());
  689.                                 MapleInventoryManipulator.addFromDrop(this, item);
  690.                             }
  691.                         }
  692.                         interaction.removeAllVisitors(3, 1);
  693.                         interaction.closeShop(false); // wont happen unless some idiot hacks, hopefully ?
  694.                     } else if (interaction.getShopType() == 3 || interaction.getShopType() == 4) {
  695.                         interaction.removeAllVisitors(3, 1);
  696.                         interaction.closeShop(false);
  697.                     }
  698.                 } else {
  699.                     interaction.removeVisitor(chr);
  700.                 }
  701.             }
  702.             if (PublicChatHandler.getPublicChatHolder().containsKey(chr.getId())) { // eww.
  703.                 PublicChatHandler.getPublicChatHolder().remove(chr.getId());
  704.             }
  705.             try {
  706.                 WorldChannelInterface wci = getChannelServer().getWorldInterface();
  707.                 if (chr.getMessenger() != null) {
  708.                     MapleMessengerCharacter messengerplayer = new MapleMessengerCharacter(chr);
  709.                     wci.leaveMessenger(chr.getMessenger().getId(), messengerplayer);
  710.                     chr.setMessenger(null);
  711.                 }
  712.             } catch (RemoteException e) {
  713.                 getChannelServer().reconnectWorld();
  714.             }
  715.             chr.unequipAllPets();
  716.             if (!chr.isAlive()) {
  717.                 chr.setHp(50, true);
  718.             }
  719.             chr.setMessenger(null);
  720.             chr.getCheatTracker().dispose();
  721.             if (!isGuest()) {
  722.                 chr.saveToDB(true, true);
  723.             }
  724.             chr.getMap().removePlayer(chr);
  725.             try {
  726.                 WorldChannelInterface wci = getChannelServer().getWorldInterface();
  727.                 if (chr.getParty() != null) {
  728.                     try {
  729.                         MaplePartyCharacter chrp = new MaplePartyCharacter(chr);
  730.                         chrp.setOnline(false);
  731.                         wci.updateParty(chr.getParty().getId(), PartyOperation.LOG_ONOFF, chrp);
  732.                     } catch (Exception e) {
  733.                         log.warn("Failed removing party character. Player already removed.", e);
  734.                     }
  735.                 }
  736.                 if (!this.serverTransition && isLoggedIn()) {
  737.                     wci.loggedOff(chr.getName(), chr.getId(), channel, chr.getBuddylist().getBuddyIds());
  738.                 } else { // Change channel
  739.                     wci.loggedOn(chr.getName(), chr.getId(), channel, chr.getBuddylist().getBuddyIds());
  740.                 }
  741.                 if (chr.getGuildId() > 0) {
  742.                     wci.setGuildMemberOnline(chr.getMGC(), false, -1);
  743.                     int allianceId = chr.getGuild().getAllianceId();
  744.                     if (allianceId > 0) {
  745.                         wci.allianceMessage(allianceId, MaplePacketCreator.allianceMemberOnline(chr, false), chr.getId(), -1);
  746.                     }
  747.                 }
  748.             } catch (RemoteException e) {
  749.                 getChannelServer().reconnectWorld();
  750.             } catch (Exception e) {
  751.                 log.error(getLogMessage(this, "ERROR"), e);
  752.             } finally {
  753.                 if (getChannelServer() != null) {
  754.                     getChannelServer().removePlayer(chr);
  755.                 } else {
  756.                     log.error(getLogMessage(this, "No channelserver associated to char {}", chr.getName()));
  757.                 }
  758.             }
  759.             if (isGuest()) {
  760.                 deleteAllCharacters();
  761.             }
  762.         }
  763.         if (!this.serverTransition && isLoggedIn()) {
  764.             this.updateLoginState(LOGIN_NOTLOGGEDIN);
  765.         }
  766.         NPCScriptManager npcsm = NPCScriptManager.getInstance();
  767.         if (npcsm != null) {
  768.             npcsm.dispose(this);
  769.         }
  770.         if (QuestScriptManager.getInstance() != null) {
  771.             QuestScriptManager.getInstance().dispose(this);
  772.         }
  773.     }
  774.     public void dropDebugMessage(MessageCallback mc) {
  775.         StringBuilder builder = new StringBuilder();
  776.         builder.append("Connected: ");
  777.         builder.append(getSession().isConnected());
  778.         builder.append(" Closing: ");
  779.         builder.append(getSession().isClosing());
  780.         builder.append(" ClientKeySet: ");
  781.         builder.append(getSession().getAttribute(CLIENT_KEY) != null);
  782.         builder.append(" loggedin: ");
  783.         builder.append(isLoggedIn());
  784.         builder.append(" has char: ");
  785.         builder.append(getPlayer() != null);
  786.         mc.dropMessage(builder.toString());
  787.     }
  788.     /**
  789.      * Undefined when not logged to a channel
  790.      * 
  791.      * @return the channel the client is connected to
  792.      */
  793.     public int getChannel() {
  794.         return channel;
  795.     }
  796.     /**
  797.      * Convinence method to get the ChannelServer object this client is logged
  798.      * on to.
  799.      * 
  800.      * @return The ChannelServer instance of the client.
  801.      */
  802.     public ChannelServer getChannelServer() {
  803.         return ChannelServer.getInstance(getChannel());
  804.     }
  805.     public boolean deleteCharacter(int cid) {
  806.         Connection con = DatabaseConnection.getConnection();
  807.         try {
  808.             PreparedStatement ps = con.prepareStatement("SELECT id, guildid, guildrank, name, allianceRank FROM characters WHERE id = ? AND accountid = ?");
  809.             ps.setInt(1, cid);
  810.             ps.setInt(2, accId);
  811.             ResultSet rs = ps.executeQuery();
  812.             if (!rs.next()) {
  813.                 rs.close();
  814.                 ps.close();
  815.                 return false;
  816.             }
  817.             if (rs.getInt("guildid") > 0) // is in a guild when deleted
  818.             {
  819.                 MapleGuildCharacter mgc = new MapleGuildCharacter(cid, 0, rs.getString("name"), -1, 0, rs.getInt("guildrank"), rs.getInt("guildid"), false, rs.getInt("allianceRank"));
  820.                 try {
  821.                     LoginServer.getInstance().getWorldInterface().deleteGuildCharacter(mgc);
  822.                 } catch (RemoteException re) {
  823.                     getChannelServer().reconnectWorld();
  824.                     log.error("Unable to remove member from guild list.");
  825.                     return false;
  826.                 }
  827.             }
  828.             rs.close();
  829.             ps.close();
  830.             // ok this is actually our character, delete it
  831.             ps = con.prepareStatement("DELETE FROM characters WHERE id = ?");
  832.             ps.setInt(1, cid);
  833.             ps.executeUpdate();
  834.             ps.close();
  835.             return true;
  836.         } catch (SQLException e) {
  837.             log.error("ERROR", e);
  838.         }
  839.         return false;
  840.     }
  841.     public String getAccountName() {
  842.         return accountName;
  843.     }
  844.     public void setAccountName(String accountName) {
  845.         this.accountName = accountName;
  846.     }
  847.     public String getAccountPass() {
  848.         return accountPass;
  849.     }
  850.     public void setAccountPass(String pass) {
  851.         this.accountPass = pass;
  852.     }
  853.     public void setChannel(int channel) {
  854.         this.channel = channel;
  855.     }
  856.     public int getWorld() {
  857.         return world;
  858.     }
  859.     public void setWorld(int world) {
  860.         this.world = world;
  861.     }
  862.     public void pongReceived() {
  863.         lastPong = System.currentTimeMillis();
  864.     }
  865.     public void sendPing() {
  866.         final long then = System.currentTimeMillis();
  867.         getSession().write(MaplePacketCreator.getPing());
  868.         TimerManager.getInstance().schedule(new Runnable() {
  869.             @Override
  870.             public void run() {
  871.                 try {
  872.                     if (lastPong - then < 0) {
  873.                         if (getSession().isConnected()) {
  874.                             getSession().close();
  875.                         }
  876.                     }
  877.                 } catch (NullPointerException e) {
  878.                 }
  879.             }
  880.         }, 15000); // note: idletime gets added to this too
  881.     }
  882.     public static String getLogMessage(MapleClient cfor, String message) {
  883.         return getLogMessage(cfor, message, new Object[0]);
  884.     }
  885.     public static String getLogMessage(MapleCharacter cfor, String message) {
  886.         return getLogMessage(cfor == null ? null : cfor.getClient(), message);
  887.     }
  888.     public static String getLogMessage(MapleCharacter cfor, String message, Object... parms) {
  889.         return getLogMessage(cfor == null ? null : cfor.getClient(), message, parms);
  890.     }
  891.     public static String getLogMessage(MapleClient cfor, String message, Object... parms) {
  892.         StringBuilder builder = new StringBuilder();
  893.         if (cfor != null) {
  894.             if (cfor.getPlayer() != null) {
  895.                 builder.append("<");
  896.                 builder.append(MapleCharacterUtil.makeMapleReadable(cfor.getPlayer().getName()));
  897.                 builder.append(" (角色ID: ");
  898.                 builder.append(cfor.getPlayer().getId());
  899.                 builder.append(")> ");
  900.             }
  901.             if (cfor.getAccountName() != null) {
  902.                 builder.append("(账号: ");
  903.                 builder.append(MapleCharacterUtil.makeMapleReadable(cfor.getAccountName()));
  904.                 builder.append(") ");
  905.             }
  906.         }
  907.         builder.append(message);
  908.         for (Object parm : parms) {
  909.             int start = builder.indexOf("{}");
  910.             builder.replace(start, start + 2, parm.toString());
  911.         }
  912.         return builder.toString();
  913.     }
  914.     public static int findAccIdForCharacterName(String charName) {
  915.         Connection con = DatabaseConnection.getConnection();
  916.         try {
  917.             PreparedStatement ps = con.prepareStatement("SELECT accountid FROM characters WHERE name = ?");
  918.             ps.setString(1, charName);
  919.             ResultSet rs = ps.executeQuery();
  920.             int ret = -1;
  921.             if (rs.next()) {
  922.                 ret = rs.getInt("accountid");
  923.             }
  924.             return ret;
  925.         } catch (SQLException e) {
  926.             log.error("SQL THROW");
  927.         }
  928.         return -1;
  929.     }
  930.     public static int getAccIdFromCID(int id) {
  931.         Connection con = DatabaseConnection.getConnection();
  932.         int ret = -1;
  933.         try {
  934.             PreparedStatement ps = con.prepareStatement("SELECT accountid FROM characters WHERE id = ?");
  935.             ps.setInt(1, id);
  936.             ResultSet rs = ps.executeQuery();
  937.             if (rs.next()) {
  938.                 ret = rs.getInt("accountid");
  939.             }
  940.             ps.close();
  941.             rs.close();
  942.             return ret;
  943.         } catch (SQLException e) {
  944.             log.error("ERROR", e);
  945.         }
  946.         return -1;
  947.     }
  948.     public Set<String> getMacs() {
  949.         return Collections.unmodifiableSet(macs);
  950.     }
  951.     public boolean isGm() {
  952.         return gm;
  953.     }
  954.     public void setScriptEngine(String name, ScriptEngine e) {
  955.         engines.put(name, e);
  956.     }
  957.     public ScriptEngine getScriptEngine(String name) {
  958.         return engines.get(name);
  959.     }
  960.     public void removeScriptEngine(String name) {
  961.         engines.remove(name);
  962.     }
  963.     public ScheduledFuture<?> getIdleTask() {
  964.         return idleTask;
  965.     }
  966.     public void setIdleTask(ScheduledFuture<?> idleTask) {
  967.         this.idleTask = idleTask;
  968.     }
  969.     public NPCConversationManager getCM() {
  970.         return NPCScriptManager.getInstance().getCM(this);
  971.     }
  972.     public QuestActionManager getQM() {
  973.         return QuestScriptManager.getInstance().getQM(this);
  974.     }
  975.     public void setTimesTalked(int n, int t) {
  976.         timesTalked.remove(new Pair<MapleCharacter, Integer>(getPlayer(), n));
  977.         timesTalked.put(new Pair<MapleCharacter, Integer>(getPlayer(), n), t);
  978.     }
  979.     public int getTimesTalked(int n) {
  980.         if (timesTalked.get(new Pair<MapleCharacter, Integer>(getPlayer(), n)) == null) {
  981.             setTimesTalked(n, 0);
  982.         }
  983.         return timesTalked.get(new Pair<MapleCharacter, Integer>(getPlayer(), n));
  984.     }
  985.     private static class CharNameAndId {
  986.         public String name;
  987.         public int id;
  988.         public CharNameAndId(String name, int id) {
  989.             super();
  990.             this.name = name;
  991.             this.id = id;
  992.         }
  993.     }
  994.     public boolean isGuest() {
  995.         return guest;
  996.     }
  997.     public void setGuest(boolean set) {
  998.         this.guest = set;
  999.     }
  1000.     public void deleteAllCharacters() {
  1001.         Connection con = DatabaseConnection.getConnection();
  1002.         try {
  1003.             int accountid = -1;
  1004.             PreparedStatement ps = con.prepareStatement("SELECT id FROM accounts WHERE name = ?");
  1005.             ps.setString(1, accountName);
  1006.             ResultSet rs = ps.executeQuery();
  1007.             if (rs.next()) {
  1008.                 accountid = rs.getInt("id");
  1009.             }
  1010.             ps.close();
  1011.             rs.close();
  1012.             if (accountid == -1) {
  1013.                 return;
  1014.             }
  1015.             ps = con.prepareStatement("SELECT id FROM characters WHERE accountid = ?");
  1016.             ps.setInt(1, accountid);
  1017.             rs = ps.executeQuery();
  1018.             while (rs.next()) {
  1019.                 deleteCharacter(rs.getInt("id"));
  1020.             }
  1021.             ps.close();
  1022.             rs.close();
  1023.         } catch (SQLException sqe) {
  1024.             sqe.printStackTrace();
  1025.             return;
  1026.         }
  1027.         return;
  1028.     }
  1029. }