/* * Copyright (C) 2004-2015 L2J Server * * This file is part of L2J Server. * * L2J Server 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. * * L2J Server 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, see . */ package com.l2jserver.gameserver.network; import com.l2jserver.Config; public class ClientStats { public int processedPackets = 0; public int droppedPackets = 0; public int unknownPackets = 0; public int totalQueueSize = 0; public int maxQueueSize = 0; public int totalBursts = 0; public int maxBurstSize = 0; public int shortFloods = 0; public int longFloods = 0; public int totalQueueOverflows = 0; public int totalUnderflowExceptions = 0; private final int[] _packetsInSecond; private long _packetCountStartTick = 0; private int _head; private int _totalCount = 0; private int _floodsInMin = 0; private long _floodStartTick = 0; private int _unknownPacketsInMin = 0; private long _unknownPacketStartTick = 0; private int _overflowsInMin = 0; private long _overflowStartTick = 0; private int _underflowReadsInMin = 0; private long _underflowReadStartTick = 0; private volatile boolean _floodDetected = false; private volatile boolean _queueOverflowDetected = false; private final int BUFFER_SIZE; public ClientStats() { BUFFER_SIZE = Config.CLIENT_PACKET_QUEUE_MEASURE_INTERVAL; _packetsInSecond = new int[BUFFER_SIZE]; _head = BUFFER_SIZE - 1; } /** * @return true if incoming packet need to be dropped */ protected final boolean dropPacket() { final boolean result = _floodDetected || _queueOverflowDetected; if (result) { droppedPackets++; } return result; } /** * Later during flood returns true (and send ActionFailed) once per second. * @param queueSize * @return true if flood detected first and ActionFailed packet need to be sent. */ protected final boolean countPacket(int queueSize) { processedPackets++; totalQueueSize += queueSize; if (maxQueueSize < queueSize) { maxQueueSize = queueSize; } if (_queueOverflowDetected && (queueSize < 2)) { _queueOverflowDetected = false; } return countPacket(); } /** * @return Counts unknown packets and return true if threshold is reached. */ protected final boolean countUnknownPacket() { unknownPackets++; final long tick = System.currentTimeMillis(); if ((tick - _unknownPacketStartTick) > 60000) { _unknownPacketStartTick = tick; _unknownPacketsInMin = 1; return false; } _unknownPacketsInMin++; return _unknownPacketsInMin > Config.CLIENT_PACKET_QUEUE_MAX_UNKNOWN_PER_MIN; } /** * @param count - current number of processed packets in burst * @return burst length and return true if execution of the queue need to be aborted. */ protected final boolean countBurst(int count) { if (count > maxBurstSize) { maxBurstSize = count; } if (count < Config.CLIENT_PACKET_QUEUE_MAX_BURST_SIZE) { return false; } totalBursts++; return true; } /** * @return Counts queue overflows and return true if threshold is reached. */ protected final boolean countQueueOverflow() { _queueOverflowDetected = true; totalQueueOverflows++; final long tick = System.currentTimeMillis(); if ((tick - _overflowStartTick) > 60000) { _overflowStartTick = tick; _overflowsInMin = 1; return false; } _overflowsInMin++; return _overflowsInMin > Config.CLIENT_PACKET_QUEUE_MAX_OVERFLOWS_PER_MIN; } /** * @return Counts underflow exceptions and return true if threshold is reached. */ protected final boolean countUnderflowException() { totalUnderflowExceptions++; final long tick = System.currentTimeMillis(); if ((tick - _underflowReadStartTick) > 60000) { _underflowReadStartTick = tick; _underflowReadsInMin = 1; return false; } _underflowReadsInMin++; return _underflowReadsInMin > Config.CLIENT_PACKET_QUEUE_MAX_UNDERFLOWS_PER_MIN; } /** * @return true if maximum number of floods per minute is reached. */ protected final boolean countFloods() { return _floodsInMin > Config.CLIENT_PACKET_QUEUE_MAX_FLOODS_PER_MIN; } private final boolean longFloodDetected() { return (_totalCount / BUFFER_SIZE) > Config.CLIENT_PACKET_QUEUE_MAX_AVERAGE_PACKETS_PER_SECOND; } /** * Later during flood returns true (and send ActionFailed) once per second. * @return true if flood detected first and ActionFailed packet need to be sent. */ private final synchronized boolean countPacket() { _totalCount++; final long tick = System.currentTimeMillis(); if ((tick - _packetCountStartTick) > 1000) { _packetCountStartTick = tick; // clear flag if no more flooding during last seconds if (_floodDetected && !longFloodDetected() && (_packetsInSecond[_head] < (Config.CLIENT_PACKET_QUEUE_MAX_PACKETS_PER_SECOND / 2))) { _floodDetected = false; } // wrap head of the buffer around the tail if (_head <= 0) { _head = BUFFER_SIZE; } _head--; _totalCount -= _packetsInSecond[_head]; _packetsInSecond[_head] = 1; return _floodDetected; } final int count = ++_packetsInSecond[_head]; if (!_floodDetected) { if (count > Config.CLIENT_PACKET_QUEUE_MAX_PACKETS_PER_SECOND) { shortFloods++; } else if (longFloodDetected()) { longFloods++; } else { return false; } _floodDetected = true; if ((tick - _floodStartTick) > 60000) { _floodStartTick = tick; _floodsInMin = 1; } else { _floodsInMin++; } return true; // Return true only in the beginning of the flood } return false; } }