/*
OrpheusMS: MapleStory Private Server based on OdinMS
Copyright (C) 2012 Aaron Weiss
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;
import gm.server.GMServer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import javax.script.ScriptEngine;
import net.MaplePacket;
import tools.DatabaseConnection;
import net.server.Channel;
import net.server.Server;
import net.server.MapleMessengerCharacter;
import net.server.MaplePartyCharacter;
import net.server.PartyOperation;
import net.server.World;
import net.server.guild.MapleGuildCharacter;
import scripting.npc.NPCConversationManager;
import scripting.npc.NPCScriptManager;
import scripting.quest.QuestActionManager;
import scripting.quest.QuestScriptManager;
import server.MapleTrade;
import server.TimerManager;
import server.maps.HiredMerchant;
import tools.HashCreator;
import tools.MapleAESOFB;
import tools.MaplePacketCreator;
import tools.HexTool;
import tools.Output;
import org.apache.mina.core.session.IoSession;
import constants.ServerConstants;
import server.MapleMiniGame;
import server.quest.MapleQuest;
import tools.MapleLogger;
public class MapleClient {
public static final int LOGIN_NOTLOGGEDIN = 0;
public static final int LOGIN_SERVER_TRANSITION = 1;
public static final int LOGIN_LOGGEDIN = 2;
public static final String CLIENT_KEY = "CLIENT";
private MapleAESOFB send;
private MapleAESOFB receive;
private IoSession session;
private MapleCharacter player;
private byte channel = 1;
private int accId = 1;
private boolean loggedIn = false;
private boolean serverTransition = false;
private Calendar birthday = null;
private String accountName = null;
private byte world;
private long lastPong;
private int gmlevel;
private Set<String> macs = new HashSet<String>();
private Map<String, ScriptEngine> engines = new HashMap<String, ScriptEngine>();
private ScheduledFuture<?> idleTask = null;
private byte characterSlots = 3;
private byte loginattempt = 0;
private String pin = null;
private int pinattempt = 0;
private String pic = null;
private int picattempt = 0;
private byte gender = -1;
public MapleClient(MapleAESOFB send, MapleAESOFB receive, IoSession session) {
this.send = send;
this.receive = receive;
this.session = session;
}
public synchronized MapleAESOFB getReceiveCrypto() {
return receive;
}
public synchronized MapleAESOFB getSendCrypto() {
return send;
}
public synchronized IoSession getSession() {
return session;
}
public MapleCharacter getPlayer() {
return player;
}
public void setPlayer(MapleCharacter player) {
this.player = player;
}
public void sendCharList(int server) {
this.session.write(MaplePacketCreator.getCharList(this, server));
}
public List<MapleCharacter> loadCharacters(int serverId) {
List<MapleCharacter> chars = new ArrayList<MapleCharacter>(15);
try {
for (CharNameAndId cni : loadCharactersInternal(serverId)) {
chars.add(MapleCharacter.loadCharFromDB(cni.id, this, false));
}
} catch (Exception e) {
}
return chars;
}
public List<String> loadCharacterNames(int serverId) {
List<String> chars = new ArrayList<String>(15);
for (CharNameAndId cni : loadCharactersInternal(serverId)) {
chars.add(cni.name);
}
return chars;
}
public String getFormattedCharacterList(int serverId) {
StringBuilder sb = new StringBuilder();
int n = 0;
for (CharNameAndId cni : loadCharactersInternal(serverId)) {
sb.append("#L").append(n).append("#").append(cni.name).append("#l\r\n");
n++;
}
return sb.toString();
}
public String getCharacterName(int n, int serverId) {
int k = 0;
for (CharNameAndId cni : loadCharactersInternal(serverId)) {
if (k == n) {
return cni.name;
}
k++;
}
return null;
}
public int getCharacterId(int n, int serverId) {
int k = 0;
for (CharNameAndId cni : loadCharactersInternal(serverId)) {
if (k == n) {
return cni.id;
}
k++;
}
return -1;
}
public boolean isCharacterInGuild(int cid) {
return (MapleCharacter.getGuildIdById(cid) != 0);
}
private List<CharNameAndId> loadCharactersInternal(int serverId) {
PreparedStatement ps;
List<CharNameAndId> chars = new ArrayList<CharNameAndId>(15);
try {
if (ServerConstants.ENABLE_HARDCORE_MODE) {
ps = DatabaseConnection.getConnection().prepareStatement("SELECT id, name FROM characters WHERE accountid = ? AND world = ? AND dead != 1");
} else {
ps = DatabaseConnection.getConnection().prepareStatement("SELECT id, name FROM characters WHERE accountid = ? AND world = ?");
}
ps.setInt(1, this.getAccID());
ps.setInt(2, serverId);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
chars.add(new CharNameAndId(rs.getString("name"), rs.getInt("id")));
}
rs.close();
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
return chars;
}
public boolean isLoggedIn() {
return loggedIn;
}
public boolean hasBannedIP() {
boolean ret = false;
try {
PreparedStatement ps = DatabaseConnection.getConnection().prepareStatement("SELECT COUNT(*) FROM ipbans WHERE ? LIKE CONCAT(ip, '%')");
ps.setString(1, session.getRemoteAddress().toString());
ResultSet rs = ps.executeQuery();
rs.next();
if (rs.getInt(1) > 0) {
ret = true;
}
rs.close();
ps.close();
} catch (SQLException e) {
}
return ret;
}
public boolean hasBannedMac() {
if (macs.isEmpty()) {
return false;
}
boolean ret = false;
int i = 0;
try {
StringBuilder sql = new StringBuilder("SELECT COUNT(*) FROM macbans WHERE mac IN (");
for (i = 0; i < macs.size(); i++) {
sql.append("?");
if (i != macs.size() - 1) {
sql.append(", ");
}
}
sql.append(")");
PreparedStatement ps = DatabaseConnection.getConnection().prepareStatement(sql.toString());
i = 0;
for (String mac : macs) {
i++;
ps.setString(i, mac);
}
ResultSet rs = ps.executeQuery();
rs.next();
if (rs.getInt(1) > 0) {
ret = true;
}
rs.close();
ps.close();
} catch (Exception e) {
}
return ret;
}
private void loadMacsIfNescessary() throws SQLException {
if (macs.isEmpty()) {
PreparedStatement ps = DatabaseConnection.getConnection().prepareStatement("SELECT macs FROM accounts WHERE id = ?");
ps.setInt(1, accId);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
for (String mac : rs.getString("macs").split(", ")) {
if (!mac.equals("")) {
macs.add(mac);
}
}
}
rs.close();
ps.close();
}
}
public void banMacs() {
Connection con = DatabaseConnection.getConnection();
try {
loadMacsIfNescessary();
List<String> filtered = new LinkedList<String>();
PreparedStatement ps = con.prepareStatement("SELECT filter FROM macfilters");
ResultSet rs = ps.executeQuery();
while (rs.next()) {
filtered.add(rs.getString("filter"));
}
rs.close();
ps.close();
ps = con.prepareStatement("INSERT INTO macbans (mac) VALUES (?)");
for (String mac : macs) {
boolean matched = false;
for (String filter : filtered) {
if (mac.matches(filter)) {
matched = true;
break;
}
}
if (!matched) {
ps.setString(1, mac);
ps.executeUpdate();
}
}
ps.close();
} catch (SQLException e) {
}
}
public int finishLogin() {
synchronized (MapleClient.class) {
if (getLoginState() > LOGIN_NOTLOGGEDIN) {
loggedIn = false;
return 7;
}
updateLoginState(LOGIN_LOGGEDIN);
}
return 0;
}
public void setPin(String pin) {
this.pin = pin;
try {
PreparedStatement ps = DatabaseConnection.getConnection().prepareStatement("UPDATE accounts SET pin = ? WHERE id = ?");
ps.setString(1, pin);
ps.setInt(2, accId);
ps.executeUpdate();
ps.close();
} catch (SQLException e) {
}
}
public String getPin() {
return pin;
}
public boolean checkPin(String other) {
pinattempt++;
if (pinattempt > 5) {
getSession().close(true);
}
if (pin.equals(other)) {
pinattempt = 0;
return true;
}
return false;
}
public void setPic(String pic) {
this.pic = pic;
try {
PreparedStatement ps = DatabaseConnection.getConnection().prepareStatement("UPDATE accounts SET pic = ? WHERE id = ?");
ps.setString(1, pic);
ps.setInt(2, accId);
ps.executeUpdate();
ps.close();
} catch (SQLException e) {
}
}
public String getPic() {
return pic;
}
public boolean checkPic(String other) {
picattempt++;
if (picattempt > 5) {
getSession().close(true);
}
if (pic.equals(other)) {
picattempt = 0;
return true;
}
return false;
}
public int login(String login, String pwd) {
loginattempt++;
if (loginattempt > 4) {
getSession().close(true);
}
int loginok = 5;
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = con.prepareStatement("SELECT id, password, salt, gender, banned, gm, pin, pic, characterslots, tos FROM accounts WHERE name = ?");
ps.setString(1, login);
rs = ps.executeQuery();
if (rs.next()) {
if (rs.getByte("banned") == 1) {
return 3;
}
accId = rs.getInt("id");
gmlevel = rs.getInt("gm");
pin = rs.getString("pin");
pic = rs.getString("pic");
gender = rs.getByte("gender");
characterSlots = rs.getByte("characterslots");
String passhash = rs.getString("password");
String salt = rs.getString("salt");
// we do not unban
byte tos = rs.getByte("tos");
ps.close();
rs.close();
if (getLoginState() > LOGIN_NOTLOGGEDIN) { // already loggedin
loggedIn = false;
loginok = 7;
} else if (pwd.equals(passhash) || checkHash(passhash, "SHA-1", pwd) || checkHash(passhash, "SHA-512", pwd + salt)) {
if (tos == 0) {
loginok = 23;
} else {
loginok = 0;
}
} else {
loggedIn = false;
loginok = 4;
}
if (loginok == 0) {
// We're going to change the hashing algorithm to SHA-512 with salt, so we can be secure! :3
SecureRandom random = new SecureRandom();
byte bytes[] = new byte[32]; // 32 bit salt (results may vary. depends on RNG algorithm)
random.nextBytes(bytes);
String saltNew = HexTool.toString(bytes).replace(" ", "").toLowerCase();
String passhashNew = HashCreator.getHash("SHA-512", pwd + saltNew);
ps = con.prepareStatement("UPDATE accounts SET password = ?, salt = ? WHERE id = ?");
ps.setString(1, passhashNew);
ps.setString(2, saltNew);
ps.setInt(3, accId);
ps.executeUpdate();
ps.close();
rs.close();
}
ps = con.prepareStatement("INSERT INTO iplog (accountid, ip) VALUES (?, ?)");
ps.setInt(1, accId);
ps.setString(2, session.getRemoteAddress().toString());
ps.executeUpdate();
}
} catch (SQLException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} finally {
try {
if (ps != null && !ps.isClosed()) {
ps.close();
}
if (rs != null && !rs.isClosed()) {
rs.close();
}
} catch (SQLException e) {
}
}
if (loginok == 0) {
loginattempt = 0;
}
return loginok;
}
public Calendar getTempBanCalendar() {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
final Calendar lTempban = Calendar.getInstance();
try {
ps = con.prepareStatement("SELECT `tempban` FROM accounts WHERE id = ?");
ps.setInt(1, getAccID());
rs = ps.executeQuery();
long blubb = rs.getLong("tempban");
if (blubb == 0) { // basically if timestamp in db is 0000-00-00
return null;
}
final Calendar today = Calendar.getInstance();
lTempban.setTimeInMillis(rs.getTimestamp("tempban").getTime());
if (today.getTimeInMillis() < lTempban.getTimeInMillis()) {
return lTempban;
}
return null;
} catch (SQLException e) {
// idc
} finally {
try {
if (ps != null) {
ps.close();
}
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
}
}
return null;// why oh why!?!
}
public static long dottedQuadToLong(String dottedQuad) throws RuntimeException {
String[] quads = dottedQuad.split("\\.");
if (quads.length != 4) {
throw new RuntimeException("Invalid IP Address format.");
}
long ipAddress = 0;
for (int i = 0; i < 4; i++) {
int quad = Integer.parseInt(quads[i]);
ipAddress += (long) (quad % 256) * (long) Math.pow(256, (double) (4 - i));
}
return ipAddress;
}
public static String getChannelServerIPFromSubnet(String clientIPAddress, byte channel) {
long ipAddress = dottedQuadToLong(clientIPAddress);
Properties subnetInfo = Server.getInstance().getSubnetInfo();
if (subnetInfo.contains("net.login.subnetcount")) {
int subnetCount = Integer.parseInt(subnetInfo.getProperty("net.login.subnetcount"));
for (int i = 0; i < subnetCount; i++) {
String[] connectionInfo = subnetInfo.getProperty("net.login.subnet." + i).split(":");
long subnet = dottedQuadToLong(connectionInfo[0]);
long channelIP = dottedQuadToLong(connectionInfo[1]);
byte channelNumber = Byte.parseByte(connectionInfo[2]);
if (((ipAddress & subnet) == (channelIP & subnet)) && (channel == channelNumber)) {
return connectionInfo[1];
}
}
}
return "0.0.0.0";
}
public void updateMacs(String macData) {
macs.addAll(Arrays.asList(macData.split(", ")));
StringBuilder newMacData = new StringBuilder();
Iterator<String> iter = macs.iterator();
PreparedStatement ps = null;
while (iter.hasNext()) {
String cur = iter.next();
newMacData.append(cur);
if (iter.hasNext()) {
newMacData.append(", ");
}
}
try {
ps = DatabaseConnection.getConnection().prepareStatement("UPDATE accounts SET macs = ? WHERE id = ?");
ps.setString(1, newMacData.toString());
ps.setInt(2, accId);
ps.executeUpdate();
ps.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (ps != null && !ps.isClosed()) {
ps.close();
}
} catch (SQLException ex) {
}
}
}
public void setAccID(int id) {
this.accId = id;
}
public int getAccID() {
return accId;
}
public void updateLoginState(int newstate) {
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("UPDATE accounts SET loggedin = ?, lastlogin = CURRENT_TIMESTAMP() WHERE id = ?");
ps.setInt(1, newstate);
ps.setInt(2, getAccID());
ps.executeUpdate();
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
if (newstate == LOGIN_NOTLOGGEDIN) {
loggedIn = false;
serverTransition = false;
} else {
serverTransition = (newstate == LOGIN_SERVER_TRANSITION);
loggedIn = !serverTransition;
}
}
public int getLoginState() {
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT loggedin, lastlogin, UNIX_TIMESTAMP(birthday) as birthday FROM accounts WHERE id = ?");
ps.setInt(1, getAccID());
ResultSet rs = ps.executeQuery();
if (!rs.next()) {
rs.close();
ps.close();
throw new RuntimeException("getLoginState - MapleClient");
}
birthday = Calendar.getInstance();
long blubb = rs.getLong("birthday");
if (blubb > 0) {
birthday.setTimeInMillis(blubb * 1000);
}
int state = rs.getInt("loggedin");
if (state == LOGIN_SERVER_TRANSITION) {
if (rs.getTimestamp("lastlogin").getTime() + 30000 < System.currentTimeMillis()) {
state = LOGIN_NOTLOGGEDIN;
updateLoginState(LOGIN_NOTLOGGEDIN);
}
} else if (state == LOGIN_LOGGEDIN && player == null) {
state = LOGIN_NOTLOGGEDIN;
updateLoginState(LOGIN_NOTLOGGEDIN);
}
rs.close();
ps.close();
if (state == LOGIN_LOGGEDIN) {
loggedIn = true;
} else if (state == LOGIN_SERVER_TRANSITION) {
ps = con.prepareStatement("UPDATE accounts SET loggedin = 0 WHERE id = ?");
ps.setInt(1, getAccID());
ps.executeUpdate();
ps.close();
} else {
loggedIn = false;
}
return state;
} catch (SQLException e) {
loggedIn = false;
e.printStackTrace();
throw new RuntimeException("login state");
}
}
public void setBirthday(String month, String day, String year) {
birthday.set(Integer.parseInt(year), Integer.parseInt(month), Integer.parseInt(day));
}
public boolean checkBirthDate(Calendar date) {
return date.get(Calendar.YEAR) == birthday.get(Calendar.YEAR) && date.get(Calendar.MONTH) == birthday.get(Calendar.MONTH) && date.get(Calendar.DAY_OF_MONTH) == birthday.get(Calendar.DAY_OF_MONTH);
}
private void removePlayer() {
try {
getWorldServer().removePlayer(player);// out of channel too.
Server.getInstance().getLoad(world).get(channel).decrementAndGet();
if (player.getMap() != null) {
player.getMap().removePlayer(player);
}
if (player.getTrade() != null) {
MapleTrade.cancelTrade(player);
}
if (gmlevel > 0) {
GMServer.getInstance().removeInGame(player.getName());
}
player.cancelAllBuffs(true);
if (player.getEventInstance() != null) {
player.getEventInstance().playerDisconnected(player);
}
player.cancelAllDebuffs();
} catch (final Throwable t) {
MapleLogger.print(MapleLogger.ACCOUNT_STUCK, t);
}
}
public final void disconnect() {// once per MapleClient instance
try {
if (player != null && isLoggedIn()) {
removePlayer();
player.saveToDB(true);
World worlda = getWorldServer();
player.saveCooldowns();
player.unequipPendantOfSpirit();
MapleMiniGame game = player.getMiniGame();
if (game != null) {
player.setMiniGame(null);
if (game.isOwner(player)) {
player.getMap().broadcastMessage(MaplePacketCreator.removeCharBox(player));
game.broadcastToVisitor(MaplePacketCreator.getMiniGameClose());
} else {
game.removeVisitor(player);
}
}
HiredMerchant merchant = player.getHiredMerchant();
if (merchant != null) {
if (merchant.isOwner(player)) {
merchant.setOpen(true);
} else {
merchant.removeVisitor(player);
}
try {
merchant.saveItems(false);
} catch (SQLException ex) {
Output.print("An error occurred while saving Hired Merchant items.");
}
}
if (player.getMessenger() != null) {
MapleMessengerCharacter messengerplayer = new MapleMessengerCharacter(player);
worlda.leaveMessenger(player.getMessenger().getId(), messengerplayer);
player.setMessenger(null);
}
NPCScriptManager npcsm = NPCScriptManager.getInstance();
if (npcsm != null) {
npcsm.dispose(this);
}
if (!player.isAlive()) {
player.setHp(50, true);
}
for (MapleQuestStatus status : player.getStartedQuests()) {
MapleQuest quest = status.getQuest();
if (quest.getTimeLimit() > 0) {
MapleQuestStatus newStatus = new MapleQuestStatus(quest, MapleQuestStatus.Status.NOT_STARTED);
newStatus.setForfeited(player.getQuest(quest).getForfeited() + 1);
player.updateQuest(newStatus);
}
}
if (player.getParty() != null) {
MaplePartyCharacter chrp = player.getMPC();
chrp.setOnline(false);
worlda.updateParty(player.getParty().getId(), PartyOperation.LOG_ONOFF, chrp);
}
if (!this.serverTransition && isLoggedIn()) {
worlda.loggedOff(player.getName(), player.getId(), channel, player.getBuddylist().getBuddyIds());
} else {
worlda.loggedOn(player.getName(), player.getId(), channel, player.getBuddylist().getBuddyIds());
}
if (player.getGuildId() > 0) {
Server.getInstance().setGuildMemberOnline(player.getMGC(), false, (byte) -1);
int allianceId = player.getGuild().getAllianceId();
if (allianceId > 0) {
Server.getInstance().allianceMessage(allianceId, MaplePacketCreator.allianceMemberOnline(player, false), player.getId(), -1);
}
}
}
} finally {
player = null;
session.close(true);
}
if (!this.serverTransition) {
this.updateLoginState(LOGIN_NOTLOGGEDIN);
}
}
public final void empty() {
if (this.player != null) {
this.player.getMount().empty();
this.player.empty();
}
this.player = null;
this.session = null;
this.engines.clear();
this.engines = null;
this.send = null;
this.receive = null;
this.channel = -1;
}
public byte getChannel() {
return channel;
}
public Channel getChannelServer() {
return Server.getInstance().getChannel(world, channel);
}
public World getWorldServer() {
return Server.getInstance().getWorld(world);
}
public Channel getChannelServer(byte channel) {
return Server.getInstance().getChannel(world, channel);
}
public boolean deleteCharacter(int cid) {
Connection con = DatabaseConnection.getConnection();
try {
PreparedStatement ps = con.prepareStatement("SELECT id, guildid, guildrank, name, allianceRank FROM characters WHERE id = ? AND accountid = ?");
ps.setInt(1, cid);
ps.setInt(2, accId);
ResultSet rs = ps.executeQuery();
if (!rs.next()) {
rs.close();
ps.close();
return false;
}
if (rs.getInt("guildid") > 0) {
try {
Server.getInstance().deleteGuildCharacter(new MapleGuildCharacter(cid, 0, rs.getString("name"), (byte) -1, (byte) -1, 0, rs.getInt("guildrank"), rs.getInt("guildid"), false, rs.getInt("allianceRank")));
} catch (Exception re) {
rs.close();
ps.close();
return false;
}
}
rs.close();
ps = con.prepareStatement("DELETE FROM wishlists WHERE charid = ?");
ps.setInt(1, cid);
ps.executeUpdate();
ps = con.prepareStatement("DELETE FROM characters WHERE id = ?");
ps.setInt(1, cid);
ps.executeUpdate();
String[] toDel = {"famelog", "inventoryitems", "keymap", "queststatus", "savedlocations", "skillmacros", "skills", "eventstats"};
for (String s : toDel) {
ps = con.prepareStatement("DELETE FROM `" + s + "` WHERE characterid = ?");
ps.setInt(1, cid);
ps.executeUpdate();
}
return true;
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
public String getAccountName() {
return accountName;
}
public void setAccountName(String a) {
this.accountName = a;
}
public void setChannel(byte channel) {
this.channel = channel;
}
public byte getWorld() {
return world;
}
public void setWorld(byte world) {
this.world = world;
}
public void pongReceived() {
lastPong = System.currentTimeMillis();
}
public void sendPing() {
final long then = System.currentTimeMillis();
announce(MaplePacketCreator.getPing());
TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
try {
if (lastPong < then) {
if (getSession() != null && getSession().isConnected()) {
getSession().close(true);
}
}
} catch (NullPointerException e) {
}
}
}, 15000);
}
public Set<String> getMacs() {
return Collections.unmodifiableSet(macs);
}
public int gmLevel() {
return this.gmlevel;
}
public void setScriptEngine(String name, ScriptEngine e) {
engines.put(name, e);
}
public ScriptEngine getScriptEngine(String name) {
return engines.get(name);
}
public void removeScriptEngine(String name) {
engines.remove(name);
}
public ScheduledFuture<?> getIdleTask() {
return idleTask;
}
public void setIdleTask(ScheduledFuture<?> idleTask) {
this.idleTask = idleTask;
}
public NPCConversationManager getCM() {
return NPCScriptManager.getInstance().getCM(this);
}
public QuestActionManager getQM() {
return QuestScriptManager.getInstance().getQM(this);
}
public boolean acceptToS() {
boolean disconnectForBeingAFaggot = false;
if (accountName == null) {
return true;
}
try {
PreparedStatement ps = DatabaseConnection.getConnection().prepareStatement("SELECT `tos` FROM accounts WHERE id = ?");
ps.setInt(1, accId);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
if (rs.getByte("tos") == 1) {
disconnectForBeingAFaggot = true;
}
}
ps.close();
rs.close();
rs = null;
ps = DatabaseConnection.getConnection().prepareStatement("UPDATE accounts SET tos = 1 WHERE id = ?");
ps.setInt(1, accId);
ps.executeUpdate();
ps.close();
} catch (SQLException e) {
}
return disconnectForBeingAFaggot;
}
private static class CharNameAndId {
public String name;
public int id;
public CharNameAndId(String name, int id) {
super();
this.name = name;
this.id = id;
}
}
public static boolean checkHash(String hash, String type, String password) {
try {
MessageDigest digester = MessageDigest.getInstance(type);
digester.update(password.getBytes("UTF-8"), 0, password.length());
return HexTool.toString(digester.digest()).replace(" ", "").toLowerCase().equals(hash);
} catch (Exception e) {
throw new RuntimeException("Encoding the string failed", e);
}
}
public short getCharacterSlots() {
return characterSlots;
}
public boolean gainCharacterSlot() {
if (characterSlots < 15) {
Connection con = DatabaseConnection.getConnection();
try {
PreparedStatement ps = con.prepareStatement("UPDATE accounts SET characterslots = ? WHERE id = ?");
ps.setInt(1, this.characterSlots += 1);
ps.setInt(2, accId);
ps.executeUpdate();
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
return true;
}
return false;
}
public final byte getGReason() {
final Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = con.prepareStatement("SELECT `greason` FROM `accounts` WHERE id = ?");
ps.setInt(1, accId);
rs = ps.executeQuery();
if (rs.next()) {
return rs.getByte("greason");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (ps != null) {
ps.close();
}
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
}
}
return 0;
}
public byte getGender() {
return gender;
}
public void setGender(byte m) {
this.gender = m;
try {
PreparedStatement ps = DatabaseConnection.getConnection().prepareStatement("UPDATE accounts SET gender = ? WHERE id = ?");
ps.setByte(1, gender);
ps.setInt(2, accId);
ps.executeUpdate();
ps.close();
} catch (SQLException e) {
}
}
public void announce(MaplePacket packet) {
session.write(packet);
}
public void saveLastKnownIP() {
String sockAddr = getSession().getRemoteAddress().toString();
Connection con;
try {
con = DatabaseConnection.getConnection();
} catch (Exception e) {
MapleLogger.print(MapleLogger.EXCEPTION_CAUGHT, e);
return;
}
try {
PreparedStatement ps = con.prepareStatement("UPDATE accounts SET lastknownip = ? WHERE name = ?");
ps.setString(1, sockAddr.substring(1, sockAddr.lastIndexOf(':')));
ps.setString(2, accountName);
ps.executeUpdate();
ps.close();
} catch (SQLException e) {
MapleLogger.print(MapleLogger.EXCEPTION_CAUGHT, e);
}
}
public boolean isGM() {
return (gmlevel >= 2);
}
public boolean isDeveloper() {
return (gmlevel >= 4);
}
public static String getAccountNameById(int id) {
try {
PreparedStatement ps = DatabaseConnection.getConnection().prepareStatement("SELECT name FROM accounts WHERE id = ?");
ps.setInt(1, id);
ResultSet rs = ps.executeQuery();
if (!rs.next()) {
rs.close();
ps.close();
return null;
}
String name = rs.getString("name");
rs.close();
ps.close();
return name;
} catch (Exception e) {
}
return null;
}
}