/* * 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. */ package silentium.gameserver.network; import silentium.gameserver.configs.MainConfig; 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 = MainConfig.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; } /** * @param queueSize * @return true if flood detected first and ActionFailed packet need to be sent. Later during flood returns true (and send ActionFailed) once * per second. */ protected final boolean countPacket(int queueSize) { processedPackets++; totalQueueSize += queueSize; if (maxQueueSize < queueSize) maxQueueSize = queueSize; if (_queueOverflowDetected && queueSize < 2) _queueOverflowDetected = false; return countPacket(); } /** * Counts unknown packets. * * @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 > MainConfig.CLIENT_PACKET_QUEUE_MAX_UNKNOWN_PER_MIN; } /** * Counts burst length. * * @param count * - current number of processed packets in burst * @return true if execution of the queue need to be aborted. */ protected final boolean countBurst(int count) { if (count > maxBurstSize) maxBurstSize = count; if (count < MainConfig.CLIENT_PACKET_QUEUE_MAX_BURST_SIZE) return false; totalBursts++; return true; } /** * Counts queue overflows. * * @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 > MainConfig.CLIENT_PACKET_QUEUE_MAX_OVERFLOWS_PER_MIN; } /** * Counts underflow exceptions. * * @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 > MainConfig.CLIENT_PACKET_QUEUE_MAX_UNDERFLOWS_PER_MIN; } /** * @return true if maximum number of floods per minute is reached. */ protected final boolean countFloods() { return _floodsInMin > MainConfig.CLIENT_PACKET_QUEUE_MAX_FLOODS_PER_MIN; } private final boolean longFloodDetected() { return (_totalCount / BUFFER_SIZE) > MainConfig.CLIENT_PACKET_QUEUE_MAX_AVERAGE_PACKETS_PER_SECOND; } /** * @return true if flood detected first and ActionFailed packet need to be sent. Later during flood returns true (and send ActionFailed) once * per second. */ 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] < MainConfig.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 > MainConfig.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; } }