package org.pokenet.server.network;
import java.net.Socket;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.Queue;
import org.apache.mina.core.session.IoSession;
import org.pokenet.server.GameServer;
import org.pokenet.server.backend.entity.Bag;
import org.pokenet.server.backend.entity.PlayerChar;
import org.pokenet.server.backend.entity.PokemonBox;
import org.pokenet.server.backend.entity.PlayerChar.Language;
import org.pokenet.server.battle.DataService;
import org.pokenet.server.battle.Pokemon;
import org.pokenet.server.battle.PokemonSpecies;
import org.pokenet.server.battle.Pokemon.ExpTypes;
import org.pokenet.server.battle.mechanics.PokemonNature;
import org.pokenet.server.battle.mechanics.moves.MoveListEntry;
import org.pokenet.server.feature.TimeService;
/**
* Handles logging players in
* @author shadowkanji
*
*/
public class LoginManager implements Runnable {
private Queue<Object []> m_loginQueue;
private Thread m_thread;
private boolean m_isRunning;
private MySqlManager m_database;
private Queue<Object []> m_passChangeQueue;
/**
* Default constructor. Requires a logout manager to be passed in so the server
* can check if player's data is not being saved as they are logging in.
* @param manager
*/
public LoginManager(LogoutManager manager) {
m_database = new MySqlManager();
m_loginQueue = new LinkedList<Object []>();
m_passChangeQueue = new LinkedList<Object []>();
m_thread = null;
}
/**
* Returns the ip address of a session
* @param s
* @return
*/
private String getIp(IoSession s) {
if(s != null) {
String ip = s.getRemoteAddress().toString();
ip = ip.substring(1);
ip = ip.substring(0, ip.indexOf(":"));
return ip;
} else {
return "";
}
}
/**
* Attempts to login a player. Upon success, it sends a packet to the player to inform them they are logged in.
* @param session
* @param l
* @param username
* @param password
*/
private void attemptLogin(IoSession session, char l, String username, String password) {
try {
//Check if we haven't reach the player limit
if(TcpProtocolHandler.getPlayerCount() >= GameServer.getMaxPlayers()) {
session.write("l2");
return;
}
//First connect to the database
m_database = new MySqlManager();
if(!m_database.connect(GameServer.getDatabaseHost(), GameServer.getDatabaseUsername(), GameServer.getDatabasePassword())) {
session.write("l1");
return;
}
//Select the database
if(!m_database.selectDatabase(GameServer.getDatabaseName())) {
session.write("l1");
return;
}
//Now, check they are not banned
ResultSet result = m_database.query("SELECT * FROM pn_bans WHERE ip='" + getIp(session) + "'");
if(result != null && result.first()) {
//This is player is banned, inform them
session.write("l4");
return;
}
//Then find the member's information
result = m_database.query("SELECT * FROM pn_members WHERE username='" + MySqlManager.parseSQL(username) + "'");
if(!result.first()){
//Member doesn't exist, say user or pass wrong. We don't want someone to guess usernames.
session.write("le");
return;
}
//Check if the password is correct
if(result.getString("password").compareTo(password) == 0) {
//Remove the player from the map to prevent duplicates
GameServer.getServiceManager().getMovementService().removePlayer(username);
long time = System.currentTimeMillis();
//Now check if they are logged in anywhere else
if(result.getString("lastLoginServer").equalsIgnoreCase(GameServer.getServerName())) {
/*
* They are already logged in on this server.
* Attach the session to the existing player if they exist, if not, just log them in
*/
if(TcpProtocolHandler.containsPlayer(username)) {
PlayerChar p = TcpProtocolHandler.getPlayer(username);
p.getTcpSession().setAttribute("player", null);
p.setLastLoginTime(time);
p.getTcpSession().close(true);
p.setTcpSession(session);
p.setLanguage(Language.values()[Integer.parseInt(String.valueOf(l))]);
m_database.query("UPDATE pn_members SET lastLoginServer='" + MySqlManager.parseSQL(GameServer.getServerName()) + "', lastLoginTime='" + time + "' WHERE username='" + MySqlManager.parseSQL(username) + "'");
m_database.query("UPDATE pn_members SET lastLoginIP='" + getIp(session) + "' WHERE username='" + MySqlManager.parseSQL(username) + "'");
m_database.query("UPDATE pn_members SET lastLanguageUsed='" + l + "' WHERE username='" + MySqlManager.parseSQL(username) + "'");
session.setAttribute("player", p);
this.initialiseClient(p, session);
} else {
session.write("l3");
return;
}
} else if(result.getString("lastLoginServer").equalsIgnoreCase("null")) {
/*
* They are not logged in elsewhere, log them in
*/
this.login(username, l, session, result);
} else {
/*
* They are logged in somewhere else.
* Check if the server is up, if it is, don't log them in. If not, log them in
*/
// if(InetAddress.getByName(result.getString("lastLoginServer")).isReachable(5000)) {
// session.write("l3");
// return;
// } else {
// //The server they were on went down and they are trying to login elsewhere
// this.login(username, l, session, result);
// }
try{
/**
* This is a dirty hack. The old method used isReachable(5000) to determine if the server was alive.
* isReachable doesn't work unless you run as root due to sending IMCP Echo packets being forbidden
* under normal user accounts.
*
* Instead, We open a socket to determine if server's alive. If it crashes, then server's down.
*/
Socket socket = new Socket(result.getString("lastLoginServer"),7002);
socket.close();
session.write("l3");
return;
}catch(Exception e){
//The server they were on went down and they are trying to login elsewhere
this.login(username, l, session, result);
}
}
} else {
//Password is wrong, say so.
session.write("le");
return;
}
m_database.close();
} catch (Exception e) {
e.printStackTrace();
session.write("lu");
/*
* Something went wrong so make sure the player is registered as logged out
*/
m_database.query("UPDATE pn_members SET lastLoginServer='null' WHERE username='" + MySqlManager.parseSQL(username) + "'");
m_database.close();
}
}
/**
* Places a player in the login queue
* @param session
* @param username
* @param password
* @param forceLogin - true if player wants to force login
*/
public void queuePlayer(IoSession session, String username, String password) {
if(m_thread == null || !m_thread.isAlive()) {
start();
}
m_loginQueue.offer(new Object[] {session, username, password});
}
/**
* Places a player in the queue to update their password
* @param session
* @param username
* @param newPassword
* @param oldPassword
*/
public void queuePasswordChange(IoSession session, String username, String newPassword, String oldPassword) {
if(m_thread == null || !m_thread.isAlive()) {
start();
}
m_passChangeQueue.offer(new Object[] {session, username, newPassword, oldPassword});
}
/**
* Called by Thread.start()
*/
public void run() {
Object [] o;
IoSession session;
String username;
String password;
String newPassword;
char l;
while(m_isRunning) {
synchronized(m_loginQueue) {
try {
if(m_loginQueue.peek() != null) {
o = m_loginQueue.poll();
session = (IoSession) o[0];
l = ((String) o[1]).charAt(0);
username = ((String) o[1]).substring(1);
password = (String) o[2];
this.attemptLogin(session, l, username, password);
}
} catch (Exception e) {
e.printStackTrace();
}
}
try {
Thread.sleep(500);
} catch (Exception e) {}
synchronized(m_passChangeQueue) {
try {
if(m_passChangeQueue.peek() != null) {
o = m_passChangeQueue.poll();
session = (IoSession) o[0];
username = (String) o[1];
newPassword = (String) o[2];
password = (String) o[3];
this.changePass(username, newPassword, password, session);
}
} catch (Exception e) {
e.printStackTrace();
}
}
try {
Thread.sleep(500);
} catch (Exception e) {}
}
m_thread = null;
}
/**
* Starts the login manager
*/
public void start() {
if(m_thread == null || !m_thread.isAlive()) {
m_thread = new Thread(this);
m_isRunning = true;
m_thread.start();
}
}
/**
* Stops the login manager
*/
public void stop() {
m_isRunning = false;
}
/**
* Changes the password of a player
* @param username
* @param newPassword
* @param oldPassword
* @param session
*/
private void changePass(String username, String newPassword, String oldPassword, IoSession session) {
m_database = new MySqlManager();
if(m_database.connect(GameServer.getDatabaseHost(), GameServer.getDatabaseUsername(), GameServer.getDatabasePassword())) {
if(m_database.selectDatabase(GameServer.getDatabaseName())) {
ResultSet result = m_database.query("SELECT * FROM pn_members WHERE username='" + MySqlManager.parseSQL(username) + "'");
try {
if(result.first()){
// if we got a result, compare their old password to the one we have stored for them
if(result.getString("password").compareTo(oldPassword) == 0) {
// old password matches the one on file, therefore they got their old password correct, so it can be changed to their new one
m_database.query("UPDATE pn_members SET password='" + MySqlManager.parseSQL(newPassword) + "' WHERE username='" + MySqlManager.parseSQL(username) + "'");
// tell them their password was changed successfully
session.write("ps");
return;
}
}
} catch (SQLException e) {
e.printStackTrace();
}
m_database.close();
}
}
// tell them we failed to change their password
session.write("pe");
}
/**
* Logs in a player
* @param username
* @param language
* @param session
* @param result
*/
private void login(String username, char language, IoSession session, ResultSet result) {
//They are not logged in elsewhere, set the current login to the current server
long time = System.currentTimeMillis();
/*
* Attempt to log the player in
*/
PlayerChar p = getPlayerObject(result);
p.setLastLoginTime(time);
p.setTcpSession(session);
p.setLanguage(Language.values()[Integer.parseInt(String.valueOf(language))]);
/*
* Update the database with login information
*/
m_database.query("UPDATE pn_members SET lastLoginServer='" + MySqlManager.parseSQL(GameServer.getServerName()) + "', lastLoginTime='" + time + "' WHERE username='" + MySqlManager.parseSQL(username) + "'");
m_database.query("UPDATE pn_members SET lastLoginIP='" + getIp(session) + "' WHERE username='" + MySqlManager.parseSQL(username) + "'");
m_database.query("UPDATE pn_members SET lastLanguageUsed='" + language + "' WHERE username='" + MySqlManager.parseSQL(username) + "'");
session.setAttribute("player", p);
/*
* Send success packet to player, set their map and add them to a movement service
*/
this.initialiseClient(p, session);
/*
* Add them to the list of players
*/
TcpProtocolHandler.addPlayer(p);
UdpProtocolHandler.addPlayer(p);
GameServer.getInstance().updatePlayerCount();
System.out.println("INFO: " + username + " logged in.");
}
/**
* Sends initial information to the client
* @param p
* @param session
*/
private void initialiseClient(PlayerChar p, IoSession session) {
session.write("ls" + p.getId() + "," + TimeService.getTime());
//Add them to the map
p.setMap(GameServer.getServiceManager().getMovementService().getMapMatrix().getMapByGamePosition(p.getMapX(), p.getMapY()), null);
//Add them to a movement service
GameServer.getServiceManager().getMovementService().getMovementManager().addPlayer(p);
//Send their Pokemon information to them
p.updateClientParty();
//Send bag to them
p.updateClientBag();
//Send money
p.updateClientMoney();
//Send their friend list to them
// p.updateClientFriends();
//Send badges
p.updateClientBadges();
p.initializeClientSkills();
}
/**
* Returns a playerchar object from a resultset of player data
* @param data
* @return
*/
private PlayerChar getPlayerObject(ResultSet result) {
try {
PlayerChar p = new PlayerChar();
Pokemon [] party = new Pokemon[6];
PokemonBox[] boxes = new PokemonBox[9];
p.setName(result.getString("username"));
p.setVisible(true);
//Set co-ordinates
p.setX(result.getInt("x"));
p.setY(result.getInt("y"));
p.setMapX(result.getInt("mapX"));
p.setMapY(result.getInt("mapY"));
p.setId(result.getInt("id"));
p.setAdminLevel(result.getInt("adminLevel"));
p.setMuted(result.getBoolean("muted"));
p.setLastHeal(result.getInt("healX"), result.getInt("healY"), result.getInt("healMapX"), result.getInt("healMapY"));
p.setSurfing(Boolean.parseBoolean(result.getString("isSurfing")));
//Set money and skills
p.setSprite(result.getInt("sprite"));
p.setMoney(result.getInt("money"));
p.setHerbalismExp(result.getInt("skHerb"));
p.setCraftingExp(result.getInt("skCraft"));
p.setFishingExp(result.getInt("skFish"));
p.setTrainingExp(result.getInt("skTrain"));
p.setCoordinatingExp(result.getInt("skCoord"));
p.setBreedingExp(result.getInt("skBreed"));
//Retrieve refences to all Pokemon
int partyId = result.getInt("party");
ResultSet partyData = m_database.query("SELECT * FROM pn_party WHERE id='" + partyId + "'");
partyData.first();
ResultSet pokemons = m_database.query("SELECT * FROM pn_pokemon WHERE currentTrainerName='" + p.getName() + "'");
int boxNumber = 0;
int boxPosition = 0;
/* Loop through all Pokemon belonging to this player and add them to their party/box */
while(pokemons.next()) {
boolean isParty = false;
int partyIndex = -1;
/* Checks if Pokemon is in party */
for(int i = 0; i < 6; i++) {
if(partyData.getInt("pokemon" + i) == pokemons.getInt("id")) {
isParty = true;
partyIndex = i;
break;
}
}
/* If the pokemon is in party, add it to party */
if(isParty) {
party[partyIndex] = getPokemonObject(pokemons);
} else {
/* Else, add it to box if space is available */
if(boxNumber < 9) {
/* Avoid null pointers */
if(boxes[boxNumber] == null)
boxes[boxNumber] = new PokemonBox();
if(boxes[boxNumber].getPokemon() == null)
boxes[boxNumber].setPokemon(new Pokemon[30]);
/* If there's space in this box, add it to the box */
if(boxPosition < 30) {
boxes[boxNumber].setPokemon(boxPosition, getPokemonObject(pokemons));
} else {
/* Else open up a new box and add it to box */
boxPosition = 0;
boxNumber++;
if(boxNumber < 9) {
boxes[boxNumber].setPokemon(new Pokemon[30]);
boxes[boxNumber].setPokemon(boxPosition, getPokemonObject(pokemons));
}
}
boxPosition++;
}
}
}
p.setParty(party);
p.setBoxes(boxes);
//Attach bag
p.setBag(getBagObject(m_database.query("SELECT * FROM pn_bag WHERE member='" + result.getInt("id") + "'"),p.getId()));
//Attach badges
p.generateBadges(result.getString("badges"));
return p;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* Returns a Pokemon object based on a set of data
* @param data
* @return
*/
private Pokemon getPokemonObject(ResultSet data) {
if(data != null) {
try {
/*
* First generate the Pokemons moves
*/
MoveListEntry [] moves = new MoveListEntry[4];
moves[0] = (data.getString("move0") != null && !data.getString("move0").equalsIgnoreCase("null") ?
DataService.getMovesList().getMove(data.getString("move0")) :
null);
moves[1] = (data.getString("move1") != null && !data.getString("move1").equalsIgnoreCase("null") ?
DataService.getMovesList().getMove(data.getString("move1")) :
null);
moves[2] = (data.getString("move2") != null && !data.getString("move2").equalsIgnoreCase("null") ?
DataService.getMovesList().getMove(data.getString("move2")) :
null);
moves[3] = (data.getString("move3") != null && !data.getString("move3").equalsIgnoreCase("null") ?
DataService.getMovesList().getMove(data.getString("move3")) :
null);
/*
* Create the new Pokemon
*/
Pokemon p = new Pokemon(
DataService.getBattleMechanics(),
PokemonSpecies.getDefaultData().getPokemonByName(data.getString("speciesName"))
,
PokemonNature.getNatureByName(data.getString("nature")),
data.getString("abilityName"),
data.getString("itemName"),
data.getInt("gender"),
data.getInt("level"),
new int[] {
data.getInt("ivHP"),
data.getInt("ivATK"),
data.getInt("ivDEF"),
data.getInt("ivSPD"),
data.getInt("ivSPATK"),
data.getInt("ivSPDEF")},
new int[] {
data.getInt("evHP"),
data.getInt("evATK"),
data.getInt("evDEF"),
data.getInt("evSPD"),
data.getInt("evSPATK"),
data.getInt("evSPDEF")},
moves,
new int[] {
data.getInt("ppUp0"),
data.getInt("ppUp1"),
data.getInt("ppUp2"),
data.getInt("ppUp3")
});
p.reinitialise();
/*
* Set exp, nickname, isShiny and exp gain type
*/
p.setBaseExp(data.getInt("baseExp"));
p.setExp(Double.parseDouble(data.getString("exp")));
p.setName(data.getString("name"));
p.setHappiness(data.getInt("happiness"));
p.setShiny(Boolean.parseBoolean(data.getString("isShiny")));
p.setExpType(ExpTypes.valueOf(data.getString("expType")));
p.setOriginalTrainer(data.getString("originalTrainerName"));
p.setDatabaseID(data.getInt("id"));
p.setDateCaught(data.getString("date"));
p.setIsFainted(Boolean.parseBoolean(data.getString("isFainted")));
/*
* Contest stats (beauty, cute, etc.)
*/
String [] cstats = data.getString("contestStats").split(",");
p.setContestStat(0, Integer.parseInt(cstats[0]));
p.setContestStat(1, Integer.parseInt(cstats[1]));
p.setContestStat(2, Integer.parseInt(cstats[2]));
p.setContestStat(3, Integer.parseInt(cstats[3]));
p.setContestStat(4, Integer.parseInt(cstats[4]));
/*
* Sets the stats
*/
p.calculateStats(true);
p.setHealth(data.getInt("hp"));
p.setRawStat(1, data.getInt("atk"));
p.setRawStat(2, data.getInt("def"));
p.setRawStat(3, data.getInt("speed"));
p.setRawStat(4, data.getInt("spATK"));
p.setRawStat(5, data.getInt("spDEF"));
/*
* Sets the pp information
*/
p.setPp(0, data.getInt("pp0"));
p.setPp(1, data.getInt("pp1"));
p.setPp(2, data.getInt("pp2"));
p.setPp(3, data.getInt("pp3"));
p.setPpUp(0, data.getInt("ppUp0"));
p.setPpUp(0, data.getInt("ppUp1"));
p.setPpUp(0, data.getInt("ppUp2"));
p.setPpUp(0, data.getInt("ppUp3"));
return p;
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
/**
* Returns a bag object
* @param data
* @return
*/
private Bag getBagObject(ResultSet data, int memberid) {
try {
Bag b = new Bag(memberid);
while(data.next()){
b.addItem(data.getInt("item"), data.getInt("quantity"));
}
return b;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}