Decouple and put all exploit fixes in their own listener.

This commit is contained in:
Olof Larsson 2013-04-18 14:02:39 +02:00
parent af094b6647
commit c098171759
6 changed files with 212 additions and 147 deletions

View File

@ -14,7 +14,8 @@ import com.massivecraft.factions.integration.SpoutFeatures;
import com.massivecraft.factions.integration.Worldguard; import com.massivecraft.factions.integration.Worldguard;
import com.massivecraft.factions.listeners.FactionsChatListener; import com.massivecraft.factions.listeners.FactionsChatListener;
import com.massivecraft.factions.listeners.FactionsEntityListener; import com.massivecraft.factions.listeners.FactionsEntityListener;
import com.massivecraft.factions.listeners.FactionsExploitListener; import com.massivecraft.factions.listeners.FactionsListenerExploit;
import com.massivecraft.factions.listeners.FactionsListenerMain;
import com.massivecraft.factions.listeners.FactionsPlayerListener; import com.massivecraft.factions.listeners.FactionsPlayerListener;
import com.massivecraft.factions.task.AutoLeaveTask; import com.massivecraft.factions.task.AutoLeaveTask;
import com.massivecraft.factions.task.EconLandRewardTask; import com.massivecraft.factions.task.EconLandRewardTask;
@ -45,7 +46,6 @@ public class Factions extends MPlugin
public FactionsPlayerListener playerListener; public FactionsPlayerListener playerListener;
public FactionsChatListener chatListener; public FactionsChatListener chatListener;
public FactionsEntityListener entityListener; public FactionsEntityListener entityListener;
public FactionsExploitListener exploitListener;
// -------------------------------------------- // // -------------------------------------------- //
// OVERRIDE // OVERRIDE
@ -83,8 +83,12 @@ public class Factions extends MPlugin
EconLandRewardTask.get().schedule(this); EconLandRewardTask.get().schedule(this);
// Register Event Handlers // Register Event Handlers
MainListener.get().setup(); FactionsListenerMain.get().setup();
// TODO: Chat goes here
FactionsListenerExploit.get().setup();
// TODO: Get rid of these
this.playerListener = new FactionsPlayerListener(); this.playerListener = new FactionsPlayerListener();
getServer().getPluginManager().registerEvents(this.playerListener, this); getServer().getPluginManager().registerEvents(this.playerListener, this);
@ -94,8 +98,7 @@ public class Factions extends MPlugin
this.entityListener = new FactionsEntityListener(); this.entityListener = new FactionsEntityListener();
getServer().getPluginManager().registerEvents(this.entityListener, this); getServer().getPluginManager().registerEvents(this.entityListener, this);
this.exploitListener = new FactionsExploitListener();
getServer().getPluginManager().registerEvents(this.exploitListener, this);
postEnable(); postEnable();
} }

View File

@ -1,11 +1,9 @@
package com.massivecraft.factions.listeners; package com.massivecraft.factions.listeners;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -16,7 +14,6 @@ import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile; import org.bukkit.entity.Projectile;
import org.bukkit.entity.TNTPrimed;
import org.bukkit.entity.Wither; import org.bukkit.entity.Wither;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
@ -127,30 +124,6 @@ public class FactionsEntityListener implements Listener
event.setCancelled(true); event.setCancelled(true);
return; return;
} }
// TNT in water/lava doesn't normally destroy any surrounding blocks, which is usually desired behavior, but...
// this optional change below provides workaround for waterwalling providing perfect protection,
// and makes cheap (non-obsidian) TNT cannons require minor maintenance between shots
Block center = event.getLocation().getBlock();
if (event.getEntity() instanceof TNTPrimed && ConfServer.handleExploitTNTWaterlog && center.isLiquid())
{
// a single surrounding block in all 6 directions is broken if the material is weak enough
List<Block> targets = new ArrayList<Block>();
targets.add(center.getRelative(0, 0, 1));
targets.add(center.getRelative(0, 0, -1));
targets.add(center.getRelative(0, 1, 0));
targets.add(center.getRelative(0, -1, 0));
targets.add(center.getRelative(1, 0, 0));
targets.add(center.getRelative(-1, 0, 0));
for (Block target : targets)
{
int id = target.getTypeId();
// ignore air, bedrock, water, lava, obsidian, enchanting table, etc.... too bad we can't get a blast resistance value through Bukkit yet
if (id != 0 && (id < 7 || id > 11) && id != 49 && id != 90 && id != 116 && id != 119 && id != 120 && id != 130)
target.breakNaturally();
}
}
} }
// mainly for flaming arrows; don't want allies or people in safe zones to be ignited even after damage event is cancelled // mainly for flaming arrows; don't want allies or people in safe zones to be ignited even after damage event is cancelled

View File

@ -1,68 +0,0 @@
package com.massivecraft.factions.listeners;
import org.bukkit.block.Block;
import org.bukkit.event.block.BlockFromToEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.Location;
import org.bukkit.Material;
import com.massivecraft.factions.ConfServer;
public class FactionsExploitListener implements Listener
{
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void obsidianGenerator(BlockFromToEvent event)
{
if (!ConfServer.handleExploitObsidianGenerators) return;
// thanks to ObGenBlocker and WorldGuard for this method
Block block = event.getToBlock();
int source = event.getBlock().getTypeId();
int target = block.getTypeId();
if ((target == 55 || target == 132) && (source == 0 || source == 10 || source == 11))
block.setTypeId(0);
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void enderPearlTeleport(PlayerTeleportEvent event)
{
if (!ConfServer.handleExploitEnderPearlClipping) return;
if (event.getCause() != PlayerTeleportEvent.TeleportCause.ENDER_PEARL) return;
// this exploit works when the target location is within 0.31 blocks or so of a door or glass block or similar...
Location target = event.getTo();
Location from = event.getFrom();
// blocks who occupy less than 1 block width or length wise need to be handled differently
Material mat = event.getTo().getBlock().getType();
if (
((mat == Material.THIN_GLASS || mat == Material.IRON_FENCE) && clippingThrough(target, from, 0.65))
|| ((mat == Material.FENCE || mat == Material.NETHER_FENCE) && clippingThrough(target, from, 0.45))
)
{
event.setTo(from);
return;
}
// simple fix otherwise: ender pearl target locations are standardized to be in the center (X/Z) of the target block, not at the edges
target.setX(target.getBlockX() + 0.5);
target.setZ(target.getBlockZ() + 0.5);
event.setTo(target);
}
public static boolean clippingThrough(Location target, Location from, double thickness)
{
return
(
(from.getX() > target.getX() && (from.getX() - target.getX() < thickness))
|| (target.getX() > from.getX() && (target.getX() - from.getX() < thickness))
|| (from.getZ() > target.getZ() && (from.getZ() - target.getZ() < thickness))
|| (target.getZ() > from.getZ() && (target.getZ() - from.getZ() < thickness))
);
}
}

View File

@ -0,0 +1,191 @@
package com.massivecraft.factions.listeners;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.entity.TNTPrimed;
import org.bukkit.event.block.BlockFromToEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.util.NumberConversions;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import com.massivecraft.factions.ConfServer;
import com.massivecraft.factions.Factions;
import com.massivecraft.mcore.util.SenderUtil;
import com.massivecraft.mcore.util.Txt;
public class FactionsListenerExploit implements Listener
{
// -------------------------------------------- //
// INSTANCE & CONSTRUCT
// -------------------------------------------- //
private static FactionsListenerExploit i = new FactionsListenerExploit();
public static FactionsListenerExploit get() { return i; }
// -------------------------------------------- //
// SETUP
// -------------------------------------------- //
public void setup()
{
Bukkit.getPluginManager().registerEvents(this, Factions.get());
}
// -------------------------------------------- //
// INTERACT SPAM
// -------------------------------------------- //
// TODO: Now that I decoupled this one it may be to sensitive.
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void interactSpam(PlayerInteractEvent event)
{
if (!ConfServer.handleExploitInteractionSpam) return;
Player player = event.getPlayer();
String playerId = SenderUtil.getSenderId(player);
InteractAttemptSpam attempt = interactSpammers.get(playerId);
if (attempt == null)
{
attempt = new InteractAttemptSpam();
interactSpammers.put(playerId, attempt);
}
int count = attempt.increment();
if (count >= 10)
{
player.sendMessage(Txt.parse("<b>Ouch, that is starting to hurt. You should give it a rest."));
player.damage(NumberConversions.floor((double)count / 10));
}
}
// for handling people who repeatedly spam attempts to open a door (or similar) in another faction's territory
private Map<String, InteractAttemptSpam> interactSpammers = new HashMap<String, InteractAttemptSpam>();
private static class InteractAttemptSpam
{
private int attempts = 0;
private long lastAttempt = System.currentTimeMillis();
// returns the current attempt count
public int increment()
{
long Now = System.currentTimeMillis();
if (Now > lastAttempt + 2000)
attempts = 1;
else
attempts++;
lastAttempt = Now;
return attempts;
}
}
// -------------------------------------------- //
// OBSIDIAN GENERATORS
// -------------------------------------------- //
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void obsidianGenerators(BlockFromToEvent event)
{
if (!ConfServer.handleExploitObsidianGenerators) return;
// thanks to ObGenBlocker and WorldGuard for this method
Block block = event.getToBlock();
int source = event.getBlock().getTypeId();
int target = block.getTypeId();
if ((target == 55 || target == 132) && (source == 0 || source == 10 || source == 11))
{
block.setTypeId(0);
}
}
// -------------------------------------------- //
// ENDER PEARL CLIPPING
// -------------------------------------------- //
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void enderPearlClipping(PlayerTeleportEvent event)
{
if (!ConfServer.handleExploitEnderPearlClipping) return;
if (event.getCause() != PlayerTeleportEvent.TeleportCause.ENDER_PEARL) return;
// this exploit works when the target location is within 0.31 blocks or so of a door or glass block or similar...
Location target = event.getTo();
Location from = event.getFrom();
// blocks who occupy less than 1 block width or length wise need to be handled differently
Material mat = event.getTo().getBlock().getType();
if (
((mat == Material.THIN_GLASS || mat == Material.IRON_FENCE) && clippingThrough(target, from, 0.65))
|| ((mat == Material.FENCE || mat == Material.NETHER_FENCE) && clippingThrough(target, from, 0.45))
)
{
event.setTo(from);
return;
}
// simple fix otherwise: ender pearl target locations are standardized to be in the center (X/Z) of the target block, not at the edges
target.setX(target.getBlockX() + 0.5);
target.setZ(target.getBlockZ() + 0.5);
event.setTo(target);
}
public static boolean clippingThrough(Location target, Location from, double thickness)
{
return
(
(from.getX() > target.getX() && (from.getX() - target.getX() < thickness))
|| (target.getX() > from.getX() && (target.getX() - from.getX() < thickness))
|| (from.getZ() > target.getZ() && (from.getZ() - target.getZ() < thickness))
|| (target.getZ() > from.getZ() && (target.getZ() - from.getZ() < thickness))
);
}
// -------------------------------------------- //
// TNT WATERLOG
// -------------------------------------------- //
// TNT in water/lava doesn't normally destroy any surrounding blocks, which is usually desired behavior, but...
// this optional change below provides workaround for waterwalling providing perfect protection,
// and makes cheap (non-obsidian) TNT cannons require minor maintenance between shots
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void tntWaterlog(EntityExplodeEvent event)
{
if (!ConfServer.handleExploitEnderPearlClipping) return;
if (!(event.getEntity() instanceof TNTPrimed)) return;
Block center = event.getLocation().getBlock();
if (!center.isLiquid()) return;
// a single surrounding block in all 6 directions is broken if the material is weak enough
List<Block> targets = new ArrayList<Block>();
targets.add(center.getRelative(0, 0, 1));
targets.add(center.getRelative(0, 0, -1));
targets.add(center.getRelative(0, 1, 0));
targets.add(center.getRelative(0, -1, 0));
targets.add(center.getRelative(1, 0, 0));
targets.add(center.getRelative(-1, 0, 0));
for (Block target : targets)
{
int id = target.getTypeId();
// ignore air, bedrock, water, lava, obsidian, enchanting table, etc.... too bad we can't get a blast resistance value through Bukkit yet
if (id != 0 && (id < 7 || id > 11) && id != 49 && id != 90 && id != 116 && id != 119 && id != 120 && id != 130)
{
target.breakNaturally();
}
}
}
}

View File

@ -1,4 +1,4 @@
package com.massivecraft.factions; package com.massivecraft.factions.listeners;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
@ -30,18 +30,25 @@ import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.server.PluginDisableEvent; import org.bukkit.event.server.PluginDisableEvent;
import org.bukkit.event.server.PluginEnableEvent; import org.bukkit.event.server.PluginEnableEvent;
import com.massivecraft.factions.BoardColl;
import com.massivecraft.factions.ConfServer;
import com.massivecraft.factions.FFlag;
import com.massivecraft.factions.FPerm;
import com.massivecraft.factions.FPlayer;
import com.massivecraft.factions.Faction;
import com.massivecraft.factions.Factions;
import com.massivecraft.factions.integration.SpoutFeatures; import com.massivecraft.factions.integration.SpoutFeatures;
import com.massivecraft.mcore.ps.PS; import com.massivecraft.mcore.ps.PS;
public class MainListener implements Listener public class FactionsListenerMain implements Listener
{ {
// -------------------------------------------- // // -------------------------------------------- //
// INSTANCE & CONSTRUCT // INSTANCE & CONSTRUCT
// -------------------------------------------- // // -------------------------------------------- //
private static MainListener i = new MainListener(); private static FactionsListenerMain i = new FactionsListenerMain();
public static MainListener get() { return i; } public static FactionsListenerMain get() { return i; }
public MainListener() {} public FactionsListenerMain() {}
// -------------------------------------------- // // -------------------------------------------- //
// SETUP // SETUP

View File

@ -1,9 +1,7 @@
package com.massivecraft.factions.listeners; package com.massivecraft.factions.listeners;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
@ -20,7 +18,6 @@ import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerKickEvent; import org.bukkit.event.player.PlayerKickEvent;
import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.util.NumberConversions;
import com.massivecraft.factions.BoardColl; import com.massivecraft.factions.BoardColl;
import com.massivecraft.factions.ConfServer; import com.massivecraft.factions.ConfServer;
@ -137,23 +134,6 @@ public class FactionsPlayerListener implements Listener
if ( ! canPlayerUseBlock(player, block, false)) if ( ! canPlayerUseBlock(player, block, false))
{ {
event.setCancelled(true); event.setCancelled(true);
if (ConfServer.handleExploitInteractionSpam)
{
String name = player.getName();
InteractAttemptSpam attempt = interactSpammers.get(name);
if (attempt == null)
{
attempt = new InteractAttemptSpam();
interactSpammers.put(name, attempt);
}
int count = attempt.increment();
if (count >= 10)
{
FPlayer me = FPlayerColl.get().get(name);
me.msg("<b>Ouch, that is starting to hurt. You should give it a rest.");
player.damage(NumberConversions.floor((double)count / 10));
}
}
return; return;
} }
@ -167,27 +147,6 @@ public class FactionsPlayerListener implements Listener
} }
// for handling people who repeatedly spam attempts to open a door (or similar) in another faction's territory
private Map<String, InteractAttemptSpam> interactSpammers = new HashMap<String, InteractAttemptSpam>();
private static class InteractAttemptSpam
{
private int attempts = 0;
private long lastAttempt = System.currentTimeMillis();
// returns the current attempt count
public int increment()
{
long Now = System.currentTimeMillis();
if (Now > lastAttempt + 2000)
attempts = 1;
else
attempts++;
lastAttempt = Now;
return attempts;
}
}
// TODO: Refactor ! justCheck -> to informIfNot // TODO: Refactor ! justCheck -> to informIfNot
// TODO: Possibly incorporate pain build... // TODO: Possibly incorporate pain build...
public static boolean playerCanUseItemHere(Player player, Location loc, Material material, boolean justCheck) public static boolean playerCanUseItemHere(Player player, Location loc, Material material, boolean justCheck)