瀏覽代碼

fordfrog's floodprotector with format and few other changes

DrHouse 16 年之前
父節點
當前提交
408c65de05

+ 0 - 3
L2_GameServer/java/config/General.properties

@@ -139,9 +139,6 @@ RestartOnDeadlock = False
 # ---------------------------------------------------------------------------
 # Optimization
 # ---------------------------------------------------------------------------
-# Flood Protector. Set the initial size of the flood protector (should equal about roughly your player count)
-# Default: 50
-FloodProtectorInitialSize = 50
 
 # Items on ground management.
 # Allow players to drop items on the ground.

+ 65 - 0
L2_GameServer/java/config/floodprotector.properties

@@ -0,0 +1,65 @@
+# ======================
+# Flood protector config
+# ======================
+# These settings apply flood protector to following features:
+# UseItem - item usage flooding
+# RollDice - rolling dice flooding
+# Firework - firework flooding
+# ItemPetSummon - item summonning and pet mounting flooding
+# HeroVoice - hero voice flooding
+# Subclass - subclass flooding
+# DropItem - drop item flooding
+# ServerBypass - server bypass flooding
+# Following settings can be applied to each feature:
+# Interval - interval in gameserver ticks (1 tick = 100ms) in which only one
+#            request is allowed
+# LogFlooding - whether flooding should be logged (only first ocurrance of
+#               flooding and total count of flood requests is logged)
+# PunishmentLimit - if number of requests within single interval exceeds
+#                   specified number then specified punishment is applied
+#                   (0 - disables punishment feature)
+# PunishmentType - type of the punishment ('none', 'ban', 'jail'), used only
+#                  if PunishmentLimit is greater than zero
+# PunishmentTime - for how many minutes should be the player/account punished,
+#                  player is punished in case of 'jail', account is punished
+#                  in case of 'ban' (0 = forever)
+FloodProtectorUseItemInterval = 4
+FloodProtectorUseItemLogFlooding = False
+FloodProtectorUseItemPunishmentLimit = 0
+FloodProtectorUseItemPunishmentType = none
+FloodProtectorUseItemPunishmentTime = 0
+FloodProtectorRollDiceInterval = 42
+FloodProtectorRollDiceLogFlooding = False
+FloodProtectorRollDicePunishmentLimit = 0
+FloodProtectorRollDicePunishmentType = none
+FloodProtectorRollDicePunishmentTime = 0
+FloodProtectorFireworkInterval = 42
+FloodProtectorFireworkLogFlooding = False
+FloodProtectorFireworkPunishmentLimit = 0
+FloodProtectorFireworkPunishmentType = none
+FloodProtectorFireworkPunishmentTime = 0
+FloodProtectorItemPetSummonInterval = 16
+FloodProtectorItemPetSummonLogFlooding = False
+FloodProtectorItemPetSummonPunishmentLimit = 0
+FloodProtectorItemPetSummonPunishmentType = none
+FloodProtectorItemPetSummonPunishmentTime = 0
+FloodProtectorHeroVoiceInterval = 100
+FloodProtectorHeroVoiceLogFlooding = False
+FloodProtectorHeroVoicePunishmentLimit = 0
+FloodProtectorHeroVoicePunishmentType = none
+FloodProtectorHeroVoicePunishmentTime = 0
+FloodProtectorSubclassInterval = 20
+FloodProtectorSubclassLogFlooding = False
+FloodProtectorSubclassPunishmentLimit = 0
+FloodProtectorSubclassPunishmentType = none
+FloodProtectorSubclassPunishmentTime = 0
+FloodProtectorDropItemInterval = 10
+FloodProtectorDropItemLogFlooding = False
+FloodProtectorDropItemPunishmentLimit = 0
+FloodProtectorDropItemPunishmentType = none
+FloodProtectorDropItemPunishmentTime = 0
+FloodProtectorServerBypassInterval = 5
+FloodProtectorServerBypassLogFlooding = False
+FloodProtectorServerBypassPunishmentLimit = 0
+FloodProtectorServerBypassPunishmentType = none
+FloodProtectorServerBypassPunishmentTime = 0

+ 77 - 3
L2_GameServer/java/net/sf/l2j/Config.java

@@ -28,6 +28,7 @@ import java.util.logging.Logger;
 
 import javolution.util.FastList;
 import javolution.util.FastMap;
+import net.sf.l2j.gameserver.util.FloodProtectorConfig;
 import net.sf.l2j.gameserver.util.StringUtil;
 
 public final class Config
@@ -54,6 +55,7 @@ public final class Config
 	public static final String CONFIGURATION_FILE = "./config/server.properties";
 	public static final String SIEGE_CONFIGURATION_FILE = "./config/siege.properties";
 	public static final String TELNET_FILE = "./config/telnet.properties";
+	public static final String FLOOD_PROTECTOR_FILE = "./config/floodprotector.properties";
 
 
 	//--------------------------------------------------
@@ -352,7 +354,22 @@ public final class Config
 	public static boolean DEADLOCK_DETECTOR;
 	public static int DEADLOCK_CHECK_INTERVAL;
 	public static boolean RESTART_ON_DEADLOCK;
-	public static int FLOODPROTECTOR_INITIALSIZE;
+	public static final FloodProtectorConfig FLOOD_PROTECTOR_USE_ITEM =
+		new FloodProtectorConfig("UseItemFloodProtector");
+	public static final FloodProtectorConfig FLOOD_PROTECTOR_ROLL_DICE =
+		new FloodProtectorConfig("RollDiceFloodProtector");
+	public static final FloodProtectorConfig FLOOD_PROTECTOR_FIREWORK =
+		new FloodProtectorConfig("FireworkFloodProtector");
+	public static final FloodProtectorConfig FLOOD_PROTECTOR_ITEM_PET_SUMMON =
+		new FloodProtectorConfig("ItemPetSummonFloodProtector");
+	public static final FloodProtectorConfig FLOOD_PROTECTOR_HERO_VOICE =
+		new FloodProtectorConfig("HeroVoiceFloodProtector");
+	public static final FloodProtectorConfig FLOOD_PROTECTOR_SUBCLASS =
+		new FloodProtectorConfig("SubclassFloodProtector");
+	public static final FloodProtectorConfig FLOOD_PROTECTOR_DROP_ITEM =
+		new FloodProtectorConfig("DropItemFloodProtector");
+	public static final FloodProtectorConfig FLOOD_PROTECTOR_SERVER_BYPASS =
+		new FloodProtectorConfig("ServerBypassFloodProtector");
 	public static boolean ALLOW_DISCARDITEM;
 	public static int AUTODESTROY_ITEM_AFTER;
 	public static int HERB_AUTO_DESTROY_TIME;
@@ -1319,7 +1336,6 @@ public final class Config
 					DEADLOCK_DETECTOR = Boolean.parseBoolean(General.getProperty("DeadLockDetector", "False"));
 					DEADLOCK_CHECK_INTERVAL = Integer.parseInt(General.getProperty("DeadLockCheckInterval", "20"));
 					RESTART_ON_DEADLOCK = Boolean.parseBoolean(General.getProperty("RestartOnDeadlock", "False"));
-					FLOODPROTECTOR_INITIALSIZE = Integer.parseInt(General.getProperty("FloodProtectorInitialSize", "50"));
 					ALLOW_DISCARDITEM = Boolean.parseBoolean(General.getProperty("AllowDiscardItem", "True"));
 					AUTODESTROY_ITEM_AFTER = Integer.parseInt(General.getProperty("AutoDestroyDroppedItemAfter", "0"));
 					HERB_AUTO_DESTROY_TIME = Integer.parseInt(General.getProperty("AutoDestroyHerbTime","15"))*1000;
@@ -1371,7 +1387,6 @@ public final class Config
 					ALLOW_RACE = Boolean.parseBoolean(General.getProperty("AllowRace", "True"));
 					ALLOW_WATER = Boolean.parseBoolean(General.getProperty("AllowWater", "True"));
 					ALLOW_RENTPET = Boolean.parseBoolean(General.getProperty("AllowRentPet", "False"));
-					FLOODPROTECTOR_INITIALSIZE = Integer.parseInt(General.getProperty("FloodProtectorInitialSize", "50"));
 					ALLOW_DISCARDITEM = Boolean.parseBoolean(General.getProperty("AllowDiscardItem", "True"));
 					ALLOWFISHING = Boolean.parseBoolean(General.getProperty("AllowFishing", "True"));
 					ALLOW_MANOR = Boolean.parseBoolean(General.getProperty("AllowManor", "True"));
@@ -1472,6 +1487,22 @@ public final class Config
 					e.printStackTrace();
 					throw new Error("Failed to Load "+GENERAL_CONFIG_FILE+" File.");
 				}
+				
+				// Load FloodProtector Properties file
+				try
+				{
+					Properties security = new Properties();
+					is = new FileInputStream(new File(FLOOD_PROTECTOR_FILE));
+					security.load(is);
+					
+					loadFloodProtectorConfigs(security);
+					
+				}
+				catch (Exception e)
+				{
+					e.printStackTrace();
+					throw new Error("Failed to Load "+FLOOD_PROTECTOR_FILE);
+				}
 
 				// Load NPC Properties file (if exists)
 				try
@@ -2179,4 +2210,47 @@ public final class Config
 			e.printStackTrace();
 		}
 	}
+	
+	/**
+	 * Loads flood protector configurations.
+	 * 
+	 * @param properties
+	 *            properties file reader
+	 */
+	private static void loadFloodProtectorConfigs(final Properties properties)
+	{
+		loadFloodProtectorConfig(properties, FLOOD_PROTECTOR_USE_ITEM, "UseItem", "4");
+		loadFloodProtectorConfig(properties, FLOOD_PROTECTOR_ROLL_DICE, "RollDice", "42");
+		loadFloodProtectorConfig(properties, FLOOD_PROTECTOR_FIREWORK, "Firework", "42");
+		loadFloodProtectorConfig(properties, FLOOD_PROTECTOR_ITEM_PET_SUMMON, "ItemPetSummon", "16");
+		loadFloodProtectorConfig(properties, FLOOD_PROTECTOR_HERO_VOICE, "HeroVoice", "100");
+		loadFloodProtectorConfig(properties, FLOOD_PROTECTOR_SUBCLASS, "Subclass", "20");
+		loadFloodProtectorConfig(properties, FLOOD_PROTECTOR_DROP_ITEM, "DropItem", "10");
+		loadFloodProtectorConfig(properties, FLOOD_PROTECTOR_SERVER_BYPASS, "ServerBypass", "5");
+	}
+	
+	/**
+	 * Loads single flood protector configuration.
+	 * 
+	 * @param properties
+	 *            properties file reader
+	 * @param config
+	 *            flood protector configuration instance
+	 * @param configString
+	 *            flood protector configuration string that determines for which flood protector
+	 *            configuration should be read
+	 * @param defaultInterval
+	 *            default flood protector interval
+	 */
+	private static void loadFloodProtectorConfig(final Properties properties,
+	        final FloodProtectorConfig config, final String configString,
+	        final String defaultInterval)
+	{
+		config.FLOOD_PROTECTION_INTERVAL = Integer.parseInt(properties.getProperty(StringUtil.concat("FloodProtector", configString, "Interval"), defaultInterval));
+		config.LOG_FLOODING = Boolean.parseBoolean(properties.getProperty(StringUtil.concat("FloodProtector", configString, "LogFlooding"), "False"));
+		config.PUNISHMENT_LIMIT = Integer.parseInt(properties.getProperty(StringUtil.concat("FloodProtector", configString, "PunishmentLimit"), "0"));
+		config.PUNISHMENT_TYPE = properties.getProperty(StringUtil.concat("FloodProtector", configString, "PunishmentType"), "none");
+		config.PUNISHMENT_TIME = Integer.parseInt(properties.getProperty(StringUtil.concat("FloodProtector", configString, "PunishmentTime"), "0"));
+		
+	}
 }

+ 8 - 4
L2_GameServer/java/net/sf/l2j/gameserver/model/actor/instance/L2PcInstance.java

@@ -213,7 +213,7 @@ import net.sf.l2j.gameserver.templates.item.L2WeaponType;
 import net.sf.l2j.gameserver.templates.skills.L2EffectType;
 import net.sf.l2j.gameserver.templates.skills.L2SkillType;
 import net.sf.l2j.gameserver.util.Broadcast;
-import net.sf.l2j.gameserver.util.FloodProtector;
+import net.sf.l2j.gameserver.util.FloodProtectors;
 import net.sf.l2j.gameserver.util.Util;
 import net.sf.l2j.util.Point3D;
 import net.sf.l2j.util.Rnd;
@@ -380,6 +380,8 @@ public final class L2PcInstance extends L2Playable
 	private int _lastCompassZone; // the last compass zone update send to the client
 
 	private boolean _isIn7sDungeon = false;
+	
+	private final FloodProtectors _floodProtectors = new FloodProtectors(this);
 
     private PunishLevel _punishLevel = PunishLevel.NONE;
     private long _punishTimer = 0;
@@ -10977,9 +10979,6 @@ public final class L2PcInstance extends L2Playable
 		// Close the connection with the client
 		closeNetConnection();
 		
-		// remove from flood protector
-		FloodProtector.removePlayer(getObjectId());
-		
 		if (getClanId() > 0)
 			getClan().broadcastToOtherOnlineMembers(new PledgeShowMemberListUpdate(this), this);
 		//ClanTable.getInstance().getClan(getClanId()).broadcastToOnlineMembers(new PledgeShowMemberListAdd(this));
@@ -12758,4 +12757,9 @@ public final class L2PcInstance extends L2Playable
     {
     	return _isInSiege;
     }
+    
+    public FloodProtectors getFloodProtectors()
+    {
+    	return _floodProtectors;
+    }
 }

+ 4 - 5
L2_GameServer/java/net/sf/l2j/gameserver/model/actor/instance/L2VillageMasterInstance.java

@@ -46,7 +46,6 @@ import net.sf.l2j.gameserver.network.serverpackets.NpcHtmlMessage;
 import net.sf.l2j.gameserver.network.serverpackets.SystemMessage;
 import net.sf.l2j.gameserver.network.serverpackets.UserInfo;
 import net.sf.l2j.gameserver.templates.chars.L2NpcTemplate;
-import net.sf.l2j.gameserver.util.FloodProtector;
 import net.sf.l2j.gameserver.util.StringUtil;
 import net.sf.l2j.gameserver.util.Util;
 
@@ -302,7 +301,7 @@ public final class L2VillageMasterInstance extends L2NpcInstance
                      * classes then disallow them to change to their most recently added sub-class choice.
                      */
                     
-                    if (!FloodProtector.tryPerformAction(player.getObjectId(), FloodProtector.PROTECTED_SUBCLASS))
+                    if (!player.getFloodProtectors().getSubclass().tryPerformAction("add subclass"))
                     {
                     	_log.warning("Player "+player.getName()+" has performed a subclass change too fast");
                     	return;
@@ -405,7 +404,7 @@ public final class L2VillageMasterInstance extends L2NpcInstance
                      * on small servers. TODO: On retail, each village master doesn't offer any subclass that is not given by itself so player
                      * always has to move to other location to change subclass after changing previously. Thanks Aikimaniac for this info.
                      */
-                    if (!FloodProtector.tryPerformAction(player.getObjectId(), FloodProtector.PROTECTED_SUBCLASS))
+                    if (!player.getFloodProtectors().getSubclass().tryPerformAction("change class"))
                     {
                     	_log.warning("Player "+player.getName()+" has performed a subclass change too fast");
                     	return;
@@ -452,8 +451,8 @@ public final class L2VillageMasterInstance extends L2NpcInstance
                      * subclass list even if false!
                      */
                 	
-                	if (!FloodProtector.tryPerformAction(player.getObjectId(), FloodProtector.PROTECTED_SUBCLASS))
-                    {
+                	if (!player.getFloodProtectors().getSubclass().tryPerformAction("change class"))
+                	{
                     	_log.warning("Player "+player.getName()+" has performed a subclass change too fast");
                     	return;
                     }

+ 0 - 4
L2_GameServer/java/net/sf/l2j/gameserver/network/clientpackets/EnterWorld.java

@@ -77,7 +77,6 @@ import net.sf.l2j.gameserver.network.serverpackets.ShortCutInit;
 import net.sf.l2j.gameserver.network.serverpackets.SkillCoolTime;
 import net.sf.l2j.gameserver.network.serverpackets.SystemMessage;
 import net.sf.l2j.gameserver.network.serverpackets.UserInfo;
-import net.sf.l2j.gameserver.util.FloodProtector;
 
 /**
  * Enter World Packet Handler<p>
@@ -125,9 +124,6 @@ public class EnterWorld extends L2GameClientPacket
 			InstanceManager.getInstance().getInstance(instanceId).removePlayer(activeChar.getObjectId());
 		}
 
-		// Register in flood protector
-		FloodProtector.registerNewPlayer(activeChar.getObjectId());
-
 		if (L2World.getInstance().findObject(activeChar.getObjectId()) != null)
 		{
 			if (Config.DEBUG)

+ 3 - 0
L2_GameServer/java/net/sf/l2j/gameserver/network/clientpackets/RequestBypassToServer.java

@@ -64,6 +64,9 @@ public final class RequestBypassToServer extends L2GameClientPacket
 
 		if (activeChar == null)
 		    return;
+		
+		if (!activeChar.getFloodProtectors().getServerBypass().tryPerformAction(_command))
+			return;
 
 		try {
 			if (_command.startsWith("admin_")) //&& activeChar.getAccessLevel() >= Config.GM_ACCESSLEVEL)

+ 1 - 2
L2_GameServer/java/net/sf/l2j/gameserver/network/clientpackets/RequestDropItem.java

@@ -27,7 +27,6 @@ import net.sf.l2j.gameserver.network.serverpackets.ItemList;
 import net.sf.l2j.gameserver.network.serverpackets.SystemMessage;
 import net.sf.l2j.gameserver.templates.item.L2EtcItemType;
 import net.sf.l2j.gameserver.templates.item.L2Item;
-import net.sf.l2j.gameserver.util.FloodProtector;
 import net.sf.l2j.gameserver.util.GMAudit;
 import net.sf.l2j.gameserver.util.IllegalPlayerAction;
 import net.sf.l2j.gameserver.util.Util;
@@ -64,7 +63,7 @@ public final class RequestDropItem extends L2GameClientPacket
         L2PcInstance activeChar = getClient().getActiveChar();
     	if (activeChar == null) return;
 		// Flood protect drop to avoid packet lag
-		if (!FloodProtector.tryPerformAction(activeChar.getObjectId(), FloodProtector.PROTECTED_DROPITEM))
+		if (!activeChar.getFloodProtectors().getDropItem().tryPerformAction("drop item"))
 			return;
     	
     	L2ItemInstance item = activeChar.getInventory().getItemByObjectId(_objectId);

+ 2 - 3
L2_GameServer/java/net/sf/l2j/gameserver/network/clientpackets/UseItem.java

@@ -36,7 +36,6 @@ import net.sf.l2j.gameserver.templates.item.L2ArmorType;
 import net.sf.l2j.gameserver.templates.item.L2Item;
 import net.sf.l2j.gameserver.templates.item.L2Weapon;
 import net.sf.l2j.gameserver.templates.item.L2WeaponType;
-import net.sf.l2j.gameserver.util.FloodProtector;
 
 /**
  * This class ...
@@ -85,8 +84,8 @@ public final class UseItem extends L2GameClientPacket
             return;
 
 		// Flood protect UseItem
-		if (!FloodProtector.tryPerformAction(activeChar.getObjectId(), FloodProtector.PROTECTED_USEITEM))
-			return;
+		if (!activeChar.getFloodProtectors().getUseItem().tryPerformAction("use item"))
+				return;
 
 		if (activeChar.getPrivateStoreType() != 0)
 		{

+ 1 - 2
L2_GameServer/java/net/sf/l2j/gameserver/skills/l2skills/L2SkillMount.java

@@ -22,7 +22,6 @@ import net.sf.l2j.gameserver.model.entity.TvTEvent;
 import net.sf.l2j.gameserver.network.SystemMessageId;
 import net.sf.l2j.gameserver.network.serverpackets.SystemMessage;
 import net.sf.l2j.gameserver.templates.StatsSet;
-import net.sf.l2j.gameserver.util.FloodProtector;
 
 public class L2SkillMount extends L2Skill
 {
@@ -47,7 +46,7 @@ public class L2SkillMount extends L2Skill
 		
 		L2PcInstance activePlayer = (L2PcInstance)caster;
 
-		if (!FloodProtector.tryPerformAction(activePlayer.getObjectId(), FloodProtector.PROTECTED_ITEMPETSUMMON))
+		if (!activePlayer.getFloodProtectors().getItemPetSummon().tryPerformAction("mount"))
 			return;
 		
 		// Dismount Action

+ 0 - 109
L2_GameServer/java/net/sf/l2j/gameserver/util/FloodProtector.java

@@ -1,109 +0,0 @@
-/*
- * 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 net.sf.l2j.gameserver.util;
-
-import javolution.util.FastMap;
-import javolution.util.FastMap.Entry;
-import net.sf.l2j.Config;
-import net.sf.l2j.gameserver.GameTimeController;
-
-/**
- * Flood protector
- * 
- * @author durgus
- */
-public class FloodProtector
-{
-	// Data Field
-	private static FastMap<Integer, Integer[]> _floodClient = new FastMap<Integer, Integer[]>(Config.FLOODPROTECTOR_INITIALSIZE).setShared(true);
-	
-	// =========================================================
-	
-	// reuse delays for protected actions (in game ticks 1 tick = 100ms)
-	private static final int[] REUSEDELAY = new int[]
-	{
-		4, 42, 42, 16, 100, 20, 10
-	};
-	
-	// protected actions
-	public static final byte PROTECTED_USEITEM = 0;
-	public static final byte PROTECTED_ROLLDICE = 1;
-	public static final byte PROTECTED_FIREWORK = 2;
-	public static final byte PROTECTED_ITEMPETSUMMON = 3;
-	public static final byte PROTECTED_HEROVOICE = 4;
-	public static final byte PROTECTED_SUBCLASS = 5;
-	public static final byte PROTECTED_DROPITEM = 6;
-	
-	/**
-	 * Add a new player to the flood protector (should be done for all players
-	 * when they enter the world)
-	 * 
-	 * @param playerObjId
-	 */
-	public static void registerNewPlayer(int playerObjId)
-	{
-		// create a new array
-		Integer[] array = new Integer[REUSEDELAY.length];
-		for (int i = 0; i < array.length; i++)
-			array[i] = 0;
-		
-		// register the player with an empty array
-		_floodClient.put(playerObjId, array);
-	}
-	
-	/**
-	 * Remove a player from the flood protector (should be done if player loggs
-	 * off)
-	 * 
-	 * @param playerObjId
-	 */
-	public static void removePlayer(int playerObjId)
-	{
-		_floodClient.remove(playerObjId);
-	}
-	
-	/**
-	 * Return the size of the flood protector
-	 * 
-	 * @return size
-	 */
-	public static int getSize()
-	{
-		return _floodClient.size();
-	}
-	
-	/**
-	 * Try to perform the requested action
-	 * 
-	 * @param playerObjId
-	 * @param action
-	 * @return true if the action may be performed
-	 */
-	public static boolean tryPerformAction(int playerObjId, int action)
-	{
-		Entry<Integer, Integer[]> entry = _floodClient.getEntry(playerObjId);
-		if (entry == null)
-			return false; // player just disconnected
-		Integer[] value = entry.getValue();
-		
-		if (value[action] < GameTimeController.getGameTicks())
-		{
-			value[action] = GameTimeController.getGameTicks() + REUSEDELAY[action];
-			entry.setValue(value);
-			return true;
-		}
-		return false;
-	}
-}

+ 162 - 0
L2_GameServer/java/net/sf/l2j/gameserver/util/FloodProtectorAction.java

@@ -0,0 +1,162 @@
+/*
+ * 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 net.sf.l2j.gameserver.util;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import net.sf.l2j.gameserver.GameTimeController;
+import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
+
+/**
+ * Flood protector implementation.
+ * 
+ * @author fordfrog
+ */
+public final class FloodProtectorAction
+{
+	
+	/**
+	 * Logger
+	 */
+	private static final Logger _log = Logger.getLogger(FloodProtectorAction.class.getName());
+	/**
+	 * Player for this instance of flood protector.
+	 */
+	private final L2PcInstance _player;
+	/**
+	 * Configuration of this instance of flood protector.
+	 */
+	private final FloodProtectorConfig _config;
+	/**
+	 * Next game tick when new request is allowed.
+	 */
+	private volatile int _nextGameTick = GameTimeController.getGameTicks();
+	/**
+	 * Request counter.
+	 */
+	private AtomicInteger _count;
+	/**
+	 * Flag determining whether exceeding request has been logged.
+	 */
+	private boolean _logged;
+	/**
+	 * Flag determining whether punishment application is in progress so that we do not apply
+	 * punisment multiple times (flooding).
+	 */
+	private volatile boolean _punishmentInProgress;
+	
+	/**
+	 * Creates new instance of FloodProtectorAction.
+	 * 
+	 * @param player
+	 *            player for which flood protection is being created
+	 * @param config
+	 *            flood protector configuration
+	 */
+	public FloodProtectorAction(final L2PcInstance player, final FloodProtectorConfig config)
+	{
+		super();
+		_player = player;
+		_config = config;
+	}
+	
+	/**
+	 * Checks whether the request is flood protected or not.
+	 * 
+	 * @param command
+	 *            command issued or short command description
+	 * 
+	 * @return true if action is allowed, otherwise false
+	 */
+	public boolean tryPerformAction(final String command)
+	{
+		final int curTick = GameTimeController.getGameTicks();
+		
+		if (curTick < _nextGameTick || _punishmentInProgress)
+		{
+			if (_config.LOG_FLOODING && !_logged && _log.isLoggable(Level.WARNING))
+			{
+				_log.warning(StringUtil.concat(_config.FLOOD_PROTECTOR_TYPE, ": Player [", _player.getName(), "] called command [", command, "] [~", String.valueOf((_config.FLOOD_PROTECTION_INTERVAL - (_nextGameTick - curTick))
+				        * GameTimeController.MILLIS_IN_TICK), " ms] after previous command"));
+				_logged = true;
+			}
+			
+			_count.incrementAndGet();
+			
+			if (!_punishmentInProgress && _config.PUNISHMENT_LIMIT > 0
+			        && _count.get() > _config.PUNISHMENT_LIMIT && _config.PUNISHMENT_TYPE != null)
+			{
+				_punishmentInProgress = true;
+				
+				if ("ban".equals(_config.PUNISHMENT_TYPE))
+				{
+					banAccount();
+				}
+				else if ("jail".equals(_config.PUNISHMENT_TYPE))
+				{
+					jailChar();
+				}
+				
+				_punishmentInProgress = false;
+			}
+			
+			return false;
+		}
+		
+		if (_count.get() > 0)
+		{
+			if (_config.LOG_FLOODING && _log.isLoggable(Level.WARNING))
+			{
+				_log.warning(StringUtil.concat(_config.FLOOD_PROTECTOR_TYPE, ": Player [", _player.getName(), "] issued [", String.valueOf(_count), "] extra requests within [~", String.valueOf(_config.FLOOD_PROTECTION_INTERVAL
+				        * GameTimeController.MILLIS_IN_TICK), " ms]"));
+			}
+		}
+		
+		_nextGameTick = curTick + _config.FLOOD_PROTECTION_INTERVAL;
+		_logged = false;
+		_count.set(0);
+		
+		return true;
+	}
+	
+	/**
+	 * Bans char account and logs out the char.
+	 */
+	private void banAccount()
+	{
+		_player.setPunishLevel(L2PcInstance.PunishLevel.ACC, _config.PUNISHMENT_TIME);
+		
+		if (_log.isLoggable(Level.WARNING))
+		{
+			_log.warning(StringUtil.concat(_config.FLOOD_PROTECTOR_TYPE, ": Account [", _player.getAccountName(), "] banned for flooding [char ", _player.getName(), "] ", _config.PUNISHMENT_TIME <= 0 ? "forever" : "for "
+			        + _config.PUNISHMENT_TIME + " mins"));
+		}
+		
+		_player.logout();
+	}
+	
+	/**
+	 * Jails char.
+	 */
+	private void jailChar()
+	{
+		_player.setPunishLevel(L2PcInstance.PunishLevel.JAIL, _config.PUNISHMENT_TIME);
+		
+		if (_log.isLoggable(Level.WARNING))
+		{
+			_log.warning(StringUtil.concat(_config.FLOOD_PROTECTOR_TYPE, ": Player [", _player.getName(), "] jailed for flooding [char ", _player.getName(), "] ", _config.PUNISHMENT_TIME <= 0 ? "forever" : "for "
+			        + _config.PUNISHMENT_TIME + " mins"));
+		}
+	}
+}

+ 59 - 0
L2_GameServer/java/net/sf/l2j/gameserver/util/FloodProtectorConfig.java

@@ -0,0 +1,59 @@
+/*
+ * 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 net.sf.l2j.gameserver.util;
+
+/**
+ * Flood protector configuration
+ * 
+ * @author fordfrog
+ */
+public final class FloodProtectorConfig
+{
+	
+	/**
+	 * Type used for identification of logging output.
+	 */
+	public String FLOOD_PROTECTOR_TYPE;
+	/**
+	 * Flood protection interval in game ticks.
+	 */
+	public int FLOOD_PROTECTION_INTERVAL;
+	/**
+	 * Whether flooding should be logged.
+	 */
+	public boolean LOG_FLOODING;
+	/**
+	 * If specified punishment limit is exceeded, punishment is applied.
+	 */
+	public int PUNISHMENT_LIMIT;
+	/**
+	 * Punishment type. Either 'none', 'ban' or 'jail'.
+	 */
+	public String PUNISHMENT_TYPE;
+	/**
+	 * For how long should the char/account be punished.
+	 */
+	public int PUNISHMENT_TIME;
+	
+	/**
+	 * Creates new instance of FloodProtectorConfig.
+	 * 
+	 * @param floodProtectorType
+	 *            {@link #FLOOD_PROTECTOR_TYPE}
+	 */
+	public FloodProtectorConfig(final String floodProtectorType)
+	{
+		super();
+		FLOOD_PROTECTOR_TYPE = floodProtectorType;
+	}
+}

+ 157 - 0
L2_GameServer/java/net/sf/l2j/gameserver/util/FloodProtectors.java

@@ -0,0 +1,157 @@
+/*
+ * 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 net.sf.l2j.gameserver.util;
+
+import net.sf.l2j.Config;
+import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
+
+/**
+ * Collection of flood protectors for single player.
+ * 
+ * @author fordfrog
+ */
+public final class FloodProtectors
+{
+	
+	/**
+	 * Use-item flood protector.
+	 */
+	private final FloodProtectorAction _useItem;
+	/**
+	 * Roll-dice flood protector.
+	 */
+	private final FloodProtectorAction _rollDice;
+	/**
+	 * Firework flood protector.
+	 */
+	private final FloodProtectorAction _firework;
+	/**
+	 * Item-pet-summon flood protector.
+	 */
+	private final FloodProtectorAction _itemPetSummon;
+	/**
+	 * Hero-voice flood protector.
+	 */
+	private final FloodProtectorAction _heroVoice;
+	/**
+	 * Subclass flood protector.
+	 */
+	private final FloodProtectorAction _subclass;
+	/**
+	 * Drop-item flood protector.
+	 */
+	private final FloodProtectorAction _dropItem;
+	/**
+	 * Server-bypass flood protector.
+	 */
+	private final FloodProtectorAction _serverBypass;
+	
+	/**
+	 * Creates new instance of FloodProtectors.
+	 * 
+	 * @param player
+	 *            player for which the collection of flood protectors is being created.
+	 */
+	public FloodProtectors(final L2PcInstance player)
+	{
+		super();
+		_useItem = new FloodProtectorAction(player, Config.FLOOD_PROTECTOR_USE_ITEM);
+		_rollDice = new FloodProtectorAction(player, Config.FLOOD_PROTECTOR_ROLL_DICE);
+		_firework = new FloodProtectorAction(player, Config.FLOOD_PROTECTOR_FIREWORK);
+		_itemPetSummon = new FloodProtectorAction(player, Config.FLOOD_PROTECTOR_ITEM_PET_SUMMON);
+		_heroVoice = new FloodProtectorAction(player, Config.FLOOD_PROTECTOR_HERO_VOICE);
+		_subclass = new FloodProtectorAction(player, Config.FLOOD_PROTECTOR_SUBCLASS);
+		_dropItem = new FloodProtectorAction(player, Config.FLOOD_PROTECTOR_DROP_ITEM);
+		_serverBypass = new FloodProtectorAction(player, Config.FLOOD_PROTECTOR_SERVER_BYPASS);
+	}
+	
+	/**
+	 * Returns {@link #_useItem}.
+	 * 
+	 * @return {@link #_useItem}
+	 */
+	public FloodProtectorAction getUseItem()
+	{
+		return _useItem;
+	}
+	
+	/**
+	 * Returns {@link #_rollDice}.
+	 * 
+	 * @return {@link #_rollDice}
+	 */
+	public FloodProtectorAction getRollDice()
+	{
+		return _rollDice;
+	}
+	
+	/**
+	 * Returns {@link #_firework}.
+	 * 
+	 * @return {@link #_firework}
+	 */
+	public FloodProtectorAction getFirework()
+	{
+		return _firework;
+	}
+	
+	/**
+	 * Returns {@link #_itemPetSummon}.
+	 * 
+	 * @return {@link #_itemPetSummon}
+	 */
+	public FloodProtectorAction getItemPetSummon()
+	{
+		return _itemPetSummon;
+	}
+	
+	/**
+	 * Returns {@link #_heroVoice}.
+	 * 
+	 * @return {@link #_heroVoice}
+	 */
+	public FloodProtectorAction getHeroVoice()
+	{
+		return _heroVoice;
+	}
+	
+	/**
+	 * Returns {@link #_subclass}.
+	 * 
+	 * @return {@link #_subclass}
+	 */
+	public FloodProtectorAction getSubclass()
+	{
+		return _subclass;
+	}
+	
+	/**
+	 * Returns {@link #_dropItem}.
+	 * 
+	 * @return {@link #_dropItem}
+	 */
+	public FloodProtectorAction getDropItem()
+	{
+		return _dropItem;
+	}
+	
+	/**
+	 * Returns {@link #_serverBypass}.
+	 * 
+	 * @return {@link #_serverBypass}
+	 */
+	public FloodProtectorAction getServerBypass()
+	{
+		return _serverBypass;
+	}
+}