/* This program 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 2, 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* http://www.gnu.org/copyleft/gpl.html
*/
package com.l2jfrozen.gameserver.network;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import javolution.util.FastList;
import com.l2jfrozen.Config;
import com.l2jfrozen.gameserver.communitybbs.Manager.RegionBBSManager;
import com.l2jfrozen.gameserver.datatables.OfflineTradeTable;
import com.l2jfrozen.gameserver.datatables.SkillTable;
import com.l2jfrozen.gameserver.datatables.sql.ClanTable;
import com.l2jfrozen.gameserver.managers.AwayManager;
import com.l2jfrozen.gameserver.model.CharSelectInfoPackage;
import com.l2jfrozen.gameserver.model.L2Clan;
import com.l2jfrozen.gameserver.model.L2World;
import com.l2jfrozen.gameserver.model.actor.instance.L2PcInstance;
import com.l2jfrozen.gameserver.model.entity.event.CTF;
import com.l2jfrozen.gameserver.model.entity.event.DM;
import com.l2jfrozen.gameserver.model.entity.event.L2Event;
import com.l2jfrozen.gameserver.model.entity.event.Raid;
import com.l2jfrozen.gameserver.model.entity.event.TvT;
import com.l2jfrozen.gameserver.model.entity.event.VIP;
import com.l2jfrozen.gameserver.model.entity.olympiad.Olympiad;
import com.l2jfrozen.gameserver.network.serverpackets.ActionFailed;
import com.l2jfrozen.gameserver.network.serverpackets.L2GameServerPacket;
import com.l2jfrozen.gameserver.network.serverpackets.ServerClose;
import com.l2jfrozen.gameserver.thread.LoginServerThread;
import com.l2jfrozen.gameserver.thread.LoginServerThread.SessionKey;
import com.l2jfrozen.gameserver.thread.ThreadPoolManager;
import com.l2jfrozen.gameserver.util.EventData;
import com.l2jfrozen.gameserver.util.FloodProtectors;
import com.l2jfrozen.logs.Log;
import com.l2jfrozen.netcore.MMOClient;
import com.l2jfrozen.netcore.MMOConnection;
import com.l2jfrozen.netcore.ReceivablePacket;
import com.l2jfrozen.protection.CatsGuard;
import com.l2jfrozen.protection.LameStub;
import com.l2jfrozen.protection.Restrictions;
import com.l2jfrozen.util.CloseUtil;
import com.l2jfrozen.util.database.L2DatabaseFactory;
/**
* @author L2JFrozen dev
*/
public final class L2GameClient extends MMOClient<MMOConnection<L2GameClient>> implements Runnable
{
protected static final Logger _log = Logger.getLogger(L2GameClient.class.getName());
/**
* CONNECTED - client has just connected AUTHED - client has authed but doesn't has character attached to it yet
* IN_GAME - client has selected a char and is in game
*
* @author KenM
*/
public static enum GameClientState
{
CONNECTED,
AUTHED,
IN_GAME
}
public static interface IExReader
{
public int read(ByteBuffer buf);
public void checkChar(L2PcInstance cha);
}
// floodprotectors
private final FloodProtectors _floodProtectors = new FloodProtectors(this);
public GameClientState state;
// Info
public String accountName;
public SessionKey sessionId;
public L2PcInstance activeChar;
private ReentrantLock _activeCharLock = new ReentrantLock();
//private boolean _isAuthedGG;
private long _connectionStartTime;
private List<Integer> _charSlotMapping = new FastList<Integer>();
// Task
//private ScheduledFuture<?> _guardCheckTask = null;
protected ScheduledFuture<?> _autoSaveInDB;
protected ScheduledFuture<?> _cleanupTask = null;
private ClientStats _stats;
// Crypt
//public GameCrypt crypt;
public GameCrypt _crypt;
private String _hwid = null;
public IExReader _reader;
public boolean SPSEnabled = false;
// Flood protection
public long packetsNextSendTick = 0;
//unknownPacket protection
private int unknownPacketCount = 0;
//protected boolean _closenow = true;
public boolean _isDetached = false;
protected boolean _forcedToClose = false;
private final ArrayBlockingQueue<ReceivablePacket<L2GameClient>> _packetQueue;
private ReentrantLock _queueLock = new ReentrantLock();
private long _last_received_packet_action_time = 0;
public L2GameClient(MMOConnection<L2GameClient> con)
{
super(con);
state = GameClientState.CONNECTED;
_connectionStartTime = System.currentTimeMillis();
_crypt = new GameCrypt();
_stats = new ClientStats();
_packetQueue = new ArrayBlockingQueue<ReceivablePacket<L2GameClient>>(com.l2jfrozen.netcore.Config.getInstance().CLIENT_PACKET_QUEUE_SIZE);
_autoSaveInDB = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new AutoSaveTask(), 300000L, 900000L);
}
public byte[] enableCrypt()
{
byte[] key = BlowFishKeygen.getRandomKey();
_crypt.setKey(key);
return key;
}
public GameClientState getState()
{
return state;
}
public void setState(GameClientState pState)
{
if (state != pState)
{
state = pState;
_packetQueue.clear();
}
}
public ClientStats getStats()
{
return _stats;
}
public long getConnectionStartTime()
{
return _connectionStartTime;
}
@Override
public boolean decrypt(ByteBuffer buf, int size)
{
_crypt.decrypt(buf.array(), buf.position(), size);
return true;
}
@Override
public boolean encrypt(ByteBuffer buf, int size)
{
_crypt.encrypt(buf.array(), buf.position(), size);
buf.position(buf.position() + size);
return true;
}
public L2PcInstance getActiveChar()
{
return activeChar;
}
public void setActiveChar(L2PcInstance pActiveChar)
{
if (pActiveChar != null && !SPSEnabled)
{
Restrictions.check(this);
}
activeChar = pActiveChar;
if (activeChar != null)
{
if (_reader != null)
{
_reader.checkChar(activeChar);
}
L2World.getInstance().storeObject(getActiveChar());
}
}
public ReentrantLock getActiveCharLock()
{
return _activeCharLock;
}
public void setAccountName(String pAccountName)
{
accountName = pAccountName;
if (_reader == null)
{
CatsGuard.getInstance().initSession(this);
}
}
public String getAccountName()
{
return accountName;
}
public void setSessionId(SessionKey sk)
{
sessionId = sk;
}
public SessionKey getSessionId()
{
return sessionId;
}
public void sendPacket(L2GameServerPacket gsp)
{
if (_isDetached)
return;
if(getConnection()!=null)
{
if(Config.DEBUG_PACKETS)
{
Log.add("[ServerPacket] SendingGameServerPacket, Client: "+this.toString()+" Packet:"+gsp.getType(), "GameServerPacketsLog");
}
getConnection().sendPacket(gsp);
gsp.runImpl(this, getActiveChar());
}
}
public boolean isDetached()
{
return _isDetached;
}
public void setDetached(boolean b)
{
_isDetached = b;
}
/**
* Method to handle character deletion
* @param charslot
* @return a byte: <li>-1: Error: No char was found for such charslot, caught exception, etc... <li>0: character is
* not member of any clan, proceed with deletion <li>1: character is member of a clan, but not clan leader
* <li>2: character is clan leader
*/
public byte markToDeleteChar(int charslot)
{
int objid = getObjectIdForSlot(charslot);
if(objid < 0)
return -1;
Connection con = null;
byte answer = -1;
try
{
con = L2DatabaseFactory.getInstance().getConnection(false);
PreparedStatement statement = con.prepareStatement("SELECT clanId from characters WHERE obj_Id=?");
statement.setInt(1, objid);
ResultSet rs = statement.executeQuery();
rs.next();
int clanId = rs.getInt(1);
answer = 0;
if(clanId != 0)
{
L2Clan clan = ClanTable.getInstance().getClan(clanId);
if(clan == null)
{
answer = 0; // jeezes!
}
else if(clan.getLeaderId() == objid)
{
answer = 2;
}
else
{
answer = 1;
}
clan = null;
}
// Setting delete time
if(answer == 0)
{
if(Config.DELETE_DAYS == 0)
{
deleteCharByObjId(objid);
}
else
{
statement = con.prepareStatement("UPDATE characters SET deletetime=? WHERE obj_Id=?");
statement.setLong(1, System.currentTimeMillis() + Config.DELETE_DAYS * 86400000L); // 24*60*60*1000 = 86400000
statement.setInt(2, objid);
statement.execute();
statement.close();
rs.close();
statement = null;
rs = null;
}
}
else
{
statement.close();
rs.close();
statement = null;
rs = null;
}
}
catch(Exception e)
{
if(Config.ENABLE_ALL_EXCEPTIONS)
e.printStackTrace();
_log.warning("Data error on update delete time of char: " + e);
answer = -1;
}
finally
{
CloseUtil.close(con);
con = null;
}
return answer;
}
public void markRestoredChar(int charslot)
{
//have to make sure active character must be nulled
/*if (getActiveChar() != null)
{
saveCharToDisk (getActiveChar());
if (Config.DEBUG) _log.fine("active Char saved");
this.setActiveChar(null);
}*/
int objid = getObjectIdForSlot(charslot);
if(objid < 0)
return;
Connection con = null;
try
{
con = L2DatabaseFactory.getInstance().getConnection(false);
PreparedStatement statement = con.prepareStatement("UPDATE characters SET deletetime=0 WHERE obj_id=?");
statement.setInt(1, objid);
statement.execute();
statement.close();
statement = null;
}
catch(Exception e)
{
if(Config.ENABLE_ALL_EXCEPTIONS)
e.printStackTrace();
_log.severe("Data error on restoring char: " + e);
}
finally
{
CloseUtil.close(con);
con = null;
}
}
public static void deleteCharByObjId(int objid)
{
if(objid < 0)
return;
Connection con = null;
try
{
con = L2DatabaseFactory.getInstance().getConnection(false);
PreparedStatement statement;
statement = con.prepareStatement("DELETE FROM character_friends WHERE char_id=? OR friend_id=?");
statement.setInt(1, objid);
statement.setInt(2, objid);
statement.execute();
statement.close();
statement = null;
statement = con.prepareStatement("DELETE FROM character_hennas WHERE char_obj_id=?");
statement.setInt(1, objid);
statement.execute();
statement.close();
statement = null;
statement = con.prepareStatement("DELETE FROM character_macroses WHERE char_obj_id=?");
statement.setInt(1, objid);
statement.execute();
statement.close();
statement = null;
statement = con.prepareStatement("DELETE FROM character_quests WHERE char_id=?");
statement.setInt(1, objid);
statement.execute();
statement.close();
statement = null;
statement = con.prepareStatement("DELETE FROM character_recipebook WHERE char_id=?");
statement.setInt(1, objid);
statement.execute();
statement.close();
statement = null;
statement = con.prepareStatement("DELETE FROM character_shortcuts WHERE char_obj_id=?");
statement.setInt(1, objid);
statement.execute();
statement.close();
statement = null;
statement = con.prepareStatement("DELETE FROM character_skills WHERE char_obj_id=?");
statement.setInt(1, objid);
statement.execute();
statement.close();
statement = null;
statement = con.prepareStatement("DELETE FROM character_skills_save WHERE char_obj_id=?");
statement.setInt(1, objid);
statement.execute();
statement.close();
statement = null;
statement = con.prepareStatement("DELETE FROM character_subclasses WHERE char_obj_id=?");
statement.setInt(1, objid);
statement.execute();
statement.close();
statement = null;
statement = con.prepareStatement("DELETE FROM heroes WHERE charId=?");
statement.setInt(1, objid);
statement.execute();
statement.close();
statement = null;
statement = con.prepareStatement("DELETE FROM olympiad_nobles WHERE charId=?");
statement.setInt(1, objid);
statement.execute();
statement.close();
statement = null;
statement = con.prepareStatement("DELETE FROM seven_signs WHERE char_obj_id=?");
statement.setInt(1, objid);
statement.execute();
statement.close();
statement = null;
statement = con.prepareStatement("DELETE FROM pets WHERE item_obj_id IN (SELECT object_id FROM items WHERE items.owner_id=?)");
statement.setInt(1, objid);
statement.execute();
statement.close();
statement = null;
statement = con.prepareStatement("DELETE FROM augmentations WHERE item_id IN (SELECT object_id FROM items WHERE items.owner_id=?)");
statement.setInt(1, objid);
statement.execute();
statement.close();
statement = null;
statement = con.prepareStatement("DELETE FROM items WHERE owner_id=?");
statement.setInt(1, objid);
statement.execute();
statement.close();
statement = null;
statement = con.prepareStatement("DELETE FROM merchant_lease WHERE player_id=?");
statement.setInt(1, objid);
statement.execute();
statement.close();
statement = null;
statement = con.prepareStatement("DELETE FROM characters WHERE obj_Id=?");
statement.setInt(1, objid);
statement.execute();
statement.close();
statement = null;
}
catch(Exception e)
{
if(Config.ENABLE_ALL_EXCEPTIONS)
e.printStackTrace();
_log.warning("Data error on deleting char: " + e);
}
finally
{
CloseUtil.close(con);
con = null;
}
}
public L2PcInstance loadCharFromDisk(int charslot)
{
//L2PcInstance character = L2PcInstance.load(getObjectIdForSlot(charslot));
final int objId = getObjectIdForSlot(charslot);
if (objId < 0)
return null;
L2PcInstance character = L2World.getInstance().getPlayer(objId);
if (character != null)
{
// exploit prevention, should not happens in normal way
if(Config.DEBUG)
_log.severe("Attempt of double login: " + character.getName()+"("+objId+") "+getAccountName());
if (character.getClient() != null)
character.getClient().closeNow();
else{
character.deleteMe();
try
{
character.store();
}
catch(Exception e2)
{
if(Config.ENABLE_ALL_EXCEPTIONS)
e2.printStackTrace();
}
}
//return null;
}
character = L2PcInstance.load(objId);
// if(character != null)
// {
// //restoreInventory(character);
// //restoreSkills(character);
// //character.restoreSkills();
// //restoreShortCuts(character);
// //restoreWarehouse(character);
//
// // preinit some values for each login
// character.setRunning(); // running is default
// character.standUp(); // standing is default
//
// character.refreshOverloaded();
// character.refreshExpertisePenalty();
// character.refreshMasteryPenality();
// character.refreshMasteryWeapPenality();
//
// character.sendPacket(new UserInfo(character));
// character.broadcastKarma();
// character.setOnlineStatus(true);
// }
// if(character == null)
// {
// _log.severe("could not restore in slot: " + charslot);
// }
//setCharacter(character);
return character;
}
/**
* @param chars
*/
public void setCharSelection(CharSelectInfoPackage[] chars)
{
_charSlotMapping.clear();
for(CharSelectInfoPackage c : chars)
{
int objectId = c.getObjectId();
_charSlotMapping.add(new Integer(objectId));
}
}
public void close(L2GameServerPacket gsp)
{
if(getConnection()!=null)
getConnection().close(gsp);
}
/**
* @param charslot
* @return
*/
private int getObjectIdForSlot(int charslot)
{
if(charslot < 0 || charslot >= _charSlotMapping.size())
{
_log.warning(toString() + " tried to delete Character in slot " + charslot + " but no characters exits at that slot.");
return -1;
}
Integer objectId = _charSlotMapping.get(charslot);
return objectId.intValue();
}
@Override
public void onForcedDisconnection(boolean critical)
{
_forcedToClose = true;
if(critical)
_log.log(Level.WARNING, "Client " + toString() + " disconnected abnormally.");
//the force operation will allow to not save client position to prevent again criticals
//and stuck
closeNow();
}
/*
L2PcInstance player = null;
if((player = getActiveChar()) !=null){
if(critical){
_log.log(Level.WARNING, "Client " + toString() + " disconnected abnormally.");
_log.log(Level.WARNING, "Character disconnected at Loc X:"+player.getX()+" Y:"+player.getY()+" Z:"+player.getZ());
_log.log(Level.WARNING, "Character disconnected in (closest) zone: "+MapRegionTable.getInstance().getClosestTownName(player));
}
if(player.isFlying())
player.removeSkill(SkillTable.getInstance().getInfo(4289, 1));
if(player.isAway())
{
AwayManager.getInstance().extraBack(player);
}
if(player.isInParty())
{
player.getParty().removePartyMember(player);
}
//Decrease boxes number
if(player._active_boxes!=-1)
player.decreaseBoxes();
if(player._inEventCTF){
CTF.onDisconnect(player);
}else if(player._inEventDM){
DM.onDisconnect(player);
}else if(player._inEventTvT){
TvT.onDisconnect(player);
}else if(player._inEventVIP){
VIP.onDisconnect(player);
}
if(Olympiad.getInstance().isRegistered(player)){
Olympiad.getInstance().unRegisterNoble(player);
}
player.deleteMe();
try
{
player.store(true);
}
catch(Exception e2)
{
if(Config.ENABLE_ALL_EXCEPTIONS)
e2.printStackTrace();
}
L2World.getInstance().removeFromAllPlayers(player);
setActiveChar(null);
LoginServerThread.getInstance().sendLogout(getAccountName());
}
stopGuardTask();
}*/
@Override
public void onDisconnection()
{
// no long running tasks here, do it async
try
{
ThreadPoolManager.getInstance().executeTask(new DisconnectTask());
}
catch(RejectedExecutionException e)
{
// server is closing
if(Config.ENABLE_ALL_EXCEPTIONS)
e.printStackTrace();
}
if (_reader != null)
CatsGuard.getInstance().doneSession(this);
}
/**
* Close client connection with {@link ServerClose} packet
*/
public void closeNow()
{
close(0);
}
/**
* Close client connection with {@link ServerClose} packet
* @param delay
*/
public void close(int delay)
{
close(ServerClose.STATIC_PACKET);
synchronized (this)
{
if (_cleanupTask != null)
cancelCleanup();
_cleanupTask = ThreadPoolManager.getInstance().scheduleGeneral(new CleanupTask(), delay); //delayed
}
}
/**
* Produces the best possible string representation of this client.
*/
@Override
public String toString()
{
try
{
InetAddress address = getConnection().getInetAddress();
String ip = "N/A";
if(address == null)
{
ip = "disconnected";
}
else
{
ip = address.getHostAddress();
}
switch(getState())
{
case CONNECTED:
return "[IP: " + ip + "]";
case AUTHED:
return "[Account: " + getAccountName() + " - IP: " + ip + "]";
case IN_GAME:
address = null;
return "[Character: " + (getActiveChar() == null ? "disconnected" : getActiveChar().getName()) + " - Account: " + getAccountName() + " - IP: " + ip + "]";
default:
address = null;
throw new IllegalStateException("Missing state on switch");
}
}
catch(NullPointerException e)
{
if(Config.ENABLE_ALL_EXCEPTIONS)
e.printStackTrace();
return "[Character read failed due to disconnect]";
}
}
protected class CleanupTask implements Runnable
{
@Override
public void run()
{
try
{
// Update BBS
try
{
RegionBBSManager.getInstance().changeCommunityBoard();
}
catch(Exception e)
{
e.printStackTrace();
}
// we are going to manually save the char below thus we can force the cancel
if (_autoSaveInDB != null)
_autoSaveInDB.cancel(true);
L2PcInstance player = L2GameClient.this.getActiveChar();
if (player != null) // this should only happen on connection loss
{
if(player.isInArenaEvent())
{
player.increaseArenaDefeats();
player.setXYZ(82698, 148638, -3473);
}
// we store all data from players who are disconnected while in an event in order to restore it in the next login
if(player.atEvent)
{
EventData data = new EventData(player.eventX, player.eventY, player.eventZ, player.eventKarma, player.eventPvpKills, player.eventPkKills, player.eventTitle, player.kills, player.eventSitForced);
L2Event.connectionLossData.put(player.getName(), data);
data = null;
}else{
if(player._inEventCTF){
CTF.onDisconnect(player);
}else if(player._inEventDM){
DM.onDisconnect(player);
}else if(player._inEventTvT){
TvT.onDisconnect(player);
} else if (player._inEventRaid) {
Raid.onDisconnect(player);
}else if(player._inEventVIP){
VIP.onDisconnect(player);
}
}
if(player.isFlying())
{
player.removeSkill(SkillTable.getInstance().getInfo(4289, 1));
}
if(player.isAway())
{
AwayManager.getInstance().extraBack(player);
}
if(Olympiad.getInstance().isRegistered(player)){
Olympiad.getInstance().unRegisterNoble(player);
}
//Decrease boxes number
if(player._active_boxes!=-1)
player.decreaseBoxes();
// prevent closing again
player.setClient(null);
player.deleteMe();
try
{
player.store(_forcedToClose);
}
catch(Exception e2)
{
if(Config.ENABLE_ALL_EXCEPTIONS)
e2.printStackTrace();
}
}
L2GameClient.this.setActiveChar(null);
L2GameClient.this.setDetached(true);
}
catch (Exception e1)
{
if(Config.ENABLE_ALL_EXCEPTIONS)
e1.printStackTrace();
_log.log(Level.WARNING, "Error while cleanup client.", e1);
}
finally
{
Restrictions.onDisconnect(L2GameClient.this);
LoginServerThread.getInstance().sendLogout(L2GameClient.this.getAccountName());
}
}
}
class AutoSaveTask implements Runnable
{
@Override
public void run()
{
try
{
L2PcInstance player = L2GameClient.this.getActiveChar();
if (player != null)
{
player.store();
}
}
catch (Throwable e)
{
_log.warning(e.toString());
}
}
}
protected class DisconnectTask implements Runnable
{
@Override
public void run()
{
try
{
// Update BBS
try
{
RegionBBSManager.getInstance().changeCommunityBoard();
}
catch(Exception e)
{
e.printStackTrace();
}
// we are going to manually save the char bellow thus we can force the cancel
if(_autoSaveInDB != null)
_autoSaveInDB.cancel(true);
L2PcInstance player = L2GameClient.this.getActiveChar();
if(player != null) // this should only happen on connection loss
{
if(player.isInArenaEvent())
{
player.increaseArenaDefeats();
player.setXYZ(82698, 148638, -3473);
}
// we store all data from players who are disconnected while in an event in order to restore it in the next login
if(player.atEvent)
{
EventData data = new EventData(player.eventX, player.eventY, player.eventZ, player.eventKarma, player.eventPvpKills, player.eventPkKills, player.eventTitle, player.kills, player.eventSitForced);
L2Event.connectionLossData.put(player.getName(), data);
data = null;
}else{
if(player._inEventCTF){
CTF.onDisconnect(player);
}else if(player._inEventDM){
DM.onDisconnect(player);
}else if(player._inEventTvT){
TvT.onDisconnect(player);
}else if (player._inEventRaid) {
Raid.onDisconnect(player);
}else if(player._inEventVIP){
VIP.onDisconnect(player);
}
}
if(player.isFlying())
{
player.removeSkill(SkillTable.getInstance().getInfo(4289, 1));
}
if(player.isAway())
{
AwayManager.getInstance().extraBack(player);
}
if(Olympiad.getInstance().isRegistered(player)){
Olympiad.getInstance().unRegisterNoble(player);
}
//Decrease boxes number
if(player._active_boxes!=-1)
player.decreaseBoxes();
if(!player.isKicked() && !Olympiad.getInstance().isRegistered(player)
&& !player.isInOlympiadMode()
&& !player.isInFunEvent()
&& ((player.isInStoreMode() && Config.OFFLINE_TRADE_ENABLE)
|| (player.isInCraftMode() && Config.OFFLINE_CRAFT_ENABLE)))
{
player.setOffline(true);
player.leaveParty();
player.store();
if(Config.OFFLINE_SET_NAME_COLOR)
{
player._originalNameColorOffline=player.getAppearance().getNameColor();
player.getAppearance().setNameColor(Config.OFFLINE_NAME_COLOR);
player.broadcastUserInfo();
}
if (player.getOfflineStartTime() == 0)
player.setOfflineStartTime(System.currentTimeMillis());
OfflineTradeTable.storeOffliner(player);
return;
}
// notify the world about our disconnect
player.deleteMe();
//store operation
try
{
player.store();
}
catch(Exception e2)
{
if(Config.ENABLE_ALL_EXCEPTIONS)
e2.printStackTrace();
}
}
L2GameClient.this.setActiveChar(null);
L2GameClient.this.setDetached(true);
player = null;
}
catch(Exception e1)
{
if(Config.ENABLE_ALL_EXCEPTIONS)
e1.printStackTrace();
_log.log(Level.WARNING, "error while disconnecting client", e1);
}
finally
{
Restrictions.onDisconnect(L2GameClient.this);
LoginServerThread.getInstance().sendLogout(L2GameClient.this.getAccountName());
}
}
}
public FloodProtectors getFloodProtectors()
{
return _floodProtectors;
}
public boolean checkUnknownPackets()
{
L2PcInstance player = getActiveChar();
if(player != null && _floodProtectors!=null && _floodProtectors.getUnknownPackets()!=null && !_floodProtectors.getUnknownPackets().tryPerformAction("check packet"))
{
unknownPacketCount++;
if(unknownPacketCount >= Config.MAX_UNKNOWN_PACKETS)
return true;
return false;
}
unknownPacketCount = 0;
return false;
}
private boolean cancelCleanup()
{
Future<?> task = _cleanupTask;
if (task != null)
{
_cleanupTask = null;
return task.cancel(true);
}
return false;
}
/**
* Returns false if client can receive packets.
* True if detached, or flood detected, or queue overflow detected and queue still not empty.
* @return
*/
public boolean dropPacket()
{
if (_isDetached) // detached clients can't receive any packets
return true;
// flood protection
if (getStats().countPacket(_packetQueue.size()))
{
sendPacket(ActionFailed.STATIC_PACKET);
return true;
}
return getStats().dropPacket();
}
/**
* Counts buffer underflow exceptions.
*/
public void onBufferUnderflow()
{
if (getStats().countUnderflowException())
{
_log.severe("Client " + toString() + " - Disconnected: Too many buffer underflow exceptions.");
closeNow();
return;
}
if (state == GameClientState.CONNECTED) // in CONNECTED state kick client immediately
{
_log.severe("Client " + toString() + " - Disconnected, too many buffer underflows in non-authed state.");
closeNow();
}
}
/**
* Add packet to the queue and start worker thread if needed
* @param packet
*/
public void execute(ReceivablePacket<L2GameClient> packet)
{
if (getStats().countFloods())
{
_log.severe("Client " + toString() + " - Disconnected, too many floods:"+getStats().longFloods+" long and "+getStats().shortFloods+" short.");
closeNow();
return;
}
if (!_packetQueue.offer(packet))
{
if (getStats().countQueueOverflow())
{
_log.severe("Client " + toString() + " - Disconnected, too many queue overflows.");
closeNow();
}
else
sendPacket(ActionFailed.STATIC_PACKET);
return;
}
if (_queueLock.isLocked()) // already processing
return;
//save last action time
_last_received_packet_action_time = System.currentTimeMillis();
//_log.severe("Client " + toString() + " - updated last action state "+_last_received_packet_action_time);
try
{
if (state == GameClientState.CONNECTED)
{
if (getStats().processedPackets > 3)
{
_log.severe("Client " + toString() + " - Disconnected, too many packets in non-authed state.");
closeNow();
return;
}
ThreadPoolManager.getInstance().executeIOPacket(this);
}
else
ThreadPoolManager.getInstance().executePacket(this);
}
catch (RejectedExecutionException e)
{
if(Config.ENABLE_ALL_EXCEPTIONS)
e.printStackTrace();
// if the server is shutdown we ignore
if (!ThreadPoolManager.getInstance().isShutdown())
{
_log.severe("Failed executing: "+packet.getClass().getSimpleName()+" for Client: "+toString());
}
}
}
@Override
public void run()
{
if (!_queueLock.tryLock())
return;
try
{
int count = 0;
while (true)
{
final ReceivablePacket<L2GameClient> packet = _packetQueue.poll();
if (packet == null) // queue is empty
return;
if (_isDetached) // clear queue immediately after detach
{
_packetQueue.clear();
return;
}
try
{
packet.run();
}
catch (Exception e)
{
if(Config.ENABLE_ALL_EXCEPTIONS)
e.printStackTrace();
_log.severe("Exception during execution "+packet.getClass().getSimpleName()+", client: "+toString()+","+e.getMessage());
}
count++;
if (getStats().countBurst(count))
return;
}
}
finally
{
_queueLock.unlock();
}
}
/**
* @return the _forcedToClose
*/
public boolean is_forcedToClose()
{
return _forcedToClose;
}
public boolean isConnectionAlive()
{
//if last received packet time is higher then Config.CHECK_CONNECTION_INACTIVITY_TIME --> check connection
if(System.currentTimeMillis() - _last_received_packet_action_time > Config.CHECK_CONNECTION_INACTIVITY_TIME){
_last_received_packet_action_time = System.currentTimeMillis();
return getConnection().isConnected()
&& !getConnection().isClosed();
}
return true;
}
public void setHWID(String hwid)
{
_hwid = hwid;
}
public String getHWid()
{
return _hwid;
}
private int _nWindowCount;
public int getnWindowCount()
{
return _nWindowCount;
}
public void setWindowCount(int _nCount)
{
_nWindowCount = _nCount;
}
public static boolean isSameHWID(String hwid1, String hwid2)
{
if(CatsGuard.getInstance().isEnabled())
return hwid1.equals(hwid2);
if(LameStub.ISLAME)
return com.lameguard.HWID.equals(hwid1, hwid2, com.lameguard.HWID.HWID_HDD | com.lameguard.HWID.HWID_CPU | com.lameguard.HWID.HWID_BIOS);
return true;
}
}