package l2p.gameserver.network;
import l2p.Config;
import l2p.database.DatabaseUtils;
import l2p.database.FiltredPreparedStatement;
import l2p.database.L2DatabaseFactory;
import l2p.database.ThreadConnection;
import l2p.extensions.network.MMOClient;
import l2p.extensions.network.MMOConnection;
import l2p.extensions.scripts.ScriptManager;
import l2p.gameserver.cache.Msg;
import l2p.gameserver.instancemanager.PlayerManager;
import l2p.gameserver.loginservercon.LSConnection;
import l2p.gameserver.loginservercon.SessionKey;
import l2p.gameserver.loginservercon.gspackets.PlayerLogout;
import l2p.gameserver.model.CharSelectInfoPackage;
import l2p.gameserver.model.L2ObjectsStorage;
import l2p.gameserver.model.L2Player;
import l2p.gameserver.serverpackets.L2GameServerPacket;
import l2p.gameserver.templates.StatsSet;
import l2p.util.GArray;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Represents a client connected on Game Server
*/
public final class L2GameClient extends MMOClient<MMOConnection<L2GameClient>>
{
protected static Logger _log = Logger.getLogger(L2GameClient.class.getName());
public GameCrypt _crypt = null;
private float _bonus = 1;
private long _bonus_expire = 0;
public GameClientState _state;
private int _upTryes = 0, _upTryesTotal = 0;
private long _upTryesRefresh = 0;
public static enum GameClientState
{
CONNECTED,
AUTHED,
IN_GAME
}
private String _loginName;
private L2Player _activeChar;
private SessionKey _sessionId = null;
private MMOConnection<L2GameClient> _connection = null;
//private byte[] _filter;
private int revision = 0;
private boolean _gameGuardOk = false;
public byte client_lang = -1;
private GArray<Integer> _charSlotMapping = new GArray<Integer>();
private PacketLogger pktLogger = null;
private boolean pktLoggerMatch = false;
public StatsSet account_fields = null;
public L2GameClient(MMOConnection<L2GameClient> con, boolean offline)
{
super(con);
if(!offline)
{
_state = GameClientState.CONNECTED;
_connection = con;
_sessionId = new SessionKey(-1, -1, -1, -1);
_crypt = new GameCrypt();
if(Config.LOG_CLIENT_PACKETS || Config.LOG_SERVER_PACKETS)
{
pktLogger = new PacketLogger(this, Config.PACKETLOGGER_FLUSH_SIZE);
if(Config.PACKETLOGGER_IPS != null)
{
if(Config.PACKETLOGGER_IPS.isIpInNets(getIpAddr()))
{
pktLoggerMatch = true;
}
}
}
}
else
{
_state = GameClientState.IN_GAME;
}
}
public L2GameClient(MMOConnection<L2GameClient> con)
{
this(con, false);
}
public void OnOfflineTrade()
{
_charSlotMapping = null;
}
public void disconnectOffline()
{
onDisconnection();
}
@Override
protected void onDisconnection()
{
if(pktLogger != null)
{
if(!pktLogger.assigned() && pktLoggerMatch)
pktLogger.assign();
pktLogger.close();
pktLogger = null;
}
if(getLoginName() == null || getLoginName().equals("") || _state != GameClientState.IN_GAME && _state != GameClientState.AUTHED)
return;
try
{
if(_activeChar != null && _activeChar.isInOfflineMode())
//LSConnection.getInstance().sendPacket(new PlayerLogout(getLoginName()));
return;
LSConnection.getInstance().removeAccount(this);
L2Player player = _activeChar;
_activeChar = null;
if(player != null && !player.isLogoutStarted())
{
player.scheduleDelete(Config.PLAYER_DISCONNECT_INGAME_TIME);
if(player.getNetConnection() != null)
{
if(!player.isInOfflineMode())
player.getNetConnection().closeNow(false);
player.setNetConnection(null);
}
player.setConnected(false);
if(Config.PLAYER_DISCONNECT_INGAME_TIME > 0)
player.broadcastUserInfo(false);
_activeChar = null;
}
setConnection(null);
}
catch(Exception e1)
{
_log.log(Level.WARNING, "error while disconnecting client", e1);
}
finally
{
LSConnection.getInstance().sendPacket(new PlayerLogout(getLoginName()));
}
super.onDisconnection();
}
public void markRestoredChar(int charslot) throws Exception
{
int objid = getObjectIdForSlot(charslot);
if(objid < 0)
{
return;
}
if(_activeChar != null && _activeChar.getObjectId() == objid)
{
_activeChar.setDeleteTimer(0);
}
ThreadConnection con = null;
FiltredPreparedStatement statement = null;
try
{
con = L2DatabaseFactory.getInstance().getConnection();
statement = con.prepareStatement("UPDATE characters SET deletetime=0 WHERE obj_id=?");
statement.setInt(1, objid);
statement.execute();
}
catch(Exception e)
{
_log.log(Level.WARNING, "data error on restore char:", e);
e.printStackTrace();
}
finally
{
DatabaseUtils.closeDatabaseCS(con, statement);
}
}
public void markToDeleteChar(int charslot) throws Exception
{
int objid = getObjectIdForSlot(charslot);
if(objid < 0)
{
return;
}
if(_activeChar != null && _activeChar.getObjectId() == objid)
{
_activeChar.setDeleteTimer((int) (System.currentTimeMillis() / 1000));
}
ThreadConnection con = null;
FiltredPreparedStatement statement = null;
try
{
con = L2DatabaseFactory.getInstance().getConnection();
statement = con.prepareStatement("UPDATE characters SET deletetime=? WHERE obj_id=?");
statement.setLong(1, (int) (System.currentTimeMillis() / 1000));
statement.setInt(2, objid);
statement.execute();
}
catch(Exception e)
{
_log.log(Level.WARNING, "data error on update deletime char:", e);
e.printStackTrace();
}
finally
{
DatabaseUtils.closeDatabaseCS(con, statement);
}
}
public void deleteChar(int charslot) throws Exception
{
//have to make sure active character must be nulled
if(_activeChar != null)
{
_activeChar.logout(false, false, true, true);
_activeChar = null;
}
int objid = getObjectIdForSlot(charslot);
if(objid == -1)
{
return;
}
PlayerManager.deleteCharByObjId(objid);
}
public L2Player loadCharFromDisk(int charslot)
{
Integer objectId = getObjectIdForSlot(charslot);
if(objectId == -1)
{
return null;
}
L2Player character = null;
L2Player old_player = L2ObjectsStorage.getPlayer(objectId);
if(old_player != null)
{
if(old_player.isInOfflineMode() || old_player.isLogoutStarted())
// оффтрейдового чара проще выбить чем восстанавливать
{
old_player.logout(false, false, true, true);
}
else
{
old_player.sendPacket(Msg.ANOTHER_PERSON_HAS_LOGGED_IN_WITH_THE_SAME_ACCOUNT);
LSConnection.getInstance().sendPacket(new PlayerLogout(getLoginName()));
if(old_player.getNetConnection() != null)
{
old_player.getNetConnection().setActiveChar(null);
old_player.getNetConnection().closeNow(false);
}
old_player.setLogoutStarted(false);
old_player.setNetConnection(this);
character = old_player;
}
}
if(character == null)
{
character = L2Player.restore(objectId);
}
if(character != null)
{
// preinit some values for each login
character.setRunning(); // running is default
character.standUp(); // standing is default
character.updateStats();
character.setOnlineStatus(true);
setActiveChar(character);
character.restoreBonus();
character.bookmarks.restore();
if(Config.USE_CLIENT_LANG)
{
switch(client_lang)
{
case 0:
character.setVar("lang@", "en");
break;
case 1:
character.setVar("lang@", "ru");
break;
}
}
if(pktLogger != null)
{
if(!pktLogger.assigned())
{
if(!pktLoggerMatch)
{
if(Config.PACKETLOGGER_CHARACTERS != null)
{
String char_name = character.getName();
for(int i = 0; i < Config.PACKETLOGGER_CHARACTERS.size(); i++)
{
String s_mask = Config.PACKETLOGGER_CHARACTERS.get(i);
if(char_name.matches(s_mask))
{
pktLoggerMatch = true;
break;
}
}
}
}
if(pktLoggerMatch)
{
pktLogger.assign();
}
else
{
pktLogger = null;
}
}
}
}
else
{
_log.warning("could not restore obj_id: " + objectId + " in slot:" + charslot);
}
return character;
}
public int getObjectIdForSlot(int charslot)
{
if(charslot < 0 || charslot >= _charSlotMapping.size())
{
_log.warning(getLoginName() + " tried to modify Character in slot " + charslot + " but no characters exits at that slot.");
return -1;
}
return _charSlotMapping.get(charslot);
}
@Override
public MMOConnection<L2GameClient> getConnection()
{
return _connection;
}
public L2Player getActiveChar()
{
return _activeChar;
}
/**
* @return Returns the sessionId.
*/
public SessionKey getSessionId()
{
return _sessionId;
}
public String getLoginName()
{
return _loginName;
}
public void setLoginName(String loginName)
{
_loginName = loginName;
if(pktLogger != null && !pktLoggerMatch && Config.PACKETLOGGER_ACCOUNTS != null)
{
for(int i = 0; i < Config.PACKETLOGGER_ACCOUNTS.size(); i++)
{
String s_mask = Config.PACKETLOGGER_ACCOUNTS.get(i);
if(loginName.matches(s_mask))
{
pktLoggerMatch = true;
break;
}
}
}
}
public void setActiveChar(L2Player cha)
{
_activeChar = cha;
if(cha != null)
// we store the connection in the player object so that external
// events can directly send events to the players client
// might be changed later to use a central event management and distribution system
{
_activeChar.setNetConnection(this);
}
}
public void setSessionId(SessionKey sessionKey)
{
_sessionId = sessionKey;
}
public void setCharSelection(CharSelectInfoPackage[] chars)
{
_charSlotMapping.clear();
for(CharSelectInfoPackage element : chars)
{
int objectId = element.getObjectId();
_charSlotMapping.add(objectId);
}
}
public void setCharSelection(int c)
{
_charSlotMapping.clear();
_charSlotMapping.add(c);
}
/**
* @return Returns the revision.
*/
public int getRevision()
{
return revision;
}
public boolean isGraciaEpilogue()
{
return getRevision() >= 140 && getRevision() <= 152;
}
/**
* @param revision The revision to set.
*/
public void setRevision(int revision)
{
this.revision = revision;
}
public void setGameGuardOk(boolean gameGuardOk)
{
_gameGuardOk = gameGuardOk;
}
public boolean isGameGuardOk()
{
return _gameGuardOk;
}
@Override
public boolean encrypt(final ByteBuffer buf, final int size)
{
if(pktLogger != null && Config.LOG_SERVER_PACKETS)
{
pktLogger.log_packet((byte) 1, buf, size);
}
_crypt.encrypt(buf.array(), buf.position(), size);
buf.position(buf.position() + size);
return true;
}
@Override
public boolean decrypt(ByteBuffer buf, int size)
{
boolean ret = _crypt.decrypt(buf.array(), buf.position(), size);
if(ret && pktLogger != null && Config.LOG_CLIENT_PACKETS)
{
pktLogger.log_packet((byte) 0, buf, size);
}
return ret;
}
public void sendPacket(L2GameServerPacket... gsp)
{
if(getConnection() == null)
{
return;
}
getConnection().sendPacket(gsp);
}
@SuppressWarnings("unchecked")
public void sendPackets(Collection<L2GameServerPacket> gsp)
{
if(getConnection() == null)
{
return;
}
getConnection().sendPackets((Collection) gsp);
}
public void close(L2GameServerPacket gsp)
{
getConnection().close(gsp);
}
public String getIpAddr()
{
try
{
return _connection.getSocket().getInetAddress().getHostAddress();
}
catch(NullPointerException e)
{
return "Disconnected";
}
}
public byte[] enableCrypt()
{
byte[] key = BlowFishKeygen.getRandomKey();
_crypt.setKey(key);
return key;
}
public float getBonus()
{
return _bonus;
}
public void setBonus(float bonus)
{
_bonus = bonus;
}
/**
* @return время окончания бонуса в unixtime
*/
public long getBonusExpire()
{
return _bonus_expire;
}
public void setBonusExpire(long time)
{
if(time < 0)
{
return;
}
if(time < System.currentTimeMillis() / 1000)
{
_bonus = 1;
return;
}
_bonus_expire = time;
}
public GameClientState getState()
{
return _state;
}
public void setState(GameClientState state)
{
_state = state;
}
/**
* @return произведено ли отключение игрока
*/
public boolean onClientPacketFail()
{
if(isPacketsFailed())
{
return true;
}
if(_upTryesRefresh == 0)
{
_upTryesRefresh = System.currentTimeMillis() + 5000;
}
else if(_upTryesRefresh < System.currentTimeMillis())
{
_upTryesRefresh = System.currentTimeMillis() + 5000;
_upTryes = 0;
}
_upTryes++;
_upTryesTotal++;
if(_upTryes > 4 || _upTryesTotal > 10)
{
_log.warning("Too many client packet fails, connection closed. IP: " + getIpAddr() + ", account:" + getLoginName());
L2Player activeChar = getActiveChar();
if(activeChar != null)
{
activeChar.logout(false, false, true, true);
}
else
{
closeNow(true);
}
_upTryesTotal = Integer.MAX_VALUE;
return true;
}
return false;
}
public boolean isPacketsFailed()
{
return _upTryesTotal == Integer.MAX_VALUE;
}
@Override
public String toString()
{
return "L2GameClient: " + (_activeChar == null ? _loginName : _activeChar) + "@" + getIpAddr();
}
}