/**
* This file is part of aion-emu <aion-emu.com>.
*
* aion-emu is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* aion-emu 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with aion-emu. If not, see <http://www.gnu.org/licenses/>.
*/
package com.aionemu.gameserver.network.loginserver;
import java.nio.channels.SocketChannel;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.apache.log4j.Logger;
import com.aionemu.commons.network.Dispatcher;
import com.aionemu.commons.network.NioServer;
import com.aionemu.gameserver.configs.network.NetworkConfig;
import com.aionemu.gameserver.model.account.AccountTime;
import com.aionemu.gameserver.network.aion.AionConnection;
import com.aionemu.gameserver.network.aion.serverpackets.SM_L2AUTH_LOGIN_CHECK;
import com.aionemu.gameserver.network.aion.serverpackets.SM_RECONNECT_KEY;
import com.aionemu.gameserver.network.factories.LoginServerConnectionFactory;
import com.aionemu.gameserver.network.loginserver.LoginServerConnection.State;
import com.aionemu.gameserver.network.loginserver.serverpackets.SM_ACCOUNT_AUTH;
import com.aionemu.gameserver.network.loginserver.serverpackets.SM_ACCOUNT_DISCONNECTED;
import com.aionemu.gameserver.network.loginserver.serverpackets.SM_ACCOUNT_RECONNECT_KEY;
import com.aionemu.gameserver.services.AccountService;
import com.aionemu.gameserver.utils.ThreadPoolManager;
import com.google.inject.Inject;
/**
* Utill class for connecting GameServer to LoginServer.
*
* @author -Nemesiss-
*
*/
public class LoginServer
{
/**
* Logger for this class.
*/
private static final Logger log = Logger.getLogger(LoginServer.class);
/**
* Map<accountId,Connection> for waiting request. This request is send to LoginServer and GameServer is waiting for
* response.
*/
private Map<Integer, AionConnection> loginRequests = new HashMap<Integer, AionConnection>();
/**
* Map<accountId,Connection> for all logged in accounts.
*/
private Map<Integer, AionConnection> loggedInAccounts = new HashMap<Integer, AionConnection>();
/**
* Connection to LoginServer.
*/
private LoginServerConnection loginServer;
private NioServer nioServer;
private LoginServerConnectionFactory lscFactory;
private boolean serverShutdown = false;
@Inject
private AccountService accountService;
@Inject
public void setNioServer(NioServer nioServer)
{
this.nioServer = nioServer;
}
@Inject
public void setLSConnectionFactory(LoginServerConnectionFactory lscFactory)
{
this.lscFactory = lscFactory;
}
/**
* Connect to LoginServer and return object representing this connection. This method is blocking and may block till
* connect successful.
*
* @return LoginServerConnection
*/
public LoginServerConnection connect()
{
SocketChannel sc;
for(;;)
{
loginServer = null;
log.info("Connecting to LoginServer: " + NetworkConfig.LOGIN_ADDRESS);
try
{
sc = SocketChannel.open(NetworkConfig.LOGIN_ADDRESS);
sc.configureBlocking(false);
Dispatcher d = nioServer.getReadWriteDispatcher();
loginServer = lscFactory.createConnection(sc, d);
return loginServer;
}
catch(Exception e)
{
log.info("Cant connect to LoginServer: " + e.getMessage());
}
try
{
/**
* 10s sleep
*/
Thread.sleep(10 * 1000);
}
catch(Exception e)
{
}
}
}
/**
* This method is called when we lost connection to LoginServer. We will disconnects all aionClients waiting for
* LoginServer response and also try reconnect to LoginServer.
*/
public void loginServerDown()
{
log.warn("Connection with LoginServer lost...");
loginServer = null;
synchronized(this)
{
/**
* We lost connection for LoginServer so client pending authentication should be disconnected [cuz
* authentication will never ends]
*/
for(AionConnection client : loginRequests.values())
{
// TODO! somme error packet!
client.close(/* closePacket, */true);
}
loginRequests.clear();
}
/**
* Reconnect after 5s if not server shutdown sequence
*/
if (!serverShutdown) {
ThreadPoolManager.getInstance().schedule(new Runnable(){
@Override
public void run()
{
connect();
}
}, 5000);
}
}
/**
* Notify that client is disconnected - we must clear waiting request to LoginServer if any to prevent leaks. Also
* notify LoginServer that this account is no longer on GameServer side.
*
* @param client
*/
public void aionClientDisconnected(int accountId)
{
synchronized(this)
{
loginRequests.remove(accountId);
loggedInAccounts.remove(accountId);
}
if(loginServer != null && loginServer.getState() == State.AUTHED)
loginServer.sendPacket(new SM_ACCOUNT_DISCONNECTED(accountId));
}
/**
* Starts authentication procedure of this client - LoginServer will sends response with information about account
* name if authentication is ok.
*
* @param accountId
* @param client
* @param loginOk
* @param playOk1
* @param playOk2
*/
public void requestAuthenticationOfClient(int accountId, AionConnection client, int loginOk, int playOk1, int playOk2)
{
/**
* There are no connection to LoginServer. We should disconnect this client since authentication is not
* possible.
*/
if(loginServer == null || loginServer.getState() != State.AUTHED)
{
System.out.println("LS !!! " + (loginServer == null ? "NULL" : loginServer.getState()));
// TODO! somme error packet!
client.close(/* closePacket, */true);
return;
}
synchronized(this)
{
if(loginRequests.containsKey(accountId))
return;
loginRequests.put(accountId, client);
}
loginServer.sendPacket(new SM_ACCOUNT_AUTH(accountId, loginOk, playOk1, playOk2));
}
/**
* This method is called by CM_ACCOUNT_AUTH_RESPONSE LoginServer packets to notify GameServer about results of
* client authentication.
*
* @param accountId
* @param accountName
* @param result
* @param accountTime
*/
public void accountAuthenticationResponse(int accountId, String accountName, boolean result, AccountTime accountTime, byte accessLevel, byte membership)
{
AionConnection client = loginRequests.remove(accountId);
if(client == null)
return;
if(result)
{
client.setState(AionConnection.State.AUTHED);
loggedInAccounts.put(accountId, client);
log.info("Account authed: " + accountId + " = " + accountName);
client.setAccount(accountService.getAccount(accountId, accountName, accountTime, accessLevel, membership));
client.sendPacket(new SM_L2AUTH_LOGIN_CHECK(true));
}
else
{
log.info("Account not authed: " + accountId);
client.close(new SM_L2AUTH_LOGIN_CHECK(false), true);
}
}
/**
* Starts reconnection to LoginServer procedure. LoginServer in response will send reconnection key.
*
* @param client
*/
public void requestAuthReconnection(AionConnection client)
{
/**
* There are no connection to LoginServer. We should disconnect this client since authentication is not
* possible.
*/
if(loginServer == null || loginServer.getState() != State.AUTHED)
{
// TODO! somme error packet!
client.close(/* closePacket, */false);
return;
}
synchronized(this)
{
if(loginRequests.containsKey(client.getAccount().getId()))
return;
loginRequests.put(client.getAccount().getId(), client);
}
loginServer.sendPacket(new SM_ACCOUNT_RECONNECT_KEY(client.getAccount().getId()));
}
/**
* This method is called by CM_ACCOUNT_RECONNECT_KEY LoginServer packets to give GameServer reconnection key for
* client that was requesting reconnection.
*
* @param accountId
* @param reconnectKey
*/
public void authReconnectionResponse(int accountId, int reconnectKey)
{
AionConnection client = loginRequests.remove(accountId);
if(client == null)
return;
log.info("Account reconnectimg: " + accountId + " = " + client.getAccount().getName());
client.close(new SM_RECONNECT_KEY(reconnectKey), false);
}
/**
* This method is called by CM_REQUEST_KICK_ACCOUNT LoginServer packets to request GameServer to disconnect client
* with given account id.
*
* @param accountId
*/
public void kickAccount(int accountId)
{
synchronized(this)
{
AionConnection client = loggedInAccounts.get(accountId);
if(client != null)
client.close(/* closePacket, */false);
/**
* This account is not logged in on this GameServer but LS thinks different...
*/
else
{
if(loginServer == null || loginServer.getState() != State.AUTHED)
return;
loginServer.sendPacket(new SM_ACCOUNT_DISCONNECTED(accountId));
}
}
}
/**
* Returns unmodifiable map with accounts that are logged in to current GS Map Key: Account ID Map Value:
* AionConnectionObject
*
* @return unmodifiable map wwith accounts
*/
public Map<Integer, AionConnection> getLoggedInAccounts()
{
return Collections.unmodifiableMap(loggedInAccounts);
}
/**
* When Game Server shutdown, have to close all pending client connection
*/
public void gameServerDisconnected()
{
synchronized(this)
{
serverShutdown = true;
/**
* GameServer shutting down, must close all pending login requests
*/
for(AionConnection client : loginRequests.values())
{
// TODO! somme error packet!
client.close(/* closePacket, */true);
}
loginRequests.clear();
loginServer.close(false);
}
log.info("GameServer disconnected from the Login Server...");
}
}