Review dual wielding.

* Account for off hand in more places.
* Use bridge methods to get rid of warnings for now.
* Adds utility methods to CheckUtils.
* Do not allow left click on off hand (knockback).
This commit is contained in:
asofold 2016-06-01 19:31:53 +02:00
parent 241ff08d47
commit 68c4ab2bf2
11 changed files with 171 additions and 68 deletions

View File

@ -13,7 +13,6 @@ import fr.neatmonster.nocheatplus.actions.ParameterName;
import fr.neatmonster.nocheatplus.checks.Check;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.ViolationData;
import fr.neatmonster.nocheatplus.compat.Bridge1_9;
import fr.neatmonster.nocheatplus.compat.BridgeHealth;
import fr.neatmonster.nocheatplus.config.ConfPaths;
import fr.neatmonster.nocheatplus.config.ConfigManager;
@ -124,17 +123,7 @@ public class FastConsume extends Check implements Listener{
// Reset interaction.
if (cancel) {
// Fake interaction to prevent violation loops with false positives.
ItemStack actualStack = Bridge1_9.getItemInMainHand(player);
if (
Bridge1_9.hasGetItemInOffHand()
&& (actualStack == null || !CheckUtils.isConsumable(actualStack.getType()))
) {
// Assume this to make sense.
actualStack = Bridge1_9.getItemInOffHand(player);
if (actualStack == null || !CheckUtils.isConsumable(actualStack.getType())) {
actualStack = null;
}
}
final ItemStack actualStack = CheckUtils.getFirstConsumableItemInHand(player);
data.instantEatFood = actualStack == null ? null : actualStack.getType();
// TODO: Allows some abuse: 1. try instantly eat (cancelled) 2. consume item directly when needed.
} else {

View File

@ -20,6 +20,7 @@ import fr.neatmonster.nocheatplus.checks.CheckListener;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.inventory.Items;
import fr.neatmonster.nocheatplus.compat.AlmostBoolean;
import fr.neatmonster.nocheatplus.compat.Bridge1_9;
import fr.neatmonster.nocheatplus.hooks.NCPExemptionManager;
import fr.neatmonster.nocheatplus.permissions.Permissions;
import fr.neatmonster.nocheatplus.stats.Counters;
@ -72,7 +73,8 @@ public class BlockBreakListener extends CheckListener {
final Player player = event.getPlayer();
// Illegal enchantments hotfix check.
if (Items.checkIllegalEnchantments(player, player.getItemInHand())) {
// TODO: Legacy / encapsulate fully there.
if (Items.checkIllegalEnchantmentsAllHands(player)) {
event.setCancelled(true);
counters.addPrimaryThread(idCancelDIllegalItem, 1);
}
@ -242,7 +244,7 @@ public class BlockBreakListener extends CheckListener {
final int tick = TickTask.getTick();
// Skip if already set to the same block without breaking within one tick difference.
final ItemStack stack = player.getItemInHand();
final ItemStack stack = Bridge1_9.getItemInMainHand(player);
final Material tool = stack == null ? null: stack.getType();
if (data.toolChanged(tool)) {
// Update.

View File

@ -11,6 +11,7 @@ import fr.neatmonster.nocheatplus.checks.Check;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.ViolationData;
import fr.neatmonster.nocheatplus.compat.AlmostBoolean;
import fr.neatmonster.nocheatplus.compat.Bridge1_9;
import fr.neatmonster.nocheatplus.permissions.Permissions;
import fr.neatmonster.nocheatplus.utilities.BlockProperties;
import fr.neatmonster.nocheatplus.utilities.PotionUtil;
@ -111,7 +112,7 @@ public class FastBreak extends Check {
player.sendMessage(data.stats.getStatsStr(true));
}
// Send info about current break:
final ItemStack stack = player.getItemInHand();
final ItemStack stack = Bridge1_9.getItemInMainHand(player);
final boolean isValidTool = BlockProperties.isValidTool(blockType, stack);
final double haste = PotionUtil.getPotionEffectAmplifier(player, PotionEffectType.FAST_DIGGING);
String msg = (isInstaBreak.decideOptimistically() ? ("[Insta=" + isInstaBreak + "]") : "[Normal]") + "[" + blockType + "] "+ elapsedTime + "u / " + expectedBreakingTime +"r (" + (isValidTool?"tool":"no-tool") + ")" + (haste == Double.NEGATIVE_INFINITY ? "" : " haste=" + ((int) haste + 1));

View File

@ -16,9 +16,11 @@ import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.checks.CheckListener;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.combined.CombinedConfig;
import fr.neatmonster.nocheatplus.compat.Bridge1_9;
import fr.neatmonster.nocheatplus.compat.BridgeHealth;
import fr.neatmonster.nocheatplus.stats.Counters;
import fr.neatmonster.nocheatplus.utilities.BlockProperties;
import fr.neatmonster.nocheatplus.utilities.CheckUtils;
/**
* Central location to listen to events that are relevant for the block interact checks.
@ -83,7 +85,7 @@ public class BlockInteractListener extends CheckListener {
case LEFT_CLICK_BLOCK:
break;
case RIGHT_CLICK_BLOCK:
final ItemStack stack = player.getItemInHand();
final ItemStack stack = Bridge1_9.getUsedItem(player, event);
if (stack != null && stack.getType() == Material.ENDER_PEARL) {
if (!BlockProperties.isPassable(block.getType())) {
final CombinedConfig ccc = CombinedConfig.getConfig(player);
@ -135,15 +137,20 @@ public class BlockInteractListener extends CheckListener {
// Just prevent using the block.
event.setUseInteractedBlock(Result.DENY);
} else {
final Result previousUseItem = event.useItemInHand();
event.setCancelled(true);
event.setUseInteractedBlock(Result.DENY);
final ItemStack stack = player.getItemInHand();
final Material mat = stack == null ? Material.AIR : stack.getType();
if (!preventUseItem && (mat.isEdible() || mat == Material.POTION)) {
if (
previousUseItem == Result.DENY || preventUseItem
// Allow consumption still.
|| !CheckUtils.isConsumable(Bridge1_9.getUsedItem(player, event))
) {
event.setUseItemInHand(Result.DENY);
}
else {
// Consumable and not prevented otherwise.
// TODO: Ender pearl?
event.setUseItemInHand(Result.ALLOW);
} else {
event.setUseItemInHand(Result.DENY);
}
}
}

View File

@ -116,7 +116,7 @@ public class BlockPlaceListener extends CheckListener {
// TODO: Revise material use (not block.get... ?)
//final Material mat = block.getType();
final Player player = event.getPlayer();
final Material placedMat = hasReplacedState ? event.getBlockPlaced().getType() : player.getItemInHand().getType(); // Safety first.
final Material placedMat = hasReplacedState ? event.getBlockPlaced().getType() : Bridge1_9.getItemInMainHand(player).getType(); // Safety first.
boolean cancelled = false;
final BlockPlaceData data = BlockPlaceData.getData(player);
@ -239,7 +239,7 @@ public class BlockPlaceListener extends CheckListener {
}
final Player player = event.getPlayer();
final ItemStack stack = player.getItemInHand();
final ItemStack stack = Bridge1_9.getUsedItem(player, event);
if (stack == null) {
return;
}

View File

@ -121,10 +121,7 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
// Hotfix attempt for enchanted books.
// TODO: maybe a generalized version for the future...
// Illegal enchantments hotfix check.
if (Items.checkIllegalEnchantments(player, Bridge1_9.getItemInMainHand(player))) {
return true;
}
if (Bridge1_9.hasGetItemInOffHand() && Items.checkIllegalEnchantments(player, Bridge1_9.getItemInOffHand(player))) {
if (Items.checkIllegalEnchantmentsAllHands(player)) {
return true;
}
@ -616,19 +613,12 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
* @return
*/
private double getKnockBackLevel(final Player player) {
double level = 0.0;
double level = 1.0; // 1.0 is the minimum knock-back value.
// TODO: Get the RELEVANT item (...).
ItemStack stack = Bridge1_9.getItemInMainHand(player);
final ItemStack stack = Bridge1_9.getItemInMainHand(player);
if (!BlockProperties.isAir(stack)) {
level = (double) stack.getEnchantmentLevel(Enchantment.KNOCKBACK);
}
if (Bridge1_9.hasGetItemInOffHand()) {
stack = Bridge1_9.getItemInOffHand(player);
if (!BlockProperties.isAir(stack)) {
level = Math.max(level, (double) stack.getEnchantmentLevel(Enchantment.KNOCKBACK));
}
}
level += 1.0; // 1.0 is the minimum knock-back value.
if (player.isSprinting()) {
// TODO: Lost sprint?
level += 1.0;

View File

@ -34,6 +34,7 @@ import fr.neatmonster.nocheatplus.checks.CheckListener;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.combined.Combined;
import fr.neatmonster.nocheatplus.checks.combined.Improbable;
import fr.neatmonster.nocheatplus.compat.Bridge1_9;
import fr.neatmonster.nocheatplus.compat.BridgeHealth;
import fr.neatmonster.nocheatplus.components.JoinLeaveListener;
import fr.neatmonster.nocheatplus.stats.Counters;
@ -356,7 +357,7 @@ public class InventoryListener extends CheckListener implements JoinLeaveListen
return;
}
// TODO: Activate mob-egg check only for specific server versions.
final ItemStack stack = player.getItemInHand();
final ItemStack stack = Bridge1_9.getUsedItem(player, event);
Entity entity = event.getRightClicked();
if (stack != null && stack.getType() == Material.MONSTER_EGG
&& (entity == null || entity instanceof LivingEntity || entity instanceof ComplexEntityPart)

View File

@ -10,24 +10,47 @@ import org.bukkit.inventory.ItemStack;
import fr.neatmonster.nocheatplus.checks.Check;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.compat.Bridge1_9;
public class Items extends Check{
private static Items instance = null;
public Items() {
super(CheckType.INVENTORY_ITEMS);
instance = this;
}
/**
* Checks for illegal enchantments (legacy). Removes enchantments from
* WRITTEN_BOOK. Check both main and off hand.
*
* @param player
* @return True if the check is failed.
*/
public static final boolean checkIllegalEnchantmentsAllHands(final Player player) {
boolean result = false;
if (checkIllegalEnchantments(player, Bridge1_9.getItemInMainHand(player))) {
result = true;
}
if (Bridge1_9.hasGetItemInOffHand() && checkIllegalEnchantments(player, Bridge1_9.getItemInOffHand(player))) {
result = true;
}
return result;
}
/**
* Checks for illegal enchantments (legacy). Removes enchantments from
* WRITTEN_BOOK.
*
* @param player
* @param stack
* @return True if the check is failed.
*/
public static final boolean checkIllegalEnchantments(final Player player, final ItemStack stack){
if (stack == null) return false;
if (stack == null) {
return false;
}
final Material type = stack.getType();
// Fastest checks first.
// TODO: Make stuff configurable.

View File

@ -4,6 +4,8 @@ import java.lang.reflect.Method;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.potion.PotionEffectType;
@ -144,4 +146,52 @@ public class Bridge1_9 {
}
}
/**
* Get the item that was used with this event, assume clicking left or right rather (not feet etc.).
* @param player
* @param event
*/
public static ItemStack getUsedItem(final Player player, final PlayerInteractEvent event) {
if (!hasGetItemInOffHand()) { // Optimistic check.
return getItemInMainHand(player);
}
else {
switch (event.getHand()) {
case HAND: {
return getItemInMainHand(player);
}
case OFF_HAND: {
return getItemInOffHand(player);
}
default: {
return null;
}
}
}
}
/**
* Get the item that was used with this event, assume right click (not feet etc.).
* @param player
* @param event
*/
public static ItemStack getUsedItem(final Player player, final PlayerInteractEntityEvent event) {
if (!hasGetItemInOffHand()) { // Optimistic check.
return getItemInMainHand(player);
}
else {
switch (event.getHand()) {
case HAND: {
return getItemInMainHand(player);
}
case OFF_HAND: {
return getItemInOffHand(player);
}
default: {
return null;
}
}
}
}
}

View File

@ -23,6 +23,7 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffectType;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.compat.Bridge1_9;
import fr.neatmonster.nocheatplus.compat.MCAccess;
import fr.neatmonster.nocheatplus.compat.blocks.BlockPropertiesSetup;
import fr.neatmonster.nocheatplus.compat.blocks.init.vanilla.VanillaBlocksFactory;
@ -263,33 +264,33 @@ public class BlockProperties {
private static BlockProps defaultBlockProps = instantType;
protected static final Material[] instantMat = new Material[]{
// Named in wiki.
Material.CROPS,
Material.TRIPWIRE_HOOK, Material.TRIPWIRE,
Material.TORCH,
Material.TNT,
Material.SUGAR_CANE_BLOCK,
Material.SAPLING,
Material.RED_ROSE, Material.YELLOW_FLOWER,
Material.REDSTONE_WIRE,
Material.REDSTONE_TORCH_ON, Material.REDSTONE_TORCH_OFF,
Material.DIODE_BLOCK_ON, Material.DIODE_BLOCK_OFF,
Material.PUMPKIN_STEM,
Material.NETHER_WARTS,
Material.BROWN_MUSHROOM, Material.RED_MUSHROOM,
Material.MELON_STEM,
Material.WATER_LILY,
Material.LONG_GRASS,
Material.FIRE,
Material.DEAD_BUSH,
//
Material.CROPS,
// Named in wiki.
Material.CROPS,
Material.TRIPWIRE_HOOK, Material.TRIPWIRE,
Material.TORCH,
Material.TNT,
Material.SUGAR_CANE_BLOCK,
Material.SAPLING,
Material.RED_ROSE, Material.YELLOW_FLOWER,
Material.REDSTONE_WIRE,
Material.REDSTONE_TORCH_ON, Material.REDSTONE_TORCH_OFF,
Material.DIODE_BLOCK_ON, Material.DIODE_BLOCK_OFF,
Material.PUMPKIN_STEM,
Material.NETHER_WARTS,
Material.BROWN_MUSHROOM, Material.RED_MUSHROOM,
Material.MELON_STEM,
Material.WATER_LILY,
Material.LONG_GRASS,
Material.FIRE,
Material.DEAD_BUSH,
//
Material.CROPS,
// 1.4
Material.COMMAND,
Material.FLOWER_POT,
Material.CARROT,
Material.POTATO,
// 1.4
Material.COMMAND,
Material.FLOWER_POT,
Material.CARROT,
Material.POTATO,
};
private static BlockCache blockCache = null;
@ -983,7 +984,9 @@ public class BlockProperties {
* @return
*/
public static long getBreakingDuration(final int blockId, final Player player) {
final long res = getBreakingDuration(blockId, player.getItemInHand(), player.getInventory().getHelmet(), player, player.getLocation(useLoc));
final long res = getBreakingDuration(blockId,
Bridge1_9.getItemInMainHand(player), player.getInventory().getHelmet(),
player, player.getLocation(useLoc));
useLoc.setWorld(null);
return res;
}

View File

@ -10,6 +10,7 @@ import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.checks.CheckType;
@ -19,6 +20,7 @@ import fr.neatmonster.nocheatplus.checks.blockbreak.BlockBreakData;
import fr.neatmonster.nocheatplus.checks.combined.CombinedData;
import fr.neatmonster.nocheatplus.checks.fight.FightData;
import fr.neatmonster.nocheatplus.checks.inventory.InventoryData;
import fr.neatmonster.nocheatplus.compat.Bridge1_9;
import fr.neatmonster.nocheatplus.hooks.APIUtils;
import fr.neatmonster.nocheatplus.hooks.NCPExemptionManager;
import fr.neatmonster.nocheatplus.logging.StaticLog;
@ -266,14 +268,49 @@ public class CheckUtils {
return NCPAPIProvider.getNoCheatPlusAPI().getGenericInstance(Random.class);
}
/**
* Test if the item is consumable, like food, potions, milk bucket.
*
* @param stack
* May be null.
* @return
*/
public static boolean isConsumable(final ItemStack stack) {
return stack == null ? false : isConsumable(stack.getType());
}
/**
* Test if the item is consumable, like food, potions, milk bucket.
*
* @param type
* May be null.
* @return
*/
public static boolean isConsumable(final Material type) {
return type.isEdible() || type == Material.POTION || type == Material.MILK_BUCKET;
return type != null &&
(type.isEdible() || type == Material.POTION || type == Material.MILK_BUCKET);
}
/**
* Return the first consumable item found, checking main hand first and then
* off hand, if available. Concerns food/edible, potions, milk bucket.
*
* @param player
* @return null in case no item is consumable.
*/
public static ItemStack getFirstConsumableItemInHand(final Player player) {
ItemStack actualStack = Bridge1_9.getItemInMainHand(player);
if (
Bridge1_9.hasGetItemInOffHand()
&& (actualStack == null || !CheckUtils.isConsumable(actualStack.getType()))
) {
// Assume this to make sense.
actualStack = Bridge1_9.getItemInOffHand(player);
if (actualStack == null || !CheckUtils.isConsumable(actualStack.getType())) {
actualStack = null;
}
}
return actualStack;
}
}