diff --git a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/Clock.java b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/Clock.java new file mode 100644 index 00000000..d8240816 --- /dev/null +++ b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/Clock.java @@ -0,0 +1,19 @@ +package fr.neatmonster.nocheatplus.time; + +/** + * Some kind of clock. + * + * @author mc_dev + * + */ +public interface Clock { + + /** + * Get the clock counter. There is no guarantee that this is monotonic, nor + * need it be thread-safe. + * + * @return + */ + public long clock(); + +} diff --git a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/monotonic/Monotonic.java b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/monotonic/Monotonic.java new file mode 100644 index 00000000..c153052a --- /dev/null +++ b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/monotonic/Monotonic.java @@ -0,0 +1,58 @@ +package fr.neatmonster.nocheatplus.time.monotonic; + + +/** + * Static provider for monotonic clocks. Note that some calls can only be made + * from the server/application main thread, thread safe methods are prefixed + * with "synch", however all call results may have different offsets. + */ +public class Monotonic { + + private static final MonotonicClock nanos = new MonotonicNanosClock(); + private static final MonotonicClock millis = new MonotonicMillisClock(); + private static final MonotonicClock synchMillis = new MonotonicSynchClock(new MonotonicMillisClock()); + private static final MonotonicClock synchNanos = new MonotonicSynchClock(new MonotonicNanosClock()); + + /** + * Monotonic nanoseconds time, corresponding to System.nanoTime().
+ * Not thread-safe, only call from the main server/application + * thread. + * + * @return Monotonic time in nanoseconds. + */ + public static long nanos() { + return nanos.clock(); + } + + /** + * Monotonic milliseconds time, corresponding to System.currentTimeMillis().
+ * Not thread-safe, only call from the main server/application + * thread. + * + * @return Monotonic time in milliseconds. + */ + public static long millis() { + return millis.clock(); + } + + /** + * Monotonic nanoseconds time, corresponding to System.nanoTime().
+ * Thread-safe. + * + * @return Monotonic time in nanoseconds. + */ + public static long synchNanos() { + return synchNanos.clock(); + } + + /** + * Monotonic milliseconds time, corresponding to System.currentTimeMillis().
+ * Thread-safe. + * + * @return Monotonic time in milliseconds. + */ + public static long synchMillis() { + return synchMillis.clock(); + } + +} diff --git a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/monotonic/MonotonicAbstractClock.java b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/monotonic/MonotonicAbstractClock.java new file mode 100644 index 00000000..4e539b20 --- /dev/null +++ b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/monotonic/MonotonicAbstractClock.java @@ -0,0 +1,43 @@ +package fr.neatmonster.nocheatplus.time.monotonic; + +/** + * Basic implementation of increasing a counter for a not necessarily monotonic + * underlying clock, fetched with fetchClock(). Not thread-safe. + * + */ +public abstract class MonotonicAbstractClock implements MonotonicClock { + + private long clock; + + private long lastFetch; + + public MonotonicAbstractClock() { + clock = fetchClock(); + lastFetch = clock; + } + + public MonotonicAbstractClock(long clock) { + reset(clock); + } + + protected abstract long fetchClock(); + + @Override + public long clock() { + // TODO: Add feature to detect running too fast and correction as well. + final long fetch = fetchClock(); + final long diff = fetch - this.lastFetch; + if (diff > 0) { + this.clock += diff; + } + this.lastFetch = fetch; + return this.clock; + } + + @Override + public void reset(long clock) { + this.clock = clock; + this.lastFetch = fetchClock(); + } + +} diff --git a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/monotonic/MonotonicClock.java b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/monotonic/MonotonicClock.java new file mode 100644 index 00000000..6e8e4254 --- /dev/null +++ b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/monotonic/MonotonicClock.java @@ -0,0 +1,19 @@ +package fr.neatmonster.nocheatplus.time.monotonic; + +import fr.neatmonster.nocheatplus.time.Clock; + +/** + * Monotonic clock. The clock() method will count since creation or call of + * reset, unless stated otherwise. + * + * @author mc_dev + * + */ +public interface MonotonicClock extends Clock { + + /** + * Monotonic clock allow resetting for some reason. + */ + public void reset(long clock); + +} diff --git a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/monotonic/MonotonicMillisClock.java b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/monotonic/MonotonicMillisClock.java new file mode 100644 index 00000000..982c935c --- /dev/null +++ b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/monotonic/MonotonicMillisClock.java @@ -0,0 +1,16 @@ +package fr.neatmonster.nocheatplus.time.monotonic; + +/** + * Monotonic clock based on System.currentTimeMillis(). Not thread-safe. + * + * @author mc_dev + * + */ +public class MonotonicMillisClock extends MonotonicAbstractClock { + + @Override + protected long fetchClock() { + return System.currentTimeMillis(); + } + +} diff --git a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/monotonic/MonotonicNanosClock.java b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/monotonic/MonotonicNanosClock.java new file mode 100644 index 00000000..1df2b747 --- /dev/null +++ b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/monotonic/MonotonicNanosClock.java @@ -0,0 +1,16 @@ +package fr.neatmonster.nocheatplus.time.monotonic; + +/** + * Monotonic clock based on System.nanoTime(). Not thread-safe. + * + * @author mc_dev + * + */ +public class MonotonicNanosClock extends MonotonicAbstractClock { + + @Override + protected long fetchClock() { + return System.nanoTime(); + } + +} diff --git a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/monotonic/MonotonicSynchClock.java b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/monotonic/MonotonicSynchClock.java new file mode 100644 index 00000000..85befbcf --- /dev/null +++ b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/monotonic/MonotonicSynchClock.java @@ -0,0 +1,29 @@ +package fr.neatmonster.nocheatplus.time.monotonic; + +/** + * Thread safe version of a monotonic clock, wrapping around a given clock. + * Since synchronized method bodies wrap around the underlying clock, the clock + * must not be used outside of this instance. + * + * @author mc_dev + * + */ +public class MonotonicSynchClock implements MonotonicClock { + + private final MonotonicClock clock; + + public MonotonicSynchClock(MonotonicClock clock) { + this.clock = clock; + } + + @Override + public synchronized long clock() { + return clock.clock(); + } + + @Override + public synchronized void reset(long clock) { + this.clock.reset(clock); + } + +} diff --git a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/monotonic/package-info.java b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/monotonic/package-info.java new file mode 100644 index 00000000..ccc1809d --- /dev/null +++ b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/monotonic/package-info.java @@ -0,0 +1,6 @@ +/** + * Provides monotonic clocks. [Taken from NC4 Planning/Framework.] + * @author mc_dev + * + */ +package fr.neatmonster.nocheatplus.time.monotonic; \ No newline at end of file diff --git a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/package-info.java b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/package-info.java new file mode 100644 index 00000000..af16a7b6 --- /dev/null +++ b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/time/package-info.java @@ -0,0 +1,4 @@ +/** + * Time related functionality. [Taken from NC4 Planning/Framework.] + */ +package fr.neatmonster.nocheatplus.time; \ No newline at end of file diff --git a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/prefixtree/CharPrefixTree.java b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/prefixtree/CharPrefixTree.java index a59c4ff1..434b6108 100644 --- a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/prefixtree/CharPrefixTree.java +++ b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/prefixtree/CharPrefixTree.java @@ -109,7 +109,7 @@ public class CharPrefixTree, L extends CharLookupEntry> /** * Test hasPrefixWords for each given argument. * @param inputs - * @return true if hasPrefixWords returns ture for any of the inputs, false otherwise. + * @return true if hasPrefixWords(String) returns true for any of the inputs, false otherwise. */ public boolean hasAnyPrefixWords(final String... inputs){ for (int i = 0; i < inputs.length; i++){ @@ -120,6 +120,20 @@ public class CharPrefixTree, L extends CharLookupEntry> return false; } + /** + * Test hasPrefixWords for each element of the collection. + * @param inputs + * @return true if hasPrefixWords(String) returns true for any of the elements, false otherwise. + */ + public boolean hasAnyPrefixWords(final Collection inputs){ + for (final String input : inputs){ + if (hasPrefixWords(input)){ + return true; + } + } + return false; + } + public boolean isPrefix(final char[] chars){ return isPrefix(toCharacterList(chars)); } diff --git a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/checks/inventory/FastConsume.java b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/checks/inventory/FastConsume.java index 84042226..e7f7e1fb 100644 --- a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/checks/inventory/FastConsume.java +++ b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/checks/inventory/FastConsume.java @@ -12,6 +12,7 @@ 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.BridgeHealth; import fr.neatmonster.nocheatplus.config.ConfPaths; import fr.neatmonster.nocheatplus.config.ConfigManager; import fr.neatmonster.nocheatplus.logging.LogUtil; @@ -44,26 +45,35 @@ public class FastConsume extends Check implements Listener{ @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) public void onItemConsume(final PlayerItemConsumeEvent event){ final Player player = event.getPlayer(); - if (!isEnabled(player)) return; + if (player.isDead() && BridgeHealth.getHealth(player) <= 0.0) { + // Eat after death. + event.setCancelled(true); + return; + } + if (!isEnabled(player)) { + return; + } final InventoryData data = InventoryData.getData(player); - if (check(player, event.getItem(), data)){ + final long time = System.currentTimeMillis(); + if (check(player, event.getItem(), time, data)){ event.setCancelled(true); DataManager.getPlayerData(player.getName(), true).task.updateInventory(); } - data.instantEatInteract = 0; + data.instantEatInteract = time; data.instantEatFood = null; } - private boolean check(final Player player, final ItemStack stack, final InventoryData data){ + private boolean check(final Player player, final ItemStack stack, final long time, final InventoryData data){ // Uses the instant-eat data for convenience. // Consistency checks... if (stack == null){ // || stack.getType() != data.instantEatFood){ // TODO: Strict version should prevent other material (?). return false; } - final long time = System.currentTimeMillis(); - final long ref = Math.max(data.instantEatInteract, data.lastClickTime); + final long ref = data.instantEatInteract == 0 ? 0 : Math.max(data.instantEatInteract, data.lastClickTime); if (time < ref){ + // Time ran backwards. + data.instantEatInteract = data.lastClickTime = time; return false; } // Check exceptions. @@ -71,11 +81,11 @@ public class FastConsume extends Check implements Listener{ final Material mat = stack == null ? null : stack.getType(); if (mat != null){ if (cc.fastConsumeWhitelist){ - if (!cc.fastConsumeItems.contains(mat.getId())){ + if (!cc.fastConsumeItems.contains(mat)){ return false; } } - else if (cc.fastConsumeItems.contains(mat.getId())){ + else if (cc.fastConsumeItems.contains(mat)){ return false; } } diff --git a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/bukkit/BlockCacheBukkit.java b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/bukkit/BlockCacheBukkit.java index 7aac3a82..d391f162 100644 --- a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/bukkit/BlockCacheBukkit.java +++ b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/bukkit/BlockCacheBukkit.java @@ -11,6 +11,9 @@ public class BlockCacheBukkit extends BlockCache{ protected World world; + /** Temporary use. Use LocUtil.clone before passing on. Call setWorld(null) after use. */ + protected final Location useLoc = new Location(null, 0, 0, 0); + public BlockCacheBukkit(World world) { setAccess(world); } @@ -18,14 +21,19 @@ public class BlockCacheBukkit extends BlockCache{ @Override public void setAccess(World world) { this.world = world; + if (world != null) { + this.maxBlockY = world.getMaxHeight() - 1; + } } + @SuppressWarnings("deprecation") @Override public int fetchTypeId(final int x, final int y, final int z) { // TODO: consider setting type id and data at once. return world.getBlockTypeIdAt(x, y, z); } + @SuppressWarnings("deprecation") @Override public int fetchData(final int x, final int y, final int z) { // TODO: consider setting type id and data at once. @@ -49,8 +57,9 @@ public class BlockCacheBukkit extends BlockCache{ if (type != EntityType.BOAT){ // && !(other instanceof Minecart)) continue; } - final Location loc = entity.getLocation(); - if (Math.abs(loc.getY() - minY) < 0.7){ + final double locY = entity.getLocation(useLoc).getY(); + useLoc.setWorld(null); + if (Math.abs(locY - minY) < 0.7){ // TODO: A "better" estimate is possible, though some more tolerance would be good. return true; } diff --git a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/bukkit/MCAccessBukkit.java b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/bukkit/MCAccessBukkit.java index d1e78ebb..36dbcce3 100644 --- a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/bukkit/MCAccessBukkit.java +++ b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/bukkit/MCAccessBukkit.java @@ -28,10 +28,12 @@ import fr.neatmonster.nocheatplus.utilities.ReflectionUtil; public class MCAccessBukkit implements MCAccess, BlockPropertiesSetup{ + // private AlmostBoolean entityPlayerAvailable = AlmostBoolean.MAYBE; + /** * Constructor to let it fail. */ - public MCAccessBukkit(){ + public MCAccessBukkit() { // TODO: Add more that might fail if not supported ? Material.AIR.isSolid(); Material.AIR.isOccluding(); @@ -43,7 +45,7 @@ public class MCAccessBukkit implements MCAccess, BlockPropertiesSetup{ public String getMCVersion() { // Bukkit API. // TODO: maybe output something else. - return "1.4.6|1.4.7|1.5.x|1.6.1|1.6.2|?"; + return "1.4.6|1.4.7|1.5.x|1.6.x|1.7.x"; // 1.7.x is bold! } @Override @@ -55,7 +57,7 @@ public class MCAccessBukkit implements MCAccess, BlockPropertiesSetup{ public CommandMap getCommandMap() { try{ return (CommandMap) ReflectionUtil.invokeMethodNoArgs(Bukkit.getServer(), "getCommandMap"); - } catch (Throwable t){ + } catch (Throwable t) { // Nasty. return null; } @@ -76,13 +78,19 @@ public class MCAccessBukkit implements MCAccess, BlockPropertiesSetup{ @Override public AlmostBoolean isBlockSolid(final int id) { + @SuppressWarnings("deprecation") final Material mat = Material.getMaterial(id); - if (mat == null) return AlmostBoolean.MAYBE; - else return AlmostBoolean.match(mat.isSolid()); + if (mat == null) { + return AlmostBoolean.MAYBE; + } + else { + return AlmostBoolean.match(mat.isSolid()); + } } @Override public AlmostBoolean isBlockLiquid(final int id) { + @SuppressWarnings("deprecation") final Material mat = Material.getMaterial(id); if (mat == null) return AlmostBoolean.MAYBE; switch (mat) { @@ -104,8 +112,10 @@ public class MCAccessBukkit implements MCAccess, BlockPropertiesSetup{ @Override public AlmostBoolean isIllegalBounds(final Player player) { - if (player.isDead()) return AlmostBoolean.NO; - if (!player.isSleeping()){ // TODO: ignored sleeping ? + if (player.isDead()) { + return AlmostBoolean.NO; + } + if (!player.isSleeping()) { // TODO: ignored sleeping ? // TODO: This can test like ... nothing ! // (Might not be necessary.) } @@ -161,40 +171,43 @@ public class MCAccessBukkit implements MCAccess, BlockPropertiesSetup{ // TODO: Might try stuff like setNoDamageTicks. BridgeHealth.damage(player, 1.0); } - + @Override public void setupBlockProperties(final WorldConfigProvider worldConfigProvider) { + // Note deprecation suppression: These ids should be unique for a server run, that should be ok for setting up generic properties. // TODO: (?) Set some generic properties matching what BlockCache.getShape returns. - final Set fullBlocks = new HashSet(); + final Set fullBlocks = new HashSet(); for (final Material mat : new Material[]{ // TODO: Ice !? / Packed ice !? Material.GLASS, Material.GLOWSTONE, Material.ICE, Material.LEAVES, Material.COMMAND, Material.BEACON, Material.PISTON_BASE, - }){ - fullBlocks.add(mat.getId()); + }) { + fullBlocks.add(mat); } - for (final Material mat : Material.values()){ - if (!mat.isBlock()) continue; - final int id = mat.getId(); - if (id < 0 || id >= 4096 || fullBlocks.contains(id)) continue; - if (!mat.isOccluding() || !mat.isSolid() || mat.isTransparent()){ + for (final Material mat : Material.values()) { + if (!mat.isBlock()) { + continue; + } + if (fullBlocks.contains(mat)) { + continue; + } + if (!mat.isOccluding() || !mat.isSolid() || mat.isTransparent()) { // Uncertain bounding-box, allow passing through. long flags = BlockProperties.F_IGN_PASSABLE; - if ((BlockProperties.isSolid(id) || BlockProperties.isGround(id)) && !BlockProperties.isLiquid(id)){ + if ((BlockProperties.isSolid(mat) || BlockProperties.isGround(mat)) && !BlockProperties.isLiquid(mat)) { // Block can be ground, so allow standing on any height. flags |= BlockProperties.F_GROUND_HEIGHT; } - BlockProperties.setBlockFlags(id, BlockProperties.getBlockFlags(id) | flags); + BlockProperties.setBlockFlags(mat, BlockProperties.getBlockFlags(mat) | flags); } } // Blocks that are reported to be full and solid, but which are not. for (final Material mat : new Material[]{ Material.ENDER_PORTAL_FRAME, - }){ - final int id = mat.getId(); + }) { final long flags = BlockProperties.F_IGN_PASSABLE | BlockProperties.F_GROUND_HEIGHT; - BlockProperties.setBlockFlags(id, BlockProperties.getBlockFlags(id) | flags); + BlockProperties.setBlockFlags(mat, BlockProperties.getBlockFlags(mat) | flags); } } @@ -209,9 +222,9 @@ public class MCAccessBukkit implements MCAccess, BlockPropertiesSetup{ try{ return mat.hasGravity(); } - catch(Throwable t){ + catch(Throwable t) { // Backwards compatibility. - switch(mat){ + switch(mat) { case SAND: case GRAVEL: return true; @@ -220,5 +233,10 @@ public class MCAccessBukkit implements MCAccess, BlockPropertiesSetup{ } } } + +// @Override +// public void correctDirection(Player player) { +// // TODO: Consider using reflection (detect CraftPlayer, access EntityPlayer + check if possible (!), use flags for if valid or invalid.) +// } } diff --git a/NCPCompatCB2511/src/main/java/fr/neatmonster/nocheatplus/compat/cb2511/BlockCacheCB2511.java b/NCPCompatCB2511/src/main/java/fr/neatmonster/nocheatplus/compat/cb2511/BlockCacheCB2511.java deleted file mode 100644 index c11f994a..00000000 --- a/NCPCompatCB2511/src/main/java/fr/neatmonster/nocheatplus/compat/cb2511/BlockCacheCB2511.java +++ /dev/null @@ -1,133 +0,0 @@ -package fr.neatmonster.nocheatplus.compat.cb2511; - -import java.util.Iterator; -import java.util.List; - -import net.minecraft.server.AxisAlignedBB; -import net.minecraft.server.IBlockAccess; -import net.minecraft.server.Material; -import net.minecraft.server.TileEntity; -import net.minecraft.server.Vec3DPool; - -import org.bukkit.World; -import org.bukkit.craftbukkit.CraftWorld; -import org.bukkit.craftbukkit.entity.CraftEntity; -import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; - -import fr.neatmonster.nocheatplus.utilities.BlockCache; - -public class BlockCacheCB2511 extends BlockCache implements IBlockAccess{ - - /** Box for one time use, no nesting, no extra storing this(!). */ - protected static final AxisAlignedBB useBox = AxisAlignedBB.a(0, 0, 0, 0, 0, 0); - - protected net.minecraft.server.World world; - - public BlockCacheCB2511(World world) { - setAccess(world); - } - - @Override - public void setAccess(final World world) { - this.world = world == null ? null : ((CraftWorld) world).getHandle(); - } - - @Override - public int fetchTypeId(final int x, final int y, final int z) { - return world.getTypeId(x, y, z); - } - - @Override - public int fetchData(final int x, final int y, final int z) { - return world.getData(x, y, z); - } - - @Override - public double[] fetchBounds(final int x, final int y, final int z){ - - // TODO: change api for this / use nodes (!) - final int id = getTypeId(x, y, z); - final net.minecraft.server.Block block = net.minecraft.server.Block.byId[id]; - if (block == null) return null; - block.updateShape(this, x, y, z); // TODO: use THIS instead of world. - - // minX, minY, minZ, maxX, maxY, maxZ - return new double[]{block.v(), block.x(), block.z(), block.w(), block.y(), block.A()}; - } - - @Override - public boolean standsOnEntity(Entity entity, final double minX, final double minY, final double minZ, final double maxX, final double maxY, final double maxZ){ - try{ - // TODO: Probably check other ids too before doing this ? - - final net.minecraft.server.Entity mcEntity = ((CraftEntity) entity).getHandle(); - - final AxisAlignedBB box = useBox.b(minX, minY, minZ, maxX, maxY, maxZ); - @SuppressWarnings("rawtypes") - final List list = world.getEntities(mcEntity, box); - @SuppressWarnings("rawtypes") - Iterator iterator = list.iterator(); - while (iterator.hasNext()) { - final net.minecraft.server.Entity other = (net.minecraft.server.Entity) iterator.next(); - final EntityType type = other.getBukkitEntity().getType(); - if (type != EntityType.BOAT && type != EntityType.MINECART) continue; - final AxisAlignedBB otherBox = other.boundingBox; - if (box.a > otherBox.d || box.d < otherBox.a || box.b > otherBox.e || box.e < otherBox.b || box.c > otherBox.f || box.f < otherBox.c) continue; - else { - return true; - } - } - } - catch (Throwable t){ - // Ignore exceptions (Context: DisguiseCraft). - } - return false; - } - - /* (non-Javadoc) - * @see fr.neatmonster.nocheatplus.utilities.BlockCache#cleanup() - */ - @Override - public void cleanup() { - super.cleanup(); - world = null; - } - - @Override - public Material getMaterial(final int x, final int y, final int z) { - return world.getMaterial(x, y, z); - } - - @Override - public TileEntity getTileEntity(final int x, final int y, final int z) { - return world.getTileEntity(x, y, z); - } - - @Override - public Vec3DPool getVec3DPool() { - return world.getVec3DPool(); - } - - @Override - public boolean isBlockFacePowered(final int arg0, final int arg1, final int arg2, final int arg3) { - return world.isBlockFacePowered(arg0, arg1, arg2, arg3); - } - - @Override - public boolean t(final int x, final int y, final int z) { - return world.t(x, y, z); - } - - /** - * Compatibility with 1.4.2. - * @param x - * @param y - * @param z - * @return - */ - public boolean s(final int x, final int y, final int z) { - return world.t(x, y, z); - } - -} diff --git a/NCPCompatCB2512/src/main/java/fr/neatmonster/nocheatplus/compat/cb2512/BlockCacheCB2512.java b/NCPCompatCB2512/src/main/java/fr/neatmonster/nocheatplus/compat/cb2512/BlockCacheCB2512.java index 239cf945..3e59f505 100644 --- a/NCPCompatCB2512/src/main/java/fr/neatmonster/nocheatplus/compat/cb2512/BlockCacheCB2512.java +++ b/NCPCompatCB2512/src/main/java/fr/neatmonster/nocheatplus/compat/cb2512/BlockCacheCB2512.java @@ -30,7 +30,12 @@ public class BlockCacheCB2512 extends BlockCache implements IBlockAccess{ @Override public void setAccess(World world) { - this.world = world == null ? null : ((CraftWorld) world).getHandle(); + if (world != null) { + this.maxBlockY = world.getMaxHeight() - 1; + this.world = ((CraftWorld) world).getHandle(); + } else { + this.world = null; + } } @Override diff --git a/NCPCompatCB2512/src/main/java/fr/neatmonster/nocheatplus/compat/cb2512/MCAccessCB2512.java b/NCPCompatCB2512/src/main/java/fr/neatmonster/nocheatplus/compat/cb2512/MCAccessCB2512.java index 7dfa8bb2..aac825cb 100644 --- a/NCPCompatCB2512/src/main/java/fr/neatmonster/nocheatplus/compat/cb2512/MCAccessCB2512.java +++ b/NCPCompatCB2512/src/main/java/fr/neatmonster/nocheatplus/compat/cb2512/MCAccessCB2512.java @@ -99,7 +99,7 @@ public class MCAccessCB2512 implements MCAccess{ @Override public double getJumpAmplifier(final Player player) { - final net.minecraft.server.v1_4_5.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); if (mcPlayer.hasEffect(MobEffectList.JUMP)) return mcPlayer.getEffect(MobEffectList.JUMP).getAmplifier(); else return Double.NEGATIVE_INFINITY; @@ -107,7 +107,7 @@ public class MCAccessCB2512 implements MCAccess{ @Override public double getFasterMovementAmplifier(final Player player) { - final net.minecraft.server.v1_4_5.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); if (mcPlayer.hasEffect(MobEffectList.FASTER_MOVEMENT)) return mcPlayer.getEffect(MobEffectList.FASTER_MOVEMENT).getAmplifier(); else return Double.NEGATIVE_INFINITY; } @@ -134,13 +134,13 @@ public class MCAccessCB2512 implements MCAccess{ @Override public boolean shouldBeZombie(final Player player) { - final net.minecraft.server.v1_4_5.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); return !mcPlayer.dead && mcPlayer.getHealth() <= 0 ; } @Override public void setDead(final Player player, final int deathTicks) { - final net.minecraft.server.v1_4_5.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); mcPlayer.deathTicks = deathTicks; mcPlayer.dead = true; } @@ -162,4 +162,13 @@ public class MCAccessCB2512 implements MCAccess{ } } +// @Override +// public void correctDirection(final Player player) { +// final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); +// // Main direction. +// mcPlayer.yaw = LocUtil.correctYaw(mcPlayer.yaw); +// mcPlayer.pitch = LocUtil.correctPitch(mcPlayer.pitch); +// // Consider setting the lastYaw here too. +// } + } diff --git a/NCPCompatCB2545/src/main/java/fr/neatmonster/nocheatplus/compat/cb2545/BlockCacheCB2545.java b/NCPCompatCB2545/src/main/java/fr/neatmonster/nocheatplus/compat/cb2545/BlockCacheCB2545.java index 8e433a90..48e3a140 100644 --- a/NCPCompatCB2545/src/main/java/fr/neatmonster/nocheatplus/compat/cb2545/BlockCacheCB2545.java +++ b/NCPCompatCB2545/src/main/java/fr/neatmonster/nocheatplus/compat/cb2545/BlockCacheCB2545.java @@ -30,7 +30,12 @@ public class BlockCacheCB2545 extends BlockCache implements IBlockAccess{ @Override public void setAccess(World world) { - this.world = world == null ? null : ((CraftWorld) world).getHandle(); + if (world != null) { + this.maxBlockY = world.getMaxHeight() - 1; + this.world = ((CraftWorld) world).getHandle(); + } else { + this.world = null; + } } @Override diff --git a/NCPCompatCB2545/src/main/java/fr/neatmonster/nocheatplus/compat/cb2545/MCAccessCB2545.java b/NCPCompatCB2545/src/main/java/fr/neatmonster/nocheatplus/compat/cb2545/MCAccessCB2545.java index 979a916e..2c2dbd5b 100644 --- a/NCPCompatCB2545/src/main/java/fr/neatmonster/nocheatplus/compat/cb2545/MCAccessCB2545.java +++ b/NCPCompatCB2545/src/main/java/fr/neatmonster/nocheatplus/compat/cb2545/MCAccessCB2545.java @@ -99,7 +99,7 @@ public class MCAccessCB2545 implements MCAccess{ @Override public double getJumpAmplifier(final Player player) { - final net.minecraft.server.v1_4_6.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); if (mcPlayer.hasEffect(MobEffectList.JUMP)) return mcPlayer.getEffect(MobEffectList.JUMP).getAmplifier(); else return Double.NEGATIVE_INFINITY; @@ -107,7 +107,7 @@ public class MCAccessCB2545 implements MCAccess{ @Override public double getFasterMovementAmplifier(final Player player) { - final net.minecraft.server.v1_4_6.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); if (mcPlayer.hasEffect(MobEffectList.FASTER_MOVEMENT)) return mcPlayer.getEffect(MobEffectList.FASTER_MOVEMENT).getAmplifier(); else return Double.NEGATIVE_INFINITY; } @@ -134,13 +134,13 @@ public class MCAccessCB2545 implements MCAccess{ @Override public boolean shouldBeZombie(final Player player) { - final net.minecraft.server.v1_4_6.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); return !mcPlayer.dead && mcPlayer.getHealth() <= 0 ; } @Override public void setDead(final Player player, final int deathTicks) { - final net.minecraft.server.v1_4_6.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); mcPlayer.deathTicks = deathTicks; mcPlayer.dead = true; } @@ -162,4 +162,13 @@ public class MCAccessCB2545 implements MCAccess{ } } +// @Override +// public void correctDirection(final Player player) { +// final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); +// // Main direction. +// mcPlayer.yaw = LocUtil.correctYaw(mcPlayer.yaw); +// mcPlayer.pitch = LocUtil.correctPitch(mcPlayer.pitch); +// // Consider setting the lastYaw here too. +// } + } diff --git a/NCPCompatCB2602/src/main/java/fr/neatmonster/nocheatplus/compat/cb2602/BlockCacheCB2602.java b/NCPCompatCB2602/src/main/java/fr/neatmonster/nocheatplus/compat/cb2602/BlockCacheCB2602.java index c28817fa..76c2da6d 100644 --- a/NCPCompatCB2602/src/main/java/fr/neatmonster/nocheatplus/compat/cb2602/BlockCacheCB2602.java +++ b/NCPCompatCB2602/src/main/java/fr/neatmonster/nocheatplus/compat/cb2602/BlockCacheCB2602.java @@ -30,7 +30,12 @@ public class BlockCacheCB2602 extends BlockCache implements IBlockAccess{ @Override public void setAccess(World world) { - this.world = world == null ? null : ((CraftWorld) world).getHandle(); + if (world != null) { + this.maxBlockY = world.getMaxHeight() - 1; + this.world = ((CraftWorld) world).getHandle(); + } else { + this.world = null; + } } @Override diff --git a/NCPCompatCB2602/src/main/java/fr/neatmonster/nocheatplus/compat/cb2602/MCAccessCB2602.java b/NCPCompatCB2602/src/main/java/fr/neatmonster/nocheatplus/compat/cb2602/MCAccessCB2602.java index 739b2511..07d9c53d 100644 --- a/NCPCompatCB2602/src/main/java/fr/neatmonster/nocheatplus/compat/cb2602/MCAccessCB2602.java +++ b/NCPCompatCB2602/src/main/java/fr/neatmonster/nocheatplus/compat/cb2602/MCAccessCB2602.java @@ -100,7 +100,7 @@ public class MCAccessCB2602 implements MCAccess{ @Override public double getJumpAmplifier(final Player player) { - final net.minecraft.server.v1_4_R1.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); if (mcPlayer.hasEffect(MobEffectList.JUMP)) return mcPlayer.getEffect(MobEffectList.JUMP).getAmplifier(); else return Double.NEGATIVE_INFINITY; @@ -108,7 +108,7 @@ public class MCAccessCB2602 implements MCAccess{ @Override public double getFasterMovementAmplifier(final Player player) { - final net.minecraft.server.v1_4_R1.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); if (mcPlayer.hasEffect(MobEffectList.FASTER_MOVEMENT)) return mcPlayer.getEffect(MobEffectList.FASTER_MOVEMENT).getAmplifier(); else return Double.NEGATIVE_INFINITY; } @@ -135,13 +135,13 @@ public class MCAccessCB2602 implements MCAccess{ @Override public boolean shouldBeZombie(final Player player) { - final net.minecraft.server.v1_4_R1.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); return !mcPlayer.dead && mcPlayer.getHealth() <= 0 ; } @Override public void setDead(final Player player, final int deathTicks) { - final net.minecraft.server.v1_4_R1.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); mcPlayer.deathTicks = deathTicks; mcPlayer.dead = true; } @@ -164,4 +164,13 @@ public class MCAccessCB2602 implements MCAccess{ } } +// @Override +// public void correctDirection(final Player player) { +// final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); +// // Main direction. +// mcPlayer.yaw = LocUtil.correctYaw(mcPlayer.yaw); +// mcPlayer.pitch = LocUtil.correctPitch(mcPlayer.pitch); +// // Consider setting the lastYaw here too. +// } + } diff --git a/NCPCompatCB2645/src/main/java/fr/neatmonster/nocheatplus/compat/cb2645/BlockCacheCB2645.java b/NCPCompatCB2645/src/main/java/fr/neatmonster/nocheatplus/compat/cb2645/BlockCacheCB2645.java index dda84adf..0a9b8fb3 100644 --- a/NCPCompatCB2645/src/main/java/fr/neatmonster/nocheatplus/compat/cb2645/BlockCacheCB2645.java +++ b/NCPCompatCB2645/src/main/java/fr/neatmonster/nocheatplus/compat/cb2645/BlockCacheCB2645.java @@ -30,7 +30,12 @@ public class BlockCacheCB2645 extends BlockCache implements IBlockAccess{ @Override public void setAccess(World world) { - this.world = world == null ? null : ((CraftWorld) world).getHandle(); + if (world != null) { + this.maxBlockY = world.getMaxHeight() - 1; + this.world = ((CraftWorld) world).getHandle(); + } else { + this.world = null; + } } @Override diff --git a/NCPCompatCB2645/src/main/java/fr/neatmonster/nocheatplus/compat/cb2645/MCAccessCB2645.java b/NCPCompatCB2645/src/main/java/fr/neatmonster/nocheatplus/compat/cb2645/MCAccessCB2645.java index 4635948e..9c8e27dc 100644 --- a/NCPCompatCB2645/src/main/java/fr/neatmonster/nocheatplus/compat/cb2645/MCAccessCB2645.java +++ b/NCPCompatCB2645/src/main/java/fr/neatmonster/nocheatplus/compat/cb2645/MCAccessCB2645.java @@ -101,7 +101,7 @@ public class MCAccessCB2645 implements MCAccess{ @Override public double getJumpAmplifier(final Player player) { - final net.minecraft.server.v1_5_R1.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); if (mcPlayer.hasEffect(MobEffectList.JUMP)) return mcPlayer.getEffect(MobEffectList.JUMP).getAmplifier(); else return Double.NEGATIVE_INFINITY; @@ -109,7 +109,7 @@ public class MCAccessCB2645 implements MCAccess{ @Override public double getFasterMovementAmplifier(final Player player) { - final net.minecraft.server.v1_5_R1.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); if (mcPlayer.hasEffect(MobEffectList.FASTER_MOVEMENT)) return mcPlayer.getEffect(MobEffectList.FASTER_MOVEMENT).getAmplifier(); else return Double.NEGATIVE_INFINITY; } @@ -136,13 +136,13 @@ public class MCAccessCB2645 implements MCAccess{ @Override public boolean shouldBeZombie(final Player player) { - final net.minecraft.server.v1_5_R1.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); return !mcPlayer.dead && mcPlayer.getHealth() <= 0 ; } @Override public void setDead(final Player player, final int deathTicks) { - final net.minecraft.server.v1_5_R1.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); mcPlayer.deathTicks = deathTicks; mcPlayer.dead = true; } @@ -164,4 +164,13 @@ public class MCAccessCB2645 implements MCAccess{ } } +// @Override +// public void correctDirection(final Player player) { +// final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); +// // Main direction. +// mcPlayer.yaw = LocUtil.correctYaw(mcPlayer.yaw); +// mcPlayer.pitch = LocUtil.correctPitch(mcPlayer.pitch); +// // Consider setting the lastYaw here too. +// } + } diff --git a/NCPCompatCB2691/src/main/java/fr/neatmonster/nocheatplus/compat/cb2691/BlockCacheCB2691.java b/NCPCompatCB2691/src/main/java/fr/neatmonster/nocheatplus/compat/cb2691/BlockCacheCB2691.java index 158bf31f..a8e9ebc3 100644 --- a/NCPCompatCB2691/src/main/java/fr/neatmonster/nocheatplus/compat/cb2691/BlockCacheCB2691.java +++ b/NCPCompatCB2691/src/main/java/fr/neatmonster/nocheatplus/compat/cb2691/BlockCacheCB2691.java @@ -30,7 +30,12 @@ public class BlockCacheCB2691 extends BlockCache implements IBlockAccess{ @Override public void setAccess(World world) { - this.world = world == null ? null : ((CraftWorld) world).getHandle(); + if (world != null) { + this.maxBlockY = world.getMaxHeight() - 1; + this.world = ((CraftWorld) world).getHandle(); + } else { + this.world = null; + } } @Override diff --git a/NCPCompatCB2691/src/main/java/fr/neatmonster/nocheatplus/compat/cb2691/MCAccessCB2691.java b/NCPCompatCB2691/src/main/java/fr/neatmonster/nocheatplus/compat/cb2691/MCAccessCB2691.java index 760d523c..6988eaf1 100644 --- a/NCPCompatCB2691/src/main/java/fr/neatmonster/nocheatplus/compat/cb2691/MCAccessCB2691.java +++ b/NCPCompatCB2691/src/main/java/fr/neatmonster/nocheatplus/compat/cb2691/MCAccessCB2691.java @@ -62,7 +62,9 @@ public class MCAccessCB2691 implements MCAccess{ final double entityHeight = Math.max(mcEntity.length, Math.max(mcEntity.height, mcEntity.boundingBox.e - mcEntity.boundingBox.b)); if (entity instanceof LivingEntity) { return Math.max(((LivingEntity) entity).getEyeHeight(), entityHeight); - } else return entityHeight; + } else { + return entityHeight; + } } @Override @@ -101,7 +103,7 @@ public class MCAccessCB2691 implements MCAccess{ @Override public double getJumpAmplifier(final Player player) { - final net.minecraft.server.v1_5_R2.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); if (mcPlayer.hasEffect(MobEffectList.JUMP)) return mcPlayer.getEffect(MobEffectList.JUMP).getAmplifier(); else return Double.NEGATIVE_INFINITY; @@ -109,7 +111,7 @@ public class MCAccessCB2691 implements MCAccess{ @Override public double getFasterMovementAmplifier(final Player player) { - final net.minecraft.server.v1_5_R2.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); if (mcPlayer.hasEffect(MobEffectList.FASTER_MOVEMENT)) return mcPlayer.getEffect(MobEffectList.FASTER_MOVEMENT).getAmplifier(); else return Double.NEGATIVE_INFINITY; } @@ -136,13 +138,13 @@ public class MCAccessCB2691 implements MCAccess{ @Override public boolean shouldBeZombie(final Player player) { - final net.minecraft.server.v1_5_R2.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); return !mcPlayer.dead && mcPlayer.getHealth() <= 0 ; } @Override public void setDead(final Player player, final int deathTicks) { - final net.minecraft.server.v1_5_R2.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); mcPlayer.deathTicks = deathTicks; mcPlayer.dead = true; } @@ -164,4 +166,13 @@ public class MCAccessCB2691 implements MCAccess{ } } +// @Override +// public void correctDirection(final Player player) { +// final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); +// // Main direction. +// mcPlayer.yaw = LocUtil.correctYaw(mcPlayer.yaw); +// mcPlayer.pitch = LocUtil.correctPitch(mcPlayer.pitch); +// // Consider setting the lastYaw here too. +// } + } diff --git a/NCPCompatCB2763/src/main/java/fr/neatmonster/nocheatplus/compat/cb2763/BlockCacheCB2763.java b/NCPCompatCB2763/src/main/java/fr/neatmonster/nocheatplus/compat/cb2763/BlockCacheCB2763.java index 7e40d4f7..018d14d4 100644 --- a/NCPCompatCB2763/src/main/java/fr/neatmonster/nocheatplus/compat/cb2763/BlockCacheCB2763.java +++ b/NCPCompatCB2763/src/main/java/fr/neatmonster/nocheatplus/compat/cb2763/BlockCacheCB2763.java @@ -30,7 +30,12 @@ public class BlockCacheCB2763 extends BlockCache implements IBlockAccess{ @Override public void setAccess(World world) { - this.world = world == null ? null : ((CraftWorld) world).getHandle(); + if (world != null) { + this.maxBlockY = world.getMaxHeight() - 1; + this.world = ((CraftWorld) world).getHandle(); + } else { + this.world = null; + } } @Override diff --git a/NCPCompatCB2763/src/main/java/fr/neatmonster/nocheatplus/compat/cb2763/MCAccessCB2763.java b/NCPCompatCB2763/src/main/java/fr/neatmonster/nocheatplus/compat/cb2763/MCAccessCB2763.java index 021a4b4a..698a48f6 100644 --- a/NCPCompatCB2763/src/main/java/fr/neatmonster/nocheatplus/compat/cb2763/MCAccessCB2763.java +++ b/NCPCompatCB2763/src/main/java/fr/neatmonster/nocheatplus/compat/cb2763/MCAccessCB2763.java @@ -101,7 +101,7 @@ public class MCAccessCB2763 implements MCAccess{ @Override public double getJumpAmplifier(final Player player) { - final net.minecraft.server.v1_5_R3.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); if (mcPlayer.hasEffect(MobEffectList.JUMP)) return mcPlayer.getEffect(MobEffectList.JUMP).getAmplifier(); else return Double.NEGATIVE_INFINITY; @@ -109,7 +109,7 @@ public class MCAccessCB2763 implements MCAccess{ @Override public double getFasterMovementAmplifier(final Player player) { - final net.minecraft.server.v1_5_R3.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); if (mcPlayer.hasEffect(MobEffectList.FASTER_MOVEMENT)) return mcPlayer.getEffect(MobEffectList.FASTER_MOVEMENT).getAmplifier(); else return Double.NEGATIVE_INFINITY; } @@ -136,13 +136,13 @@ public class MCAccessCB2763 implements MCAccess{ @Override public boolean shouldBeZombie(final Player player) { - final net.minecraft.server.v1_5_R3.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); return !mcPlayer.dead && mcPlayer.getHealth() <= 0 ; } @Override public void setDead(final Player player, final int deathTicks) { - final net.minecraft.server.v1_5_R3.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); mcPlayer.deathTicks = deathTicks; mcPlayer.dead = true; } @@ -165,4 +165,13 @@ public class MCAccessCB2763 implements MCAccess{ } } +// @Override +// public void correctDirection(final Player player) { +// final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); +// // Main direction. +// mcPlayer.yaw = LocUtil.correctYaw(mcPlayer.yaw); +// mcPlayer.pitch = LocUtil.correctPitch(mcPlayer.pitch); +// // Consider setting the lastYaw here too. +// } + } diff --git a/NCPCompatCB2794/pom.xml b/NCPCompatCB2794/pom.xml index de04b5ad..c9b990b7 100644 --- a/NCPCompatCB2794/pom.xml +++ b/NCPCompatCB2794/pom.xml @@ -24,11 +24,6 @@ craftbukkit 1.6.1-R0.1-SNAPSHOT - - org.bukkit - bukkit - 1.6.1-R0.1-SNAPSHOT - Compatibility for CB 2794 (MC 1.6.1). diff --git a/NCPCompatCB2794/src/main/java/fr/neatmonster/nocheatplus/compat/cb2794/BlockCacheCB2794.java b/NCPCompatCB2794/src/main/java/fr/neatmonster/nocheatplus/compat/cb2794/BlockCacheCB2794.java index c95861ae..ef77e59d 100644 --- a/NCPCompatCB2794/src/main/java/fr/neatmonster/nocheatplus/compat/cb2794/BlockCacheCB2794.java +++ b/NCPCompatCB2794/src/main/java/fr/neatmonster/nocheatplus/compat/cb2794/BlockCacheCB2794.java @@ -30,7 +30,12 @@ public class BlockCacheCB2794 extends BlockCache implements IBlockAccess{ @Override public void setAccess(World world) { - this.world = world == null ? null : ((CraftWorld) world).getHandle(); + if (world != null) { + this.maxBlockY = world.getMaxHeight() - 1; + this.world = ((CraftWorld) world).getHandle(); + } else { + this.world = null; + } } @Override diff --git a/NCPCompatCB2794/src/main/java/fr/neatmonster/nocheatplus/compat/cb2794/MCAccessCB2794.java b/NCPCompatCB2794/src/main/java/fr/neatmonster/nocheatplus/compat/cb2794/MCAccessCB2794.java index 337bbefe..de8828d9 100644 --- a/NCPCompatCB2794/src/main/java/fr/neatmonster/nocheatplus/compat/cb2794/MCAccessCB2794.java +++ b/NCPCompatCB2794/src/main/java/fr/neatmonster/nocheatplus/compat/cb2794/MCAccessCB2794.java @@ -101,7 +101,7 @@ public class MCAccessCB2794 implements MCAccess{ @Override public double getJumpAmplifier(final Player player) { - final net.minecraft.server.v1_6_R1.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); if (mcPlayer.hasEffect(MobEffectList.JUMP)) return mcPlayer.getEffect(MobEffectList.JUMP).getAmplifier(); else return Double.NEGATIVE_INFINITY; @@ -109,7 +109,7 @@ public class MCAccessCB2794 implements MCAccess{ @Override public double getFasterMovementAmplifier(final Player player) { - final net.minecraft.server.v1_6_R1.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); if (mcPlayer.hasEffect(MobEffectList.FASTER_MOVEMENT)) return mcPlayer.getEffect(MobEffectList.FASTER_MOVEMENT).getAmplifier(); else return Double.NEGATIVE_INFINITY; } @@ -136,13 +136,13 @@ public class MCAccessCB2794 implements MCAccess{ @Override public boolean shouldBeZombie(final Player player) { - final net.minecraft.server.v1_6_R1.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); return !mcPlayer.dead && mcPlayer.getHealth() <= 0.0f ; } @Override public void setDead(final Player player, final int deathTicks) { - final net.minecraft.server.v1_6_R1.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); mcPlayer.deathTicks = deathTicks; mcPlayer.dead = true; } @@ -159,4 +159,13 @@ public class MCAccessCB2794 implements MCAccess{ return mat.hasGravity(); } +// @Override +// public void correctDirection(final Player player) { +// final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); +// // Main direction. +// mcPlayer.yaw = LocUtil.correctYaw(mcPlayer.yaw); +// mcPlayer.pitch = LocUtil.correctPitch(mcPlayer.pitch); +// // Consider setting the lastYaw here too. +// } + } diff --git a/NCPCompatCB2808/pom.xml b/NCPCompatCB2808/pom.xml index 8bd2c317..63128f47 100644 --- a/NCPCompatCB2808/pom.xml +++ b/NCPCompatCB2808/pom.xml @@ -24,11 +24,6 @@ craftbukkit 1.6.2-R1.0 - - org.bukkit - bukkit - 1.6.2-R1.0 - Compatibility for CB2808 (MC 1.6.2). diff --git a/NCPCompatCB2808/src/main/java/fr/neatmonster/nocheatplus/compat/cb2808/BlockCacheCB2808.java b/NCPCompatCB2808/src/main/java/fr/neatmonster/nocheatplus/compat/cb2808/BlockCacheCB2808.java index 68198c56..e3cb158e 100644 --- a/NCPCompatCB2808/src/main/java/fr/neatmonster/nocheatplus/compat/cb2808/BlockCacheCB2808.java +++ b/NCPCompatCB2808/src/main/java/fr/neatmonster/nocheatplus/compat/cb2808/BlockCacheCB2808.java @@ -30,7 +30,12 @@ public class BlockCacheCB2808 extends BlockCache implements IBlockAccess{ @Override public void setAccess(World world) { - this.world = world == null ? null : ((CraftWorld) world).getHandle(); + if (world != null) { + this.maxBlockY = world.getMaxHeight() - 1; + this.world = ((CraftWorld) world).getHandle(); + } else { + this.world = null; + } } @Override diff --git a/NCPCompatCB2808/src/main/java/fr/neatmonster/nocheatplus/compat/cb2808/MCAccessCB2808.java b/NCPCompatCB2808/src/main/java/fr/neatmonster/nocheatplus/compat/cb2808/MCAccessCB2808.java index e2d7900b..6569d10d 100644 --- a/NCPCompatCB2808/src/main/java/fr/neatmonster/nocheatplus/compat/cb2808/MCAccessCB2808.java +++ b/NCPCompatCB2808/src/main/java/fr/neatmonster/nocheatplus/compat/cb2808/MCAccessCB2808.java @@ -102,7 +102,7 @@ public class MCAccessCB2808 implements MCAccess{ @Override public double getJumpAmplifier(final Player player) { - final net.minecraft.server.v1_6_R2.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); if (mcPlayer.hasEffect(MobEffectList.JUMP)) return mcPlayer.getEffect(MobEffectList.JUMP).getAmplifier(); else return Double.NEGATIVE_INFINITY; @@ -110,7 +110,7 @@ public class MCAccessCB2808 implements MCAccess{ @Override public double getFasterMovementAmplifier(final Player player) { - final net.minecraft.server.v1_6_R2.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); if (mcPlayer.hasEffect(MobEffectList.FASTER_MOVEMENT)) return mcPlayer.getEffect(MobEffectList.FASTER_MOVEMENT).getAmplifier(); else return Double.NEGATIVE_INFINITY; } @@ -137,13 +137,13 @@ public class MCAccessCB2808 implements MCAccess{ @Override public boolean shouldBeZombie(final Player player) { - final net.minecraft.server.v1_6_R2.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); return !mcPlayer.dead && mcPlayer.getHealth() <= 0.0f ; } @Override public void setDead(final Player player, final int deathTicks) { - final net.minecraft.server.v1_6_R2.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); mcPlayer.deathTicks = deathTicks; mcPlayer.dead = true; } @@ -160,4 +160,13 @@ public class MCAccessCB2808 implements MCAccess{ return mat.hasGravity(); } +// @Override +// public void correctDirection(final Player player) { +// final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); +// // Main direction. +// mcPlayer.yaw = LocUtil.correctYaw(mcPlayer.yaw); +// mcPlayer.pitch = LocUtil.correctPitch(mcPlayer.pitch); +// // Consider setting the lastYaw here too. +// } + } diff --git a/NCPCompatCB2882/pom.xml b/NCPCompatCB2882/pom.xml index e148a5e0..2002de66 100644 --- a/NCPCompatCB2882/pom.xml +++ b/NCPCompatCB2882/pom.xml @@ -24,11 +24,6 @@ craftbukkit 1.6.4-R0.1-SNAPSHOT - - org.bukkit - bukkit - 1.6.4-R0.1-SNAPSHOT - Compatibility for CB2882 (MC 1.6.4). diff --git a/NCPCompatCB2882/src/main/java/fr/neatmonster/nocheatplus/compat/cb2882/BlockCacheCB2882.java b/NCPCompatCB2882/src/main/java/fr/neatmonster/nocheatplus/compat/cb2882/BlockCacheCB2882.java index 1dd6adec..26b931c0 100644 --- a/NCPCompatCB2882/src/main/java/fr/neatmonster/nocheatplus/compat/cb2882/BlockCacheCB2882.java +++ b/NCPCompatCB2882/src/main/java/fr/neatmonster/nocheatplus/compat/cb2882/BlockCacheCB2882.java @@ -30,7 +30,12 @@ public class BlockCacheCB2882 extends BlockCache implements IBlockAccess{ @Override public void setAccess(World world) { - this.world = world == null ? null : ((CraftWorld) world).getHandle(); + if (world != null) { + this.maxBlockY = world.getMaxHeight() - 1; + this.world = ((CraftWorld) world).getHandle(); + } else { + this.world = null; + } } @Override diff --git a/NCPCompatCB2882/src/main/java/fr/neatmonster/nocheatplus/compat/cb2882/MCAccessCB2882.java b/NCPCompatCB2882/src/main/java/fr/neatmonster/nocheatplus/compat/cb2882/MCAccessCB2882.java index 386d07a2..4dc894c4 100644 --- a/NCPCompatCB2882/src/main/java/fr/neatmonster/nocheatplus/compat/cb2882/MCAccessCB2882.java +++ b/NCPCompatCB2882/src/main/java/fr/neatmonster/nocheatplus/compat/cb2882/MCAccessCB2882.java @@ -103,7 +103,7 @@ public class MCAccessCB2882 implements MCAccess{ @Override public double getJumpAmplifier(final Player player) { - final net.minecraft.server.v1_6_R3.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); if (mcPlayer.hasEffect(MobEffectList.JUMP)) return mcPlayer.getEffect(MobEffectList.JUMP).getAmplifier(); else return Double.NEGATIVE_INFINITY; @@ -111,7 +111,7 @@ public class MCAccessCB2882 implements MCAccess{ @Override public double getFasterMovementAmplifier(final Player player) { - final net.minecraft.server.v1_6_R3.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); if (mcPlayer.hasEffect(MobEffectList.FASTER_MOVEMENT)) return mcPlayer.getEffect(MobEffectList.FASTER_MOVEMENT).getAmplifier(); else return Double.NEGATIVE_INFINITY; } @@ -138,13 +138,13 @@ public class MCAccessCB2882 implements MCAccess{ @Override public boolean shouldBeZombie(final Player player) { - final net.minecraft.server.v1_6_R3.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); return !mcPlayer.dead && mcPlayer.getHealth() <= 0.0f ; } @Override public void setDead(final Player player, final int deathTicks) { - final net.minecraft.server.v1_6_R3.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); mcPlayer.deathTicks = deathTicks; mcPlayer.dead = true; } @@ -161,4 +161,13 @@ public class MCAccessCB2882 implements MCAccess{ return mat.hasGravity(); } +// @Override +// public void correctDirection(final Player player) { +// final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); +// // Main direction. +// mcPlayer.yaw = LocUtil.correctYaw(mcPlayer.yaw); +// mcPlayer.pitch = LocUtil.correctPitch(mcPlayer.pitch); +// // Consider setting the lastYaw here too. +// } + } diff --git a/NCPCompatCB2511/pom.xml b/NCPCompatCB2922/pom.xml similarity index 80% rename from NCPCompatCB2511/pom.xml rename to NCPCompatCB2922/pom.xml index a0c3f46b..49488c06 100644 --- a/NCPCompatCB2511/pom.xml +++ b/NCPCompatCB2922/pom.xml @@ -2,9 +2,9 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 fr.neatmonster - ncpcompatcb2511 + ncpcompatcb2922 jar - NCPCompatCB2511 + NCPCompatCB2922 static @@ -22,11 +22,11 @@ org.bukkit craftbukkit - 1.4.5-R0.2 + 1.7.2-R0.4-SNAPSHOT - Compatibility up to CB2512 (i.e. 1.4.5-R0.2). + Compatibility for CB2922 (MC 1.7.2). Version updating is done for the NoCheatPlus sub-module. \ No newline at end of file diff --git a/NCPCompatCB2922/src/main/java/fr/neatmonster/nocheatplus/compat/cb2922/BlockCacheCB2922.java b/NCPCompatCB2922/src/main/java/fr/neatmonster/nocheatplus/compat/cb2922/BlockCacheCB2922.java new file mode 100644 index 00000000..3ee5b850 --- /dev/null +++ b/NCPCompatCB2922/src/main/java/fr/neatmonster/nocheatplus/compat/cb2922/BlockCacheCB2922.java @@ -0,0 +1,127 @@ +package fr.neatmonster.nocheatplus.compat.cb2922; + +import java.util.Iterator; +import java.util.List; + +import net.minecraft.server.v1_7_R1.AxisAlignedBB; +import net.minecraft.server.v1_7_R1.Block; +import net.minecraft.server.v1_7_R1.EntityBoat; +import net.minecraft.server.v1_7_R1.IBlockAccess; +import net.minecraft.server.v1_7_R1.TileEntity; +import net.minecraft.server.v1_7_R1.Vec3DPool; + +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_7_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_7_R1.entity.CraftEntity; +import org.bukkit.entity.Entity; + +import fr.neatmonster.nocheatplus.utilities.BlockCache; + +public class BlockCacheCB2922 extends BlockCache implements IBlockAccess{ + + /** Box for one time use, no nesting, no extra storing this(!). */ + protected static final AxisAlignedBB useBox = AxisAlignedBB.a(0, 0, 0, 0, 0, 0); + + protected net.minecraft.server.v1_7_R1.WorldServer world; + + public BlockCacheCB2922(World world) { + setAccess(world); + } + + @Override + public void setAccess(World world) { + if (world != null) { + this.maxBlockY = world.getMaxHeight() - 1; + this.world = ((CraftWorld) world).getHandle(); + } else { + this.world = null; + } + } + + @Override + public int fetchTypeId(final int x, final int y, final int z) { + return world.getTypeId(x, y, z); + } + + @Override + public int fetchData(final int x, final int y, final int z) { + return world.getData(x, y, z); + } + + @Override + public double[] fetchBounds(final int x, final int y, final int z){ + + // TODO: change api for this / use nodes (!) + final int id = getTypeId(x, y, z); + final net.minecraft.server.v1_7_R1.Block block = net.minecraft.server.v1_7_R1.Block.e(id); + if (block == null) return null; + block.updateShape(this, x, y, z); // TODO: use THIS instead of world. + + // minX, minY, minZ, maxX, maxY, maxZ + return new double[]{block.x(), block.z(), block.B(), block.y(), block.A(), block.C()}; + } + + @Override + public boolean standsOnEntity(final Entity entity, final double minX, final double minY, final double minZ, final double maxX, final double maxY, final double maxZ){ + try{ + // TODO: Probably check other ids too before doing this ? + + final net.minecraft.server.v1_7_R1.Entity mcEntity = ((CraftEntity) entity).getHandle(); + + final AxisAlignedBB box = useBox.b(minX, minY, minZ, maxX, maxY, maxZ); + @SuppressWarnings("rawtypes") + final List list = world.getEntities(mcEntity, box); + @SuppressWarnings("rawtypes") + final Iterator iterator = list.iterator(); + while (iterator.hasNext()) { + final net.minecraft.server.v1_7_R1.Entity other = (net.minecraft.server.v1_7_R1.Entity) iterator.next(); + if (!(other instanceof EntityBoat)){ // && !(other instanceof EntityMinecart)) continue; + continue; + } + if (minY >= other.locY && minY - other.locY <= 0.7){ + return true; + } + // Still check this for some reason. + final AxisAlignedBB otherBox = other.boundingBox; + if (box.a > otherBox.d || box.d < otherBox.a || box.b > otherBox.e || box.e < otherBox.b || box.c > otherBox.f || box.f < otherBox.c) continue; + else { + return true; + } + } + } + catch (Throwable t){ + // Ignore exceptions (Context: DisguiseCraft). + } + return false; + } + + /* (non-Javadoc) + * @see fr.neatmonster.nocheatplus.utilities.BlockCache#cleanup() + */ + @Override + public void cleanup() { + super.cleanup(); + world = null; + } + + @Override + public TileEntity getTileEntity(final int x, final int y, final int z) { + return world.getTileEntity(x, y, z); + } + + @Override + public int getBlockPower(final int arg0, final int arg1, final int arg2, final int arg3) { + return world.getBlockPower(arg0, arg1, arg2, arg3); + } + + @Override + public Block getType(int x, int y, int z) { + return world.getType(x, y, z); + } + + @Override + public Vec3DPool getVec3DPool() { + return world.getVec3DPool(); + } + +} diff --git a/NCPCompatCB2511/src/main/java/fr/neatmonster/nocheatplus/compat/cb2511/MCAccessCB2511.java b/NCPCompatCB2922/src/main/java/fr/neatmonster/nocheatplus/compat/cb2922/MCAccessCB2922.java similarity index 57% rename from NCPCompatCB2511/src/main/java/fr/neatmonster/nocheatplus/compat/cb2511/MCAccessCB2511.java rename to NCPCompatCB2922/src/main/java/fr/neatmonster/nocheatplus/compat/cb2922/MCAccessCB2922.java index 1bb55af7..9e5499ac 100644 --- a/NCPCompatCB2511/src/main/java/fr/neatmonster/nocheatplus/compat/cb2511/MCAccessCB2511.java +++ b/NCPCompatCB2922/src/main/java/fr/neatmonster/nocheatplus/compat/cb2922/MCAccessCB2922.java @@ -1,19 +1,19 @@ -package fr.neatmonster.nocheatplus.compat.cb2511; +package fr.neatmonster.nocheatplus.compat.cb2922; -import net.minecraft.server.AxisAlignedBB; -import net.minecraft.server.Block; -import net.minecraft.server.DamageSource; -import net.minecraft.server.EntityComplexPart; -import net.minecraft.server.EntityPlayer; -import net.minecraft.server.MobEffectList; +import net.minecraft.server.v1_7_R1.AxisAlignedBB; +import net.minecraft.server.v1_7_R1.Block; +import net.minecraft.server.v1_7_R1.DamageSource; +import net.minecraft.server.v1_7_R1.EntityComplexPart; +import net.minecraft.server.v1_7_R1.EntityPlayer; +import net.minecraft.server.v1_7_R1.MobEffectList; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.command.CommandMap; -import org.bukkit.craftbukkit.CraftServer; -import org.bukkit.craftbukkit.entity.CraftEntity; -import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_7_R1.CraftServer; +import org.bukkit.craftbukkit.v1_7_R1.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_7_R1.entity.CraftPlayer; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; @@ -23,33 +23,29 @@ import fr.neatmonster.nocheatplus.compat.MCAccess; import fr.neatmonster.nocheatplus.utilities.BlockCache; import fr.neatmonster.nocheatplus.utilities.ReflectionUtil; - -public class MCAccessCB2511 implements MCAccess { +public class MCAccessCB2922 implements MCAccess{ /** - * Constructor to let it fail in time. + * Constructor to let it fail. */ - public MCAccessCB2511(){ + public MCAccessCB2922(){ getCommandMap(); - ReflectionUtil.checkMembers("net.minecraft.server.", new String[]{"Entity" , "dead"}); - ReflectionUtil.checkMembers("net.minecraft.server.", new String[]{"EntityPlayer" , "netServerHandler"}); - ReflectionUtil.checkMethodReturnTypesNoArgs(net.minecraft.server.Block.class, - new String[]{"v", "w", "x", "y", "z", "A"}, double.class); + ReflectionUtil.checkMembers("net.minecraft.server.v1_7_R1.", new String[]{"Entity" , "dead"}); + // block bounds, original: minX, maxX, minY, maxY, minZ, maxZ + ReflectionUtil.checkMethodReturnTypesNoArgs(net.minecraft.server.v1_7_R1.Block.class, + new String[]{"x", "y", "z", "A", "B", "C"}, double.class); + // TODO: Nail it down further. } @Override public String getMCVersion() { - return "1.4.2|1.4.4|1.4.5"; + // 1_7_R1 + return "1.7.2"; } @Override public String getServerVersionTag() { - return "CB2511"; - } - - @Override - public BlockCache getBlockCache(World world) { - return new BlockCacheCB2511(world); + return "CB2922"; } @Override @@ -57,9 +53,14 @@ public class MCAccessCB2511 implements MCAccess { return ((CraftServer) Bukkit.getServer()).getCommandMap(); } + @Override + public BlockCache getBlockCache(final World world) { + return new BlockCacheCB2922(world); + } + @Override public double getHeight(final Entity entity) { - final net.minecraft.server.Entity mcEntity = ((CraftEntity) entity).getHandle(); + final net.minecraft.server.v1_7_R1.Entity mcEntity = ((CraftEntity) entity).getHandle(); final double entityHeight = Math.max(mcEntity.length, Math.max(mcEntity.height, mcEntity.boundingBox.e - mcEntity.boundingBox.b)); if (entity instanceof LivingEntity) { return Math.max(((LivingEntity) entity).getEyeHeight(), entityHeight); @@ -68,16 +69,16 @@ public class MCAccessCB2511 implements MCAccess { @Override public AlmostBoolean isBlockSolid(final int id) { - final Block block = Block.byId[id]; - if (block == null || block.material == null) return AlmostBoolean.MAYBE; - else return AlmostBoolean.match(block.material.isSolid()); + final Block block = Block.e(id); + if (block == null || block.getMaterial() == null) return AlmostBoolean.MAYBE; + else return AlmostBoolean.match(block.getMaterial().isSolid()); } @Override public AlmostBoolean isBlockLiquid(final int id) { - final Block block = Block.byId[id]; - if (block == null || block.material == null) return AlmostBoolean.MAYBE; - else return AlmostBoolean.match(block.material.isLiquid()); + final Block block = Block.e(id); + if (block == null || block.getMaterial() == null) return AlmostBoolean.MAYBE; + else return AlmostBoolean.match(block.getMaterial().isLiquid()); } @Override @@ -89,6 +90,7 @@ public class MCAccessCB2511 implements MCAccess { public AlmostBoolean isIllegalBounds(final Player player) { final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); if (entityPlayer.dead) return AlmostBoolean.NO; + // TODO: Does this need a method call for the "real" box? Might be no problem during moving events, though. final AxisAlignedBB box = entityPlayer.boundingBox; if (!entityPlayer.isSleeping()){ // This can not really test stance but height of bounding box. @@ -101,7 +103,7 @@ public class MCAccessCB2511 implements MCAccess { @Override public double getJumpAmplifier(final Player player) { - final net.minecraft.server.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); if (mcPlayer.hasEffect(MobEffectList.JUMP)) return mcPlayer.getEffect(MobEffectList.JUMP).getAmplifier(); else return Double.NEGATIVE_INFINITY; @@ -109,7 +111,7 @@ public class MCAccessCB2511 implements MCAccess { @Override public double getFasterMovementAmplifier(final Player player) { - final net.minecraft.server.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); if (mcPlayer.hasEffect(MobEffectList.FASTER_MOVEMENT)) return mcPlayer.getEffect(MobEffectList.FASTER_MOVEMENT).getAmplifier(); else return Double.NEGATIVE_INFINITY; } @@ -126,7 +128,7 @@ public class MCAccessCB2511 implements MCAccess { @Override public void dealFallDamage(final Player player, final double damage) { - ((CraftPlayer) player).getHandle().damageEntity(DamageSource.FALL, (int) Math.round(damage)); + ((CraftPlayer) player).getHandle().damageEntity(DamageSource.FALL, (float) damage); } @Override @@ -136,13 +138,13 @@ public class MCAccessCB2511 implements MCAccess { @Override public boolean shouldBeZombie(final Player player) { - final net.minecraft.server.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); - return !mcPlayer.dead && mcPlayer.getHealth() <= 0 ; + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + return !mcPlayer.dead && mcPlayer.getHealth() <= 0.0f ; } @Override public void setDead(final Player player, final int deathTicks) { - final net.minecraft.server.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); mcPlayer.deathTicks = deathTicks; mcPlayer.dead = true; } @@ -155,13 +157,17 @@ public class MCAccessCB2511 implements MCAccess { @Override public boolean hasGravity(final Material mat) { - switch(mat){ - case SAND: - case GRAVEL: - return true; - default: - return false; - } + // TODO: Test/check. + return mat.hasGravity(); } - + +// @Override +// public void correctDirection(final Player player) { +// final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); +// // Main direction. +// mcPlayer.yaw = LocUtil.correctYaw(mcPlayer.yaw); +// mcPlayer.pitch = LocUtil.correctPitch(mcPlayer.pitch); +// // Consider setting the lastYaw here too. +// } + } diff --git a/NCPCompatCB3026/pom.xml b/NCPCompatCB3026/pom.xml new file mode 100644 index 00000000..4da38b1a --- /dev/null +++ b/NCPCompatCB3026/pom.xml @@ -0,0 +1,32 @@ + + 4.0.0 + fr.neatmonster + ncpcompatcb3026 + jar + NCPCompatCB3026 + static + + + fr.neatmonster + nocheatplus-parent + static + + + + + fr.neatmonster + ncpcore + static + + + org.bukkit + craftbukkit + 1.7.5-R0.1-SNAPSHOT + + + + Compatibility for CB3026 (MC 1.7.5). + +Version updating is done for the NoCheatPlus sub-module. + \ No newline at end of file diff --git a/NCPCompatCB3026/src/main/java/fr/neatmonster/nocheatplus/compat/cb3026/BlockCacheCB3026.java b/NCPCompatCB3026/src/main/java/fr/neatmonster/nocheatplus/compat/cb3026/BlockCacheCB3026.java new file mode 100644 index 00000000..556c38b3 --- /dev/null +++ b/NCPCompatCB3026/src/main/java/fr/neatmonster/nocheatplus/compat/cb3026/BlockCacheCB3026.java @@ -0,0 +1,121 @@ +package fr.neatmonster.nocheatplus.compat.cb3026; + +import java.util.Iterator; +import java.util.List; + +import net.minecraft.server.v1_7_R2.AxisAlignedBB; +import net.minecraft.server.v1_7_R2.Block; +import net.minecraft.server.v1_7_R2.EntityBoat; +import net.minecraft.server.v1_7_R2.IBlockAccess; +import net.minecraft.server.v1_7_R2.TileEntity; + +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_7_R2.CraftWorld; +import org.bukkit.craftbukkit.v1_7_R2.entity.CraftEntity; +import org.bukkit.entity.Entity; + +import fr.neatmonster.nocheatplus.utilities.BlockCache; + +public class BlockCacheCB3026 extends BlockCache implements IBlockAccess{ + + /** Box for one time use, no nesting, no extra storing this(!). */ + protected static final AxisAlignedBB useBox = AxisAlignedBB.a(0, 0, 0, 0, 0, 0); + + protected net.minecraft.server.v1_7_R2.WorldServer world; + + public BlockCacheCB3026(World world) { + setAccess(world); + } + + @Override + public void setAccess(World world) { + if (world != null) { + this.maxBlockY = world.getMaxHeight() - 1; + this.world = ((CraftWorld) world).getHandle(); + } else { + this.world = null; + } + } + + @Override + public int fetchTypeId(final int x, final int y, final int z) { + return world.getTypeId(x, y, z); + } + + @Override + public int fetchData(final int x, final int y, final int z) { + return world.getData(x, y, z); + } + + @Override + public double[] fetchBounds(final int x, final int y, final int z){ + + // TODO: change api for this / use nodes (!) + final int id = getTypeId(x, y, z); + final net.minecraft.server.v1_7_R2.Block block = net.minecraft.server.v1_7_R2.Block.e(id); + if (block == null) return null; + block.updateShape(this, x, y, z); // TODO: use THIS instead of world. + + // minX, minY, minZ, maxX, maxY, maxZ + return new double[]{block.x(), block.z(), block.B(), block.y(), block.A(), block.C()}; + } + + @Override + public boolean standsOnEntity(final Entity entity, final double minX, final double minY, final double minZ, final double maxX, final double maxY, final double maxZ){ + try{ + // TODO: Probably check other ids too before doing this ? + + final net.minecraft.server.v1_7_R2.Entity mcEntity = ((CraftEntity) entity).getHandle(); + + final AxisAlignedBB box = useBox.b(minX, minY, minZ, maxX, maxY, maxZ); + @SuppressWarnings("rawtypes") + final List list = world.getEntities(mcEntity, box); + @SuppressWarnings("rawtypes") + final Iterator iterator = list.iterator(); + while (iterator.hasNext()) { + final net.minecraft.server.v1_7_R2.Entity other = (net.minecraft.server.v1_7_R2.Entity) iterator.next(); + if (!(other instanceof EntityBoat)){ // && !(other instanceof EntityMinecart)) continue; + continue; + } + if (minY >= other.locY && minY - other.locY <= 0.7){ + return true; + } + // Still check this for some reason. + final AxisAlignedBB otherBox = other.boundingBox; + if (box.a > otherBox.d || box.d < otherBox.a || box.b > otherBox.e || box.e < otherBox.b || box.c > otherBox.f || box.f < otherBox.c) continue; + else { + return true; + } + } + } + catch (Throwable t){ + // Ignore exceptions (Context: DisguiseCraft). + } + return false; + } + + /* (non-Javadoc) + * @see fr.neatmonster.nocheatplus.utilities.BlockCache#cleanup() + */ + @Override + public void cleanup() { + super.cleanup(); + world = null; + } + + @Override + public TileEntity getTileEntity(final int x, final int y, final int z) { + return world.getTileEntity(x, y, z); + } + + @Override + public int getBlockPower(final int arg0, final int arg1, final int arg2, final int arg3) { + return world.getBlockPower(arg0, arg1, arg2, arg3); + } + + @Override + public Block getType(int x, int y, int z) { + return world.getType(x, y, z); + } + +} diff --git a/NCPCompatCB3026/src/main/java/fr/neatmonster/nocheatplus/compat/cb3026/MCAccessCB3026.java b/NCPCompatCB3026/src/main/java/fr/neatmonster/nocheatplus/compat/cb3026/MCAccessCB3026.java new file mode 100644 index 00000000..21f6ce64 --- /dev/null +++ b/NCPCompatCB3026/src/main/java/fr/neatmonster/nocheatplus/compat/cb3026/MCAccessCB3026.java @@ -0,0 +1,173 @@ +package fr.neatmonster.nocheatplus.compat.cb3026; + +import net.minecraft.server.v1_7_R2.AxisAlignedBB; +import net.minecraft.server.v1_7_R2.Block; +import net.minecraft.server.v1_7_R2.DamageSource; +import net.minecraft.server.v1_7_R2.EntityComplexPart; +import net.minecraft.server.v1_7_R2.EntityPlayer; +import net.minecraft.server.v1_7_R2.MobEffectList; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.command.CommandMap; +import org.bukkit.craftbukkit.v1_7_R2.CraftServer; +import org.bukkit.craftbukkit.v1_7_R2.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_7_R2.entity.CraftPlayer; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; + +import fr.neatmonster.nocheatplus.compat.AlmostBoolean; +import fr.neatmonster.nocheatplus.compat.MCAccess; +import fr.neatmonster.nocheatplus.utilities.BlockCache; +import fr.neatmonster.nocheatplus.utilities.ReflectionUtil; + +public class MCAccessCB3026 implements MCAccess{ + + /** + * Constructor to let it fail. + */ + public MCAccessCB3026(){ + getCommandMap(); + ReflectionUtil.checkMembers("net.minecraft.server.v1_7_R2.", new String[]{"Entity" , "dead"}); + // block bounds, original: minX, maxX, minY, maxY, minZ, maxZ + ReflectionUtil.checkMethodReturnTypesNoArgs(net.minecraft.server.v1_7_R2.Block.class, + new String[]{"x", "y", "z", "A", "B", "C"}, double.class); + // TODO: Nail it down further. + } + + @Override + public String getMCVersion() { + // 1_7_R2 + return "1.7.5"; + } + + @Override + public String getServerVersionTag() { + return "CB3026"; + } + + @Override + public CommandMap getCommandMap() { + return ((CraftServer) Bukkit.getServer()).getCommandMap(); + } + + @Override + public BlockCache getBlockCache(final World world) { + return new BlockCacheCB3026(world); + } + + @Override + public double getHeight(final Entity entity) { + final net.minecraft.server.v1_7_R2.Entity mcEntity = ((CraftEntity) entity).getHandle(); + final double entityHeight = Math.max(mcEntity.length, Math.max(mcEntity.height, mcEntity.boundingBox.e - mcEntity.boundingBox.b)); + if (entity instanceof LivingEntity) { + return Math.max(((LivingEntity) entity).getEyeHeight(), entityHeight); + } else return entityHeight; + } + + @Override + public AlmostBoolean isBlockSolid(final int id) { + final Block block = Block.e(id); + if (block == null || block.getMaterial() == null) return AlmostBoolean.MAYBE; + else return AlmostBoolean.match(block.getMaterial().isSolid()); + } + + @Override + public AlmostBoolean isBlockLiquid(final int id) { + final Block block = Block.e(id); + if (block == null || block.getMaterial() == null) return AlmostBoolean.MAYBE; + else return AlmostBoolean.match(block.getMaterial().isLiquid()); + } + + @Override + public double getWidth(final Entity entity) { + return ((CraftEntity) entity).getHandle().width; + } + + @Override + public AlmostBoolean isIllegalBounds(final Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + if (entityPlayer.dead) return AlmostBoolean.NO; + // TODO: Does this need a method call for the "real" box? Might be no problem during moving events, though. + final AxisAlignedBB box = entityPlayer.boundingBox; + if (!entityPlayer.isSleeping()){ + // This can not really test stance but height of bounding box. + final double dY = Math.abs(box.e - box.b); + if (dY > 1.8) return AlmostBoolean.YES; // dY > 1.65D || + if (dY < 0.1D && entityPlayer.length >= 0.1) return AlmostBoolean.YES; + } + return AlmostBoolean.MAYBE; + } + + @Override + public double getJumpAmplifier(final Player player) { + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + + if (mcPlayer.hasEffect(MobEffectList.JUMP)) return mcPlayer.getEffect(MobEffectList.JUMP).getAmplifier(); + else return Double.NEGATIVE_INFINITY; + } + + @Override + public double getFasterMovementAmplifier(final Player player) { + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + if (mcPlayer.hasEffect(MobEffectList.FASTER_MOVEMENT)) return mcPlayer.getEffect(MobEffectList.FASTER_MOVEMENT).getAmplifier(); + else return Double.NEGATIVE_INFINITY; + } + + @Override + public int getInvulnerableTicks(final Player player) { + return ((CraftPlayer) player).getHandle().invulnerableTicks; + } + + @Override + public void setInvulnerableTicks(final Player player, final int ticks) { + ((CraftPlayer) player).getHandle().invulnerableTicks = ticks; + } + + @Override + public void dealFallDamage(final Player player, final double damage) { + ((CraftPlayer) player).getHandle().damageEntity(DamageSource.FALL, (float) damage); + } + + @Override + public boolean isComplexPart(final Entity entity) { + return ((CraftEntity) entity).getHandle() instanceof EntityComplexPart; + } + + @Override + public boolean shouldBeZombie(final Player player) { + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + return !mcPlayer.dead && mcPlayer.getHealth() <= 0.0f ; + } + + @Override + public void setDead(final Player player, final int deathTicks) { + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + mcPlayer.deathTicks = deathTicks; + mcPlayer.dead = true; + } + + @Override + public long getKeepAliveTime(final Player player) { + // TODO: Implement if possible. + return Long.MIN_VALUE; + } + + @Override + public boolean hasGravity(final Material mat) { + // TODO: Test/check. + return mat.hasGravity(); + } + +// @Override +// public void correctDirection(final Player player) { +// final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); +// // Main direction. +// mcPlayer.yaw = LocUtil.correctYaw(mcPlayer.yaw); +// mcPlayer.pitch = LocUtil.correctPitch(mcPlayer.pitch); +// // Consider setting the lastYaw here too. +// } + +} diff --git a/NCPCompatCB3043/pom.xml b/NCPCompatCB3043/pom.xml new file mode 100644 index 00000000..40663b19 --- /dev/null +++ b/NCPCompatCB3043/pom.xml @@ -0,0 +1,32 @@ + + 4.0.0 + fr.neatmonster + ncpcompatcb3043 + jar + NCPCompatCB3043 + static + + + fr.neatmonster + nocheatplus-parent + static + + + + + fr.neatmonster + ncpcore + static + + + org.bukkit + craftbukkit + 1.7.9-R0.2-SNAPSHOT + + + + Compatibility for CB3043 (MC 1.7.10). + +Version updating is done for the NoCheatPlus sub-module. + \ No newline at end of file diff --git a/NCPCompatCB3043/src/main/java/fr/neatmonster/nocheatplus/compat/cb3043/BlockCacheCB3043.java b/NCPCompatCB3043/src/main/java/fr/neatmonster/nocheatplus/compat/cb3043/BlockCacheCB3043.java new file mode 100644 index 00000000..819c45b2 --- /dev/null +++ b/NCPCompatCB3043/src/main/java/fr/neatmonster/nocheatplus/compat/cb3043/BlockCacheCB3043.java @@ -0,0 +1,121 @@ +package fr.neatmonster.nocheatplus.compat.cb3043; + +import java.util.Iterator; +import java.util.List; + +import net.minecraft.server.v1_7_R3.AxisAlignedBB; +import net.minecraft.server.v1_7_R3.Block; +import net.minecraft.server.v1_7_R3.EntityBoat; +import net.minecraft.server.v1_7_R3.IBlockAccess; +import net.minecraft.server.v1_7_R3.TileEntity; + +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_7_R3.CraftWorld; +import org.bukkit.craftbukkit.v1_7_R3.entity.CraftEntity; +import org.bukkit.entity.Entity; + +import fr.neatmonster.nocheatplus.utilities.BlockCache; + +public class BlockCacheCB3043 extends BlockCache implements IBlockAccess{ + + /** Box for one time use, no nesting, no extra storing this(!). */ + protected static final AxisAlignedBB useBox = AxisAlignedBB.a(0, 0, 0, 0, 0, 0); + + protected net.minecraft.server.v1_7_R3.WorldServer world; + + public BlockCacheCB3043(World world) { + setAccess(world); + } + + @Override + public void setAccess(World world) { + if (world != null) { + this.maxBlockY = world.getMaxHeight() - 1; + this.world = ((CraftWorld) world).getHandle(); + } else { + this.world = null; + } + } + + @Override + public int fetchTypeId(final int x, final int y, final int z) { + return world.getTypeId(x, y, z); + } + + @Override + public int fetchData(final int x, final int y, final int z) { + return world.getData(x, y, z); + } + + @Override + public double[] fetchBounds(final int x, final int y, final int z){ + + // TODO: change api for this / use nodes (!) + final int id = getTypeId(x, y, z); + final net.minecraft.server.v1_7_R3.Block block = net.minecraft.server.v1_7_R3.Block.e(id); + if (block == null) return null; + block.updateShape(this, x, y, z); // TODO: use THIS instead of world. + + // minX, minY, minZ, maxX, maxY, maxZ + return new double[]{block.x(), block.z(), block.B(), block.y(), block.A(), block.C()}; + } + + @Override + public boolean standsOnEntity(final Entity entity, final double minX, final double minY, final double minZ, final double maxX, final double maxY, final double maxZ){ + try{ + // TODO: Probably check other ids too before doing this ? + + final net.minecraft.server.v1_7_R3.Entity mcEntity = ((CraftEntity) entity).getHandle(); + + final AxisAlignedBB box = useBox.b(minX, minY, minZ, maxX, maxY, maxZ); + @SuppressWarnings("rawtypes") + final List list = world.getEntities(mcEntity, box); + @SuppressWarnings("rawtypes") + final Iterator iterator = list.iterator(); + while (iterator.hasNext()) { + final net.minecraft.server.v1_7_R3.Entity other = (net.minecraft.server.v1_7_R3.Entity) iterator.next(); + if (!(other instanceof EntityBoat)){ // && !(other instanceof EntityMinecart)) continue; + continue; + } + if (minY >= other.locY && minY - other.locY <= 0.7){ + return true; + } + // Still check this for some reason. + final AxisAlignedBB otherBox = other.boundingBox; + if (box.a > otherBox.d || box.d < otherBox.a || box.b > otherBox.e || box.e < otherBox.b || box.c > otherBox.f || box.f < otherBox.c) continue; + else { + return true; + } + } + } + catch (Throwable t){ + // Ignore exceptions (Context: DisguiseCraft). + } + return false; + } + + /* (non-Javadoc) + * @see fr.neatmonster.nocheatplus.utilities.BlockCache#cleanup() + */ + @Override + public void cleanup() { + super.cleanup(); + world = null; + } + + @Override + public TileEntity getTileEntity(final int x, final int y, final int z) { + return world.getTileEntity(x, y, z); + } + + @Override + public int getBlockPower(final int arg0, final int arg1, final int arg2, final int arg3) { + return world.getBlockPower(arg0, arg1, arg2, arg3); + } + + @Override + public Block getType(int x, int y, int z) { + return world.getType(x, y, z); + } + +} diff --git a/NCPCompatCB3043/src/main/java/fr/neatmonster/nocheatplus/compat/cb3043/MCAccessCB3043.java b/NCPCompatCB3043/src/main/java/fr/neatmonster/nocheatplus/compat/cb3043/MCAccessCB3043.java new file mode 100644 index 00000000..945fd318 --- /dev/null +++ b/NCPCompatCB3043/src/main/java/fr/neatmonster/nocheatplus/compat/cb3043/MCAccessCB3043.java @@ -0,0 +1,192 @@ +package fr.neatmonster.nocheatplus.compat.cb3043; + +import net.minecraft.server.v1_7_R3.AxisAlignedBB; +import net.minecraft.server.v1_7_R3.Block; +import net.minecraft.server.v1_7_R3.DamageSource; +import net.minecraft.server.v1_7_R3.EntityComplexPart; +import net.minecraft.server.v1_7_R3.EntityPlayer; +import net.minecraft.server.v1_7_R3.MobEffectList; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.command.CommandMap; +import org.bukkit.craftbukkit.v1_7_R3.CraftServer; +import org.bukkit.craftbukkit.v1_7_R3.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_7_R3.entity.CraftPlayer; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; + +import fr.neatmonster.nocheatplus.compat.AlmostBoolean; +import fr.neatmonster.nocheatplus.compat.MCAccess; +import fr.neatmonster.nocheatplus.utilities.BlockCache; +import fr.neatmonster.nocheatplus.utilities.ReflectionUtil; + +public class MCAccessCB3043 implements MCAccess{ + + /** + * Constructor to let it fail. + */ + public MCAccessCB3043() { + getCommandMap(); + ReflectionUtil.checkMembers("net.minecraft.server.v1_7_R3.", new String[] {"Entity" , "dead"}); + // block bounds, original: minX, maxX, minY, maxY, minZ, maxZ + ReflectionUtil.checkMethodReturnTypesNoArgs(net.minecraft.server.v1_7_R3.Block.class, + new String[]{"x", "y", "z", "A", "B", "C"}, double.class); + // TODO: Nail it down further. + } + + @Override + public String getMCVersion() { + // 1_7_R3 + return "1.7.8|1.7.9"; + } + + @Override + public String getServerVersionTag() { + return "CB3043"; + } + + @Override + public CommandMap getCommandMap() { + return ((CraftServer) Bukkit.getServer()).getCommandMap(); + } + + @Override + public BlockCache getBlockCache(final World world) { + return new BlockCacheCB3043(world); + } + + @Override + public double getHeight(final Entity entity) { + final net.minecraft.server.v1_7_R3.Entity mcEntity = ((CraftEntity) entity).getHandle(); + final double entityHeight = Math.max(mcEntity.length, Math.max(mcEntity.height, mcEntity.boundingBox.e - mcEntity.boundingBox.b)); + if (entity instanceof LivingEntity) { + return Math.max(((LivingEntity) entity).getEyeHeight(), entityHeight); + } else return entityHeight; + } + + @Override + public AlmostBoolean isBlockSolid(final int id) { + final Block block = Block.e(id); + if (block == null || block.getMaterial() == null) { + return AlmostBoolean.MAYBE; + } + else { + return AlmostBoolean.match(block.getMaterial().isSolid()); + } + } + + @Override + public AlmostBoolean isBlockLiquid(final int id) { + final Block block = Block.e(id); + if (block == null || block.getMaterial() == null) { + return AlmostBoolean.MAYBE; + } + else { + return AlmostBoolean.match(block.getMaterial().isLiquid()); + } + } + + @Override + public double getWidth(final Entity entity) { + return ((CraftEntity) entity).getHandle().width; + } + + @Override + public AlmostBoolean isIllegalBounds(final Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + if (entityPlayer.dead) return AlmostBoolean.NO; + // TODO: Does this need a method call for the "real" box? Might be no problem during moving events, though. + final AxisAlignedBB box = entityPlayer.boundingBox; + if (!entityPlayer.isSleeping()) { + // This can not really test stance but height of bounding box. + final double dY = Math.abs(box.e - box.b); + if (dY > 1.8) { + return AlmostBoolean.YES; // dY > 1.65D || + } + if (dY < 0.1D && entityPlayer.length >= 0.1) { + return AlmostBoolean.YES; + } + } + return AlmostBoolean.MAYBE; + } + + @Override + public double getJumpAmplifier(final Player player) { + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + if (mcPlayer.hasEffect(MobEffectList.JUMP)) { + return mcPlayer.getEffect(MobEffectList.JUMP).getAmplifier(); + } + else { + return Double.NEGATIVE_INFINITY; + } + } + + @Override + public double getFasterMovementAmplifier(final Player player) { + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + if (mcPlayer.hasEffect(MobEffectList.FASTER_MOVEMENT)) { + return mcPlayer.getEffect(MobEffectList.FASTER_MOVEMENT).getAmplifier(); + } + else { + return Double.NEGATIVE_INFINITY; + } + } + + @Override + public int getInvulnerableTicks(final Player player) { + return ((CraftPlayer) player).getHandle().invulnerableTicks; + } + + @Override + public void setInvulnerableTicks(final Player player, final int ticks) { + ((CraftPlayer) player).getHandle().invulnerableTicks = ticks; + } + + @Override + public void dealFallDamage(final Player player, final double damage) { + ((CraftPlayer) player).getHandle().damageEntity(DamageSource.FALL, (float) damage); + } + + @Override + public boolean isComplexPart(final Entity entity) { + return ((CraftEntity) entity).getHandle() instanceof EntityComplexPart; + } + + @Override + public boolean shouldBeZombie(final Player player) { + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + return !mcPlayer.dead && mcPlayer.getHealth() <= 0.0f ; + } + + @Override + public void setDead(final Player player, final int deathTicks) { + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + mcPlayer.deathTicks = deathTicks; + mcPlayer.dead = true; + } + + @Override + public long getKeepAliveTime(final Player player) { + // TODO: Implement if possible. + return Long.MIN_VALUE; + } + + @Override + public boolean hasGravity(final Material mat) { + // TODO: Test/check. + return mat.hasGravity(); + } + +// @Override +// public void correctDirection(final Player player) { +// final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); +// // Main direction. +// mcPlayer.yaw = LocUtil.correctYaw(mcPlayer.yaw); +// mcPlayer.pitch = LocUtil.correctPitch(mcPlayer.pitch); +// // Consider setting the lastYaw here too. +// } + +} diff --git a/NCPCompatCBDev/pom.xml b/NCPCompatCBDev/pom.xml index 90a8ba42..413d78a2 100644 --- a/NCPCompatCBDev/pom.xml +++ b/NCPCompatCBDev/pom.xml @@ -22,16 +22,17 @@ org.bukkit craftbukkit - 1.7.2-R0.4-SNAPSHOT - - - org.bukkit - bukkit - 1.7.2-R0.4-SNAPSHOT + 1.7.10-R0.1-SNAPSHOT Compatibility for the development version (latest of the supported Minecraft versions). -Version updating is done for the NoCheatPlus sub-module. +Version updating is done for the NoCheatPlus sub-module. + +To add a new compat module three other pom.xml files have to be modified: +- Root pom (parent): Add as module. +- NCPPlugin: Add as dependency. +- NochCheatPlus: Add as include (source). + \ No newline at end of file diff --git a/NCPCompatCBDev/src/main/java/fr/neatmonster/nocheatplus/compat/cbdev/BlockCacheCBDev.java b/NCPCompatCBDev/src/main/java/fr/neatmonster/nocheatplus/compat/cbdev/BlockCacheCBDev.java index de748232..0d9820d6 100644 --- a/NCPCompatCBDev/src/main/java/fr/neatmonster/nocheatplus/compat/cbdev/BlockCacheCBDev.java +++ b/NCPCompatCBDev/src/main/java/fr/neatmonster/nocheatplus/compat/cbdev/BlockCacheCBDev.java @@ -3,16 +3,15 @@ package fr.neatmonster.nocheatplus.compat.cbdev; import java.util.Iterator; import java.util.List; -import net.minecraft.server.v1_7_R1.AxisAlignedBB; -import net.minecraft.server.v1_7_R1.Block; -import net.minecraft.server.v1_7_R1.EntityBoat; -import net.minecraft.server.v1_7_R1.IBlockAccess; -import net.minecraft.server.v1_7_R1.TileEntity; -import net.minecraft.server.v1_7_R1.Vec3DPool; +import net.minecraft.server.v1_7_R4.AxisAlignedBB; +import net.minecraft.server.v1_7_R4.Block; +import net.minecraft.server.v1_7_R4.EntityBoat; +import net.minecraft.server.v1_7_R4.IBlockAccess; +import net.minecraft.server.v1_7_R4.TileEntity; import org.bukkit.World; -import org.bukkit.craftbukkit.v1_7_R1.CraftWorld; -import org.bukkit.craftbukkit.v1_7_R1.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_7_R4.CraftWorld; +import org.bukkit.craftbukkit.v1_7_R4.entity.CraftEntity; import org.bukkit.entity.Entity; import fr.neatmonster.nocheatplus.utilities.BlockCache; @@ -22,7 +21,7 @@ public class BlockCacheCBDev extends BlockCache implements IBlockAccess{ /** Box for one time use, no nesting, no extra storing this(!). */ protected static final AxisAlignedBB useBox = AxisAlignedBB.a(0, 0, 0, 0, 0, 0); - protected net.minecraft.server.v1_7_R1.WorldServer world; + protected net.minecraft.server.v1_7_R4.WorldServer world; public BlockCacheCBDev(World world) { setAccess(world); @@ -30,7 +29,12 @@ public class BlockCacheCBDev extends BlockCache implements IBlockAccess{ @Override public void setAccess(World world) { - this.world = world == null ? null : ((CraftWorld) world).getHandle(); + if (world != null) { + this.maxBlockY = world.getMaxHeight() - 1; + this.world = ((CraftWorld) world).getHandle(); + } else { + this.world = null; + } } @Override @@ -48,8 +52,10 @@ public class BlockCacheCBDev extends BlockCache implements IBlockAccess{ // TODO: change api for this / use nodes (!) final int id = getTypeId(x, y, z); - final net.minecraft.server.v1_7_R1.Block block = net.minecraft.server.v1_7_R1.Block.e(id); - if (block == null) return null; + final net.minecraft.server.v1_7_R4.Block block = net.minecraft.server.v1_7_R4.Block.getById(id); + if (block == null) { + return null; + } block.updateShape(this, x, y, z); // TODO: use THIS instead of world. // minX, minY, minZ, maxX, maxY, maxZ @@ -61,7 +67,7 @@ public class BlockCacheCBDev extends BlockCache implements IBlockAccess{ try{ // TODO: Probably check other ids too before doing this ? - final net.minecraft.server.v1_7_R1.Entity mcEntity = ((CraftEntity) entity).getHandle(); + final net.minecraft.server.v1_7_R4.Entity mcEntity = ((CraftEntity) entity).getHandle(); final AxisAlignedBB box = useBox.b(minX, minY, minZ, maxX, maxY, maxZ); @SuppressWarnings("rawtypes") @@ -69,7 +75,7 @@ public class BlockCacheCBDev extends BlockCache implements IBlockAccess{ @SuppressWarnings("rawtypes") final Iterator iterator = list.iterator(); while (iterator.hasNext()) { - final net.minecraft.server.v1_7_R1.Entity other = (net.minecraft.server.v1_7_R1.Entity) iterator.next(); + final net.minecraft.server.v1_7_R4.Entity other = (net.minecraft.server.v1_7_R4.Entity) iterator.next(); if (!(other instanceof EntityBoat)){ // && !(other instanceof EntityMinecart)) continue; continue; } @@ -78,7 +84,9 @@ public class BlockCacheCBDev extends BlockCache implements IBlockAccess{ } // Still check this for some reason. final AxisAlignedBB otherBox = other.boundingBox; - if (box.a > otherBox.d || box.d < otherBox.a || box.b > otherBox.e || box.e < otherBox.b || box.c > otherBox.f || box.f < otherBox.c) continue; + if (box.a > otherBox.d || box.d < otherBox.a || box.b > otherBox.e || box.e < otherBox.b || box.c > otherBox.f || box.f < otherBox.c) { + continue; + } else { return true; } @@ -104,11 +112,6 @@ public class BlockCacheCBDev extends BlockCache implements IBlockAccess{ return world.getTileEntity(x, y, z); } - @Override - public Vec3DPool getVec3DPool() { - return world.getVec3DPool(); - } - @Override public int getBlockPower(final int arg0, final int arg1, final int arg2, final int arg3) { return world.getBlockPower(arg0, arg1, arg2, arg3); diff --git a/NCPCompatCBDev/src/main/java/fr/neatmonster/nocheatplus/compat/cbdev/MCAccessCBDev.java b/NCPCompatCBDev/src/main/java/fr/neatmonster/nocheatplus/compat/cbdev/MCAccessCBDev.java index c543fe59..97866e30 100644 --- a/NCPCompatCBDev/src/main/java/fr/neatmonster/nocheatplus/compat/cbdev/MCAccessCBDev.java +++ b/NCPCompatCBDev/src/main/java/fr/neatmonster/nocheatplus/compat/cbdev/MCAccessCBDev.java @@ -1,19 +1,19 @@ package fr.neatmonster.nocheatplus.compat.cbdev; -import net.minecraft.server.v1_7_R1.AxisAlignedBB; -import net.minecraft.server.v1_7_R1.Block; -import net.minecraft.server.v1_7_R1.DamageSource; -import net.minecraft.server.v1_7_R1.EntityComplexPart; -import net.minecraft.server.v1_7_R1.EntityPlayer; -import net.minecraft.server.v1_7_R1.MobEffectList; +import net.minecraft.server.v1_7_R4.AxisAlignedBB; +import net.minecraft.server.v1_7_R4.Block; +import net.minecraft.server.v1_7_R4.DamageSource; +import net.minecraft.server.v1_7_R4.EntityComplexPart; +import net.minecraft.server.v1_7_R4.EntityPlayer; +import net.minecraft.server.v1_7_R4.MobEffectList; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.command.CommandMap; -import org.bukkit.craftbukkit.v1_7_R1.CraftServer; -import org.bukkit.craftbukkit.v1_7_R1.entity.CraftEntity; -import org.bukkit.craftbukkit.v1_7_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_7_R4.CraftServer; +import org.bukkit.craftbukkit.v1_7_R4.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_7_R4.entity.CraftPlayer; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; @@ -28,24 +28,24 @@ public class MCAccessCBDev implements MCAccess{ /** * Constructor to let it fail. */ - public MCAccessCBDev(){ + public MCAccessCBDev() { getCommandMap(); - ReflectionUtil.checkMembers("net.minecraft.server.v1_7_R1.", new String[]{"Entity" , "dead"}); + ReflectionUtil.checkMembers("net.minecraft.server.v1_7_R4.", new String[] {"Entity" , "dead"}); // block bounds, original: minX, maxX, minY, maxY, minZ, maxZ - ReflectionUtil.checkMethodReturnTypesNoArgs(net.minecraft.server.v1_7_R1.Block.class, + ReflectionUtil.checkMethodReturnTypesNoArgs(net.minecraft.server.v1_7_R4.Block.class, new String[]{"x", "y", "z", "A", "B", "C"}, double.class); // TODO: Nail it down further. } @Override public String getMCVersion() { - // 1_7_R1 - return "1.7.2"; + // 1_7_R4 + return "1.7.10"; } @Override public String getServerVersionTag() { - return "CB2922-DEV"; + return "CB3100-DEV"; } @Override @@ -60,7 +60,7 @@ public class MCAccessCBDev implements MCAccess{ @Override public double getHeight(final Entity entity) { - final net.minecraft.server.v1_7_R1.Entity mcEntity = ((CraftEntity) entity).getHandle(); + final net.minecraft.server.v1_7_R4.Entity mcEntity = ((CraftEntity) entity).getHandle(); final double entityHeight = Math.max(mcEntity.length, Math.max(mcEntity.height, mcEntity.boundingBox.e - mcEntity.boundingBox.b)); if (entity instanceof LivingEntity) { return Math.max(((LivingEntity) entity).getEyeHeight(), entityHeight); @@ -69,16 +69,24 @@ public class MCAccessCBDev implements MCAccess{ @Override public AlmostBoolean isBlockSolid(final int id) { - final Block block = Block.e(id); - if (block == null || block.getMaterial() == null) return AlmostBoolean.MAYBE; - else return AlmostBoolean.match(block.getMaterial().isSolid()); + final Block block = Block.getById(id); + if (block == null || block.getMaterial() == null) { + return AlmostBoolean.MAYBE; + } + else { + return AlmostBoolean.match(block.getMaterial().isSolid()); + } } @Override public AlmostBoolean isBlockLiquid(final int id) { - final Block block = Block.e(id); - if (block == null || block.getMaterial() == null) return AlmostBoolean.MAYBE; - else return AlmostBoolean.match(block.getMaterial().isLiquid()); + final Block block = Block.getById(id); + if (block == null || block.getMaterial() == null) { + return AlmostBoolean.MAYBE; + } + else { + return AlmostBoolean.match(block.getMaterial().isLiquid()); + } } @Override @@ -89,31 +97,44 @@ public class MCAccessCBDev implements MCAccess{ @Override public AlmostBoolean isIllegalBounds(final Player player) { final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); - if (entityPlayer.dead) return AlmostBoolean.NO; + if (entityPlayer.dead) { + return AlmostBoolean.NO; + } // TODO: Does this need a method call for the "real" box? Might be no problem during moving events, though. final AxisAlignedBB box = entityPlayer.boundingBox; - if (!entityPlayer.isSleeping()){ + if (!entityPlayer.isSleeping()) { // This can not really test stance but height of bounding box. final double dY = Math.abs(box.e - box.b); - if (dY > 1.8) return AlmostBoolean.YES; // dY > 1.65D || - if (dY < 0.1D && entityPlayer.length >= 0.1) return AlmostBoolean.YES; + if (dY > 1.8) { + return AlmostBoolean.YES; // dY > 1.65D || + } + if (dY < 0.1D && entityPlayer.length >= 0.1) { + return AlmostBoolean.YES; + } } return AlmostBoolean.MAYBE; } @Override public double getJumpAmplifier(final Player player) { - final net.minecraft.server.v1_7_R1.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); - - if (mcPlayer.hasEffect(MobEffectList.JUMP)) return mcPlayer.getEffect(MobEffectList.JUMP).getAmplifier(); - else return Double.NEGATIVE_INFINITY; + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + if (mcPlayer.hasEffect(MobEffectList.JUMP)) { + return mcPlayer.getEffect(MobEffectList.JUMP).getAmplifier(); + } + else { + return Double.NEGATIVE_INFINITY; + } } @Override public double getFasterMovementAmplifier(final Player player) { - final net.minecraft.server.v1_7_R1.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); - if (mcPlayer.hasEffect(MobEffectList.FASTER_MOVEMENT)) return mcPlayer.getEffect(MobEffectList.FASTER_MOVEMENT).getAmplifier(); - else return Double.NEGATIVE_INFINITY; + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + if (mcPlayer.hasEffect(MobEffectList.FASTER_MOVEMENT)) { + return mcPlayer.getEffect(MobEffectList.FASTER_MOVEMENT).getAmplifier(); + } + else { + return Double.NEGATIVE_INFINITY; + } } @Override @@ -138,13 +159,13 @@ public class MCAccessCBDev implements MCAccess{ @Override public boolean shouldBeZombie(final Player player) { - final net.minecraft.server.v1_7_R1.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); return !mcPlayer.dead && mcPlayer.getHealth() <= 0.0f ; } @Override public void setDead(final Player player, final int deathTicks) { - final net.minecraft.server.v1_7_R1.EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); + final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); mcPlayer.deathTicks = deathTicks; mcPlayer.dead = true; } @@ -161,4 +182,13 @@ public class MCAccessCBDev implements MCAccess{ return mat.hasGravity(); } +// @Override +// public void correctDirection(final Player player) { +// final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); +// // Main direction. +// mcPlayer.yaw = LocUtil.correctYaw(mcPlayer.yaw); +// mcPlayer.pitch = LocUtil.correctPitch(mcPlayer.pitch); +// // Consider setting the lastYaw here too. +// } + } diff --git a/NCPCompatProtocolLib/pom.xml b/NCPCompatProtocolLib/pom.xml new file mode 100644 index 00000000..2c52aedd --- /dev/null +++ b/NCPCompatProtocolLib/pom.xml @@ -0,0 +1,39 @@ + + 4.0.0 + fr.neatmonster + ncpcompatprotocollib + jar + NCPCompatProtocolLib + static + + + fr.neatmonster + nocheatplus-parent + static + + + + + comphenix-rep + Comphenix Repository + http://repo.comphenix.net/content/groups/public + + + + + + fr.neatmonster + ncpcore + static + + + com.comphenix.protocol + ProtocolLib + LATEST + + + + Features using the plugin ProtocolLib: +http://dev.bukkit.org/bukkit-plugins/protocollib/ + \ No newline at end of file diff --git a/NCPCompatProtocolLib/src/main/java/fr/neatmonster/nocheatplus/net/protocollib/MoveFrequency.java b/NCPCompatProtocolLib/src/main/java/fr/neatmonster/nocheatplus/net/protocollib/MoveFrequency.java new file mode 100644 index 00000000..c660242d --- /dev/null +++ b/NCPCompatProtocolLib/src/main/java/fr/neatmonster/nocheatplus/net/protocollib/MoveFrequency.java @@ -0,0 +1,91 @@ +package fr.neatmonster.nocheatplus.net.protocollib; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.bukkit.entity.Player; +import org.bukkit.event.Listener; +import org.bukkit.plugin.Plugin; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.PacketType.Protocol; +import com.comphenix.protocol.PacketType.Sender; +import com.comphenix.protocol.events.PacketAdapter; +import com.comphenix.protocol.events.PacketEvent; + +import fr.neatmonster.nocheatplus.components.JoinLeaveListener; +import fr.neatmonster.nocheatplus.utilities.ActionFrequency; + +/** + * Prevent extremely fast ticking by just sending packets that don't do anything + * new and also don't trigger moving events in CraftBukkit. + * + * @author dev1mc + * + */ +public class MoveFrequency extends PacketAdapter implements Listener, JoinLeaveListener { + + // TODO: Optimized options (receive only, other?). + // TODO: Async version ? + +// private static Collection getPacketTypes() { +// final Collection packetTypes = PacketType.fromName("C03PacketPlayer"); +// if (packetTypes.isEmpty()) { +// throw new RuntimeException("Packet types not available."); +// } +// return packetTypes; +// } + + private Map freqMap = new LinkedHashMap(); + + public MoveFrequency(Plugin plugin) { + // PacketPlayInFlying[3, legacy: 10] + super(plugin, PacketType.findCurrent(Protocol.PLAY, Sender.CLIENT, 3)); //getPacketTypes()); + // TODO: Try to get packet by name first + legacy first. + } + + private ActionFrequency addName(String name) { + Map freqMap = new HashMap(this.freqMap); + ActionFrequency freq = new ActionFrequency(5, 1000); + freqMap.put(name, freq); + this.freqMap = freqMap; + return freq; + } + + private void removeName(String name) { + Map freq = new HashMap(this.freqMap); + freq.remove(name); + this.freqMap = freq; + } + + @Override + public void playerJoins(Player player) { + addName(player.getName()); // Could spare that one. + } + + @Override + public void playerLeaves(Player player) { + removeName(player.getName()); + } + + private ActionFrequency getFreq(String name) { + ActionFrequency freq = this.freqMap.get(name); + if (freq == null) { + return addName(name); + } else { + return freq; + } + } + + @Override + public void onPacketReceiving(PacketEvent event) { + // TODO: Add several (at least has look + has pos individually, maybe none/onground) + ActionFrequency freq = getFreq(event.getPlayer().getName()); + freq.add(System.currentTimeMillis(), 1f); + if (freq.score(1f) > 300) { + event.setCancelled(true); + } + } + +} diff --git a/NCPCompatProtocolLib/src/main/java/fr/neatmonster/nocheatplus/net/protocollib/ProtocolLibComponent.java b/NCPCompatProtocolLib/src/main/java/fr/neatmonster/nocheatplus/net/protocollib/ProtocolLibComponent.java new file mode 100644 index 00000000..b9269091 --- /dev/null +++ b/NCPCompatProtocolLib/src/main/java/fr/neatmonster/nocheatplus/net/protocollib/ProtocolLibComponent.java @@ -0,0 +1,54 @@ +package fr.neatmonster.nocheatplus.net.protocollib; + +import java.util.LinkedList; +import java.util.List; + +import org.bukkit.plugin.Plugin; + +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.ProtocolManager; +import com.comphenix.protocol.events.PacketAdapter; + +import fr.neatmonster.nocheatplus.components.DisableListener; +import fr.neatmonster.nocheatplus.logging.LogUtil; + +/** + * Quick and dirty ProtocolLib setup. + * @author dev1mc + * + */ +public class ProtocolLibComponent implements DisableListener{ + + private final List registeredPacketAdapters = new LinkedList(); + + public ProtocolLibComponent(Plugin plugin) { + // Register with ProtocolLib + final ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager(); + LogUtil.logInfo("[NoCheatPlus] ProtocolLib seems to be available."); + try { + PacketAdapter adapter = new MoveFrequency(plugin); + protocolManager.addPacketListener(adapter); + registeredPacketAdapters.add(adapter); + } catch (Throwable t) { + LogUtil.logWarning("[NoCheatPlus] Could not register some packet-level hook."); + LogUtil.logWarning(t); // TODO: Maybe temporary. + } + } + + @Override + public void onDisable() { + final ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager(); + for (PacketAdapter adapter : registeredPacketAdapters) { + try { + protocolManager.removePacketListener(adapter); + } catch (Throwable t) { + LogUtil.logWarning("[NoCheatPlus] Failed to unregister protocol listener: " + adapter.getClass().getName()); + } + } + } + + + + + +} diff --git a/NCPCore/pom.xml b/NCPCore/pom.xml index 1fa17efd..ea491af8 100644 --- a/NCPCore/pom.xml +++ b/NCPCore/pom.xml @@ -17,7 +17,7 @@ org.bukkit bukkit - 1.7.2-R0.4-SNAPSHOT + 1.7.9-R0.2 fr.neatmonster diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/actions/ParameterName.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/actions/ParameterName.java index 49179780..a2016df8 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/actions/ParameterName.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/actions/ParameterName.java @@ -4,19 +4,22 @@ package fr.neatmonster.nocheatplus.actions; * Some wildcards that are used in commands and log messages. */ public enum ParameterName { + // TODO: Cleanup for some kind of policies: useful names, alternative names, prefer generic names. BLOCK_ID("blockid"), + BLOCK_TYPE("blocktype"), CHECK("check"), TAGS("tags"), DISTANCE("distance"), - FALL_DISTANCE("falldistance"), + FALL_DISTANCE("falldistance"), // TODO: rather not deprecate ? FOOD("food"), IP("ip"), LIMIT("limit"), LOCATION_FROM("locationfrom"), LOCATION_TO("locationto"), PACKETS("packets"), - PLAYER("player"), - REACH_DISTANCE("reachdistance"), + PLAYER("player"), // TODO: playername rather ? + displayname ? + REACH_DISTANCE("reachdistance"), // TODO: deprecate ? + UUID("uuid"), VIOLATIONS("violations"), WORLD("world"); diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/ViolationData.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/ViolationData.java index 04e51db7..d1c818cd 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/ViolationData.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/ViolationData.java @@ -135,6 +135,8 @@ public class ViolationData implements IViolationInfo, ActionData{ return player.getName(); case VIOLATIONS: return String.valueOf((long) Math.round(vL)); + case UUID: + return player.getUniqueId().toString(); default: break; } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockbreak/BlockBreakConfig.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockbreak/BlockBreakConfig.java index ddcdaef0..5196d375 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockbreak/BlockBreakConfig.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockbreak/BlockBreakConfig.java @@ -65,7 +65,6 @@ public class BlockBreakConfig extends ACheckConfig { public final long fastBreakGrace; public final long fastBreakDelay; public final int fastBreakModSurvival; - public final int fastBreakModCreative; public final ActionList fastBreakActions; @@ -112,7 +111,6 @@ public class BlockBreakConfig extends ACheckConfig { fastBreakBucketDur = data.getInt(ConfPaths.BLOCKBREAK_FASTBREAK_BUCKETS_DUR, 4000); fastBreakBucketFactor = (float) data.getDouble(ConfPaths.BLOCKBREAK_FASTBREAK_BUCKETS_FACTOR, 0.99); fastBreakBuckets = data.getInt(ConfPaths.BLOCKBREAK_FASTBREAK_BUCKETS_N, 30); - fastBreakModCreative = data.getInt(ConfPaths.BLOCKBREAK_FASTBREAK_MOD_CREATIVE, 0); fastBreakModSurvival = data.getInt(ConfPaths.BLOCKBREAK_FASTBREAK_MOD_SURVIVAL); // Fastbreak actions, shared. fastBreakActions = data.getOptimizedActionList(ConfPaths.BLOCKBREAK_FASTBREAK_ACTIONS, Permissions.BLOCKBREAK_FASTBREAK); diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockbreak/BlockBreakListener.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockbreak/BlockBreakListener.java index a1d191e4..f3e03ab0 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockbreak/BlockBreakListener.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockbreak/BlockBreakListener.java @@ -18,6 +18,7 @@ import org.bukkit.inventory.ItemStack; 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.hooks.NCPExemptionManager; import fr.neatmonster.nocheatplus.permissions.Permissions; import fr.neatmonster.nocheatplus.utilities.BlockProperties; @@ -48,7 +49,7 @@ public class BlockBreakListener extends CheckListener { /** The wrong block check. */ private final WrongBlock wrongBlock = addCheck(new WrongBlock()); - private boolean isInstaBreak = false; + private AlmostBoolean isInstaBreak = AlmostBoolean.NO; public BlockBreakListener(){ super(CheckType.BLOCKBREAK); @@ -60,18 +61,19 @@ public class BlockBreakListener extends CheckListener { * @param event * the event */ - @EventHandler( - ignoreCancelled = false, priority = EventPriority.LOWEST) + @EventHandler(ignoreCancelled = false, priority = EventPriority.LOWEST) public void onBlockBreak(final BlockBreakEvent event) { final Player player = event.getPlayer(); // Illegal enchantments hotfix check. - if (Items.checkIllegalEnchantments(player, player.getItemInHand())) event.setCancelled(true); + if (Items.checkIllegalEnchantments(player, player.getItemInHand())) { + event.setCancelled(true); + } // Cancelled events only leads to resetting insta break. - if (event.isCancelled()){ - isInstaBreak = false; + if (event.isCancelled()) { + isInstaBreak = AlmostBoolean.NO; return; } @@ -92,38 +94,45 @@ public class BlockBreakListener extends CheckListener { final GameMode gameMode = player.getGameMode(); // Has the player broken a block that was not damaged before? - if (wrongBlock.isEnabled(player) && wrongBlock.check(player, block, cc, data, isInstaBreak)) + if (wrongBlock.isEnabled(player) && wrongBlock.check(player, block, cc, data, isInstaBreak)) { cancelled = true; + } // Has the player broken more blocks per second than allowed? - if (!cancelled && frequency.isEnabled(player) && frequency.check(player, cc, data)) + if (!cancelled && frequency.isEnabled(player) && frequency.check(player, cc, data)) { cancelled = true; + } // Has the player broken blocks faster than possible? - if (!cancelled && gameMode != GameMode.CREATIVE && fastBreak.isEnabled(player) && fastBreak.check(player, block, isInstaBreak, cc, data)) - cancelled = true; + if (!cancelled && gameMode != GameMode.CREATIVE && fastBreak.isEnabled(player) && fastBreak.check(player, block, isInstaBreak, cc, data)) { + cancelled = true; + } // Did the arm of the player move before breaking this block? - if (!cancelled && noSwing.isEnabled(player) && noSwing.check(player, data)) - cancelled = true; + if (!cancelled && noSwing.isEnabled(player) && noSwing.check(player, data)) { + cancelled = true; + } // Is the block really in reach distance? - if (!cancelled && reach.isEnabled(player) && reach.check(player, block, data)) - cancelled = true; + if (!cancelled && reach.isEnabled(player) && reach.check(player, block, data)) { + cancelled = true; + } // Did the player look at the block at all? - if (!cancelled && direction.isEnabled(player) && direction.check(player, block, data)) - cancelled = true; + if (!cancelled && direction.isEnabled(player) && direction.check(player, block, data)) { + cancelled = true; + } // Destroying liquid blocks. - if (!cancelled && BlockProperties.isLiquid(block.getTypeId()) && !player.hasPermission(Permissions.BLOCKBREAK_BREAK_LIQUID) && !NCPExemptionManager.isExempted(player, CheckType.BLOCKBREAK_BREAK)){ + if (!cancelled && BlockProperties.isLiquid(block.getType()) && !player.hasPermission(Permissions.BLOCKBREAK_BREAK_LIQUID) && !NCPExemptionManager.isExempted(player, CheckType.BLOCKBREAK_BREAK)){ cancelled = true; } - // At least one check failed and demanded to cancel the event. - if (cancelled){ + // On cancel... + if (cancelled) { event.setCancelled(cancelled); // Reset damage position: + // TODO: Review this (!), check if set at all !? data.clickedX = block.getX(); data.clickedY = block.getY(); data.clickedZ = block.getZ(); @@ -133,16 +142,17 @@ public class BlockBreakListener extends CheckListener { // data.clickedX = Integer.MAX_VALUE; } - if (isInstaBreak){ + if (isInstaBreak.decideOptimistically()) { data.wasInstaBreak = now; } - else + else { data.wasInstaBreak = 0; + } // Adjust data. data.fastBreakBreakTime = now; // data.fastBreakfirstDamage = now; - isInstaBreak = false; + isInstaBreak = AlmostBoolean.NO; } /** @@ -175,18 +185,32 @@ public class BlockBreakListener extends CheckListener { // Return if it is not left clicking a block. // (Allows right click to be ignored.) - isInstaBreak = false; - if (event.getAction() != Action.LEFT_CLICK_BLOCK) return; + isInstaBreak = AlmostBoolean.NO; + if (event.getAction() != Action.LEFT_CLICK_BLOCK) { + return; + } checkBlockDamage(event.getPlayer(), event.getClickedBlock(), event); - } - @EventHandler( - ignoreCancelled = false, priority = EventPriority.MONITOR) + @EventHandler(ignoreCancelled = false, priority = EventPriority.LOWEST) + public void onBlockDamageLowest(final BlockDamageEvent event) { + if (event.getInstaBreak()) { + // Indicate that this might have been set by CB/MC. + isInstaBreak = AlmostBoolean.MAYBE; + } + } + + @EventHandler(ignoreCancelled = false, priority = EventPriority.MONITOR) public void onBlockDamage(final BlockDamageEvent event) { -// System.out.println("Damage("+event.isCancelled()+"): " + event.getBlock()); - if (!event.isCancelled() && event.getInstaBreak()) isInstaBreak = true; - else isInstaBreak = false; + if (!event.isCancelled() && event.getInstaBreak()) { + // Keep MAYBE. + if (isInstaBreak != AlmostBoolean.MAYBE) { + isInstaBreak = AlmostBoolean.YES; + } + } + else { + isInstaBreak = AlmostBoolean.NO; + } checkBlockDamage(event.getPlayer(), event.getBlock(), event); } @@ -202,8 +226,9 @@ public class BlockBreakListener extends CheckListener { // } // Do not care about null blocks. - if (block == null) - return; + if (block == null) { + return; + } final int tick = TickTask.getTick(); // Skip if already set to the same block without breaking within one tick difference. @@ -211,10 +236,14 @@ public class BlockBreakListener extends CheckListener { final Material tool = stack == null ? null: stack.getType(); if (data.toolChanged(tool)) { // Update. - } else if (tick < data.clickedTick) { - // Update. + } else if (tick < data.clickedTick || now < data.fastBreakfirstDamage || now < data.fastBreakBreakTime) { + // Time/tick ran backwards: Update. + // Tick running backwards should not happen in the main thread unless for reload. A plugin could reset it (not intended). } else if (data.fastBreakBreakTime < data.fastBreakfirstDamage && data.clickedX == block.getX() && data.clickedZ == block.getZ() && data.clickedY == block.getY()){ - if (tick - data.clickedTick <= 1 ) return; + // Preserve first damage time. + if (tick - data.clickedTick <= 1 ) { + return; + } } // (Always set, the interact event only fires once: the first time.) // Only record first damage: diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockbreak/Direction.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockbreak/Direction.java index 8b6fdf13..cf7f59b9 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockbreak/Direction.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockbreak/Direction.java @@ -13,6 +13,9 @@ import fr.neatmonster.nocheatplus.utilities.TrigUtil; * The Direction check will find out if a player tried to interact with something that's not in their field of view. */ public class Direction extends Check { + + /** For temporary use: LocUtil.clone before passing deeply, call setWorld(null) after use. */ + private final Location useLoc = new Location(null, 0, 0, 0); /** * Instantiates a new direction check. @@ -36,7 +39,7 @@ public class Direction extends Check { // How far "off" is the player with their aim. We calculate from the players eye location and view direction to // the center of the target block. If the line of sight is more too far off, "off" will be bigger than 0. - final Location loc = player.getLocation(); + final Location loc = player.getLocation(useLoc); final Vector direction = loc.getDirection(); final double off = TrigUtil.directionCheck(loc, player.getEyeHeight(), direction, block, TrigUtil.DIRECTION_PRECISION); @@ -52,10 +55,11 @@ public class Direction extends Check { // cancel the event. cancel = executeActions(player, data.directionVL, distance, BlockBreakConfig.getConfig(player).directionActions); - } else + } else { // Player did likely nothing wrong, reduce violation counter to reward them. data.directionVL *= 0.9D; - + } + useLoc.setWorld(null); return cancel; } } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockbreak/FastBreak.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockbreak/FastBreak.java index b98b6d60..391cd6ba 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockbreak/FastBreak.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockbreak/FastBreak.java @@ -1,6 +1,6 @@ package fr.neatmonster.nocheatplus.checks.blockbreak; -import org.bukkit.GameMode; +import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; @@ -10,6 +10,7 @@ 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.AlmostBoolean; import fr.neatmonster.nocheatplus.permissions.Permissions; import fr.neatmonster.nocheatplus.utilities.BlockProperties; import fr.neatmonster.nocheatplus.utilities.PotionUtil; @@ -28,7 +29,7 @@ public class FastBreak extends Check { } /** - * Checks a player. + * Checks a player for fastbreak. This is NOT for creative mode. * * @param player * the player @@ -40,84 +41,83 @@ public class FastBreak extends Check { * @param elaspedTime * @return true, if successful */ - public boolean check(final Player player, final Block block, final boolean isInstaBreak, final BlockBreakConfig cc, final BlockBreakData data) { + public boolean check(final Player player, final Block block, final AlmostBoolean isInstaBreak, final BlockBreakConfig cc, final BlockBreakData data) { final long now = System.currentTimeMillis(); boolean cancel = false; - // First, check the game mode of the player and choose the right limit. - final long breakingTime; - final int id = block.getTypeId(); - if (player.getGameMode() == GameMode.CREATIVE) - // Modifier defaults to 0, the Frequency check is responsible for those. - breakingTime = Math.max(0, Math.round((double) cc.fastBreakModCreative / 100D * (double) 100)); - else - breakingTime = Math.max(0, Math.round((double) cc.fastBreakModSurvival / 100D * (double) BlockProperties.getBreakingDuration(id, player))); - // fastBreakfirstDamage is the first interact on block (!). + // Determine expected breaking time by block type. + final Material blockType = block.getType(); + final long expectedBreakingTime = Math.max(0, Math.round((double) BlockProperties.getBreakingDuration(blockType, player) * (double) cc.fastBreakModSurvival / 100D)); + final long elapsedTime; + // TODO: Concept for unbreakable blocks? Context: extreme VL. // TODO: Should it be breakingTime instead of 0 for inconsistencies? - if (cc.fastBreakStrict){ + if (cc.fastBreakStrict) { // Counting interact...break. elapsedTime = (data.fastBreakBreakTime > data.fastBreakfirstDamage) ? 0 : now - data.fastBreakfirstDamage; } - else{ + else { // Counting break...break. elapsedTime = (data.fastBreakBreakTime > now) ? 0 : now - data.fastBreakBreakTime; } // Check if the time used time is lower than expected. -// if (isInstaBreak){ -// // Ignore those for now. -// } -// else - if (elapsedTime < 0){ + if (isInstaBreak.decideOptimistically()) { + // Ignore those for now. + // TODO: Find out why this was commented out long ago a) did not fix mcMMO b) exploits. + // TODO: Maybe adjust time to min(time, SOMETHING) for MAYBE/YES. + } + else if (elapsedTime < 0) { // Ignore it. TODO: ? } - else if (elapsedTime + cc.fastBreakDelay < breakingTime){ + else if (elapsedTime + cc.fastBreakDelay < expectedBreakingTime) { // lag or cheat or Minecraft. // Count in server side lag, if desired. - final float lag = cc.lag ? TickTask.getLag(breakingTime, true) : 1f; + final float lag = cc.lag ? TickTask.getLag(expectedBreakingTime, true) : 1f; - final long missingTime = breakingTime - (long) (lag * elapsedTime); + final long missingTime = expectedBreakingTime - (long) (lag * elapsedTime); - if (missingTime > 0){ + if (missingTime > 0) { // Add as penalty data.fastBreakPenalties.add(now, (float) missingTime); // Only raise a violation, if the total penalty score exceeds the contention duration (for lag, delay). - if (data.fastBreakPenalties.score(cc.fastBreakBucketFactor) > cc.fastBreakGrace){ + if (data.fastBreakPenalties.score(cc.fastBreakBucketFactor) > cc.fastBreakGrace) { // TODO: maybe add one absolute penalty time for big amounts to stop breaking until then final double vlAdded = (double) missingTime / 1000.0; data.fastBreakVL += vlAdded; final ViolationData vd = new ViolationData(this, player, data.fastBreakVL, vlAdded, cc.fastBreakActions); - if (vd.needsParameters()) vd.setParameter(ParameterName.BLOCK_ID, "" + id); + if (vd.needsParameters()) { + vd.setParameter(ParameterName.BLOCK_TYPE, blockType.toString()); + vd.setParameter(ParameterName.BLOCK_ID, Integer.toString(BlockProperties.getId(blockType))); + } cancel = executeActions(vd); } // else: still within contention limits. } } - else if (breakingTime > cc.fastBreakDelay){ + else if (expectedBreakingTime > cc.fastBreakDelay) { // Fast breaking does not decrease violation level. data.fastBreakVL *= 0.9D; } - if ((cc.fastBreakDebug || cc.debug) && player.hasPermission(Permissions.ADMINISTRATION_DEBUG)){ + if ((cc.fastBreakDebug || cc.debug) && player.hasPermission(Permissions.ADMINISTRATION_DEBUG)) { // General stats: - if (data.stats != null){ - data.stats.addStats(data.stats.getId(Integer.toString(block.getTypeId())+"u", true), elapsedTime); - data.stats.addStats(data.stats.getId(Integer.toString(block.getTypeId())+ "r", true), breakingTime); + if (data.stats != null) { + data.stats.addStats(data.stats.getId(blockType+ "/u", true), elapsedTime); + data.stats.addStats(data.stats.getId(blockType + "/r", true), expectedBreakingTime); player.sendMessage(data.stats.getStatsStr(true)); } // Send info about current break: - final int blockId = block.getTypeId(); final ItemStack stack = player.getItemInHand(); - final boolean isValidTool = BlockProperties.isValidTool(blockId, stack); + final boolean isValidTool = BlockProperties.isValidTool(blockType, stack); final double haste = PotionUtil.getPotionEffectAmplifier(player, PotionEffectType.FAST_DIGGING); - String msg = (isInstaBreak ? "[Insta]" : "[Normal]") + "[" + blockId + "] "+ elapsedTime + "u / " + breakingTime +"r (" + (isValidTool?"tool":"no-tool") + ")" + (haste == Double.NEGATIVE_INFINITY ? "" : " haste=" + ((int) haste + 1)); + String msg = (isInstaBreak.decideOptimistically() ? ("[Insta=" + isInstaBreak + "]") : "[Normal]") + "[" + blockType + "] "+ elapsedTime + "u / " + expectedBreakingTime +"r (" + (isValidTool?"tool":"no-tool") + ")" + (haste == Double.NEGATIVE_INFINITY ? "" : " haste=" + ((int) haste + 1)); player.sendMessage(msg); // net.minecraft.server.Item mcItem = net.minecraft.server.Item.byId[stack.getTypeId()]; -// if (mcItem != null){ +// if (mcItem != null) { // double x = mcItem.getDestroySpeed(((CraftItemStack) stack).getHandle(), net.minecraft.server.Block.byId[blockId]); // player.sendMessage("mc speed: " + x); // } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockbreak/Reach.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockbreak/Reach.java index 9f5662c9..76cf68a8 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockbreak/Reach.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockbreak/Reach.java @@ -3,6 +3,7 @@ package fr.neatmonster.nocheatplus.checks.blockbreak; import java.util.Map; import org.bukkit.GameMode; +import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.entity.Player; @@ -16,6 +17,9 @@ import fr.neatmonster.nocheatplus.utilities.TrigUtil; * The Reach check will find out if a player interacts with something that's too far away. */ public class Reach extends Check { + + /** For temporary use: LocUtil.clone before passing deeply, call setWorld(null) after use. */ + private final Location useLoc = new Location(null, 0, 0, 0); /** The maximum distance allowed to interact with a block in creative mode. */ public static final double CREATIVE_DISTANCE = 5.6D; @@ -47,7 +51,10 @@ public class Reach extends Check { // Distance is calculated from eye location to center of targeted block. If the player is further away from their // target than allowed, the difference will be assigned to "distance". - final double distance = TrigUtil.distance(player.getEyeLocation(), block) - distanceLimit; + final Location loc = player.getLocation(useLoc); + loc.setY(loc.getY() + player.getEyeHeight()); + final double distance = TrigUtil.distance(loc, block) - distanceLimit; + useLoc.setWorld(null); if (distance > 0) { // They failed, increment violation level. diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockbreak/WrongBlock.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockbreak/WrongBlock.java index 7ac49bb7..4341395d 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockbreak/WrongBlock.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockbreak/WrongBlock.java @@ -6,6 +6,7 @@ import org.bukkit.entity.Player; import fr.neatmonster.nocheatplus.checks.Check; import fr.neatmonster.nocheatplus.checks.CheckType; import fr.neatmonster.nocheatplus.checks.combined.Improbable; +import fr.neatmonster.nocheatplus.compat.AlmostBoolean; import fr.neatmonster.nocheatplus.permissions.Permissions; import fr.neatmonster.nocheatplus.utilities.TrigUtil; @@ -26,45 +27,50 @@ public class WrongBlock extends Check { * @param isInstaBreak * @return */ - public boolean check(final Player player, final Block block, final BlockBreakConfig cc, final BlockBreakData data, final boolean isInstaBreak){ + public boolean check(final Player player, final Block block, final BlockBreakConfig cc, final BlockBreakData data, final AlmostBoolean isInstaBreak) { boolean cancel = false; final boolean wrongTime = data.fastBreakfirstDamage < data.fastBreakBreakTime; - final int dist = TrigUtil.manhattan(data.clickedX, data.clickedY, data.clickedZ, block); + final int dist = Math.min(4, data.clickedX == Integer.MAX_VALUE ? 100 : TrigUtil.manhattan(data.clickedX, data.clickedY, data.clickedZ, block)); final boolean wrongBlock; final long now = System.currentTimeMillis(); - if (dist == 0){ - if (wrongTime){ + // TODO: Remove isInstaBreak argument or use it. + if (dist == 0) { + if (wrongTime) { data.fastBreakBreakTime = now; data.fastBreakfirstDamage = now; // Could set to wrong block, but prefer to transform it into a quasi insta break. } wrongBlock = false; } - else if (dist == 1){ + else if (dist == 1) { // One might to a concession in case of instant breaking. - if (now - data.wasInstaBreak < 60) + if (now - data.wasInstaBreak < 60) { wrongBlock = false; - else + } + else { wrongBlock = true; + } } - else + else { + // Note that the maximally counted distance is set above. wrongBlock = true; + } - if (wrongBlock){ - // Manhattan distance. - - if ((cc.fastBreakDebug || cc.debug) && player.hasPermission(Permissions.ADMINISTRATION_DEBUG)){ + if (wrongBlock) { + if ((cc.fastBreakDebug || cc.debug) && player.hasPermission(Permissions.ADMINISTRATION_DEBUG)) { player.sendMessage("WrongBlock failure with dist: " + dist); } data.wrongBlockVL.add(now, (float) (dist + 1) / 2f); final float score = data.wrongBlockVL.score(0.9f); - if (score > cc.wrongBLockLevel){ - if (executeActions(player, score, 1D, cc.wrongBlockActions)) + if (score > cc.wrongBLockLevel) { + if (executeActions(player, score, 1D, cc.wrongBlockActions)) { cancel = true; - if (Improbable.check(player, 2.0f, now, "blockbreak.wrongblock")) + } + if (Improbable.check(player, 2.0f, now, "blockbreak.wrongblock")) { cancel = true; + } } } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockinteract/BlockInteractListener.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockinteract/BlockInteractListener.java index 2fd31b4a..71f3b420 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockinteract/BlockInteractListener.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockinteract/BlockInteractListener.java @@ -15,6 +15,7 @@ import org.bukkit.inventory.ItemStack; import fr.neatmonster.nocheatplus.checks.CheckListener; import fr.neatmonster.nocheatplus.checks.CheckType; import fr.neatmonster.nocheatplus.checks.combined.CombinedConfig; +import fr.neatmonster.nocheatplus.compat.BridgeHealth; import fr.neatmonster.nocheatplus.utilities.BlockProperties; /** @@ -36,6 +37,9 @@ public class BlockInteractListener extends CheckListener { /** Speed of interaction. */ private final Speed speed = addCheck(new Speed()); + /** For temporary use: LocUtil.clone before passing deeply, call setWorld(null) after use. */ + private final Location useLoc = new Location(null, 0, 0, 0); + public BlockInteractListener(){ super(CheckType.BLOCKINTERACT); } @@ -49,6 +53,15 @@ public class BlockInteractListener extends CheckListener { @EventHandler( ignoreCancelled = false, priority = EventPriority.LOWEST) protected void onPlayerInteract(final PlayerInteractEvent event) { + final Player player = event.getPlayer(); + // Cancel interact events for dead players. + if (player.isDead() && BridgeHealth.getHealth(player) <= 0.0) { + // Auto-soup after death. + event.setUseInteractedBlock(Result.DENY); + event.setUseItemInHand(Result.DENY); + event.setCancelled(true); + return; + } // TODO: Re-arrange for interact spam, possibly move ender pearl stuff to a method. final Action action = event.getAction(); @@ -57,7 +70,7 @@ public class BlockInteractListener extends CheckListener { if (block == null){ return; } - final Player player = event.getPlayer(); + final BlockInteractData data = BlockInteractData.getData(player); data.setLastBlock(block, action); switch(action){ @@ -65,8 +78,8 @@ public class BlockInteractListener extends CheckListener { break; case RIGHT_CLICK_BLOCK: final ItemStack stack = player.getItemInHand(); - if (stack != null && stack.getTypeId() == Material.ENDER_PEARL.getId()){ - if (!BlockProperties.isPassable(block.getTypeId())){ + if (stack != null && stack.getType() == Material.ENDER_PEARL){ + if (!BlockProperties.isPassable(block.getType())){ final CombinedConfig ccc = CombinedConfig.getConfig(player); if (ccc.enderPearlCheck && ccc.enderPearlPreventClickBlock){ event.setUseItemInHand(Result.DENY); @@ -86,7 +99,7 @@ public class BlockInteractListener extends CheckListener { boolean cancelled = false; final BlockFace face = event.getBlockFace(); - final Location loc = player.getLocation(); + final Location loc = player.getLocation(useLoc); // Interaction speed. if (!cancelled && speed.isEnabled(player) && speed.check(player, data, cc)){ @@ -114,5 +127,6 @@ public class BlockInteractListener extends CheckListener { event.setUseItemInHand(Result.DENY); event.setCancelled(true); } + useLoc.setWorld(null); } } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockplace/Against.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockplace/Against.java index 2d7c651f..a227f28d 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockplace/Against.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockplace/Against.java @@ -9,8 +9,8 @@ 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.checks.blockbreak.BlockBreakData; import fr.neatmonster.nocheatplus.checks.blockinteract.BlockInteractData; +import fr.neatmonster.nocheatplus.permissions.Permissions; import fr.neatmonster.nocheatplus.utilities.BlockProperties; import fr.neatmonster.nocheatplus.utilities.TickTask; import fr.neatmonster.nocheatplus.utilities.TrigUtil; @@ -29,8 +29,8 @@ public class Against extends Check { boolean violation = false; // TODO: Make more precise (workarounds like WATER_LILY, general points). // Workaround for signs on cactus and similar. - final int againstId = blockAgainst.getTypeId(); - if (againstId == Material.AIR.getId()) { + final Material againstType = blockAgainst.getType(); + if (againstType == null || againstType == Material.AIR) { // Attempt to workaround blocks like cactus. final BlockInteractData bdata = BlockInteractData.getData(player); if (bdata.lastType != null && bdata.lastX != Integer.MAX_VALUE && TickTask.getTick() == bdata.lastTick && TrigUtil.manhattan(bdata.lastX, bdata.lastY, bdata.lastZ, blockAgainst) == 0) { @@ -41,19 +41,20 @@ public class Against extends Check { return false; } } - if (BlockProperties.isLiquid(againstId)) { - if ((placedMat != Material.WATER_LILY || !BlockProperties.isLiquid(block.getRelative(BlockFace.DOWN).getTypeId()))) { + if (BlockProperties.isLiquid(againstType)) { + if ((placedMat != Material.WATER_LILY || !BlockProperties.isLiquid(block.getRelative(BlockFace.DOWN).getType())) && !player.hasPermission(Permissions.BLOCKPLACE_AGAINST_LIQUIDS)) { violation = true; } } - else if (againstId == Material.AIR.getId()) { + else if (againstType == Material.AIR && !player.hasPermission(Permissions.BLOCKPLACE_AGAINST_AIR)) { violation = true; } // Handle violation and return. if (violation) { data.againstVL += 1.0; final ViolationData vd = new ViolationData(this, player, data.againstVL, 1, cc.againstActions); - vd.setParameter(ParameterName.BLOCK_ID, Integer.toString(placedMat.getId())); + vd.setParameter(ParameterName.BLOCK_TYPE, placedMat.toString()); + vd.setParameter(ParameterName.BLOCK_ID, Integer.toString(BlockProperties.getId(placedMat))); return executeActions(vd); } else { data.againstVL *= 0.99; // Assume one false positive every 100 blocks. diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockplace/BlockPlaceConfig.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockplace/BlockPlaceConfig.java index 4a39b027..71d7dba2 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockplace/BlockPlaceConfig.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockplace/BlockPlaceConfig.java @@ -1,8 +1,11 @@ package fr.neatmonster.nocheatplus.checks.blockplace; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; +import org.bukkit.Material; import org.bukkit.entity.Player; import fr.neatmonster.nocheatplus.actions.ActionList; @@ -69,6 +72,7 @@ public class BlockPlaceConfig extends ACheckConfig { public final ActionList fastPlaceActions; public final boolean noSwingCheck; + public final Set noSwingExceptions = new HashSet(); public final ActionList noSwingActions; public final boolean reachCheck; @@ -104,6 +108,7 @@ public class BlockPlaceConfig extends ACheckConfig { fastPlaceActions = data.getOptimizedActionList(ConfPaths.BLOCKPLACE_FASTPLACE_ACTIONS, Permissions.BLOCKPLACE_FASTPLACE); noSwingCheck = data.getBoolean(ConfPaths.BLOCKPLACE_NOSWING_CHECK); + data.readMaterialFromList(ConfPaths.BLOCKPLACE_NOSWING_EXCEPTIONS, noSwingExceptions); noSwingActions = data.getOptimizedActionList(ConfPaths.BLOCKPLACE_NOSWING_ACTIONS, Permissions.BLOCKPLACE_NOSWING); reachCheck = data.getBoolean(ConfPaths.BLOCKPLACE_REACH_CHECK); diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockplace/BlockPlaceListener.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockplace/BlockPlaceListener.java index 0b7d38e0..4922ca99 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockplace/BlockPlaceListener.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockplace/BlockPlaceListener.java @@ -75,6 +75,9 @@ public class BlockPlaceListener extends CheckListener { /** The speed check. */ private final Speed speed = addCheck(new Speed()); + /** For temporary use: LocUtil.clone before passing deeply, call setWorld(null) after use. */ + private final Location useLoc = new Location(null, 0, 0, 0); + public BlockPlaceListener(){ super(CheckType.BLOCKPLACE); } @@ -122,7 +125,7 @@ public class BlockPlaceListener extends CheckListener { } // No swing check (player doesn't swing their arm when placing a lily pad). - if (!cancelled && placedMat != Material.WATER_LILY && noSwing.isEnabled(player) && noSwing.check(player, data, cc)) { + if (!cancelled && !cc.noSwingExceptions.contains(placedMat) && noSwing.isEnabled(player) && noSwing.check(player, data, cc)) { // Consider skipping all insta placables or using simplified version (true or true within time frame). cancelled = true; } @@ -146,6 +149,8 @@ public class BlockPlaceListener extends CheckListener { if (cancelled) { event.setCancelled(cancelled); } + // Cleanup + // Reminder(currently unused): useLoc.setWorld(null); } @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) @@ -264,7 +269,7 @@ public class BlockPlaceListener extends CheckListener { boolean cancel = false; if (speed.isEnabled(player)){ final long now = System.currentTimeMillis(); - final Location loc = player.getLocation(); + final Location loc = player.getLocation(useLoc); if (Combined.checkYawRate(player, loc.getYaw(), now, loc.getWorld().getName())){ // Yawrate (checked extra). cancel = true; @@ -285,21 +290,22 @@ public class BlockPlaceListener extends CheckListener { // Do nothing ! // TODO: Might have further flags? } - else if (!BlockProperties.isPassable(projectile.getLocation())){ + else if (!BlockProperties.isPassable(projectile.getLocation(useLoc))){ // Launch into a block. // TODO: This might be a general check later. cancel = true; } else{ - if (!BlockProperties.isPassable(player.getEyeLocation(), projectile.getLocation())){ + if (!BlockProperties.isPassable(player.getEyeLocation(), projectile.getLocation(useLoc))){ + // (Spare a useLoc2, for this is seldom rather.) // Something between player // TODO: This might be a general check later. cancel = true; } else{ - final Material mat = player.getLocation().getBlock().getType(); + final Material mat = player.getLocation(useLoc).getBlock().getType(); final long flags = BlockProperties.F_CLIMBABLE | BlockProperties.F_LIQUID | BlockProperties.F_IGN_PASSABLE; - if (mat != Material.AIR && (BlockProperties.getBlockFlags(mat.getId()) & flags) == 0 && !mcAccess.hasGravity(mat)){ + if (mat != null && mat != Material.AIR && (BlockProperties.getBlockFlags(mat) & flags) == 0 && !mcAccess.hasGravity(mat)){ // Still fails on piston traps etc. if (!BlockProperties.isPassable(player.getLocation(), projectile.getLocation()) && !BlockProperties.isOnGroundOrResetCond(player, player.getLocation(), MovingConfig.getConfig(player).yOnGround)){ cancel = true; @@ -313,5 +319,7 @@ public class BlockPlaceListener extends CheckListener { if (cancel){ event.setCancelled(true); } + // Cleanup. + useLoc.setWorld(null); } } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockplace/Direction.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockplace/Direction.java index ec291447..ab96fc4b 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockplace/Direction.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockplace/Direction.java @@ -13,6 +13,9 @@ import fr.neatmonster.nocheatplus.utilities.TrigUtil; * The Direction check will find out if a player tried to interact with something that's not in their field of view. */ public class Direction extends Check { + + /** For temporary use: LocUtil.clone before passing deeply, call setWorld(null) after use. */ + private final Location useLoc = new Location(null, 0, 0, 0); /** * Instantiates a new direction check. @@ -38,7 +41,7 @@ public class Direction extends Check { // How far "off" is the player with their aim. We calculate from the players eye location and view direction to // the center of the target block. If the line of sight is more too far off, "off" will be bigger than 0. - final Location loc = player.getLocation(); + final Location loc = player.getLocation(useLoc); final Vector direction = loc.getDirection(); double off = TrigUtil.directionCheck(loc, player.getEyeHeight(), direction, against, TrigUtil.DIRECTION_PRECISION); @@ -75,10 +78,11 @@ public class Direction extends Check { // Execute whatever actions are associated with this check and the violation level and find out if we should // cancel the event. cancel = executeActions(player, data.directionVL, distance, cc.directionActions); - } else + } else { // Player did likely nothing wrong, reduce violation counter to reward them. data.directionVL *= 0.9D; - + } + useLoc.setWorld(null); return cancel; } } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockplace/FastPlace.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockplace/FastPlace.java index ca73fbbd..4bf88271 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockplace/FastPlace.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockplace/FastPlace.java @@ -38,14 +38,14 @@ public class FastPlace extends Check { // Short term arrivals. final int tick = TickTask.getTick(); - if (tick < data.fastPlaceShortTermTick ){ + if (tick < data.fastPlaceShortTermTick ) { // TickTask got reset. data.fastPlaceShortTermTick = tick; data.fastPlaceShortTermCount = 1; } else if (tick - data.fastPlaceShortTermTick < cc.fastPlaceShortTermTicks){ // Account for server side lag. - if (!cc.lag || TickTask.getLag(50L * (tick - data.fastPlaceShortTermTick), true) < 1.2){ + if (!cc.lag || TickTask.getLag(50L * (tick - data.fastPlaceShortTermTick), true) < 1.2f){ // Within range, add. data.fastPlaceShortTermCount ++; } @@ -62,9 +62,9 @@ public class FastPlace extends Check { // Find if one of both or both are violations: final float fullViolation; - if (fullScore > cc.fastPlaceLimit){ + if (fullScore > cc.fastPlaceLimit) { // Account for server side lag. - if (cc.lag){ + if (cc.lag) { fullViolation = fullScore / TickTask.getLag(data.fastPlaceBuckets.bucketDuration() * data.fastPlaceBuckets.numberOfBuckets(), true) - cc.fastPlaceLimit; } else{ @@ -78,14 +78,16 @@ public class FastPlace extends Check { final float violation = Math.max(fullViolation, shortTermViolation); boolean cancel = false; - if (violation > 0){ - final double change = violation / 1000; + if (violation > 0f) { + final double change = (double) violation; data.fastPlaceVL += change; cancel = executeActions(player, data.fastPlaceVL, change, cc.fastPlaceActions); } - else if (data.fastPlaceVL > 0d && fullScore < cc.fastPlaceLimit * .75) + else if (data.fastPlaceVL > 0d && fullScore < cc.fastPlaceLimit * .75) { data.fastPlaceVL *= 0.95; + } return cancel; } + } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockplace/Reach.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockplace/Reach.java index f2433a7c..80d7b593 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockplace/Reach.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockplace/Reach.java @@ -3,6 +3,7 @@ package fr.neatmonster.nocheatplus.checks.blockplace; import java.util.Map; import org.bukkit.GameMode; +import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.entity.Player; @@ -22,6 +23,9 @@ public class Reach extends Check { /** The maximum distance allowed to interact with a block in survival mode. */ public static final double SURVIVAL_DISTANCE = 5.2D; + + /** For temporary use: LocUtil.clone before passing deeply, call setWorld(null) after use. */ + private final Location useLoc = new Location(null, 0, 0, 0); /** * Instantiates a new reach check. @@ -35,6 +39,7 @@ public class Reach extends Check { * * @param player * the player + * @param loc * @param cc * @param data2 * @param location @@ -49,7 +54,9 @@ public class Reach extends Check { // Distance is calculated from eye location to center of targeted block. If the player is further away from their // target than allowed, the difference will be assigned to "distance". - final double distance = TrigUtil.distance(player.getEyeLocation(), block) - distanceLimit; + final Location eyeLoc = player.getLocation(useLoc); + eyeLoc.setY(eyeLoc.getY() + player.getEyeHeight()); + final double distance = TrigUtil.distance(eyeLoc, block) - distanceLimit; if (distance > 0) { // They failed, increment violation level. @@ -66,7 +73,9 @@ public class Reach extends Check { data.reachVL *= 0.9D; } - + // Cleanup. + useLoc.setWorld(null); + return cancel; } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/ChatConfig.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/ChatConfig.java index 38a8c6ff..04062b00 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/ChatConfig.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/ChatConfig.java @@ -15,6 +15,7 @@ import fr.neatmonster.nocheatplus.config.ConfPaths; import fr.neatmonster.nocheatplus.config.ConfigFile; import fr.neatmonster.nocheatplus.config.ConfigManager; import fr.neatmonster.nocheatplus.permissions.Permissions; +import fr.neatmonster.nocheatplus.utilities.ColorUtil; /** * Configurations specific for the "chat" checks. Every world gets one of these assigned to it, or if a world doesn't @@ -104,7 +105,8 @@ public class ChatConfig extends AsyncCheckConfig { public final float textMessageNoLetter; public final float textGlobalWeight; public final float textPlayerWeight; - public boolean textEngineMaximum; + public final boolean textEngineMaximum; + public final boolean textAllowVLReset; public final boolean textDebug; public final boolean chatWarningCheck; @@ -120,6 +122,7 @@ public class ChatConfig extends AsyncCheckConfig { public final long loginsStartupDelay; public final boolean consoleOnlyCheck; + public final String consoleOnlyMessage; public final boolean relogCheck; @@ -192,6 +195,7 @@ public class ChatConfig extends AsyncCheckConfig { textEngineMaximum = config.getBoolean(ConfPaths.CHAT_TEXT_ENGINE_MAXIMUM, true); textDebug = config.getBoolean(ConfPaths.CHAT_TEXT_DEBUG, false); textFreqNormActions = config.getOptimizedActionList(ConfPaths.CHAT_TEXT_FREQ_NORM_ACTIONS, Permissions.CHAT_TEXT); + textAllowVLReset = config.getBoolean(ConfPaths.CHAT_TEXT_ALLOWVLRESET); chatWarningCheck = config.getBoolean(ConfPaths.CHAT_WARNING_CHECK); chatWarningLevel = (float) config.getDouble(ConfPaths.CHAT_WARNING_LEVEL); @@ -214,6 +218,7 @@ public class ChatConfig extends AsyncCheckConfig { relogActions = config.getOptimizedActionList(ConfPaths.CHAT_RELOG_ACTIONS, Permissions.CHAT_RELOG); consoleOnlyCheck = config.getBoolean(ConfPaths.PROTECT_COMMANDS_CONSOLEONLY_ACTIVE); + consoleOnlyMessage = ColorUtil.replaceColors(config.getString(ConfPaths.PROTECT_COMMANDS_CONSOLEONLY_MSG)); } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/ChatListener.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/ChatListener.java index 72977247..62f1bad8 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/ChatListener.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/ChatListener.java @@ -1,8 +1,9 @@ package fr.neatmonster.nocheatplus.checks.chat; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; -import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -159,39 +160,46 @@ public class ChatListener extends CheckListener implements INotifyReload, JoinLe // Trim is necessary because the server accepts leading spaces with commands. final String message = event.getMessage(); final String lcMessage = message.trim().toLowerCase(); + // TODO: Remove bukkit: etc. + final String[] split = lcMessage.split(" ", 2); final String alias = split[0].substring(1); final Command command = CommandUtil.getCommand(alias); - final String lcAltMessage; + + final List messageVars = new ArrayList(); // Could as well use an array and allow null on input of SimpleCharPrefixTree. + messageVars.add(lcMessage); + String checkMessage = message; // Message to run chat checks on. if (command != null){ - lcAltMessage = "/" + command.getLabel().toLowerCase() + (split.length > 1 ? (" " + split[1]) : ""); + messageVars.add("/" + command.getLabel().toLowerCase() + (split.length > 1 ? (" " + split[1]) : "")); } - else{ - lcAltMessage = lcMessage; - } - - // Prevent /op and /deop commands from being used by players. - if (cc.consoleOnlyCheck && consoleOnlyCommands.hasAnyPrefixWords(lcMessage, lcAltMessage)) { + if (alias.indexOf(":") != -1) { + final int index = message.indexOf(":") + 1; + if (index < message.length()) { + checkMessage = message.substring(index); + messageVars.add(checkMessage.toLowerCase()); + } + } + // Prevent commands from being used by players (e.g. /op /deop /reload). + if (cc.consoleOnlyCheck && consoleOnlyCommands.hasAnyPrefixWords(messageVars)) { if (command == null || command.testPermission(player)){ - player.sendMessage(ChatColor.RED + "I'm sorry, but this command can't be executed in chat. Use the console instead!"); + player.sendMessage(cc.consoleOnlyMessage); } event.setCancelled(true); return; } // Handle as chat or command. - final boolean handleAsChat = chatCommands.hasAnyPrefixWords(lcMessage, lcAltMessage); - if (handleAsChat){ + if (chatCommands.hasAnyPrefixWords(messageVars)){ // Treat as chat. // TODO: Consider requesting permission updates on these, for consistency. - // TODO: Cut off the command (?). - if (textChecks(player, message, true, false)) { + // TODO: Cut off the command ?. + if (textChecks(player, checkMessage, true, false)) { event.setCancelled(true); } } - else if (!commandExclusions.hasAnyPrefixWords(lcMessage, lcAltMessage)){ + else if (!commandExclusions.hasAnyPrefixWords(messageVars)){ // Treat as command. - if (commands.isEnabled(player) && commands.check(player, message, captcha)) { + if (commands.isEnabled(player) && commands.check(player, checkMessage, captcha)) { event.setCancelled(true); } } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/Text.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/Text.java index 37ffd1e5..798c7a66 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/Text.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/Text.java @@ -70,7 +70,7 @@ public class Text extends AsyncCheck implements INotifyReload{ // Set some things from the global config. final ConfigFile config = ConfigManager.getConfigFile(); final NoCheatPlusAPI api = NCPAPIProvider.getNoCheatPlusAPI(); - if (engine != null){ + if (engine != null) { engine.clear(); api.removeComponent(engine); } @@ -80,7 +80,7 @@ public class Text extends AsyncCheck implements INotifyReload{ @Override public void onReload() { - synchronized(engine){ + synchronized(engine) { engine.clear(); } init(); @@ -120,7 +120,7 @@ public class Text extends AsyncCheck implements INotifyReload{ boolean debug = cc.textDebug || cc.debug; final List debugParts; - if (debug){ + if (debug) { debugParts = new LinkedList(); debugParts.add("[NoCheatPlus][chat.text] Message ("+player.getName()+"/"+message.length()+"): "); } @@ -143,13 +143,13 @@ public class Text extends AsyncCheck implements INotifyReload{ // Full message processing. ------------ // Upper case. - if (letterCounts.fullCount.upperCase > msgLen / 3){ + if (letterCounts.fullCount.upperCase > msgLen / 3) { final float wUpperCase = 0.6f * letterCounts.fullCount.getUpperCaseRatio(); score += wUpperCase * cc.textMessageUpperCase; } // Letters vs. word length. - if (msgLen > 4){ + if (msgLen > 4) { final float fullRep = letterCounts.fullCount.getLetterCountRatio(); // Long messages: very small and very big are bad ! final float wRepetition = (float) msgLen / 15.0f * Math.abs(0.5f - fullRep); @@ -157,7 +157,7 @@ public class Text extends AsyncCheck implements INotifyReload{ // Number of words vs. length of message final float fnWords = (float) letterCounts.words.length / (float) msgLen; - if (fnWords > 0.75f){ // TODO: balance or configure or remove ? + if (fnWords > 0.75f) { // TODO: balance or configure or remove ? score += fnWords * cc.textMessagePartition; } } @@ -165,40 +165,40 @@ public class Text extends AsyncCheck implements INotifyReload{ final CombinedData cData = CombinedData.getData(player); final long timeout = 8000; // TODO: maybe set dynamically in data. // Repetition of last message. - if (cc.textMsgRepeatSelf != 0f && time - data.chatLastTime < timeout){ - if (StringUtil.isSimilar(lcMessage, data.chatLastMessage, 0.8f)){ + if (cc.textMsgRepeatSelf != 0f && time - data.chatLastTime < timeout) { + if (StringUtil.isSimilar(lcMessage, data.chatLastMessage, 0.8f)) { final float timeWeight = (float) (timeout - (time - data.chatLastTime)) / (float) timeout; score += cc.textMsgRepeatSelf * timeWeight; } } // Repetition of last global message. - if (cc.textMsgRepeatGlobal != 0f && time - lastGlobalTime < timeout){ - if (StringUtil.isSimilar(lcMessage, lastGlobalMessage, 0.8f)){ + if (cc.textMsgRepeatGlobal != 0f && time - lastGlobalTime < timeout) { + if (StringUtil.isSimilar(lcMessage, lastGlobalMessage, 0.8f)) { final float timeWeight = (float) (timeout - (time - lastGlobalTime)) / (float) timeout; score += cc.textMsgRepeatGlobal * timeWeight; } } // Repetition of last cancelled message. - if (cc.textMsgRepeatCancel != 0f && time - lastCancelledTime < timeout){ - if (StringUtil.isSimilar(lcMessage, lastCancelledMessage, 0.8f)){ + if (cc.textMsgRepeatCancel != 0f && time - lastCancelledTime < timeout) { + if (StringUtil.isSimilar(lcMessage, lastCancelledMessage, 0.8f)) { final float timeWeight = (float) (timeout - (time - lastCancelledTime)) / (float) timeout; score += cc.textMsgRepeatCancel * timeWeight; } } // Chat quickly after join. - if (cc.textMsgAfterJoin != 0f && time - cData.lastJoinTime < timeout){ + if (cc.textMsgAfterJoin != 0f && time - cData.lastJoinTime < timeout) { final float timeWeight = (float) (timeout - (time - cData.lastJoinTime)) / (float) timeout; score += cc.textMsgAfterJoin * timeWeight; } // Chat without moving. - if (cc.textMsgNoMoving != 0f && time - cData.lastMoveTime > timeout){ + if (cc.textMsgNoMoving != 0f && time - cData.lastMoveTime > timeout) { score += cc.textMsgNoMoving; } // Per word checks. ------------------- float wWords = 0.0f; final float avwLen = (float) msgLen / (float) letterCounts.words.length; - for (final WordLetterCount word: letterCounts.words){ + for (final WordLetterCount word: letterCounts.words) { float wWord = 0.0f; final int wLen = word.word.length(); // TODO: ? used letters vs. word length. @@ -232,7 +232,7 @@ public class Text extends AsyncCheck implements INotifyReload{ engMap = engine.process(letterCounts, player.getName(), cc, data); // TODO: more fine grained sync !s // TODO: different methods (add or max or add+max or something else). - for (final Float res : engMap.values()){ + for (final Float res : engMap.values()) { if (cc.textEngineMaximum) wEngine = Math.max(wEngine, res.floatValue()); else wEngine += res.floatValue(); } @@ -252,40 +252,46 @@ public class Text extends AsyncCheck implements INotifyReload{ final float shortTermAccumulated = cc.textFreqShortTermWeight * data.chatShortTermFrequency.score(cc.textFreqShortTermFactor); final boolean shortTermViolation = shortTermAccumulated > cc.textFreqShortTermLevel; - if (normalViolation || shortTermViolation){ + if (normalViolation || shortTermViolation) { lastCancelledMessage = lcMessage; lastCancelledTime = time; final double added; - if (shortTermViolation) added = (shortTermAccumulated - cc.textFreqShortTermLevel)/ 3.0; - else added = (accumulated - cc.textFreqNormLevel) / 10.0; - data.textVL += added; + if (shortTermViolation) { + added = (shortTermAccumulated - cc.textFreqShortTermLevel)/ 3.0; + } else { + added = (accumulated - cc.textFreqNormLevel) / 10.0; + } + data.textVL += added; - if (captcha.shouldStartCaptcha(cc, data)){ + if (captcha.shouldStartCaptcha(cc, data)) { captcha.sendNewCaptcha(player, cc, data); cancel = true; } else{ - if (shortTermViolation){ - if (executeActions(player, data.textVL, added, cc.textFreqShortTermActions, isMainThread)) - cancel = true; + if (shortTermViolation) { + if (executeActions(player, data.textVL, added, cc.textFreqShortTermActions, isMainThread)) { + cancel = true; + } } - else if (normalViolation){ - if (executeActions(player, data.textVL, added, cc.textFreqNormActions, isMainThread)) - cancel = true; + else if (normalViolation) { + if (executeActions(player, data.textVL, added, cc.textFreqNormActions, isMainThread)) { + cancel = true; + } } } } - else if (cc.chatWarningCheck && time - data.chatWarningTime > cc.chatWarningTimeout && (100f * accumulated / cc.textFreqNormLevel > cc.chatWarningLevel || 100f * shortTermAccumulated / cc.textFreqShortTermLevel > cc.chatWarningLevel)){ + else if (cc.chatWarningCheck && time - data.chatWarningTime > cc.chatWarningTimeout && (100f * accumulated / cc.textFreqNormLevel > cc.chatWarningLevel || 100f * shortTermAccumulated / cc.textFreqShortTermLevel > cc.chatWarningLevel)) { NCPAPIProvider.getNoCheatPlusAPI().sendMessageOnTick(player.getName(), ColorUtil.replaceColors(cc.chatWarningMessage)); data.chatWarningTime = time; } else { data.textVL *= 0.95; - if (normalScore < 2.0f * cc.textFreqNormWeight && shortTermScore < 2.0f * cc.textFreqShortTermWeight) - // Reset the VL. + if (cc.textAllowVLReset && normalScore < 2.0f * cc.textFreqNormWeight && shortTermScore < 2.0f * cc.textFreqShortTermWeight) { + // Reset the VL. // TODO: maybe elaborate on resetting conditions (after some timeout just divide by two or so?). data.textVL = 0.0; + } } if (debug) { diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Angle.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Angle.java index 265bcbf3..d2639368 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Angle.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Angle.java @@ -32,9 +32,7 @@ public class Angle extends Check { * @param worldChanged * @return true, if successful */ - public boolean check(final Player player, final boolean worldChanged) { - final FightConfig cc = FightConfig.getConfig(player); - final FightData data = FightData.getData(player); + public boolean check(final Player player, final boolean worldChanged, final FightData data, final FightConfig cc) { if (worldChanged){ // TODO: clear some data. @@ -44,12 +42,15 @@ public class Angle extends Check { boolean cancel = false; // Remove the old locations from the map. - for (final long time : new TreeMap(data.angleHits).navigableKeySet()) - if (System.currentTimeMillis() - time > 1000L) - data.angleHits.remove(time); + for (final long time : new TreeMap(data.angleHits).navigableKeySet()) { + if (System.currentTimeMillis() - time > 1000L) { + data.angleHits.remove(time); + } + } // Add the new location to the map. - data.angleHits.put(System.currentTimeMillis(), player.getLocation()); + // TODO: Alter method to store something less fat. + data.angleHits.put(System.currentTimeMillis(), player.getLocation()); // This needs to be a copy at present. // Not enough data to calculate deltas. if (data.angleHits.size() < 2) diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Critical.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Critical.java index e10ce69d..2e5d7ca0 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Critical.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Critical.java @@ -38,14 +38,8 @@ public class Critical extends Check { * the player * @return true, if successful */ - public boolean check(final Player player) { - final FightConfig cc = FightConfig.getConfig(player); - final FightData data = FightData.getData(player); - + public boolean check(final Player player, final Location loc, final FightData data, final FightConfig cc) { boolean cancel = false; - - // We'll need the PlayerLocation to know some important stuff. - final Location loc = player.getLocation(); final float mcFallDistance = player.getFallDistance(); final MovingConfig mCc = MovingConfig.getConfig(player); diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Direction.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Direction.java index 3a7c9834..08dbd0eb 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Direction.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Direction.java @@ -7,6 +7,7 @@ import org.bukkit.util.Vector; import fr.neatmonster.nocheatplus.checks.Check; import fr.neatmonster.nocheatplus.checks.CheckType; +import fr.neatmonster.nocheatplus.checks.moving.LocationTrace.TraceEntry; import fr.neatmonster.nocheatplus.utilities.TrigUtil; /** @@ -22,7 +23,7 @@ public class Direction extends Check { } /** - * Checks a player. + * "Classic" check. * * @param player * the player @@ -30,16 +31,14 @@ public class Direction extends Check { * the damaged * @return true, if successful */ - public boolean check(final Player player, final Entity damaged) { - final FightConfig cc = FightConfig.getConfig(player); - final FightData data = FightData.getData(player); - - boolean cancel = false; + public boolean check(final Player player, final Location loc, final Entity damaged, final Location dLoc, final FightData data, final FightConfig cc) { + boolean cancel = false; // Safeguard, if entity is complex, this check will fail due to giant and hard to define hitboxes. // if (damaged instanceof EntityComplex || damaged instanceof EntityComplexPart) - if (mcAccess.isComplexPart(damaged)) - return false; + if (mcAccess.isComplexPart(damaged)) { + return false; + } // Find out how wide the entity is. final double width = mcAccess.getWidth(damaged); @@ -47,14 +46,11 @@ public class Direction extends Check { // entity.height is broken and will always be 0, therefore. Calculate height instead based on boundingBox. final double height = mcAccess.getHeight(damaged); - final Location dLoc = damaged.getLocation(); - // TODO: allow any hit on the y axis (might just adapt interface to use foot position + height)! // How far "off" is the player with their aim. We calculate from the players eye location and view direction to // the center of the target entity. If the line of sight is more too far off, "off" will be bigger than 0. - final Location loc = player.getLocation(); - final Vector direction = player.getEyeLocation().getDirection(); + final Vector direction = loc.getDirection(); final double off; if (cc.directionStrict){ @@ -81,10 +77,117 @@ public class Direction extends Check { // Deal an attack penalty time. data.attackPenalty.applyPenalty(cc.directionPenalty); } - } else - // Reward the player by lowering their violation level. + } else { + // Reward the player by lowering their violation level. data.directionVL *= 0.8D; - + } + return cancel; } + + /** + * Data context for iterating over TraceEntry instances. + * @param player + * @param loc + * @param damaged + * @param damagedLoc + * @param data + * @param cc + * @return + */ + public DirectionContext getContext(final Player player, final Location loc, final Entity damaged, final Location damagedLoc, final FightData data, final FightConfig cc) { + final DirectionContext context = new DirectionContext(); + context.damagedComplex = mcAccess.isComplexPart(damaged); + // Find out how wide the entity is. + context.damagedWidth = mcAccess.getWidth(damaged); + // entity.height is broken and will always be 0, therefore. Calculate height instead based on boundingBox. + context.damagedHeight = mcAccess.getHeight(damaged); + context.direction = loc.getDirection(); + context.lengthDirection = context.direction.length(); + return context; + } + + /** + * Check if the player fails the direction check, no change of FightData. + * @param player + * @param loc + * @param damaged + * @param dLoc + * @param context + * @param data + * @param cc + * @return + */ + public boolean loopCheck(final Player player, final Location loc, final Entity damaged, final TraceEntry dLoc, final DirectionContext context, final FightData data, final FightConfig cc) { + + // Ignore complex entities for the moment. + if (context.damagedComplex) { + // TODO: Revise :p + return false; + } + boolean cancel = false; + + // TODO: allow any hit on the y axis (might just adapt interface to use foot position + height)! + + // How far "off" is the player with their aim. We calculate from the players eye location and view direction to + // the center of the target entity. If the line of sight is more too far off, "off" will be bigger than 0. + + final double off; + if (cc.directionStrict){ + off = TrigUtil.combinedDirectionCheck(loc, player.getEyeHeight(), context.direction, dLoc.x, dLoc.y + context.damagedHeight / 2D, dLoc.z, context.damagedWidth, context.damagedHeight, TrigUtil.DIRECTION_PRECISION, 80.0); + } + else{ + // Also take into account the angle. + off = TrigUtil.directionCheck(loc, player.getEyeHeight(), context.direction, dLoc.x, dLoc.y + context.damagedHeight / 2D, dLoc.z, context.damagedWidth, context.damagedHeight, TrigUtil.DIRECTION_PRECISION); + } + + if (off > 0.1) { + // Player failed the check. Let's try to guess how far they were from looking directly to the entity... + final Vector blockEyes = new Vector(dLoc.x - loc.getX(), dLoc.y + context.damagedHeight / 2D - loc.getY() - player.getEyeHeight(), dLoc.z - loc.getZ()); + final double distance = blockEyes.crossProduct(context.direction).length() / context.lengthDirection; + context.minViolation = Math.min(context.minViolation, distance); + } + context.minResult = Math.min(context.minResult, off); + + return cancel; + } + + /** + * Apply changes to FightData according to check results (context), trigger violations. + * @param player + * @param loc + * @param damaged + * @param context + * @param forceViolation + * @param data + * @param cc + * @return + */ + public boolean loopFinish(final Player player, final Location loc, final Entity damaged, final DirectionContext context, final boolean forceViolation, final FightData data, final FightConfig cc) { + boolean cancel = false; + final double off = forceViolation && context.minViolation != Double.MAX_VALUE ? context.minViolation : context.minResult; + if (off == Double.MAX_VALUE) { + return false; + } + else if (off > 0.1) { + // Add the overall violation level of the check. + data.directionVL += context.minViolation; + + // Execute whatever actions are associated with this check and the violation level and find out if we should + // cancel the event. + cancel = executeActions(player, data.directionVL, context.minViolation, cc.directionActions); + + if (cancel) { + // Deal an attack penalty time. + data.attackPenalty.applyPenalty(cc.directionPenalty); + } + } + else { + // Reward the player by lowering their violation level. + data.directionVL *= 0.8D; + } + + return cancel; + } + } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/DirectionContext.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/DirectionContext.java new file mode 100644 index 00000000..f8c139ef --- /dev/null +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/DirectionContext.java @@ -0,0 +1,23 @@ +package fr.neatmonster.nocheatplus.checks.fight; + +import org.bukkit.util.Vector; + +/** + * Context data for the direction check, for repeated use within a loop. + * @author mc_dev + * + */ +public class DirectionContext { + + public boolean damagedComplex; + public double damagedWidth; + public double damagedHeight; + public Vector direction = null; + public double lengthDirection; + + /** Minimum value for the distance that was a violation. */ + public double minViolation = Double.MAX_VALUE; + /** Minimum value for off. */ + public double minResult = Double.MAX_VALUE; + +} diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/FastHeal.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/FastHeal.java index 5dcd7a6d..09e10e2f 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/FastHeal.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/FastHeal.java @@ -32,7 +32,8 @@ public class FastHeal extends Check { else{ // Violation. final double correctedDiff = ((double) time - data.fastHealRefTime) * TickTask.getLag(cc.fastHealInterval); - // TODO: Consider using a simple buffer as well (to get closer to the correct interva). + // TODO: Consider using a simple buffer as well (to get closer to the correct interval). + // TODO: Check if we added a buffer. if (correctedDiff < cc.fastHealInterval){ data.fastHealBuffer -= (cc.fastHealInterval - correctedDiff); if (data.fastHealBuffer <= 0){ diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/FightData.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/FightData.java index f91149bf..a35d9fd3 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/FightData.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/FightData.java @@ -131,7 +131,7 @@ public class FightData extends ACheckData { // Shared public String lastWorld = ""; public int lastAttackTick = 0; - public double lastAttackedX = Integer.MAX_VALUE; + public double lastAttackedX = Double.MAX_VALUE; public double lastAttackedY; public double lastAttackedZ; @@ -197,4 +197,12 @@ public class FightData extends ACheckData { // Start with full fast-heal buffer. fastHealBuffer = cc.fastHealBuffer; } + + public void onWorldChange() { + angleHits.clear(); + lastAttackedX = Double.MAX_VALUE; + lastAttackTick = 0; + lastWorld = ""; + } + } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/FightListener.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/FightListener.java index 9bf0d71b..1954ab88 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/FightListener.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/FightListener.java @@ -1,5 +1,7 @@ package fr.neatmonster.nocheatplus.checks.fight; +import java.util.Iterator; + import org.bukkit.Location; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Entity; @@ -25,6 +27,8 @@ import fr.neatmonster.nocheatplus.checks.CheckType; import fr.neatmonster.nocheatplus.checks.combined.Combined; import fr.neatmonster.nocheatplus.checks.combined.Improbable; import fr.neatmonster.nocheatplus.checks.inventory.Items; +import fr.neatmonster.nocheatplus.checks.moving.LocationTrace; +import fr.neatmonster.nocheatplus.checks.moving.LocationTrace.TraceEntry; import fr.neatmonster.nocheatplus.checks.moving.MediumLiftOff; import fr.neatmonster.nocheatplus.checks.moving.MovingConfig; import fr.neatmonster.nocheatplus.checks.moving.MovingData; @@ -74,6 +78,12 @@ public class FightListener extends CheckListener implements JoinLeaveListener{ /** The speed check. */ private final Speed speed = addCheck(new Speed()); + /** For temporary use: LocUtil.clone before passing deeply, call setWorld(null) after use. */ + private final Location useLoc1 = new Location(null, 0, 0, 0); + + /** For temporary use: LocUtil.clone before passing deeply, call setWorld(null) after use. */ + private final Location useLoc2 = new Location(null, 0, 0, 0); + public FightListener(){ super(CheckType.FIGHT); } @@ -100,15 +110,21 @@ public class FightListener extends CheckListener implements JoinLeaveListener{ final long now = System.currentTimeMillis(); final boolean worldChanged = !worldName.equals(data.lastWorld); - final Location loc = player.getLocation(); - final Location targetLoc = damaged.getLocation(); + final Location loc = player.getLocation(useLoc1); +// // Bad pitch/yaw, just in case. +// if (LocUtil.needsDirectionCorrection(useLoc1.getYaw(), useLoc1.getPitch())) { +// mcAccess.correctDirection(player); +// player.getLocation(useLoc1); +// } + final Location damagedLoc = damaged.getLocation(useLoc2); // final double targetDist = CheckUtils.distance(loc, targetLoc); // TODO: Calculate distance as is done in fight.reach ! final double targetMove; final int tickAge; final long msAge; // Milliseconds the ticks actually took. final double normalizedMove; // Blocks per second. // TODO: relative distance (player - target)! - if (data.lastAttackedX == Integer.MAX_VALUE || tick < data.lastAttackTick || worldChanged || tick - data.lastAttackTick > 20){ + // TODO: Use trace for this ? + if (data.lastAttackedX == Double.MAX_VALUE || tick < data.lastAttackTick || worldChanged || tick - data.lastAttackTick > 20){ // TODO: 20 ? tickAge = 0; targetMove = 0.0; @@ -118,7 +134,7 @@ public class FightListener extends CheckListener implements JoinLeaveListener{ else{ tickAge = tick - data.lastAttackTick; // TODO: Maybe use 3d distance if dy(normalized) is too big. - targetMove = TrigUtil.distance(data.lastAttackedX, data.lastAttackedZ, targetLoc.getX(), targetLoc.getZ()); + targetMove = TrigUtil.distance(data.lastAttackedX, data.lastAttackedZ, damagedLoc.getX(), damagedLoc.getZ()); msAge = (long) (50f * TickTask.getLag(50L * tickAge) * (float) tickAge); normalizedMove = msAge == 0 ? targetMove : targetMove * Math.min(20.0, 1000.0 / (double) msAge); } @@ -126,15 +142,34 @@ public class FightListener extends CheckListener implements JoinLeaveListener{ // TODO: dist < width => skip some checks (direction, ..) - // Check for self hit exploits (mind that projectiles should be excluded) + final LocationTrace damagedTrace; + final Player damagedPlayer; if (damaged instanceof Player){ - final Player damagedPlayer = (Player) damaged; + damagedPlayer = (Player) damaged; +// // Bad pitch/yaw, just in case. +// if (LocUtil.needsDirectionCorrection(useLoc2.getYaw(), useLoc2.getPitch())) { +// mcAccess.correctDirection(damagedPlayer); +// damagedPlayer.getLocation(useLoc2); +// } + // Log. if (cc.debug && damagedPlayer.hasPermission(Permissions.ADMINISTRATION_DEBUG)){ damagedPlayer.sendMessage("Attacked by " + player.getName() + ": inv=" + mcAccess.getInvulnerableTicks(damagedPlayer) + " ndt=" + damagedPlayer.getNoDamageTicks()); } + // Check for self hit exploits (mind that projectiles are excluded from this.) if (selfHit.isEnabled(player) && selfHit.check(player, damagedPlayer, data, cc)) { cancelled = true; } + // Get+update the damaged players. + // TODO: Problem with NPCs: data stays (not a big problem). + // (This is done even if the event has already been cancelled, to keep track, if the player is on a horse.) + damagedTrace = MovingData.getData(damagedPlayer).updateTrace(damagedPlayer, damagedLoc, tick); + } else { + damagedPlayer = null; // TODO: This is a temporary workaround. + // Use a fake trace. + // TODO: Provide for entities too? E.g. one per player, or a fully fledged bookkeeping thing (EntityData). + //final MovingConfig mcc = MovingConfig.getConfig(damagedLoc.getWorld().getName()); + damagedTrace = null; //new LocationTrace(mcc.traceSize, mcc.traceMergeDist); + //damagedTrace.addEntry(tick, damagedLoc.getX(), damagedLoc.getY(), damagedLoc.getZ()); } if (cc.cancelDead){ @@ -176,51 +211,131 @@ public class FightListener extends CheckListener implements JoinLeaveListener{ } } - if (angle.isEnabled(player)) { + if (!cancelled && critical.isEnabled(player) && critical.check(player, loc, data, cc)) { + cancelled = true; + } + + if (!cancelled && knockback.isEnabled(player) && knockback.check(player, data, cc)) { + cancelled = true; + } + + if (!cancelled && noSwing.isEnabled(player) && noSwing.check(player, data, cc)) { + cancelled = true; + } + + if (!cancelled && player.isBlocking() && !player.hasPermission(Permissions.MOVING_SURVIVALFLY_BLOCKING)) { + cancelled = true; + } + + // TODO: Order of all these checks ... + // Checks that use LocationTrace. + + // TODO: Later optimize (...), should reverse check window ? + + // First loop through reach and direction, to determine a window. + final boolean reachEnabled = !cancelled && reach.isEnabled(player); + final boolean directionEnabled = !cancelled && direction.isEnabled(player); + + if (reachEnabled || directionEnabled) { + if (damagedPlayer != null) { + // TODO: Move to a method (trigonometric checks). + final ReachContext reachContext = reachEnabled ? reach.getContext(player, loc, damaged, damagedLoc, data, cc) : null; + final DirectionContext directionContext = directionEnabled ? direction.getContext(player, loc, damaged, damagedLoc, data, cc) : null; + + final long traceOldest = tick; // - damagedTrace.getMaxSize(); // TODO: Set by window. + // TODO: Iterating direction: could also start from latest, be it on occasion. + Iterator traceIt = damagedTrace.maxAgeIterator(traceOldest); + + boolean violation = true; // No tick with all checks passed. + boolean reachPassed = !reachEnabled; // Passed individually for some tick. + boolean directionPassed = !directionEnabled; // Passed individually for some tick. + // TODO: Maintain a latency estimate + max diff and invalidate completely (i.e. iterate from latest NEXT time)], or just max latency. + while (traceIt.hasNext()) { + final TraceEntry entry = traceIt.next(); + // Simplistic just check both until end or hit. + // TODO: Other default distances/tolerances. + boolean thisPassed = true; + if (reachEnabled) { + if (reach.loopCheck(player, loc, damagedPlayer, entry, reachContext, data, cc)) { + thisPassed = false; + } else { + reachPassed = true; + } + } + // TODO: For efficiency one could omit checking at all if reach is failed all the time. + if (directionEnabled && (reachPassed || !directionPassed)) { + if (direction.loopCheck(player, damagedLoc, damagedPlayer, entry, directionContext, data, cc)) { + thisPassed = false; + } else { + directionPassed = true; + } + } + if (thisPassed) { + // TODO: Log/set estimated latency. + violation = false; + break; + } + } + // TODO: How to treat mixed state: violation && reachPassed && directionPassed [current: use min violation // thinkable: silent cancel, if actions have cancel (!)] + // TODO: Adapt according to strictness settings? + if (reachEnabled) { + // TODO: Might ignore if already cancelled by mixed/silent cancel. + if (reach.loopFinish(player, loc, damagedPlayer, reachContext, violation, data, cc)) { + cancelled = true; + } + } + if (directionEnabled) { + // TODO: Might ignore if already cancelled. + if (direction.loopFinish(player, loc, damagedPlayer, directionContext, violation, data, cc)) { + cancelled = true; + } + } + // TODO: Log exact state, probably record min/max latency (individually). + } else { + // Still use the classic methods for non-players. maybe[] + if (reachEnabled && reach.check(player, loc, damaged, damagedLoc, data, cc)) { + cancelled = true; + } + + if (directionEnabled && direction.check(player, loc, damaged, damagedLoc, data, cc)) { + cancelled = true; + } + } + } + + // Check angle with allowed window. + if (angle.isEnabled(player)) { + // TODO: Revise, use own trace. // The "fast turning" checks are checked in any case because they accumulate data. - // Improbable yaw changing. + // Improbable yaw changing: Moving events might be missing up to a ten degrees change. if (Combined.checkYawRate(player, loc.getYaw(), now, worldName, cc.yawRateCheck)) { // (Check or just feed). // TODO: Work into this somehow attacking the same aim and/or similar aim position (not cancel then). cancelled = true; } // Angle check. - if (angle.check(player, worldChanged)) cancelled = true; + if (angle.check(player, worldChanged, data, cc)) { + if (!cancelled && cc.debug) { + System.out.println(player.getName() + " fight.angle cancel without yawrate cancel."); + } + cancelled = true; + } } - - if (!cancelled && critical.isEnabled(player) && critical.check(player)) - cancelled = true; - - if (!cancelled && knockback.isEnabled(player) && knockback.check(player)) - cancelled = true; - - if (!cancelled && noSwing.isEnabled(player) && noSwing.check(player)) - cancelled = true; - - if (!cancelled && player.isBlocking() && !player.hasPermission(Permissions.MOVING_SURVIVALFLY_BLOCKING)) - cancelled = true; - - // TODO: Order of the last two [might put first] ? - - if (!cancelled && reach.isEnabled(player) && reach.check(player, damaged)) - cancelled = true; - - if (!cancelled && direction.isEnabled(player) && direction.check(player, damaged)) - cancelled = true; // Set values. data.lastWorld = worldName; data.lastAttackTick = tick; - data.lastAttackedX = targetLoc.getX(); - data.lastAttackedY = targetLoc.getY(); - data.lastAttackedZ = targetLoc.getZ(); + data.lastAttackedX = damagedLoc.getX(); + data.lastAttackedY = damagedLoc.getY(); + data.lastAttackedZ = damagedLoc.getZ(); // data.lastAttackedDist = targetDist; // Care for the "lost sprint problem": sprint resets, client moves as if still... // TODO: Use stored distance calculation same as reach check? // TODO: For pvp: make use of "player was there" heuristic later on. // TODO: Confine further with simple pre-conditions. - if (!cancelled && TrigUtil.distance(loc.getX(), loc.getZ(), targetLoc.getX(), targetLoc.getZ()) < 4.5){ + // TODO: Evaluate if moving traces can help here. + if (!cancelled && TrigUtil.distance(loc.getX(), loc.getZ(), damagedLoc.getX(), damagedLoc.getZ()) < 4.5){ final MovingData mData = MovingData.getData(player); // Check if fly checks is an issue at all, re-check "real sprinting". if (mData.fromX != Double.MAX_VALUE && mData.mediumLiftOff != MediumLiftOff.LIMIT_JUMP){ @@ -233,7 +348,7 @@ public class FightListener extends CheckListener implements JoinLeaveListener{ // TODO: What would mData.lostSprintCount > 0 mean here? mData.lostSprintCount = 7; if ((cc.debug || mc.debug) && BuildParameters.debugLevel > 0){ - System.out.println(player.getName() + " (lostsprint) hDist to last from: " + hDist + " | targetdist=" + TrigUtil.distance(loc.getX(), loc.getZ(), targetLoc.getX(), targetLoc.getZ()) + " | sprinting=" + player.isSprinting() + " | food=" + player.getFoodLevel() +" | hbuf=" + mData.sfHorizontalBuffer); + System.out.println(player.getName() + " (lostsprint) hDist to last from: " + hDist + " | targetdist=" + TrigUtil.distance(loc.getX(), loc.getZ(), damagedLoc.getX(), damagedLoc.getZ()) + " | sprinting=" + player.isSprinting() + " | food=" + player.getFoodLevel() +" | hbuf=" + mData.sfHorizontalBuffer); } } } @@ -248,7 +363,11 @@ public class FightListener extends CheckListener implements JoinLeaveListener{ System.out.println(player.getName() + " ~ attack penalty."); } } - + + // Cleanup. + useLoc1.setWorld(null); + useLoc2.setWorld(null); + return cancelled; } @@ -374,7 +493,9 @@ public class FightListener extends CheckListener implements JoinLeaveListener{ final Entity entity = event.getEntity(); if (entity instanceof Player){ final Player player = (Player) entity; - if (godMode.isEnabled(player)) godMode.death(player); + if (godMode.isEnabled(player)) { + godMode.death(player); + } } } @@ -408,6 +529,11 @@ public class FightListener extends CheckListener implements JoinLeaveListener{ final Entity entity = event.getEntity(); if (!(entity instanceof Player)) return; final Player player = (Player) entity; + if (player.isDead() && BridgeHealth.getHealth(player) <= 0.0) { + // Heal after death. + event.setCancelled(true); + return; + } if (event.getRegainReason() != RegainReason.SATIATED) { return; } @@ -444,8 +570,7 @@ public class FightListener extends CheckListener implements JoinLeaveListener{ @EventHandler(priority = EventPriority.MONITOR) public void onPlayerChangedWorld(final PlayerChangedWorldEvent event){ - final FightData data = FightData.getData(event.getPlayer()); - data.angleHits.clear(); + FightData.getData(event.getPlayer()).onWorldChange(); } @EventHandler(ignoreCancelled = false, priority = EventPriority.MONITOR) diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/GodMode.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/GodMode.java index 70e3e06c..5c2428df 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/GodMode.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/GodMode.java @@ -173,7 +173,7 @@ public class GodMode extends Check { public void death(final Player player) { // TODO: Is this still relevant ? // First check if the player is really dead (e.g. another plugin could have just fired an artificial event). - if (BridgeHealth.getHealth(player) <= 0.0 && player.isDead()){ + if (BridgeHealth.getHealth(player) <= 0.0 && player.isDead()) { try { // Schedule a task to be executed in roughly 1.5 seconds. // TODO: Get plugin otherwise !? diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Knockback.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Knockback.java index 30c7aa2a..c9405b1d 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Knockback.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Knockback.java @@ -26,10 +26,7 @@ public class Knockback extends Check { * the player * @return true, if successful */ - public boolean check(final Player player) { - final FightConfig cc = FightConfig.getConfig(player); - final FightData data = FightData.getData(player); - + public boolean check(final Player player, final FightData data, final FightConfig cc) { boolean cancel = false; final long time = System.currentTimeMillis(); diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/NoSwing.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/NoSwing.java index ba3f4e39..c54c8777 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/NoSwing.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/NoSwing.java @@ -24,9 +24,7 @@ public class NoSwing extends Check { * the player * @return true, if successful */ - public boolean check(final Player player) { - final FightData data = FightData.getData(player); - + public boolean check(final Player player, final FightData data, final FightConfig cc) { boolean cancel = false; // Did they swing his arm before? @@ -40,9 +38,10 @@ public class NoSwing extends Check { // Execute whatever actions are associated with this check and the violation level and find out if we should // cancel the event. - cancel = executeActions(player, data.noSwingVL, 1D, FightConfig.getConfig(player).noSwingActions); + cancel = executeActions(player, data.noSwingVL, 1D, cc.noSwingActions); } return cancel; } + } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Reach.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Reach.java index 0294e45c..1e277840 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Reach.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Reach.java @@ -12,9 +12,11 @@ import org.bukkit.util.Vector; import fr.neatmonster.nocheatplus.checks.Check; import fr.neatmonster.nocheatplus.checks.CheckType; import fr.neatmonster.nocheatplus.checks.combined.Improbable; +import fr.neatmonster.nocheatplus.checks.moving.LocationTrace.TraceEntry; import fr.neatmonster.nocheatplus.permissions.Permissions; import fr.neatmonster.nocheatplus.utilities.StringUtil; import fr.neatmonster.nocheatplus.utilities.TickTask; +import fr.neatmonster.nocheatplus.utilities.TrigUtil; /** * The Reach check will find out if a player interacts with something that's too far away. @@ -43,7 +45,7 @@ public class Reach extends Check { } /** - * Checks a player. + * "Classic" check. * * @param player * the player @@ -51,10 +53,7 @@ public class Reach extends Check { * the damaged * @return true, if successful */ - public boolean check(final Player player, final Entity damaged) { - final FightConfig cc = FightConfig.getConfig(player); - final FightData data = FightData.getData(player); - + public boolean check(final Player player, final Location pLoc, final Entity damaged, final Location dRef, final FightData data, final FightConfig cc) { boolean cancel = false; // The maximum distance allowed to interact with an entity in survival mode. @@ -67,20 +66,17 @@ public class Reach extends Check { final double distanceLimit = player.getGameMode() == GameMode.CREATIVE ? CREATIVE_DISTANCE : SURVIVAL_DISTANCE + getDistMod(damaged); final double distanceMin = (distanceLimit - DYNAMIC_RANGE) / distanceLimit; - // Reference locations to check distance for. - final Location dRef = damaged.getLocation(); final double height = mcAccess.getHeight(damaged); - final Location pRef = player.getEyeLocation(); // Refine y position. // TODO: Make a little more accurate by counting in the actual bounding box. - final double pY = pRef.getY(); + final double pY = pLoc.getY() + player.getEyeHeight(); final double dY = dRef.getY(); if (pY <= dY); // Keep the foot level y. else if (pY >= dY + height) dRef.setY(dY + height); // Highest ref y. else dRef.setY(pY); // Level with damaged. - final Vector pRel = dRef.toVector().subtract(pRef.toVector()); + final Vector pRel = dRef.toVector().subtract(pLoc.toVector().setY(pY)); // TODO: Run calculations on numbers only :p. // Distance is calculated from eye location to center of targeted. If the player is further away from their target // than allowed, the difference will be assigned to "distance". @@ -138,4 +134,139 @@ public class Reach extends Check { return cancel; } + + /** + * Data context for iterating over TraceEntry instances. + * @param player + * @param pLoc + * @param damaged + * @param damagedLoc + * @param data + * @param cc + * @return + */ + public ReachContext getContext(final Player player, final Location pLoc, final Entity damaged, final Location damagedLoc, final FightData data, final FightConfig cc) { + final ReachContext context = new ReachContext(); + context.distanceLimit = player.getGameMode() == GameMode.CREATIVE ? CREATIVE_DISTANCE : cc.reachSurvivalDistance + getDistMod(damaged); + context.distanceMin = (context.distanceLimit - cc.reachReduceDistance) / context.distanceLimit; + context.damagedHeight = mcAccess.getHeight(damaged); + //context.eyeHeight = player.getEyeHeight(); + context.pY = pLoc.getY() + player.getEyeHeight(); + return context; + } + + /** + * Check if the player fails the reach check, no change of FightData. + * @param player + * @param pLoc + * @param damaged + * @param dRef + * @param context + * @param data + * @param cc + * @return + */ + public boolean loopCheck(final Player player, final Location pLoc, final Entity damaged, final TraceEntry dRef, final ReachContext context, final FightData data, final FightConfig cc) { + boolean cancel = false; + + // Refine y position. + final double dY = dRef.y; + double y = dRef.y; + + if (context.pY <= dY) { + // Keep the foot level y. + } + else if (context.pY >= dY + context.damagedHeight) { + y = dY + context.damagedHeight; // Highest ref y. + } + else { + y = context.pY; // Level with damaged. + } + + // Distance is calculated from eye location to center of targeted. If the player is further away from their target + // than allowed, the difference will be assigned to "distance". + // TODO: Run check on squared distances (quite easy to change to stored boundary-sq values). + final double lenpRel = TrigUtil.distance(dRef.x, y, dRef.z, pLoc.getX(), context.pY, pLoc.getZ()); + + double violation = lenpRel - context.distanceLimit; + + if (violation > 0 || lenpRel - context.distanceLimit * data.reachMod > 0){ + // TODO: The silent cancel parts should be sen as "no violation" ? + // Set minimum violation in context + context.minViolation = Math.min(context.minViolation, lenpRel); + cancel = true; + } + context.minResult = Math.min(context.minResult, lenpRel); + + return cancel; + + } + + /** + * Apply changes to FightData according to check results (context), trigger violations. + * @param player + * @param pLoc + * @param damaged + * @param context + * @param forceViolation + * @param data + * @param cc + * @return + */ + public boolean loopFinish(final Player player, final Location pLoc, final Entity damaged, final ReachContext context, final boolean forceViolation, final FightData data, final FightConfig cc) { + final double lenpRel = forceViolation && context.minViolation != Double.MAX_VALUE ? context.minViolation : context.minResult; + if (lenpRel == Double.MAX_VALUE) { + return false; + } + double violation = lenpRel - context.distanceLimit; + boolean cancel = false; + if (violation > 0) { + // They failed, increment violation level. This is influenced by lag, so don't do it if there was lag. + if (TickTask.getLag(1000) < 1.5f){ + // TODO: 1.5 is a fantasy value. + data.reachVL += violation; + } + + // Execute whatever actions are associated with this check and the violation level and find out if we should + // cancel the event. + cancel = executeActions(player, data.reachVL, violation, cc.reachActions); + if (Improbable.check(player, (float) violation / 2f, System.currentTimeMillis(), "fight.reach")){ + cancel = true; + } + if (cancel && cc.reachPenalty > 0){ + // Apply an attack penalty time. + data.attackPenalty.applyPenalty(cc.reachPenalty); + } + } + else if (lenpRel - context.distanceLimit * data.reachMod > 0){ + // Silent cancel. + if (cc.reachPenalty > 0) { + data.attackPenalty.applyPenalty(cc.reachPenalty / 2); + } + cancel = true; + Improbable.feed(player, (float) (lenpRel - context.distanceLimit * data.reachMod) / 4f, System.currentTimeMillis()); + } + else{ + // Player passed the check, reward them. + data.reachVL *= 0.8D; + + } + // Adaption amount for dynamic range. + final double DYNAMIC_STEP = cc.reachReduceStep / cc.reachSurvivalDistance; + if (!cc.reachReduce){ + data.reachMod = 1d; + } + else if (lenpRel > context.distanceLimit - cc.reachReduceDistance){ + data.reachMod = Math.max(context.distanceMin, data.reachMod - DYNAMIC_STEP); + } + else{ + data.reachMod = Math.min(1.0, data.reachMod + DYNAMIC_STEP); + } + + if (cc.debug && player.hasPermission(Permissions.ADMINISTRATION_DEBUG)){ + player.sendMessage("NC+: Attack/reach " + damaged.getType()+ " height="+ StringUtil.fdec3.format(context.damagedHeight) + " dist=" + StringUtil.fdec3.format(lenpRel) +" @" + StringUtil.fdec3.format(data.reachMod)); + } + + return cancel; + } } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/ReachContext.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/ReachContext.java new file mode 100644 index 00000000..59ba9963 --- /dev/null +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/ReachContext.java @@ -0,0 +1,22 @@ +package fr.neatmonster.nocheatplus.checks.fight; + +/** + * Context data for the reach check, for repeated use within a loop. + * @author mc_dev + * + */ +public class ReachContext { + + public double distanceLimit; + public double distanceMin; + public double damagedHeight; + /** Attacking player. */ + public double eyeHeight; + /** Eye location y of the attacking player. */ + public double pY; + /** Minimum value of lenpRel that was a violation. */ + public double minViolation = Double.MAX_VALUE; + /** Minimum value of lenpRel. */ + public double minResult = Double.MAX_VALUE; + +} diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/inventory/InstantBow.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/inventory/InstantBow.java index ef6291eb..b270d3de 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/inventory/InstantBow.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/inventory/InstantBow.java @@ -42,20 +42,30 @@ public class InstantBow extends Check { final long expectedPullDuration = (long) (maxTime - maxTime * (1f - force) * (1f - force)) - cc.instantBowDelay; // Time taken to pull the string. - final long pullDuration = now - (cc.instantBowStrict ? data.instantBowInteract : data.instantBowShoot); + final long pullDuration; + final boolean valid; + if (cc.instantBowStrict) { + // The interact time is invalid, if set to 0. + valid = data.instantBowInteract != 0; + pullDuration = valid ? (now - data.instantBowInteract) : 0L; + } else { + valid = true; + pullDuration = now - data.instantBowShoot; + } - if ((!cc.instantBowStrict || data.instantBowInteract > 0) && pullDuration >= expectedPullDuration){ + if (valid && (!cc.instantBowStrict || data.instantBowInteract > 0L) && pullDuration >= expectedPullDuration) { // The player was slow enough, reward them by lowering their violation level. data.instantBowVL *= 0.9D; } - else if (data.instantBowInteract > now){ + else if (valid && data.instantBowInteract > now) { // Security check if time ran backwards. // TODO: Maybe this can be removed, though TickTask does not reset at the exact moment. } else { // Account for server side lag. - final long correctedPullduration = cc.lag ? (long) (TickTask.getLag(expectedPullDuration, true) * pullDuration) : pullDuration; - if (correctedPullduration < expectedPullDuration){ + // (Do not apply correction to invalid pulling.) + final long correctedPullduration = valid ? (cc.lag ? (long) (TickTask.getLag(expectedPullDuration, true) * pullDuration) : pullDuration) : 0; + if (correctedPullduration < expectedPullDuration) { // TODO: Consider: Allow one time but set yawrate penalty time ? final double difference = (expectedPullDuration - pullDuration) / 100D; @@ -68,7 +78,7 @@ public class InstantBow extends Check { } } - if (cc.debug && player.hasPermission(Permissions.ADMINISTRATION_DEBUG)){ + if (cc.debug && player.hasPermission(Permissions.ADMINISTRATION_DEBUG)) { player.sendMessage(ChatColor.YELLOW + "NCP: " + ChatColor.GRAY + "Bow shot - force: " + force +", " + (cc.instantBowStrict || pullDuration < 2 * expectedPullDuration ? ("pull time: " + pullDuration) : "") + "(" + expectedPullDuration +")"); } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/inventory/InventoryConfig.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/inventory/InventoryConfig.java index ea70196c..2df29008 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/inventory/InventoryConfig.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/inventory/InventoryConfig.java @@ -5,6 +5,7 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import org.bukkit.Material; import org.bukkit.entity.Player; import fr.neatmonster.nocheatplus.actions.ActionList; @@ -71,7 +72,7 @@ public class InventoryConfig extends ACheckConfig { public final boolean fastConsumeCheck; public final long fastConsumeDuration; public final boolean fastConsumeWhitelist; - public final Set fastConsumeItems = new HashSet(); + public final Set fastConsumeItems = new HashSet(); public final ActionList fastConsumeActions; public final boolean instantBowCheck; diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/inventory/InventoryData.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/inventory/InventoryData.java index 39f427f1..25f67678 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/inventory/InventoryData.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/inventory/InventoryData.java @@ -79,7 +79,8 @@ public class InventoryData extends ACheckData { public int fastClickLastCursorAmount = 0; // Data of the instant bow check. - public long instantBowInteract; + /** Last time right click interact on bow. A value of 0 means 'invalid'.*/ + public long instantBowInteract = 0; public long instantBowShoot; // Data of the instant eat check. diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/inventory/InventoryListener.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/inventory/InventoryListener.java index 95b6e90b..838b6c33 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/inventory/InventoryListener.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/inventory/InventoryListener.java @@ -29,6 +29,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.BridgeHealth; import fr.neatmonster.nocheatplus.components.JoinLeaveListener; import fr.neatmonster.nocheatplus.utilities.InventoryUtil; @@ -55,6 +56,9 @@ public class InventoryListener extends CheckListener implements JoinLeaveListen private final Open open = addCheck(new Open()); + /** For temporary use: LocUtil.clone before passing deeply, call setWorld(null) after use. */ + private final Location useLoc = new Location(null, 0, 0, 0); + public InventoryListener(){ super(CheckType.INVENTORY); } @@ -73,7 +77,7 @@ public class InventoryListener extends CheckListener implements JoinLeaveListen final Player player = (Player) event.getEntity(); if (instantBow.isEnabled(player)){ final long now = System.currentTimeMillis(); - final Location loc = player.getLocation(); + final Location loc = player.getLocation(useLoc); if (Combined.checkYawRate(player, loc.getYaw(), now, loc.getWorld().getName())){ // No else if with this, could be cancelled due to other checks feeding, does not have actions. event.setCancelled(true); @@ -87,6 +91,7 @@ public class InventoryListener extends CheckListener implements JoinLeaveListen // Combined fighting speed (Else if: Matter of taste, preventing extreme cascading and actions spam). event.setCancelled(true); } + useLoc.setWorld(null); } } } @@ -107,6 +112,10 @@ public class InventoryListener extends CheckListener implements JoinLeaveListen if (instantEat.isEnabled(player) && instantEat.check(player, event.getFoodLevel())){ event.setCancelled(true); } + else if (player.isDead() && BridgeHealth.getHealth(player) <= 0.0) { + // Eat after death. + event.setCancelled(true); + } } } @@ -219,6 +228,7 @@ public class InventoryListener extends CheckListener implements JoinLeaveListen if (event.hasItem()){ final ItemStack item = event.getItem(); final Material type = item.getType(); + // TODO: Get Magic values (800) from the config. if (type == Material.BOW){ final long now = System.currentTimeMillis(); // It was a bow, the player starts to pull the string, remember this time. @@ -233,9 +243,13 @@ public class InventoryListener extends CheckListener implements JoinLeaveListen } else resetAll = true; // Illegal enchantments hotfix check. - if (Items.checkIllegalEnchantments(player, item)) event.setCancelled(true); + if (Items.checkIllegalEnchantments(player, item)) { + event.setCancelled(true); + } + } + else { + resetAll = true; } - else resetAll = true; if (resetAll){ // Nothing that we are interested in, reset data. @@ -248,9 +262,16 @@ public class InventoryListener extends CheckListener implements JoinLeaveListen @EventHandler(ignoreCancelled = false, priority = EventPriority.LOWEST) public final void onPlayerInteractEntity(final PlayerInteractEntityEvent event) { final Player player = event.getPlayer(); - if (player.getGameMode() == GameMode.CREATIVE) return; + if (player.getGameMode() == GameMode.CREATIVE) { + return; + } + if (player.isDead() && BridgeHealth.getHealth(player) <= 0.0) { + // No zombies. + event.setCancelled(true); + return; + } final ItemStack stack = player.getItemInHand(); - if (stack != null && stack.getTypeId() == Material.MONSTER_EGG.getId() && items.isEnabled(player)){ + if (stack != null && stack.getType() == Material.MONSTER_EGG && items.isEnabled(player)){ event.setCancelled(true); } } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/LocUtil.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/LocUtil.java index 14cef87c..c353168f 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/LocUtil.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/LocUtil.java @@ -1,36 +1,45 @@ package fr.neatmonster.nocheatplus.checks.moving; import org.bukkit.Location; +import org.bukkit.World; import fr.neatmonster.nocheatplus.utilities.PlayerLocation; +/** + * Auxiliary methods for Location handling, mainly intended for use with set-back locations. + * @author mc_dev + * + */ public class LocUtil { /** - * Simple get a copy (not actually using cloning). + * Get a copy of a location (not actually using cloning). * @param loc - * @return + * @return A new Location instance. + * @throws NullPointerException if World is null. */ public static final Location clone(final Location loc){ - return new Location(loc.getWorld(), loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch()); + return new Location(testWorld(loc.getWorld()), loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch()); } /** - * Clone with given yaw and pitch. + * Get a copy of a location (not actually using cloning), override yaw and pitch with given values. * @param loc * @param yaw * @param pitch - * @return + * @return A new Location instance. + * @throws NullPointerException if the resulting world is null. */ public static final Location clone(final Location loc, final float yaw, final float pitch){ - return new Location(loc.getWorld(), loc.getX(), loc.getY(), loc.getZ(), yaw, pitch); + return new Location(testWorld(loc.getWorld()), loc.getX(), loc.getY(), loc.getZ(), yaw, pitch); } /** - * Clone with yaw and pitch of ref, use ref if setBack is null. - * @param setBack - * @param ref - * @return + * Clone setBack, with yaw and pitch taken from ref, if setBack is null, ref is cloned fully. + * @param setBack Can be null. + * @param ref Must not be null. + * @return A new Location instance. + * @throws NullPointerException if the resulting world is null. */ public static final Location clone(final Location setBack, final Location ref) { if (setBack == null){ @@ -49,26 +58,121 @@ public class LocUtil { } /** - * SA + * Update setBack by loc. * @param setBack * @param loc + * @throws NullPointerException if loc.getWorld() is null. */ public static final void set(final Location setBack, final Location loc) { - setBack.setWorld(loc.getWorld()); + setBack.setWorld(testWorld(loc.getWorld())); setBack.setX(loc.getX()); setBack.setY(loc.getY()); setBack.setZ(loc.getZ()); setBack.setYaw(loc.getYaw()); setBack.setPitch(loc.getPitch()); } - + + /** + * Update setBack by loc. + * @param setBack + * @param loc + * @throws NullPointerException if loc.getWorld() is null. + */ public static final void set(final Location setBack, final PlayerLocation loc) { - setBack.setWorld(loc.getWorld()); + setBack.setWorld(testWorld(loc.getWorld())); setBack.setX(loc.getX()); setBack.setY(loc.getY()); setBack.setZ(loc.getZ()); setBack.setYaw(loc.getYaw()); setBack.setPitch(loc.getPitch()); } + + /** + * Throw a NullPointerException if world is null. + * @param world + * @return + */ + private static World testWorld(final World world) { + if (world == null) { + throw new NullPointerException("World must not be null."); + } else { + return world; + } + } + + /** + * Quick out of bounds check for yaw. + * @param yaw + * @return + */ + public static final boolean needsYawCorrection(final float yaw) { + return yaw == Float.NaN || yaw < 0f || yaw >= 360f; + } + + /** + * Quick out of bounds check for pitch. + * @param pitch + * @return + */ + public static final boolean needsPitchCorrection(final float pitch) { + return pitch == Float.NaN || pitch < -90f || pitch > 90f; + } + + /** + * Quick out of bounds check for yaw and pitch. + * @param yaw + * @param pitch + * @return + */ + public static final boolean needsDirectionCorrection(final float yaw, final float pitch) { + return needsYawCorrection(yaw) || needsPitchCorrection(pitch); + } + + /** + * Ensure 0 <= yaw < 360. + * @param yaw + * @return + */ + public static final float correctYaw(float yaw) { + if (yaw == Float.NaN) { + return 0f; + } + if (yaw >= 360f) { + if (yaw > 10000f) { + yaw = 0f; + } else { + while (yaw > 360f) { + yaw -= 360f; + } + } + } + if (yaw < 0f) { + if (yaw < -10000f) { + yaw = 0f; + } else { + while (yaw < 0f) { + yaw += 360f; + } + } + } + return yaw; + } + + /** + * Ensure -90 <= pitch <= 90. + * @param pitch + * @return + */ + public static final float correctPitch(float pitch) { + if (pitch == Float.NaN) { + return 0f; + } else if (pitch < -90f) { + return -90f; + } else if (pitch > 90f) { + return 90f; + } else { + return pitch; + } + } } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/LocationTrace.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/LocationTrace.java new file mode 100644 index 00000000..51b46e91 --- /dev/null +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/LocationTrace.java @@ -0,0 +1,242 @@ +package fr.neatmonster.nocheatplus.checks.moving; + +import java.util.Iterator; + +import fr.neatmonster.nocheatplus.utilities.TrigUtil; + +/** + * This class is meant to record locations for players moving, in order to allow to be more + * lenient for the case of latency for player-player interaction such as with fighting. + *
+ * NOTES on intended use:
+ *
  • Is meant to always carry some location.
  • + *
  • Records only the end-positions of a move.
  • + *
  • Prefer calling add(...) with the current location, before iterating. Alternative: guard with isEmpty().
  • + *
  • Updating on teleport events is not intended - if the distance is too big, Minecraft should prevent interaction anyway.
  • + * @author mc_dev + * + */ +public class LocationTrace { + + public static final class TraceEntry { + + /** We keep it open, if ticks or ms are used. */ + public long time; + /** Coordinates. */ + public double x, y, z; + public double lastDistSq; + + public void set(long time, double x, double y, double z, double lastDistSq) { + this.x = x; + this.y = y; + this.z = z; + this.time = time; + this.lastDistSq = lastDistSq; + } + } + + /** + * Iterate from oldest to latest. Not a fully featured Iterator. + * @author mc_dev + * + */ + public static final class TraceIterator implements Iterator{ + private final TraceEntry[] entries; + /** Index as in LocationTrace */ + private final int index; + private final int size; + private int currentIndex; + private final boolean ascend; + + protected TraceIterator(TraceEntry[] entries, int index, int size, int currentIndex, boolean ascend) { + if (currentIndex >= entries.length || currentIndex < 0 || + currentIndex <= index - size || currentIndex > index && currentIndex <= index - size + entries.length) { + // This should also prevent iterators for size == 0, for the moment (!). + throw new IllegalArgumentException("startIndex out of bounds."); + } + this.entries = entries; + this.index = index; + this.size = size; + this.currentIndex = currentIndex; + this.ascend = ascend; + } + + @Override + public final TraceEntry next() { + if (!hasNext()) { + throw new IndexOutOfBoundsException("No more entries to iterate."); + } + final TraceEntry entry = entries[currentIndex]; + if (ascend) { + currentIndex ++; + if (currentIndex >= entries.length) { + currentIndex = 0; + } + int ref = index - size + 1; + if (ref < 0) { + ref += entries.length; + } + if (currentIndex == ref) { + // Invalidate the iterator. + currentIndex = -1; + } + } else { + currentIndex --; + if (currentIndex < 0) { + currentIndex = entries.length - 1; + } + if (currentIndex == index) { + // Invalidate the iterator. + currentIndex = - 1; + } + } + return entry; + } + + @Override + public final boolean hasNext() { + // Just check if currentIndex is within range. + return currentIndex >= 0 && currentIndex <= index && currentIndex > index - size || currentIndex > index && currentIndex >= index - size + entries.length; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + } + + /** A Ring. */ + private final TraceEntry[] entries; + /** Last element index. */ + private int index = -1; + /** Number of valid entries. */ + private int size = 0; + private final double mergeDist; + private final double mergeDistSq; + + // (No world name stored: Should be reset on world changes.) + + public LocationTrace(int bufferSize, double mergeDist) { + // TODO: Might consider a cut-off distance/age (performance saving for iteration). + if (bufferSize < 1) { + throw new IllegalArgumentException("Expect bufferSize > 0, got instead: " + bufferSize); + } + entries = new TraceEntry[bufferSize]; + for (int i = 0; i < bufferSize; i++) { + entries[i] = new TraceEntry(); + } + this.mergeDist = mergeDist; + this.mergeDistSq = mergeDist * mergeDist; + } + + public final void addEntry(final long time, final double x, final double y, final double z) { + double lastDistSq = 0.0; + if (size > 0) { + final TraceEntry latestEntry = entries[index]; + // TODO: Consider duration of staying there ? + if (x == latestEntry.x && y == latestEntry.y && z == latestEntry.z) { + latestEntry.time = time; + return; + } + lastDistSq = TrigUtil.distanceSquared(x, y, z, latestEntry.x, latestEntry.y, latestEntry.z); + // TODO: Think about minMergeSize (1 = never merge the first two, size = first fill the ring). + if (size > 1 && lastDistSq <= mergeDistSq) { + // TODO: Could use Manhattan, after all. + // Only merge if last distance was not greater than mergeDist, to prevent too-far-off entries. + if (latestEntry.lastDistSq <= mergeDistSq) { + // Update lastDistSq, due to shifting the elements position. + final TraceEntry secondLatest = index - 1 < 0 ? entries[index - 1 + entries.length] : entries[index - 1]; + lastDistSq = TrigUtil.distanceSquared(x, y, z, secondLatest.x, secondLatest.y, secondLatest.z); + latestEntry.set(time, x, y, z, lastDistSq); + return; + } + } + } + // Advance index. + index++; + if (index == entries.length) { + index = 0; + } + if (size < entries.length) { + size ++; + } + final TraceEntry newEntry = entries[index]; + newEntry.set(time, x, y, z, lastDistSq); + } + + /** Reset content pointers - call with world changes. */ + public void reset() { + index = 0; + size = 0; + } + + /** + * Get the actual number of valid elements. After some time of moving this should be entries.length. + * @return + */ + public int size() { + return size; + } + public boolean isEmpty() { + return size == 0; + } + + /** + * Get size of ring buffer (maximal possible number of elements). + * @return + */ + public int getMaxSize() { + return entries.length; + } + + public double getMergeDist() { + return mergeDist; + } + + /** + * Iterate from latest to oldest. + * @return + */ + public TraceIterator latestIterator() { + return new TraceIterator(entries, index, size, index, false); + } + + /** + * Iterate from oldest to latest. + * @return + */ + public TraceIterator oldestIterator() { + final int currentIndex = index - size + 1; + return new TraceIterator(entries, index, size, currentIndex < 0 ? currentIndex + entries.length : currentIndex, true); + } + + + /** + * Iterate from entry with max. age to latest, always includes latest. + * @param tick Absolute tick value for oldest accepted tick. + * @param ms Absolute ms value for oldest accepted ms; + * @return + */ + public TraceIterator maxAgeIterator(long time) { + int currentIndex = index; + int tempIndex = currentIndex; + int steps = 1; + while (steps < size) { + tempIndex --; + if (tempIndex < 0) { + tempIndex += size; + } + final TraceEntry entry = entries[tempIndex]; + if (entry.time >= time) { + // Continue. + currentIndex = tempIndex; + } else { + break; + } + steps ++; + } + return new TraceIterator(entries, index, size, currentIndex, true); + } + +} diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MorePackets.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MorePackets.java index 08dc9574..2b76ac1a 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MorePackets.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MorePackets.java @@ -60,8 +60,12 @@ public class MorePackets extends Check { if (!data.hasMorePacketsSetBack()){ // TODO: Check if other set-back is appropriate or if to set on other events. - if (data.hasSetBack()) data.setMorePacketsSetBack(data.getSetBack(to)); - else data.setMorePacketsSetBack(from); + if (data.hasSetBack()) { + data.setMorePacketsSetBack(data.getSetBack(to)); + } + else { + data.setMorePacketsSetBack(from); + } } // Take a packet from the buffer. @@ -76,10 +80,8 @@ public class MorePackets extends Check { // Execute whatever actions are associated with this check and the violation level and find out if we should // cancel the event. - if (executeActions(player, data.morePacketsVL, -data.morePacketsBuffer, - MovingConfig.getConfig(player).morePacketsActions)){ + if (executeActions(player, data.morePacketsVL, -data.morePacketsBuffer, MovingConfig.getConfig(player).morePacketsActions)){ newTo = data.getMorePacketsSetBack(); - data.setTeleported(newTo); } } @@ -93,23 +95,28 @@ public class MorePackets extends Check { // If there was a long pause (maybe server lag?), allow buffer to grow up to 100. if (seconds > 2) { - if (data.morePacketsBuffer > 100) - data.morePacketsBuffer = 100; - } else if (data.morePacketsBuffer > 50) + if (data.morePacketsBuffer > 100) { + data.morePacketsBuffer = 100; + } + } else if (data.morePacketsBuffer > 50) { // Only allow growth up to 50. data.morePacketsBuffer = 50; - + } // Set the new "last" time. data.morePacketsLastTime = time; // Set the new "setback" location. - if (newTo == null) data.setMorePacketsSetBack(from); - } else if (data.morePacketsLastTime > time) + if (newTo == null) { + data.setMorePacketsSetBack(from); + } + } else if (data.morePacketsLastTime > time) { // Security check, maybe system time changed. - data.morePacketsLastTime = time; + data.morePacketsLastTime = time; + } - if (newTo == null) - return null; + if (newTo == null) { + return null; + } // Compose a new location based on coordinates of "newTo" and viewing direction of "event.getTo()" to allow the // player to look somewhere else despite getting pulled back by NoCheatPlus. diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MoveInfo.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MoveInfo.java index ae35257d..3d4df336 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MoveInfo.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MoveInfo.java @@ -13,6 +13,9 @@ import fr.neatmonster.nocheatplus.utilities.PlayerLocation; * */ public class MoveInfo { + + /** For temporary use. Might need cloning for passing to external API. Only use after calling MoveInfo.set! */ + public final Location useLoc = new Location(null, 0, 0, 0); public final BlockCache cache; public final PlayerLocation from; public final PlayerLocation to; @@ -24,24 +27,31 @@ public class MoveInfo { } /** - * Demands at least setting from. + * Initialize from, and if given to. Note that useLoc is left untouched (!). * @param player - * @param from - * @param to + * @param from Must not be null. + * @param to Can be null. * @param yOnGround */ public final void set(final Player player, final Location from, final Location to, final double yOnGround){ + this.cache.setAccess(from.getWorld()); this.from.set(from, player, yOnGround); - this.cache.setAccess(from.getWorld()); this.from.setBlockCache(cache); if (to != null){ this.to.set(to, player, yOnGround); this.to.setBlockCache(cache); } + // Note: using set to reset to by passing null won't work. } + + /** + * Clear caches and remove World references and such. Also resets the world of useLoc. + */ public final void cleanup(){ + useLoc.setWorld(null); from.cleanup(); to.cleanup(); cache.cleanup(); } + } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingConfig.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingConfig.java index f06fdfb9..65b52b4d 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingConfig.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingConfig.java @@ -137,8 +137,16 @@ public class MovingConfig extends ACheckConfig { public final long sprintingGrace; public final boolean assumeSprint; public final int speedGrace; + public final boolean enforceLocation; + + // Vehicles public final boolean vehicleEnforceLocation; public final boolean vehiclePreventDestroyOwn; + + // Trace + public final int traceSize; + public final double traceMergeDist; + /** * Instantiates a new moving configuration. @@ -214,9 +222,14 @@ public class MovingConfig extends ACheckConfig { sprintingGrace = Math.max(0L, (long) (config.getDouble(ConfPaths.MOVING_SPRINTINGGRACE) * 1000.0)); // Config: seconds. assumeSprint = config.getBoolean(ConfPaths.MOVING_ASSUMESPRINT); speedGrace = Math.max(0, (int) Math.round(config.getDouble(ConfPaths.MOVING_SPEEDGRACE) * 20.0)); // Config: seconds + enforceLocation = config.getBoolean(ConfPaths.MOVING_ENFORCELOCATION); vehicleEnforceLocation = config.getBoolean(ConfPaths.MOVING_VEHICLES_ENFORCELOCATION); vehiclePreventDestroyOwn = config.getBoolean(ConfPaths.MOVING_VEHICLES_PREVENTDESTROYOWN); + + traceSize = config.getInt(ConfPaths.MOVING_TRACE_SIZE); + traceMergeDist = config.getDouble(ConfPaths.MOVING_TRACE_MERGEDIST); + } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingData.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingData.java index cd5f757f..8b51844e 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingData.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingData.java @@ -57,9 +57,13 @@ public class MovingData extends ACheckData { * @return the data */ public static MovingData getData(final Player player) { - if (!playersMap.containsKey(player.getName())) - playersMap.put(player.getName(), new MovingData()); - return playersMap.get(player.getName()); + // Note that the trace might be null after just calling this. + MovingData data = playersMap.get(player.getName()); + if (data == null) { + data = new MovingData(); + playersMap.put(player.getName(), data); + } + return data; } public static ICheckData removeData(final String playerName) { @@ -81,6 +85,12 @@ public class MovingData extends ACheckData { } } + public static void onReload() { + for (final MovingData data : playersMap.values()) { + data.deleteTrace(); // Safe side. + } + } + ///////////////// // Not static. ///////////////// @@ -119,6 +129,8 @@ public class MovingData extends ACheckData { public double fromX = Double.MAX_VALUE, fromY, fromZ; /** Last to coordinates. */ public double toX = Double.MAX_VALUE, toY, toZ; + /** Moving trace (to positions). This is initialized on "playerJoins, i.e. MONITOR, and set to null on playerLeaves."*/ + private LocationTrace trace = null; // sf rather /** To/from was ground or web or assumed to be etc. */ @@ -269,8 +281,12 @@ public class MovingData extends ACheckData { * @param loc */ public void resetPositions(final Location loc) { - if (loc == null) resetPositions(Double.MAX_VALUE, 0, 0); - else resetPositions(loc.getX(), loc.getY(), loc.getZ()); + if (loc == null) { + resetPositions(Double.MAX_VALUE, 0, 0); + } + else { + resetPositions(loc.getX(), loc.getY(), loc.getZ()); + } } /** @@ -278,8 +294,12 @@ public class MovingData extends ACheckData { * @param loc */ public void resetPositions(PlayerLocation loc) { - if (loc == null) resetPositions(Double.MAX_VALUE, 0, 0); - else resetPositions(loc.getX(), loc.getY(), loc.getZ()); + if (loc == null) { + resetPositions(Double.MAX_VALUE, 0, 0); + } + else { + resetPositions(loc.getX(), loc.getY(), loc.getZ()); + } } /** @@ -375,8 +395,12 @@ public class MovingData extends ACheckData { } public boolean hasSetBackWorldChanged(final Location loc) { - if (setBack == null) return true; - else return setBack.getWorld().equals(loc.getWorld()); + if (setBack == null) { + return true; + } + else { + return setBack.getWorld().equals(loc.getWorld()); + } } @@ -418,13 +442,21 @@ public class MovingData extends ACheckData { } public final void setMorePacketsSetBack(final PlayerLocation loc) { - if (morePacketsSetback == null) morePacketsSetback = loc.getLocation(); - else LocUtil.set(morePacketsSetback, loc); + if (morePacketsSetback == null) { + morePacketsSetback = loc.getLocation(); + } + else { + LocUtil.set(morePacketsSetback, loc); + } } public final void setMorePacketsSetBack(final Location loc) { - if (morePacketsSetback == null) morePacketsSetback = LocUtil.clone(loc); - else LocUtil.set(morePacketsSetback, loc); + if (morePacketsSetback == null) { + morePacketsSetback = LocUtil.clone(loc); + } + else { + LocUtil.set(morePacketsSetback, loc); + } } public Location getMorePacketsSetBack() { @@ -436,13 +468,21 @@ public class MovingData extends ACheckData { } public final void setMorePacketsVehicleSetBack(final PlayerLocation loc) { - if (morePacketsVehicleSetback == null) morePacketsVehicleSetback = loc.getLocation(); - else LocUtil.set(morePacketsVehicleSetback, loc); + if (morePacketsVehicleSetback == null) { + morePacketsVehicleSetback = loc.getLocation(); + } + else { + LocUtil.set(morePacketsVehicleSetback, loc); + } } public final void setMorePacketsVehicleSetBack(final Location loc) { - if (morePacketsVehicleSetback == null) morePacketsVehicleSetback = LocUtil.clone(loc); - else LocUtil.set(morePacketsVehicleSetback, loc); + if (morePacketsVehicleSetback == null) { + morePacketsVehicleSetback = LocUtil.clone(loc); + } + else { + LocUtil.set(morePacketsVehicleSetback, loc); + } } public final Location getMorePacketsVehicleSetBack() { @@ -617,6 +657,7 @@ public class MovingData extends ACheckData { */ public void onPlayerLeave() { removeAllVelocity(); + deleteTrace(); } /** @@ -665,4 +706,67 @@ public class MovingData extends ACheckData { } } + /** + * This tests for a LocationTrace instance being set at all, not for locations having been added. + * @return + */ + public boolean hasTrace() { + return trace != null; + } + + /** + * Convenience: Access method to simplify coding, being aware of some plugins using Player implementations as NPCs, leading to traces not being present. + * @return + */ + public LocationTrace getTrace(final Player player) { + if (trace == null) { + final MovingConfig cc = MovingConfig.getConfig(player); + trace = new LocationTrace(cc.traceSize, cc.traceMergeDist); + } + return trace; + } + + /** + * Convenience + * @param player + * @param loc + */ + public void resetTrace(final Player player, final Location loc, final long time) { + final MovingConfig cc = MovingConfig.getConfig(player); + resetTrace(loc, time, cc.traceSize, cc.traceMergeDist); + } + + /** + * Convenience method to add a location to the trace, creates the trace if necessary. + * @param player + * @param loc + * @param time + * @return Updated LocationTrace instance, for convenient use, without sticking too much to MovingData. + */ + public LocationTrace updateTrace(final Player player, final Location loc, final long time) { + final LocationTrace trace = getTrace(player); + trace.addEntry(time, loc.getX(), loc.getY(), loc.getZ()); + return trace; + } + + /** + * Convenience: Create or just reset the trace, add the current location. + * @param loc + * @param size + * @param mergeDist + * @param traceMergeDist + */ + public void resetTrace(final Location loc, final long time, final int size, double mergeDist) { + if (trace == null || trace.getMaxSize() != size || trace.getMergeDist() != mergeDist) { + trace = new LocationTrace(size, mergeDist); + } else { + trace.reset(); + } + trace.addEntry(time, loc.getX(), loc.getY(), loc.getZ()); + } + + public void deleteTrace() { + trace = null; + } + } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java index 02374184..cd5e5f6d 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java @@ -114,7 +114,9 @@ public class MovingListener extends CheckListener implements TickListener, IRemo event.setTo(setBack); restored = true; } - else data.resetSetBack(); + else { + data.resetSetBack(); + } } if (!restored) { pLoc.set(loc, player); @@ -125,13 +127,16 @@ public class MovingListener extends CheckListener implements TickListener, IRemo } } pLoc.cleanup(); - if (!restored && MovingConfig.getConfig(player).tempKickIllegal) { - // TODO: correct the location ? - NCPAPIProvider.getNoCheatPlusAPI().denyLogin(player.getName(), 24L * 60L * 60L * 1000L); - LogUtil.logSevere("[NCP] could not restore location for " + player.getName() + " deny login for 24 hours"); + if (!restored) { + // TODO: reset the bounding box of the player ? + if (MovingConfig.getConfig(player).tempKickIllegal) { + NCPAPIProvider.getNoCheatPlusAPI().denyLogin(player.getName(), 24L * 60L * 60L * 1000L); + LogUtil.logSevere("[NCP] could not restore location for " + player.getName() + ", kicking them and deny login for 24 hours"); + } else { + LogUtil.logSevere("[NCP] could not restore location for " + player.getName() + ", kicking them."); + } + CheckUtils.kickIllegalMove(player); } - // TODO: reset the bounding box of the player ? - CheckUtils.kickIllegalMove(player); } @@ -170,12 +175,19 @@ public class MovingListener extends CheckListener implements TickListener, IRemo */ private final Map processingEvents = new HashMap(); - private final Set hoverTicks = new LinkedHashSet(30); + /** Player names to check hover for, case insensitive. */ + private final Set hoverTicks = new LinkedHashSet(30); // TODO: Rename + + /** Player names to check enforcing the location for in onTick, case insensitive. */ + private final Set playersEnforce = new LinkedHashSet(30); private int hoverTicksStep = 5; private final Set normalVehicles = new HashSet(); + /** Location for temporary use with getLocation(useLoc). Always call setWorld(null) after use. Use LocUtil.clone before passing to other API. */ + private final Location useLoc = new Location(null, 0, 0, 0); // TODO: Put to use... + public MovingListener() { super(CheckType.MOVING); } @@ -212,17 +224,18 @@ public class MovingListener extends CheckListener implements TickListener, IRemo if (!data.hasSetBack() || blockY + 1D < data.getSetBackY()) return; - final Location loc = player.getLocation(); + final Location loc = player.getLocation(useLoc); if (Math.abs(loc.getX() - 0.5 - block.getX()) <= 1D && Math.abs(loc.getZ() - 0.5 - block.getZ()) <= 1D && loc.getY() - blockY > 0D && loc.getY() - blockY < 2D - && (canJumpOffTop(mat.getId()) || BlockProperties.isLiquid(mat.getId()))) { + && (canJumpOffTop(mat) || BlockProperties.isLiquid(mat))) { // The creative fly and/or survival fly check is enabled, the // block was placed below the player and is // solid, so do what we have to do. data.setSetBackY(blockY + 1D); data.sfJumpPhase = 0; } + useLoc.setWorld(null); } /** @@ -230,9 +243,9 @@ public class MovingListener extends CheckListener implements TickListener, IRemo * @param id * @return */ - private static boolean canJumpOffTop(final int id) { + private static boolean canJumpOffTop(final Material blockType) { // TODO: Test if this can be removed! - return BlockProperties.isGround(id) || BlockProperties.isSolid(id); + return BlockProperties.isGround(blockType) || BlockProperties.isSolid(blockType); } /** @@ -261,28 +274,29 @@ public class MovingListener extends CheckListener implements TickListener, IRemo if (bedLeave.isEnabled(player) && bedLeave.checkBed(player)) { // Check if the player has to be reset. // To "cancel" the event, we teleport the player. - final Location loc = player.getLocation(); + final Location loc = player.getLocation(useLoc); final MovingData data = MovingData.getData(player); final MovingConfig cc = MovingConfig.getConfig(player); Location target = null; final boolean sfCheck = shouldCheckSurvivalFly(player, data, cc); - if (sfCheck) target = data.getSetBack(loc); + if (sfCheck) { + target = data.getSetBack(loc); + } if (target == null) { // TODO: Add something to guess the best set back location (possibly data.guessSetBack(Location)). - target = loc; + target = LocUtil.clone(loc); } - if (target != null) { - // Actually this should not possibly be null, this is a block for "future" purpose, feel free to criticize it. - if (sfCheck && cc.sfFallDamage && noFall.isEnabled(player)) { - // Check if to deal damage. - double y = loc.getY(); - if (data.hasSetBack()) y = Math.min(y, data.getSetBackY()); - noFall.checkDamage(player, data, y); - } - // Teleport. - data.setTeleported(target); // Should be enough. | new Location(target.getWorld(), target.getX(), target.getY(), target.getZ(), target.getYaw(), target.getPitch()); - player.teleport(target, TeleportCause.PLUGIN);// TODO: schedule / other measures ? + if (sfCheck && cc.sfFallDamage && noFall.isEnabled(player)) { + // Check if to deal damage. + double y = loc.getY(); + if (data.hasSetBack()) y = Math.min(y, data.getSetBackY()); + noFall.checkDamage(player, data, y); } + // Cleanup + useLoc.setWorld(null); + // Teleport. + data.prepareSetBack(target); // Should be enough. | new Location(target.getWorld(), target.getX(), target.getY(), target.getZ(), target.getYaw(), target.getPitch()); + player.teleport(target, TeleportCause.PLUGIN);// TODO: schedule / other measures ? } else{ // Reset bed ... @@ -303,10 +317,19 @@ public class MovingListener extends CheckListener implements TickListener, IRemo // Maybe this helps with people teleporting through Multiverse portals having problems? final Player player = event.getPlayer(); final MovingData data = MovingData.getData(player); + final MovingConfig cc = MovingConfig.getConfig(player); data.clearFlyData(); data.clearMorePacketsData(); // TODO: Might omit this if neither check is activated. - data.setSetBack(player.getLocation()); + final Location loc = player.getLocation(useLoc); + data.setSetBack(loc); + data.resetPositions(loc); + data.resetTrace(loc, TickTask.getTick(), cc.traceSize, cc.traceMergeDist); + if (cc.enforceLocation) { + // Just in case. + playersEnforce.add(player.getName()); + } + useLoc.setWorld(null); } /** @@ -348,6 +371,12 @@ public class MovingListener extends CheckListener implements TickListener, IRemo final Location to = event.getTo(); Location newTo = null; +// // Check problematic yaw/pitch values. +// if (LocUtil.needsDirectionCorrection(from.getYaw(), from.getPitch()) +// || LocUtil.needsDirectionCorrection(to.getYaw(), to.getPitch())) { +// DataManager.getPlayerData(player).task.correctDirection(); +// } + // TODO: Check illegal moves here anyway (!). // TODO: Check if vehicle move logs correctly (fake). @@ -371,23 +400,38 @@ public class MovingListener extends CheckListener implements TickListener, IRemo // Ignore changing worlds. earlyReturn = true; } else { - // COntinue with full processing. earlyReturn = false; } + // TODO: Might log base parts here (+extras). if (earlyReturn) { + // TODO: Remove player from enforceLocation ? // TODO: Log "early return: " + tags. if (newTo != null) { + // Illegal Yaw/Pitch. + if (LocUtil.needsYawCorrection(newTo.getYaw())) { + newTo.setYaw(LocUtil.correctYaw(newTo.getYaw())); + } + if (LocUtil.needsPitchCorrection(newTo.getPitch())) { + newTo.setPitch(LocUtil.correctPitch(newTo.getPitch())); + } + // Set. + // TODO: Reset positions? enforceLocation? event.setTo(newTo); } return; } + // newTo should be null here. // TODO: Order this to above "early return"? // Set up data / caching. final MoveInfo moveInfo; - if (parkedInfo.isEmpty()) moveInfo = new MoveInfo(mcAccess); - else moveInfo = parkedInfo.remove(parkedInfo.size() - 1); + if (parkedInfo.isEmpty()) { + moveInfo = new MoveInfo(mcAccess); + } + else { + moveInfo = parkedInfo.remove(parkedInfo.size() - 1); + } final MovingConfig cc = MovingConfig.getConfig(player); moveInfo.set(player, from, to, cc.yOnGround); // TODO: Data resetting above ? @@ -405,6 +449,19 @@ public class MovingListener extends CheckListener implements TickListener, IRemo return; } + // The players location. + final Location loc = (cc.noFallCheck || cc.passableCheck) ? player.getLocation(moveInfo.useLoc) : null; + + + // Check for location consistency. + if (cc.enforceLocation && playersEnforce.contains(playerName)) { + // NOTE: The setback should not be set before this, even if not yet set. + // Last to vs. from. + newTo = enforceLocation(player, from, data); + // TODO: Remove anyway ? + playersEnforce.remove(playerName); + } + final long time = System.currentTimeMillis(); if (player.isSprinting() || cc.assumeSprint) { // Hard to confine assumesprint further (some logics change with hdist or sprinting). @@ -444,10 +501,6 @@ public class MovingListener extends CheckListener implements TickListener, IRemo final int tick = TickTask.getTick(); data.removeInvalidVelocity(tick - cc.velocityActivationTicks); data.velocityTick(); - - // The players location. - // TODO: Change to getLocation(moveInfo.loc) once 1.4.5 support is dropped. - final Location loc = (cc.noFallCheck || cc.passableCheck) ? player.getLocation() : null; // Check passable first to prevent set-back override. // TODO: Redesign to set set-backs later (queue + invalidate). @@ -564,30 +617,21 @@ public class MovingListener extends CheckListener implements TickListener, IRemo } } - // Did one of the checks decide we need a new "to"-location? - if (newTo != null) { - // Reset some data. - data.prepareSetBack(newTo); - - // Set new to-location. - // TODO: Clone here for the case of using loc with loc = player.getLocation(moveInfo.loc). - // TODO: Actually should be a newly created location already (data.getSetBack). - event.setTo(newTo); - - // Debug. - if (cc.debug) { - System.out.println(player.getName() + " set back to: " + newTo.getWorld() + StringUtil.fdec3.format(newTo.getX()) + ", " + StringUtil.fdec3.format(newTo.getY()) + ", " + StringUtil.fdec3.format(newTo.getZ())); - } - } - // Set positions. - // TODO: Consider setting in Monitor (concept missing for changing coordinates, could double-check). - data.fromX = from.getX(); - data.fromY = from.getY(); - data.fromZ = from.getZ(); - data.toX = to.getX(); - data.toY = to.getY(); - data.toZ = to.getZ(); + if (newTo == null) { + // Set positions. + // TODO: Consider setting in Monitor (concept missing for changing coordinates, could double-check). + data.fromX = from.getX(); + data.fromY = from.getY(); + data.fromZ = from.getZ(); + data.toX = to.getX(); + data.toY = to.getY(); + data.toZ = to.getZ(); + } + else { + // Set-back handling. + onSetBack(player, event, newTo, data, cc); + } // Cleanup. moveInfo.cleanup(); @@ -595,6 +639,36 @@ public class MovingListener extends CheckListener implements TickListener, IRemo } /** + * + * @param player + * @param event + * @param newTo Must be a cloned or new Location instance, free for whatever other plugins do with it. + * @param data + * @param cc + */ + private void onSetBack(final Player player, final PlayerMoveEvent event, final Location newTo, final MovingData data, final MovingConfig cc) { + // Illegal Yaw/Pitch. + if (LocUtil.needsYawCorrection(newTo.getYaw())) { + newTo.setYaw(LocUtil.correctYaw(newTo.getYaw())); + } + if (LocUtil.needsPitchCorrection(newTo.getPitch())) { + newTo.setPitch(LocUtil.correctPitch(newTo.getPitch())); + } + + // Reset some data. + data.prepareSetBack(newTo); + data.resetPositions(newTo); // TODO: Might move into prepareSetBack, experimental here. + + // Set new to-location. + event.setTo(newTo); + + // Debug. + if (cc.debug) { + System.out.println(player.getName() + " set back to: " + newTo.getWorld() + StringUtil.fdec3.format(newTo.getX()) + ", " + StringUtil.fdec3.format(newTo.getY()) + ", " + StringUtil.fdec3.format(newTo.getZ())); + } + } + + /** * Called from player-move checking, if the player is inside of a vehicle. * @param player * @param from @@ -612,7 +686,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo data.sfLowJump = false; // TODO: What with processingEvents.remove(player.getName()); if (vehicle != null) { - final Location vLoc = vehicle.getLocation(); + final Location vLoc = vehicle.getLocation(); // TODO: Use a location as argument. // (Auto detection of missing events, might fire one time too many per plugin run.) if (!normalVehicles.contains(vehicle.getType())) { onVehicleMove(vehicle, vLoc, vLoc, true); @@ -675,12 +749,14 @@ public class MovingListener extends CheckListener implements TickListener, IRemo // Feed combined check. final CombinedData data = CombinedData.getData(player); - data.lastMoveTime = now; + data.lastMoveTime = now; // TODO: Evaluate moving this to MovingData !? final Location from = event.getFrom(); final String fromWorldName = from.getWorld().getName(); // Feed yawrate and reset moving data positions if necessary. + final MovingData mData = MovingData.getData(player); + final long time = TickTask.getTick(); if (!event.isCancelled()) { final Location to = event.getTo(); final String toWorldName = to.getWorld().getName(); @@ -688,20 +764,26 @@ public class MovingListener extends CheckListener implements TickListener, IRemo // TODO: maybe even not count vehicles at all ? if (player.isInsideVehicle()) { // TODO: refine (!). - MovingData.getData(player).resetPositions(player.getVehicle().getLocation()); + final Location ref = player.getVehicle().getLocation(useLoc); + mData.resetPositions(ref); + useLoc.setWorld(null); + mData.resetTrace(player, ref, time); } else if (!fromWorldName.equals(toWorldName)) { - MovingData.getData(player).resetPositions(to); + mData.resetPositions(to); + mData.resetTrace(player, to, time); } else{ // Slightly redundant at present. - MovingData.getData(player).setTo(to); + mData.setTo(to); + mData.resetTrace(player, to, time); } } else { // TODO: teleported + other resetting ? Combined.feedYawRate(player, from.getYaw(), now, fromWorldName, data); - MovingData.getData(player).resetPositions(from); + mData.resetPositions(from); + mData.resetTrace(player, from, time); // TODO: Should probably leave this to the teleport event! } } @@ -719,31 +801,6 @@ public class MovingListener extends CheckListener implements TickListener, IRemo data.clearMorePacketsData(); // TODO: This event might be redundant (!). } - - /** - * When a player respawns, all information related to the moving checks - * becomes invalid. - * - * @param event - * the event - */ - @EventHandler(priority = EventPriority.MONITOR) - public void onPlayerRespawn(final PlayerRespawnEvent event) { - final Player player = event.getPlayer(); - final MovingData data = MovingData.getData(player); - data.clearFlyData(); - data.clearMorePacketsData(); - data.setSetBack(event.getRespawnLocation()); - // TODO: consider data.resetPositions(data.setBack); - // (Not putting hover in at respawn due to chunk sending.) - // TODO: Might use grace ticks for this too (and bigger teleports). -// final MovingConfig cc = MovingConfig.getConfig(player); -// if (cc.sfHoverCheck) { -// // Assume the player might be hovering. -// data.sfHoverTicks = 0; -// hoverTicks.add(player.getName()); -// } - } /** * Clear fly data on death. @@ -755,7 +812,8 @@ public class MovingListener extends CheckListener implements TickListener, IRemo final MovingData data = MovingData.getData(player); data.clearFlyData(); data.clearMorePacketsData(); - data.setSetBack(player.getLocation()); // TODO: Monitor this change (!). + data.setSetBack(player.getLocation(useLoc)); // TODO: Monitor this change (!). + useLoc.setWorld(null); } /** @@ -843,7 +901,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo event.setTo(ref); } else{ - ref = from; + ref = from; // Player.getLocation ? event.setCancelled(true); } } @@ -857,7 +915,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo // public void run() { // if (!data.hasSetBackWorldChanged(setBack)) { // && data.isSetBack(setBack)) { // player.sendMessage("SETBACK FROM MC DERP."); -// player.teleport(setBack); +// player.teleport(setBack, TeleportCause.PLUGIN); // } // } // }); @@ -1016,11 +1074,11 @@ public class MovingListener extends CheckListener implements TickListener, IRemo if (!from.getWorld().equals(to.getWorld())) return; final MovingData data = MovingData.getData(player); - data.vehicleConsistency = MoveConsistency.getConsistency(from, to, player.getLocation()); + data.vehicleConsistency = MoveConsistency.getConsistency(from, to, player.getLocation(useLoc)); switch (data.vehicleConsistency) { case FROM: case TO: - data.resetPositions(player.getLocation()); // TODO: Drop MC 1.4! + data.resetPositions(player.getLocation(useLoc)); // TODO: Drop MC 1.4! break; case INCONSISTENT: // TODO: Any exploits exist? -> TeleportUtil.forceMount(player, vehicle) @@ -1062,26 +1120,40 @@ public class MovingListener extends CheckListener implements TickListener, IRemo // TODO: Reset on world changes or not? data.morePacketsVehicleTaskId = Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new VehicleSetBack(vehicle, player, newTo, cc.debug)); } + useLoc.setWorld(null); } @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = false) public void onEntityDamage(final EntityDamageEvent event) { - if (event.getCause() != DamageCause.FALL) return; + if (event.getCause() != DamageCause.FALL) { + return; + } final Entity entity = event.getEntity(); - if (!(entity instanceof Player)) return; + if (!(entity instanceof Player)) { + return; + } final Player player = (Player) entity; final MovingData data = MovingData.getData(player); + if (player.isInsideVehicle()) { + // Ignore vehicles (noFallFallDistance will be inaccurate anyway). + data.clearNoFallData(); + return; + } final MovingConfig cc = MovingConfig.getConfig(player); if (event.isCancelled() || !shouldCheckSurvivalFly(player, data, cc) || !noFall.isEnabled(player)) { data.clearNoFallData(); return; } - final Location loc = player.getLocation(); + final Location loc = player.getLocation(useLoc); boolean allowReset = true; if (!data.noFallSkipAirCheck) { final MoveInfo moveInfo; - if (parkedInfo.isEmpty()) moveInfo = new MoveInfo(mcAccess); - else moveInfo = parkedInfo.remove(parkedInfo.size() - 1); + if (parkedInfo.isEmpty()) { + moveInfo = new MoveInfo(mcAccess); + } + else { + moveInfo = parkedInfo.remove(parkedInfo.size() - 1); + } moveInfo.set(player, loc, null, cc.noFallyOnGround); // NOTE: No isIllegal check here. final PlayerLocation pLoc = moveInfo.from; @@ -1139,43 +1211,113 @@ public class MovingListener extends CheckListener implements TickListener, IRemo } } // Entity fall-distance should be reset elsewhere. + // Cleanup. + useLoc.setWorld(null); } + /** + * When a player respawns, all information related to the moving checks + * becomes invalid. + * + * @param event + * the event + */ + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerRespawn(final PlayerRespawnEvent event) { + final Player player = event.getPlayer(); + final MovingData data = MovingData.getData(player); + final MovingConfig cc = MovingConfig.getConfig(player); + + final Location loc = event.getRespawnLocation(); + data.clearFlyData(); + data.setSetBack(loc); + + // Handle respawn like join. + dataOnJoin(player, loc, data, cc); + } + @Override public void playerJoins(final Player player) { final MovingData data = MovingData.getData(player); - // TODO: on existing set back: detect world changes and loss of world on join (+ set up some paradigm). - data.clearMorePacketsData(); - data.removeAllVelocity(); - final Location loc = player.getLocation(); + final MovingConfig cc = MovingConfig.getConfig(player); + + final Location loc = player.getLocation(useLoc); - // Correct set-back on world changes. - if (loc == null) { - // Bug on server side ? + // Correct set-back on join. + if (data.hasSetBackWorldChanged(loc)) { data.clearFlyData(); + data.setSetBack(loc); } else if (!data.hasSetBack()) { // TODO: Might consider something else like with respawn. Check if it is passable ? data.setSetBack(loc); } - else if (data.hasSetBackWorldChanged(loc)) { - data.clearFlyData(); - data.setSetBack(loc); - } - if (data.fromX == Double.MAX_VALUE && data.toX == Double.MAX_VALUE) { - // TODO: re-think: more fine grained reset? - data.resetPositions(loc); + + dataOnJoin(player, loc, data, cc); + + // Cleanup. + useLoc.setWorld(null); + + } + + + /** + * Alter data for players joining (join, respawn).
    + * Do before, if necessary:
    + *
  • data.clearFlyData()
  • + *
  • data.setSetBack(...)
  • + * @param player + * @param loc Can be useLoc (!). + * @param data + * @param cc + */ + private void dataOnJoin(Player player, Location loc, MovingData data, MovingConfig cc) { + final int tick = TickTask.getTick(); + // Check loaded chunks. + if (cc.loadChunksOnJoin) { + final int loaded = BlockCache.ensureChunksLoaded(loc.getWorld(), loc.getX(), loc.getZ(), 3.0); + if (loaded > 0 && cc.debug && BuildParameters.debugLevel > 0) { + // DEBUG + LogUtil.logInfo("[NoCheatPlus] Player join: Loaded " + loaded + " chunk" + (loaded == 1 ? "" : "s") + " for the world " + loc.getWorld().getName() + " for player: " + player.getName()); + } } + // Always reset position to this one. + // TODO: more fine grained reset? + data.resetPositions(loc); + data.clearMorePacketsData(); + data.removeAllVelocity(); + data.resetTrace(loc, tick, cc.traceSize, cc.traceMergeDist); + // More resetting. data.vDistAcc.clear(); - data.toWasReset = false; - data.fromWasReset = false; + data.toWasReset = BlockProperties.isOnGroundOrResetCond(player, loc, cc.yOnGround); + data.fromWasReset = data.toWasReset; + + // Enforcing the location. + if (cc.enforceLocation) { + playersEnforce.add(player.getName()); + } // Hover. - final MovingConfig cc = MovingConfig.getConfig(player); + initHover(player, data, cc, data.toWasReset); // isOnGroundOrResetCond + +// // Bad pitch/yaw, just in case. +// if (LocUtil.needsDirectionCorrection(useLoc.getYaw(), useLoc.getPitch())) { +// DataManager.getPlayerData(player).task.correctDirection(); +// } + } + + /** + * Initialize the hover check for a player (login, respawn). + * @param player + * @param data + * @param cc + * @param isOnGroundOrResetCond + */ + private void initHover(final Player player, final MovingData data, final MovingConfig cc, final boolean isOnGroundOrResetCond) { // Reset hover ticks until a better method is used. - if (cc.sfHoverCheck) { + if (!isOnGroundOrResetCond && cc.sfHoverCheck) { // Start as if hovering already. // Could check shouldCheckSurvivalFly(player, data, cc), but this should be more sharp (gets checked on violation). data.sfHoverTicks = 0; @@ -1186,16 +1328,6 @@ public class MovingListener extends CheckListener implements TickListener, IRemo data.sfHoverLoginTicks = 0; data.sfHoverTicks = -1; } - - // Check loaded chunks. - if (cc.loadChunksOnJoin) { - final int loaded = BlockCache.ensureChunksLoaded(loc.getWorld(), loc.getX(), loc.getZ(), 3.0); - if (loaded > 0 && cc.debug && BuildParameters.debugLevel > 0) { - // DEBUG - LogUtil.logInfo("[NoCheatPlus] Player join: Loaded " + loaded + " chunk" + (loaded == 1 ? "" : "s") + " for the world " + loc.getWorld().getName() + " for player: " + player.getName()); - } - } - } @Override @@ -1252,7 +1384,8 @@ public class MovingListener extends CheckListener implements TickListener, IRemo final MovingData data = MovingData.getData(player); data.removeAllVelocity(); // Event should have a vehicle, in case check this last. - data.vehicleConsistency = MoveConsistency.getConsistency(event.getVehicle().getLocation(), null, player.getLocation()); + data.vehicleConsistency = MoveConsistency.getConsistency(event.getVehicle().getLocation(), null, player.getLocation(useLoc)); + useLoc.setWorld(null); // TODO: A pool ? // TODO: more resetting, visible check ? } @@ -1272,7 +1405,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo final MovingConfig cc = MovingConfig.getConfig(player); // TODO: Loc can be inconsistent, determine which to use ! - final Location pLoc = player.getLocation(); + final Location pLoc = player.getLocation(useLoc); Location loc = pLoc; // The location to use as set-back. // TODO: Which vehicle to use ? // final Entity vehicle = player.getVehicle(); @@ -1290,7 +1423,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo loc = vLoc; // if (data.vehicleConsistency != MoveConsistency.INCONSISTENT) { final Location oldLoc = new Location(pLoc.getWorld(), data.toX, data.toY, data.toZ); - if (MoveConsistency.getConsistency(oldLoc, null, pLoc) != MoveConsistency.INCONSISTENT) { + if (data.toX != Double.MAX_VALUE && MoveConsistency.getConsistency(oldLoc, null, pLoc) != MoveConsistency.INCONSISTENT) { loc = oldLoc; } } @@ -1302,7 +1435,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo } // Adjust loc if in liquid (meant for boats !?). - if (BlockProperties.isLiquid(loc.getBlock().getTypeId())) { + if (BlockProperties.isLiquid(loc.getBlock().getType())) { loc.setY(Location.locToBlock(loc.getY()) + 1.25); } @@ -1318,6 +1451,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo data.verticalFreedom = 1.2; data.verticalVelocity = 0.15; data.verticalVelocityUsed = 0; + useLoc.setWorld(null); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @@ -1334,15 +1468,42 @@ public class MovingListener extends CheckListener implements TickListener, IRemo @Override public void onTick(final int tick, final long timeLast) { - // Hover checks ! + final List rem = new ArrayList(hoverTicks.size()); // Pessimistic. + // TODO: Change to per world checking (as long as configs are per world). + + // Enforcing location check. + for (final String playerName : playersEnforce) { + final Player player = DataManager.getPlayerExact(playerName); + if (player == null || !player.isOnline()) { + rem.add(playerName); + continue; + } else if (player.isDead() || player.isSleeping() || player.isInsideVehicle()) { + // Don't remove but also don't check [subject to change]. + continue; + } + final MovingData data = MovingData.getData(player); + final Location newTo = enforceLocation(player, player.getLocation(useLoc), data); + if (newTo != null) { + data.prepareSetBack(newTo); + player.teleport(newTo, TeleportCause.PLUGIN); + } + } + if (!rem.isEmpty()) { + playersEnforce.removeAll(rem); + } + // Hover check (survivalfly). + rem.clear(); if (tick % hoverTicksStep != 0) { // Only check every so and so ticks. return; } final MoveInfo info; - if (parkedInfo.isEmpty()) info = new MoveInfo(mcAccess); - else info = parkedInfo.remove(parkedInfo.size() - 1); - final List rem = new ArrayList(hoverTicks.size()); // Pessimistic. + if (parkedInfo.isEmpty()) { + info = new MoveInfo(mcAccess); + } + else { + info = parkedInfo.remove(parkedInfo.size() - 1); + } for (final String playerName : hoverTicks) { // TODO: put players into the set (+- one tick would not matter ?) // TODO: might add an online flag to data ! @@ -1383,10 +1544,28 @@ public class MovingListener extends CheckListener implements TickListener, IRemo rem.add(playerName); } } - info.cleanup(); // Just in case. - parkedInfo.add(info); hoverTicks.removeAll(rem); rem.clear(); + info.cleanup(); + parkedInfo.add(info); + useLoc.setWorld(null); + } + + private Location enforceLocation(final Player player, final Location loc, final MovingData data) { + if (data.toX != Double.MAX_VALUE && TrigUtil.distanceSquared(data.toX, data.toY, data.toZ, loc.getX(), loc.getY(), loc.getZ()) > 1.0 / 256.0) { + // Teleport back. + // TODO: Add history / alert? + //player.sendMessage(ChatColor.RED + "NCP: enforce location !"); // TODO: DEBUG - REMOVE. + if (data.hasSetBack()) { + // Might have to re-check all context with playerJoins and keeping old set-backs... + // Could use a flexible set-back policy (switch to in-air on login). + return data.getSetBack(loc); + } else { + return new Location(player.getWorld(), data.toX, data.toY, data.toZ, loc.getYaw(), loc.getPitch()); + } + } else { + return null; + } } /** @@ -1399,8 +1578,9 @@ public class MovingListener extends CheckListener implements TickListener, IRemo */ private boolean checkHover(final Player player, final MovingData data, final MovingConfig cc, final MoveInfo info) { // Check if player is on ground. - final Location loc = player.getLocation(); + final Location loc = player.getLocation(useLoc); // useLoc.setWorld(null) is done in onTick. info.set(player, loc, null, cc.yOnGround); + // (Could use useLoc of MoveInfo here. Note orderm though.) final boolean res; // TODO: Collect flags, more margin ? final int loaded = info.from.ensureChunksLoaded(); @@ -1452,12 +1632,14 @@ public class MovingListener extends CheckListener implements TickListener, IRemo @Override public IData removeData(String playerName) { hoverTicks.remove(playerName); + playersEnforce.remove(playerName); return null; } @Override public void removeAllData() { hoverTicks.clear(); + playersEnforce.clear(); parkedInfo.clear(); } @@ -1469,6 +1651,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo } parkedInfo.clear(); hoverTicksStep = Math.max(1, ConfigManager.getConfigFile().getInt(ConfPaths.MOVING_SURVIVALFLY_HOVER_STEP)); + MovingData.onReload(); } } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/NoFall.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/NoFall.java index e8672d52..9dfc9e5b 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/NoFall.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/NoFall.java @@ -1,6 +1,7 @@ package fr.neatmonster.nocheatplus.checks.moving; import org.bukkit.Bukkit; +import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.entity.EntityDamageEvent; @@ -16,6 +17,9 @@ import fr.neatmonster.nocheatplus.utilities.StringUtil; * A check to see if people cheat by tricking the server to not deal them fall damage. */ public class NoFall extends Check { + + /** For temporary use: LocUtil.clone before passing deeply, call setWorld(null) after use. */ + private final Location useLoc = new Location(null, 0, 0, 0); /** * Instantiates a new no fall check. @@ -206,12 +210,21 @@ public class NoFall extends Check { public void onLeave(final Player player) { final MovingData data = MovingData.getData(player); final float fallDistance = player.getFallDistance(); - if (data.noFallFallDistance - fallDistance > 0){ - // Might use tolerance, might log, might use method (compare: MovingListener.onEntityDamage). - // Might consider triggering violations here as well. - final float yDiff = (float) (data.noFallMaxY - player.getLocation().getY()); - final float maxDist = Math.max(yDiff, Math.max(data.noFallFallDistance, fallDistance)); - player.setFallDistance(maxDist); + if (data.noFallFallDistance - fallDistance > 0.0) { + final double playerY = player.getLocation(useLoc).getY(); + useLoc.setWorld(null); + if (player.getAllowFlight() || player.isFlying() || player.getGameMode() == GameMode.CREATIVE) { + // Forestall potential issues with flying plugins. + player.setFallDistance(0f); + data.noFallFallDistance = 0f; + data.noFallMaxY = playerY; + } else { + // Might use tolerance, might log, might use method (compare: MovingListener.onEntityDamage). + // Might consider triggering violations here as well. + final float yDiff = (float) (data.noFallMaxY - playerY); + final float maxDist = Math.max(yDiff, Math.max(data.noFallFallDistance, fallDistance)); + player.setFallDistance(maxDist); + } } } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/Passable.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/Passable.java index 9e2bb85d..ab15d757 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/Passable.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/Passable.java @@ -136,7 +136,7 @@ public class Passable extends Check { System.out.println(player.getName() + " Using set-back location for passable."); } } else if (cc.debug) { - System.out.println(player.getName() + " Ignorng set-back for passable."); + System.out.println(player.getName() + " Ignoring set-back for passable."); } } @@ -155,7 +155,8 @@ public class Passable extends Check { // TODO: Consider another set back position for this, also keeping track of players moving around in blocks. final Location newTo; if (loc != null) { - newTo = loc; + // Ensure the given location is cloned. + newTo = LocUtil.clone(loc); } else { newTo = from.getLocation(); if (cc.debug) { diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/SurvivalFly.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/SurvivalFly.java index 73e8c698..22e35e54 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/SurvivalFly.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/SurvivalFly.java @@ -8,6 +8,7 @@ import java.util.Set; import org.bukkit.Location; import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; import fr.neatmonster.nocheatplus.actions.ParameterName; import fr.neatmonster.nocheatplus.checks.Check; @@ -65,6 +66,9 @@ public class SurvivalFly extends Check { private final Set reallySneaking = new HashSet(30); + /** For temporary use: LocUtil.clone before passing deeply, call setWorld(null) after use. */ + private final Location useLoc = new Location(null, 0, 0, 0); + /** * Instantiates a new survival fly check. */ @@ -148,8 +152,6 @@ public class SurvivalFly extends Check { // Mixed checks (lost ground). ///////////////////////////////// -// data.stats.addStats(data.stats.getId("sfCheck", true), 1); - // "Lost ground" workaround. if (fromOnGround || from.isResetCond()) { @@ -161,7 +163,6 @@ public class SurvivalFly extends Check { // TODO: Consider if (!resetTo) ? // Check lost-ground workarounds. resetFrom = lostGround(player, from, to, hDistance, yDistance, sprinting, data, cc); -// data.stats.addStats(data.stats.getId("sfLostGround", true), resetFrom ? 1 : 0); // Note: if not setting resetFrom, other places have to check assumeGround... } @@ -428,7 +429,7 @@ public class SurvivalFly extends Check { } // Invalidation of vertical velocity. - if (data.verticalVelocityUsed > cc.velocityGraceTicks && yDistance <= 0 && data.sfLastYDist > 0) { + if (data.verticalVelocityUsed > cc.velocityGraceTicks && yDistance <= 0 && data.sfLastYDist > 0 && data.sfLastYDist != Double.MAX_VALUE) { // data.verticalFreedom = 0; // TODO: <- why? data.verticalVelocityCounter = 0; data.verticalVelocity = 0; @@ -637,7 +638,7 @@ public class SurvivalFly extends Check { if (cc.survivalFlyAccountingV) { // Currently only for "air" phases. // Vertical. - if (yDirChange && data.sfLastYDist > 0) { + if (yDirChange && data.sfLastYDist > 0) { // (Double.MAX_VALUE is checked above.) // Change to descending phase. data.vDistAcc.clear(); // Allow adding 0. @@ -719,6 +720,7 @@ public class SurvivalFly extends Check { /** * Check on change of y direction. + *
    Note: data.sfLastYDist must not be Double.MAX_VALUE when calling this. * @param yDistance * @param vDistanceAboveLimit * @return vDistanceAboveLimit @@ -737,7 +739,9 @@ public class SurvivalFly extends Check { vDistanceAboveLimit = Math.max(vDistanceAboveLimit, Math.abs(yDistance)); tags.add("ychincfly"); } - else tags.add("ychincair"); + else { + tags.add("ychincair"); + } } } else{ @@ -869,7 +873,7 @@ public class SurvivalFly extends Check { allowHop = false; // Magic! // 2x horizontal speed increase detection. - if (hDistance - data.sfLastHDist >= walkSpeed * 0.5 && data.bunnyhopDelay == bunnyHopMax - 1) { + if (data.sfLastHDist != Double.MAX_VALUE && hDistance - data.sfLastHDist >= walkSpeed * 0.5 && data.bunnyhopDelay == bunnyHopMax - 1) { if (data.sfLastYDist == 0.0 && (data.fromWasReset || data.toWasReset) && yDistance >= 0.4) { // TODO: Confine to last was hop (according to so far input on this topic). tags.add(DOUBLE_BUNNY); @@ -960,7 +964,7 @@ public class SurvivalFly extends Check { // Check workarounds. if (yDistance <= 0.5) { // TODO: mediumLiftOff: refine conditions (general) , to should be near water level. - if (data.mediumLiftOff == MediumLiftOff.GROUND && !BlockProperties.isLiquid(from.getTypeIdAbove()) || !to.isInLiquid() || (toOnGround || data.sfLastYDist - yDistance >= 0.010 || to.isAboveStairs())) { + if (data.mediumLiftOff == MediumLiftOff.GROUND && !BlockProperties.isLiquid(from.getTypeIdAbove()) || !to.isInLiquid() || (toOnGround || data.sfLastYDist != Double.MAX_VALUE && data.sfLastYDist - yDistance >= 0.010 || to.isAboveStairs())) { vAllowedDistance = walkSpeed * modSwim + 0.5; vDistanceAboveLimit = yDistance - vAllowedDistance; } @@ -1084,10 +1088,9 @@ public class SurvivalFly extends Check { // Half block step up. if (yDistance <= 0.5 && hDistance < 0.5 && setBackYDistance <= 1.3 + 0.2 * data.jumpAmplifier && to.isOnGround()) { - if (data.sfLastYDist < 0 || from.isOnGround(0.5 - Math.abs(yDistance))) { + if (data.sfLastYDist < 0.0 || from.isOnGround(0.5 - Math.abs(yDistance))) { return applyLostGround(player, from, true, data, "step"); } -// else data.stats.addStats(data.stats.getId("sfLostGround_" + "step", true), 0); } // Interpolation check. @@ -1096,9 +1099,9 @@ public class SurvivalFly extends Check { // TODO: Check use of jump-amplifier. // TODO: Might check fall distance. // && data.sfJumpPhase > 3 <- Seems to be a problem with cake on a block + jump over both mini edges (...). - if (data.fromX != Double.MAX_VALUE && yDistance > 0 && data.sfLastYDist < 0 && !to.isOnGround()) { + if (data.fromX != Double.MAX_VALUE && yDistance > 0 && data.sfLastYDist < 0.0 && !to.isOnGround()) { // TODO: Check if last-y-dist or sprinting should be considered. - if (setBackYDistance > 0D && setBackYDistance <= 1.5D + 0.2 * data.jumpAmplifier || setBackYDistance < 0 && Math.abs(setBackYDistance) < 3.0) { + if (setBackYDistance > 0.0 && setBackYDistance <= 1.5D + 0.2 * data.jumpAmplifier || setBackYDistance < 0.0 && Math.abs(setBackYDistance) < 3.0) { // Interpolate from last to-coordinates to the from // coordinates (with some safe-guard). final double dX = from.getX() - data.fromX; @@ -1116,7 +1119,6 @@ public class SurvivalFly extends Check { if (BlockProperties.isOnGround(from.getBlockCache(), Math.min(data.fromX, from.getX()) - r, iY - yMargin, Math.min(data.fromZ, from.getZ()) - r, Math.max(data.fromX, from.getX()) + r, iY + 0.25, Math.max(data.fromZ, from.getZ()) + r, 0L)) { return applyLostGround(player, from, true, data, "interpolate"); } -// else data.stats.addStats(data.stats.getId("sfLostGround_" + "interpolate", true), 0); } } } @@ -1151,26 +1153,24 @@ public class SurvivalFly extends Check { // TODO: <= 7 might work with speed II, not sure with above. // TODO: account for speed/sprint // TODO: account for half steps !? - if (from.isOnGround(0.6, 0.4, 0, 0L) ) { + if (from.isOnGround(0.6, 0.4, 0.0, 0L) ) { // TODO: further narrow down bounds ? // Temporary "fix". return applyLostGround(player, from, true, data, "pyramid"); } -// else data.stats.addStats(data.stats.getId("sfLostGround_" + "pyramid", true), 0); } // Check for jumping up strange blocks like flower pots on top of other blocks. - if (yDistance == 0 && data.sfLastYDist > 0 && data.sfLastYDist < 0.25 && data.sfJumpPhase <= 6 + data.jumpAmplifier * 3 && setBackYDistance > 1.0 && setBackYDistance < 1.5 + 0.2 * data.jumpAmplifier && !to.isOnGround()) { + if (yDistance == 0.0 && data.sfLastYDist > 0.0 && data.sfLastYDist < 0.25 && data.sfJumpPhase <= 6 + data.jumpAmplifier * 3.0 && setBackYDistance > 1.0 && setBackYDistance < 1.5 + 0.2 * data.jumpAmplifier && !to.isOnGround()) { // TODO: confine by block types ? if (from.isOnGround(0.25, 0.4, 0, 0L) ) { // Temporary "fix". return applyLostGround(player, from, true, data, "ministep"); } -// else data.stats.addStats(data.stats.getId("sfLostGround_" + "ministep", true), 0); } } // Lost ground while falling onto/over edges of blocks. - if (yDistance < 0 && hDistance <= 0.5 && data.sfLastYDist < 0 && yDistance > data.sfLastYDist && !to.isOnGround()) { + if (yDistance < 0 && hDistance <= 0.5 && data.sfLastYDist < 0.0 && yDistance > data.sfLastYDist && !to.isOnGround()) { // TODO: Should this be an extra lost-ground(to) check, setting toOnGround [for no-fall no difference]? // TODO: yDistance <= 0 might be better. // Also clear accounting data. @@ -1178,7 +1178,6 @@ public class SurvivalFly extends Check { if (from.isOnGround(0.5, 0.2, 0) || to.isOnGround(0.5, Math.min(0.2, 0.01 + hDistance), Math.min(0.1, 0.01 + -yDistance))) { return applyLostGround(player, from, true, data, "edge"); } -// else data.stats.addStats(data.stats.getId("sfLostGround_" + "edge", true), 0); } // Nothing found. @@ -1213,7 +1212,6 @@ public class SurvivalFly extends Check { // (Usually yDistance should be -0.078) return applyLostGround(player, from, true, data, "fastedge"); } -// else data.stats.addStats(data.stats.getId("sfLostGround_" + "fastedge", true), 0); } return false; } @@ -1246,7 +1244,6 @@ public class SurvivalFly extends Check { // Tell NoFall that we assume the player to have been on ground somehow. data.noFallAssumeGround = true; tags.add("lostground_" + tag); -// data.stats.addStats(data.stats.getId("sfLostGround_" + tag, true), 1); return true; } @@ -1310,7 +1307,7 @@ public class SurvivalFly extends Check { if (data.hasSetBack()) { final Location newTo = data.getSetBack(loc); data.prepareSetBack(newTo); - player.teleport(newTo); + player.teleport(newTo, TeleportCause.PLUGIN); } else{ // Solve by extra actions ? Special case (probably never happens)? @@ -1344,7 +1341,10 @@ public class SurvivalFly extends Check { } if (data.sfCobwebVL < 550) { // Totally random ! // Silently set back. - if (!data.hasSetBack()) data.setSetBack(player.getLocation()); // ? check moment of call. + if (!data.hasSetBack()) { + data.setSetBack(player.getLocation(useLoc)); // ? check moment of call. + useLoc.setWorld(null); + } data.sfJumpPhase = 0; data.sfLastYDist = data.sfLastHDist = Double.MAX_VALUE; return true; diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/VehicleSetBack.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/VehicleSetBack.java index 764cc722..e4bafd8e 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/VehicleSetBack.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/VehicleSetBack.java @@ -30,7 +30,7 @@ public class VehicleSetBack implements Runnable{ final MovingData data = MovingData.getData(player); data.morePacketsVehicleTaskId = -1; try{ - data.setTeleported(location); + data.prepareSetBack(location); TeleportUtil.teleport(vehicle, player, location, debug); } catch(Throwable t){ diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/CommandUtil.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/CommandUtil.java index 90cce2ed..bfe52fdf 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/CommandUtil.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/CommandUtil.java @@ -24,57 +24,63 @@ public class CommandUtil { * Return plugin + server commands [Subject to change]. * @return Returns null if not CraftBukkit or CommandMap not available. */ - public static CommandMap getCommandMap(){ - // TODO: compat / null - try{ + public static CommandMap getCommandMap() { + try { return NCPAPIProvider.getNoCheatPlusAPI().getMCAccess().getCommandMap(); } - catch(Throwable t){ + catch (Throwable t) { LogUtil.logSevere(t); return null; } } /** - * Fails with an exception if SimpleCommandMap is not found, currently. + * Get all Command instances, that NCP can get hold of. Attempt to get a SimpleCommandMap instance from the server to get the actually registered commands, but also get commands from JavaPlugin instances. * @return */ - public static Collection getCommands(){ - CommandMap commandMap = getCommandMap(); - if (commandMap != null && commandMap instanceof SimpleCommandMap){ - return ((SimpleCommandMap) commandMap).getCommands(); + public static Collection getCommands() { + final Collection commands = new LinkedHashSet(500); + + // All (?) commands from the SimpleCommandMap of the server, if available. + final CommandMap commandMap = getCommandMap(); + if (commandMap != null && commandMap instanceof SimpleCommandMap) { + commands.addAll(((SimpleCommandMap) commandMap).getCommands()); } - else{ - final Collection commands = new LinkedHashSet(100); - for (final Plugin plugin : Bukkit.getPluginManager().getPlugins()){ - if (plugin instanceof JavaPlugin){ - final JavaPlugin javaPlugin = (JavaPlugin) plugin; - final Map> map = javaPlugin.getDescription().getCommands(); - if (map != null){ - for (String label : map.keySet()){ - Command command = javaPlugin.getCommand(label); - if (command != null) commands.add(command); + // TODO: Fall-back for Vanilla / CB commands? [Fall-back should be altering permission defaults, though negating permissions is the right way.] + + // Fall-back: plugin commands. + for (final Plugin plugin : Bukkit.getPluginManager().getPlugins()) { + if (plugin instanceof JavaPlugin) { + final JavaPlugin javaPlugin = (JavaPlugin) plugin; + final Map> map = javaPlugin.getDescription().getCommands(); + if (map != null) { + for (String label : map.keySet()) { + Command command = javaPlugin.getCommand(label); + if (command != null) { + commands.add(command); } } } } - // TODO: Vanilla / CB commands !? - return commands; } + + return commands; } /** * Get the command label (trim + lower case), include server commands [subject to change]. * @param alias * @param strict If to return null if no command is found. - * @return + * @return The command label, if possible to find, or the alias itself (+ trim + lower-case). */ - public static String getCommandLabel(final String alias, final boolean strict){ + public static String getCommandLabel(final String alias, final boolean strict) { final Command command = getCommand(alias); - if (command == null){ + if (command == null) { return strict ? null : alias.trim().toLowerCase(); } - else return command.getLabel().trim().toLowerCase(); + else { + return command.getLabel().trim().toLowerCase(); + } } /** @@ -85,7 +91,7 @@ public class CommandUtil { public static Command getCommand(final String alias) { final String lcAlias = alias.trim().toLowerCase(); final CommandMap map = getCommandMap(); - if (map != null){ + if (map != null) { return map.getCommand(lcAlias); } else { // TODO: maybe match versus plugin commands. @@ -101,18 +107,22 @@ public class CommandUtil { public static List getCheckTypeTabMatches(final String input) { final String ref = input.toUpperCase().replace('-', '_').replace('.', '_'); final List res = new ArrayList(); - for (final CheckType checkType : CheckType.values()){ + for (final CheckType checkType : CheckType.values()) { final String name = checkType.name(); - if (name.startsWith(ref)) res.add(name); - } - if (ref.indexOf('_') == -1){ - for (final CheckType checkType : CheckType.values()){ - final String name = checkType.name(); - final String[] split = name.split("_", 2); - if (split.length > 1 && split[1].startsWith(ref)) res.add(name); + if (name.startsWith(ref)) { + res.add(name); } } - if (!res.isEmpty()){ + if (ref.indexOf('_') == -1) { + for (final CheckType checkType : CheckType.values()) { + final String name = checkType.name(); + final String[] split = name.split("_", 2); + if (split.length > 1 && split[1].startsWith(ref)) { + res.add(name); + } + } + } + if (!res.isEmpty()) { Collections.sort(res); return res; } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/NoCheatPlusCommand.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/NoCheatPlusCommand.java index 8e178742..93b9683f 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/NoCheatPlusCommand.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/NoCheatPlusCommand.java @@ -22,7 +22,7 @@ import fr.neatmonster.nocheatplus.command.admin.CommandsCommand; import fr.neatmonster.nocheatplus.command.admin.InfoCommand; import fr.neatmonster.nocheatplus.command.admin.InspectCommand; import fr.neatmonster.nocheatplus.command.admin.LagCommand; -import fr.neatmonster.nocheatplus.command.admin.NCPVersionCommand; +import fr.neatmonster.nocheatplus.command.admin.VersionCommand; import fr.neatmonster.nocheatplus.command.admin.ReloadCommand; import fr.neatmonster.nocheatplus.command.admin.RemovePlayerCommand; import fr.neatmonster.nocheatplus.command.admin.exemption.ExemptCommand; @@ -89,7 +89,7 @@ public class NoCheatPlusCommand extends BaseCommand{ new KickCommand(plugin), new KickListCommand(plugin), new LagCommand(plugin), - new NCPVersionCommand(plugin), + new VersionCommand(plugin), new NotifyCommand(plugin), new ReloadCommand(plugin, notifyReload), new RemovePlayerCommand(plugin), diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/actions/BanCommand.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/actions/BanCommand.java index 6a0e61f0..62032056 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/actions/BanCommand.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/actions/BanCommand.java @@ -1,9 +1,10 @@ package fr.neatmonster.nocheatplus.command.actions; import java.util.List; +import java.util.UUID; import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; +import org.bukkit.Server; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -14,6 +15,7 @@ import fr.neatmonster.nocheatplus.command.BaseCommand; import fr.neatmonster.nocheatplus.logging.LogUtil; import fr.neatmonster.nocheatplus.permissions.Permissions; import fr.neatmonster.nocheatplus.players.DataManager; +import fr.neatmonster.nocheatplus.utilities.IdUtil; public class BanCommand extends BaseCommand { @@ -23,32 +25,62 @@ public class BanCommand extends BaseCommand { @Override public boolean onCommand(final CommandSender sender, Command command, String label, String[] args) { + + // TODO: Consider supporting vanilla syntax or removing this command :p. + // Args contains "ban" as first arg. - if (args.length < 2) return false; - final String name = args[1]; + if (args.length < 2) { + return false; + } + final String name = args[1].trim(); final String reason; - if (args.length > 2) reason = AbstractCommand.join(args, 2); - else reason = ""; + if (args.length > 2) { + reason = AbstractCommand.join(args, 2); + } + else { + reason = ""; + } ban(sender, name, reason); return true; } + /** + * + * @param sender + * @param name Trimmed name. + * @param reason + */ void ban(CommandSender sender, String name, String reason) { + final Server server = Bukkit.getServer(); Player player = DataManager.getPlayer(name); + // Pro logic below. + if (player == null && !IdUtil.isValidMinecraftUserName(name)) { + UUID id = IdUtil.UUIDFromStringSafe(name); + if (id != null) { + LogUtil.logWarning("Banning by UUID might not work (" + id.toString()+"), relay to the vanilla command."); + } else { + LogUtil.logWarning("Might not be a valid user name: " + name); + } + } if (player != null){ player.kickPlayer(reason); } - OfflinePlayer offlinePlayer = Bukkit.getServer().getOfflinePlayer(name); - offlinePlayer.setBanned(true); - LogUtil.logInfo("[NoCheatPlus] (" + sender.getName() + ") Banned " + offlinePlayer.getName() + " : " + reason); + // Relay to the server command for compatibility reasons. + server.dispatchCommand(server.getConsoleSender(), "ban " + name); + logBan(sender, player, name, reason); + + } + + private void logBan(CommandSender sender, Player player, String name, String reason) { + LogUtil.logInfo("[NoCheatPlus] (" + sender.getName() + ") Banned " + name + (player != null ? ("/" + player.getName()) : "") + " : " + reason); } /* (non-Javadoc) * @see fr.neatmonster.nocheatplus.command.AbstractCommand#onTabComplete(org.bukkit.command.CommandSender, org.bukkit.command.Command, java.lang.String, java.lang.String[]) */ @Override - public List onTabComplete(CommandSender sender, Command command, - String alias, String[] args) { + public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + // TODO: Consider adding player names and other. return null; } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/admin/InspectCommand.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/admin/InspectCommand.java index 39a56265..3e154be6 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/admin/InspectCommand.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/admin/InspectCommand.java @@ -13,6 +13,7 @@ import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.potion.PotionEffect; import fr.neatmonster.nocheatplus.command.BaseCommand; +import fr.neatmonster.nocheatplus.compat.BridgeHealth; import fr.neatmonster.nocheatplus.permissions.Permissions; import fr.neatmonster.nocheatplus.players.DataManager; @@ -53,7 +54,7 @@ public class InspectCommand extends BaseCommand { builder.append(" (" + (player.isOnline() ? "online" : "offline") + (player.isDead() ? ",dead" : "") + (player.isValid() ? "" : ",invalid") + (player.isInsideVehicle() ? (",vehicle=" + player.getVehicle().getType() + "@" + locString(player.getVehicle().getLocation())) : "")+ "):"); // TODO: isValid, isDead, isInsideVehicle ... // Health. - builder.append(" health=" + f1.format(player.getHealth()) + "/" + f1.format(player.getMaxHealth())); + builder.append(" health=" + f1.format(BridgeHealth.getHealth(player)) + "/" + f1.format(BridgeHealth.getMaxHealth(player))); // Food. builder.append(" food=" + player.getFoodLevel()); // Exp. diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/admin/NCPVersionCommand.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/admin/VersionCommand.java similarity index 91% rename from NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/admin/NCPVersionCommand.java rename to NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/admin/VersionCommand.java index 101ed5ff..badf93ca 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/admin/NCPVersionCommand.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/admin/VersionCommand.java @@ -18,9 +18,9 @@ import fr.neatmonster.nocheatplus.hooks.NCPHookManager; import fr.neatmonster.nocheatplus.permissions.Permissions; import fr.neatmonster.nocheatplus.utilities.StringUtil; -public class NCPVersionCommand extends BaseCommand{ +public class VersionCommand extends BaseCommand{ - public NCPVersionCommand(JavaPlugin plugin) { + public VersionCommand(JavaPlugin plugin) { super(plugin, "version", Permissions.COMMAND_VERSION, new String[]{"versions", "ver"}); } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/admin/notify/NotifyOffCommand.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/admin/notify/NotifyOffCommand.java index 79792314..688568b6 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/admin/notify/NotifyOffCommand.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/admin/notify/NotifyOffCommand.java @@ -29,7 +29,7 @@ public class NotifyOffCommand extends BaseCommand { return true; } DataManager.getPlayerData(sender.getName(), true).setNotifyOff(true); - sender.sendMessage(ChatColor.RED + "NCP: " + ChatColor.WHITE + "Notifications are now turned " + ChatColor.RED + "off" + ChatColor.WHITE + "."); + sender.sendMessage(TAG + "Notifications are now turned " + ChatColor.RED + "off" + ChatColor.WHITE + "."); return true; } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/admin/notify/NotifyOnCommand.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/admin/notify/NotifyOnCommand.java index 98b73dec..7648ac76 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/admin/notify/NotifyOnCommand.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/command/admin/notify/NotifyOnCommand.java @@ -29,7 +29,7 @@ public class NotifyOnCommand extends BaseCommand { return true; } DataManager.getPlayerData(sender.getName(), true).setNotifyOff(false); - sender.sendMessage(ChatColor.RED + "NCP: " + ChatColor.WHITE + "Notifications are now turned " + ChatColor.YELLOW + "on" + ChatColor.WHITE + "."); + sender.sendMessage(TAG + "Notifications are now turned " + ChatColor.YELLOW + "on" + ChatColor.WHITE + "."); return true; } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/AlmostBoolean.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/AlmostBoolean.java index 539f3d1b..54a58efe 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/AlmostBoolean.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/AlmostBoolean.java @@ -1,21 +1,38 @@ package fr.neatmonster.nocheatplus.compat; +/** + * Some tri-state with booleans in mind. + * @author mc_dev + * + */ public enum AlmostBoolean{ - YES(true), - NO(false), - MAYBE(false); - + YES, + NO, + MAYBE; + + /** + * "Match" a boolean. + * @param value + * @return + */ public static final AlmostBoolean match(final boolean value) { return value ? YES : NO; } - - private final boolean decision; - - private AlmostBoolean(final boolean decision){ - this.decision = decision; - } + /** + * Pessimistic interpretation: true iff YES. + * @return + */ public boolean decide(){ - return decision; + return this == YES; } + + /** + * Optimistic interpretation: true iff not NO. + * @return + */ + public boolean decideOptimistically() { + return this != NO; + } + } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/MCAccess.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/MCAccess.java index 505abedc..b6b3cf2b 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/MCAccess.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/MCAccess.java @@ -135,5 +135,12 @@ public interface MCAccess { * @return */ public boolean hasGravity(Material type); - + +// /** +// * Correct the direction (yaw + pitch). If this can't be done lightly it should just do nothing. Check pitch and yaw before calling, use auxiliary methods from LocUtil. +// * @param player +// */ +// public void correctDirection(Player player); + + } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/blocks/init/BlockInit.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/blocks/init/BlockInit.java index add4dd29..86a329e4 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/blocks/init/BlockInit.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/blocks/init/BlockInit.java @@ -12,29 +12,29 @@ import fr.neatmonster.nocheatplus.utilities.BlockProperties; */ public class BlockInit { - // TODO: Add "assertMaterialExist" for id and id + name (name: add assertMaterialMatches) also with name with RuntimeException + // TODO: Change to assert names only?, would be better with being able to feed MC names or map them as well, though. /** * Check for Material existence, throw RuntimeException if not. * @param id */ public static void assertMaterialExists(int id) { - if (Material.getMaterial(id) == null){ + if (BlockProperties.getMaterial(id) == null) { throw new RuntimeException("Material " + id + " does not exist."); } } /** - * Check for material existence and naming (exact match). + * Check for material existence and naming (exact match). * @param id * @param name */ public static void assertMaterialName(int id, String name) { - Material mat = Material.getMaterial(id); - if ( mat == null){ + Material mat = BlockProperties.getMaterial(id); + if ( mat == null) { throw new RuntimeException("Material " + id + " does not exist."); } - if (mat.name().equals(name)){ + if (mat.name().equals(name)) { throw new RuntimeException("Name for Material " + id + " ('" + mat.name() + "') does not match '" + name + "'."); } } @@ -45,13 +45,13 @@ public class BlockInit { * @param parts */ public static void assertMaterialNameMatch(int id, String... parts) { - Material mat = Material.getMaterial(id); - if ( mat == null){ + Material mat = BlockProperties.getMaterial(id); + if ( mat == null) { throw new RuntimeException("Material " + id + " does not exist."); } String name = mat.name().toLowerCase(); - for (String part : parts){ - if (name.indexOf(part.toLowerCase()) < 0){ + for (String part : parts) { + if (name.indexOf(part.toLowerCase()) < 0) { throw new RuntimeException("Name for Material " + id + " ('" + mat.name() + "') should contain '" + part + "'."); } } @@ -62,8 +62,8 @@ public class BlockInit { * @param newId * @param mat */ - public static void setPropsAs(int newId, Material mat){ - setPropsAs(newId, mat.getId()); + public static void setPropsAs(int newId, Material mat) { + BlockProperties.setBlockProps(newId, BlockProperties.getBlockProps(mat)); } /** @@ -71,7 +71,7 @@ public class BlockInit { * @param newId * @param mat */ - public static void setPropsAs(int newId, int otherId){ + public static void setPropsAs(int newId, int otherId) { BlockProperties.setBlockProps(newId, BlockProperties.getBlockProps(otherId)); } @@ -80,7 +80,7 @@ public class BlockInit { * @param newId * @param mat */ - public static void setAs(int newId, Material mat){ + public static void setAs(int newId, Material mat) { BlockFlags.setFlagsAs(newId, mat); setPropsAs(newId, mat); } @@ -90,7 +90,7 @@ public class BlockInit { * @param newId * @param mat */ - public static void setAs(int newId, int otherId){ + public static void setAs(int newId, int otherId) { BlockFlags.setFlagsAs(newId, otherId); setPropsAs(newId, otherId); } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/ConfPaths.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/ConfPaths.java index 8616c6c6..58440013 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/ConfPaths.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/ConfPaths.java @@ -84,6 +84,7 @@ public abstract class ConfPaths { private static final String PROTECT_COMMANDS = PROTECT + "commands."; private static final String PROTECT_COMMANDS_CONSOLEONLY = PROTECT_COMMANDS + "consoleonly."; public static final String PROTECT_COMMANDS_CONSOLEONLY_ACTIVE = PROTECT_COMMANDS_CONSOLEONLY + "active"; + public static final String PROTECT_COMMANDS_CONSOLEONLY_MSG = PROTECT_COMMANDS_CONSOLEONLY + "message"; public static final String PROTECT_COMMANDS_CONSOLEONLY_CMDS = PROTECT_COMMANDS_CONSOLEONLY + "commands"; // Plugins settings. private static final String PROTECT_PLUGINS = PROTECT + "plugins."; @@ -123,7 +124,6 @@ public abstract class ConfPaths { public static final String BLOCKBREAK_FASTBREAK_BUCKETS_FACTOR = BLOCKBREAK_FASTBREAK_BUCKETS + "factor"; public static final String BLOCKBREAK_FASTBREAK_DELAY = BLOCKBREAK_FASTBREAK + "delay"; public static final String BLOCKBREAK_FASTBREAK_GRACE = BLOCKBREAK_FASTBREAK + "grace"; - public static final String BLOCKBREAK_FASTBREAK_MOD_CREATIVE = BLOCKBREAK_FASTBREAK + "intervalcreative"; public static final String BLOCKBREAK_FASTBREAK_MOD_SURVIVAL = BLOCKBREAK_FASTBREAK + "intervalsurvival"; public static final String BLOCKBREAK_FASTBREAK_ACTIONS = BLOCKBREAK_FASTBREAK + "actions"; @@ -200,6 +200,7 @@ public abstract class ConfPaths { private static final String BLOCKPLACE_NOSWING = BLOCKPLACE + "noswing."; public static final String BLOCKPLACE_NOSWING_CHECK = BLOCKPLACE_NOSWING + "active"; + public static final String BLOCKPLACE_NOSWING_EXCEPTIONS = BLOCKPLACE_NOSWING + "exceptions"; public static final String BLOCKPLACE_NOSWING_ACTIONS = BLOCKPLACE_NOSWING + "actions"; private static final String BLOCKPLACE_REACH = BLOCKPLACE + "reach."; @@ -243,6 +244,7 @@ public abstract class ConfPaths { public static final String CHAT_TEXT_CHECK = CHAT_TEXT + "active"; public static final String CHAT_TEXT_DEBUG = CHAT_TEXT + "debug"; public static final String CHAT_TEXT_ENGINE_MAXIMUM = CHAT_TEXT + "maximum"; + public static final String CHAT_TEXT_ALLOWVLRESET = CHAT_TEXT + "allowvlreset"; public static final String CHAT_TEXT_FREQ = CHAT_TEXT + "frequency."; public static final String CHAT_TEXT_FREQ_NORM = CHAT_TEXT_FREQ + "normal."; public static final String CHAT_TEXT_FREQ_NORM_FACTOR = CHAT_TEXT_FREQ_NORM + "factor"; @@ -567,11 +569,17 @@ public abstract class ConfPaths { public static final String MOVING_SPRINTINGGRACE = MOVING + "sprintinggrace"; public static final String MOVING_ASSUMESPRINT = MOVING + "assumesprint"; public static final String MOVING_SPEEDGRACE = MOVING + "speedgrace"; + public static final String MOVING_ENFORCELOCATION = MOVING + "enforcelocation"; private static final String MOVING_VEHICLES = MOVING + "vehicles."; public static final String MOVING_VEHICLES_ENFORCELOCATION = MOVING_VEHICLES + "enforcelocation"; public static final String MOVING_VEHICLES_PREVENTDESTROYOWN = MOVING_VEHICLES + "preventdestroyown"; + private static final String MOVING_TRACE = MOVING + "trace."; + public static final String MOVING_TRACE_SIZE = MOVING_TRACE + "size"; + public static final String MOVING_TRACE_MERGEDIST = MOVING_TRACE + "mergedist"; + + public static final String STRINGS = "strings"; // Compatibility section (possibly temporary). @@ -608,5 +616,7 @@ public abstract class ConfPaths { public static final String INVENTORY_ENSURECLOSE = "checks.inventory.ensureclose"; @Deprecated public static final String MISCELLANEOUS_REPORTTOMETRICS = "miscellaneous.reporttometrics"; + @Deprecated + public static final String BLOCKBREAK_FASTBREAK_MOD_CREATIVE = "checks.blockbreak.fastbreak.intervalcreative"; } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/DefaultConfig.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/DefaultConfig.java index 2eb86b0c..f54adfbb 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/DefaultConfig.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/DefaultConfig.java @@ -16,7 +16,7 @@ public class DefaultConfig extends ConfigFile { * NCP build needed for this config. * (Should only increment with changing or removing paths.) */ - public static final int buildNumber = 661; + public static final int buildNumber = 695; // TODO: auto input full version or null to an extra variable or several [fail safe for other syntax checking]? @@ -71,6 +71,7 @@ public class DefaultConfig extends ConfigFile { set(ConfPaths.PROTECT_PLUGINS_HIDE_NOCOMMAND_CMDS, new LinkedList()); // Commands (other). set(ConfPaths.PROTECT_COMMANDS_CONSOLEONLY_ACTIVE, false); + set(ConfPaths.PROTECT_COMMANDS_CONSOLEONLY_MSG, "&cI'm sorry, but this command can't be executed in chat. Use the console instead!"); set(ConfPaths.PROTECT_COMMANDS_CONSOLEONLY_CMDS, Arrays.asList("op", "deop")); // Client motd. set(ConfPaths.PROTECT_CLIENTS_MOTD_ACTIVE, true); @@ -81,7 +82,7 @@ public class DefaultConfig extends ConfigFile { set(ConfPaths.BLOCKBREAK_FASTBREAK_CHECK, true); set(ConfPaths.BLOCKBREAK_FASTBREAK_STRICT, true); - set(ConfPaths.BLOCKBREAK_FASTBREAK_DELAY, 90); + set(ConfPaths.BLOCKBREAK_FASTBREAK_DELAY, 100); set(ConfPaths.BLOCKBREAK_FASTBREAK_MOD_SURVIVAL, 100); set(ConfPaths.BLOCKBREAK_FASTBREAK_GRACE, 2000); set(ConfPaths.BLOCKBREAK_FASTBREAK_ACTIONS, "cancel vl>0 log:fastbreak:3:5:cif cancel"); @@ -115,7 +116,7 @@ public class DefaultConfig extends ConfigFile { set(ConfPaths.BLOCKINTERACT_SPEED_ACTIONS, "cancel vl>200 log:bspeed:0:2:if cancel vl>1000 cancel log:bspeed:0:2:icf cmd:kickbspeed"); set(ConfPaths.BLOCKINTERACT_VISIBLE_CHECK, true); - set(ConfPaths.BLOCKINTERACT_VISIBLE_ACTIONS, "cancel vl>5 log:bvisible:0:2:if cancel"); + set(ConfPaths.BLOCKINTERACT_VISIBLE_ACTIONS, "cancel vl>100 log:bvisible:0:10:if cancel"); // BLOCKPLACE set(ConfPaths.BLOCKPLACE_AGAINST_CHECK, true); @@ -137,6 +138,7 @@ public class DefaultConfig extends ConfigFile { set(ConfPaths.BLOCKPLACE_REACH_ACTIONS, "cancel vl>5 log:breach:0:2:if cancel"); set(ConfPaths.BLOCKPLACE_NOSWING_CHECK, true); + set(ConfPaths.BLOCKPLACE_NOSWING_EXCEPTIONS, Arrays.asList(Material.WATER_LILY.toString(), Material.FLINT_AND_STEEL.toString())); set(ConfPaths.BLOCKPLACE_NOSWING_ACTIONS, "cancel vl>10 log:noswing:0:5:if cancel"); set(ConfPaths.BLOCKPLACE_SPEED_CHECK, true); @@ -167,6 +169,7 @@ public class DefaultConfig extends ConfigFile { // Text (ordering on purpose). set(ConfPaths.CHAT_TEXT_CHECK, true); + set(ConfPaths.CHAT_TEXT_ALLOWVLRESET, true); set(ConfPaths.CHAT_TEXT_FREQ_NORM_MIN, 0.0); set(ConfPaths.CHAT_TEXT_FREQ_NORM_FACTOR, 0.9D); set(ConfPaths.CHAT_TEXT_FREQ_NORM_WEIGHT, 6); @@ -393,6 +396,10 @@ public class DefaultConfig extends ConfigFile { set(ConfPaths.MOVING_SURVIVALFLY_HOVER_FALLDAMAGE, true); set(ConfPaths.MOVING_SURVIVALFLY_HOVER_SFVIOLATION, 500); + // Moving Trace + set(ConfPaths.MOVING_TRACE_SIZE, 60); + set(ConfPaths.MOVING_TRACE_MERGEDIST, 0.9752); // Let all the hackers read code! + // Vehicles. set(ConfPaths.MOVING_VEHICLES_PREVENTDESTROYOWN, true); set(ConfPaths.MOVING_VEHICLES_ENFORCELOCATION, true); @@ -407,8 +414,9 @@ public class DefaultConfig extends ConfigFile { set(ConfPaths.MOVING_TEMPKICKILLEGAL, true); set(ConfPaths.MOVING_LOADCHUNKS_JOIN, true); set(ConfPaths.MOVING_SPRINTINGGRACE, 2.0); - set(ConfPaths.MOVING_ASSUMESPRINT, false); + set(ConfPaths.MOVING_ASSUMESPRINT, true); set(ConfPaths.MOVING_SPEEDGRACE, 4.0); + set(ConfPaths.MOVING_ENFORCELOCATION, true); // TODO: An extra file might suit these. final String start = "[player] failed [check]: "; diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/RawConfigFile.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/RawConfigFile.java index 5cb09975..2efe8a09 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/RawConfigFile.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/RawConfigFile.java @@ -12,21 +12,49 @@ import fr.neatmonster.nocheatplus.logging.LogUtil; public class RawConfigFile extends YamlConfiguration{ + private static String prepareMatchMaterial(String content) { + return content.replace(' ', '_').replace('-', '_').replace('.', '_'); + } + /** * Attempt to get an int id from a string.
    * Will return out of range numbers, attempts to parse materials. * @param content * @return */ - public static Integer parseTypeId(String content) { + @SuppressWarnings("deprecation") + public static Integer parseTypeId(String content) { content = content.trim().toUpperCase(); try{ return Integer.parseInt(content); } catch(NumberFormatException e){} try{ - Material mat = Material.matchMaterial(content.replace(' ', '_').replace('-', '_').replace('.', '_')); - if (mat != null) return mat.getId(); + Material mat = Material.matchMaterial(prepareMatchMaterial(content)); + if (mat != null) { + return mat.getId(); + } + } + catch (Exception e) {} + return null; + } + + /** + * Attempt to get a Material from a string.
    + * Will attempt to match the name but also type ids. + * @param content + * @return + */ + @SuppressWarnings("deprecation") + public static Material parseMaterial(String content) { + content = content.trim().toUpperCase(); + try{ + Integer id = Integer.parseInt(content); + return Material.getMaterial(id); + } + catch(NumberFormatException e){} + try{ + return Material.matchMaterial(prepareMatchMaterial(content)); } catch (Exception e) {} return null; @@ -90,9 +118,11 @@ public class RawConfigFile extends YamlConfiguration{ /** * Attempt to get a type id from the path somehow, return null if nothing found.
    * Will attempt to interpret strings, will return negative or out of range values. + * @deprecated Not used, will be replaced by getMaterial, if needed. * @param path * @return */ + @Deprecated public Integer getTypeId(final String path){ return getTypeId(path, null); } @@ -100,10 +130,12 @@ public class RawConfigFile extends YamlConfiguration{ /** * Attempt to get a type id from the path somehow, return preset if nothing found.
    * Will attempt to interpret strings, will return negative or out of range values. + * @deprecated Not used, will be replaced by getMaterial, if needed. * @param path * @param preset * @return */ + @Deprecated public Integer getTypeId(final String path, final Integer preset){ String content = getString(path, null); if (content != null){ @@ -119,7 +151,7 @@ public class RawConfigFile extends YamlConfiguration{ * @param path * @param target Collection to fill ids into. */ - public void readMaterialFromList(final String path, final Collection target) { + public void readMaterialIdsFromList(final String path, final Collection target) { final List content = getStringList(path); if (content == null || content.isEmpty()) return; for (final String entry : content){ @@ -132,6 +164,25 @@ public class RawConfigFile extends YamlConfiguration{ } } } + + /** + * Outputs warnings to console. + * @param path + * @param target Collection to fill materials into. + */ + public void readMaterialFromList(final String path, final Collection target) { + final List content = getStringList(path); + if (content == null || content.isEmpty()) return; + for (final String entry : content){ + final Material mat = parseMaterial(entry); + if (mat == null){ + LogUtil.logWarning("[NoCheatPlus] Bad material entry (" + path +"): " + entry); + } + else{ + target.add(mat); + } + } + } /* (non-Javadoc) * @see org.bukkit.configuration.file.YamlConfiguration#saveToString() diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/logging/DebugUtil.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/logging/DebugUtil.java index 398666ff..8bb881f0 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/logging/DebugUtil.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/logging/DebugUtil.java @@ -19,6 +19,8 @@ import fr.neatmonster.nocheatplus.utilities.build.BuildParameters; */ public class DebugUtil { + // TODO: Add useLoc1 and useLoc2. + public static boolean isSamePos(final double x1, final double y1, final double z1, final double x2, final double y2, final double z2){ return x1 == x2 && y1 == y2 && z1 == z2; } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/permissions/PermissionUtil.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/permissions/PermissionUtil.java index a32468d5..0e3ca50d 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/permissions/PermissionUtil.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/permissions/PermissionUtil.java @@ -38,7 +38,7 @@ public class PermissionUtil { * @param permissionDefault * @param permissionMessage */ - public CommandProtectionEntry(Command command, String label, String permission, PermissionDefault permissionDefault, String permissionMessage){ + public CommandProtectionEntry(Command command, String label, String permission, PermissionDefault permissionDefault, String permissionMessage) { this.command = command; this.label = label; this.permission = permission; @@ -46,14 +46,19 @@ public class PermissionUtil { this.permissionMessage = permissionMessage; } - public void restore(){ - Command registered = CommandUtil.getCommand(label); - if (registered == null || registered != command) return; - if (!label.equalsIgnoreCase(command.getLabel().trim().toLowerCase())) command.setLabel(label); + public void restore() { + // (Don't skip resetting, as there could be fall-back aliases.) +// Command registered = CommandUtil.getCommand(label); +// if (registered == null || registered != command) return; + if (!label.equalsIgnoreCase(command.getLabel().trim().toLowerCase())) { + command.setLabel(label); + } command.setPermission(permission); - if (permission != null && permissionDefault != null){ + if (permission != null && permissionDefault != null) { Permission perm = Bukkit.getPluginManager().getPermission(permission); - if (perm != null) perm.setDefault(permissionDefault); + if (perm != null && perm.getDefault() != permissionDefault) { + perm.setDefault(permissionDefault); + } } command.setPermissionMessage(permissionMessage); } @@ -61,12 +66,12 @@ public class PermissionUtil { /** * - * @param commands + * @param commands Command white-list. * @param permissionBase * @param ops * @return */ - public static List protectCommands(Collection commands, String permissionBase, boolean ops){ + public static List protectCommands(Collection commands, String permissionBase, boolean ops) { return protectCommands(permissionBase, commands, true, ops); } @@ -78,7 +83,7 @@ public class PermissionUtil { * @param ops * @return */ - public static List protectCommands(String permissionBase, Collection ignoredCommands, boolean invertIgnored, boolean ops){ + public static List protectCommands(String permissionBase, Collection ignoredCommands, boolean invertIgnored, boolean ops) { return protectCommands(permissionBase, ignoredCommands, invertIgnored, ops, ColorUtil.replaceColors(ConfigManager.getConfigFile().getString(ConfPaths.PROTECT_PLUGINS_HIDE_NOCOMMAND_MSG))); } @@ -91,30 +96,33 @@ public class PermissionUtil { * @param permissionMessage * @return */ - public static List protectCommands(String permissionBase, Collection ignoredCommands, boolean invertIgnored, boolean ops, String permissionMessage){ - Set checked = new HashSet(); - for (String label : ignoredCommands){ + public static List protectCommands(final String permissionBase, final Collection ignoredCommands, final boolean invertIgnored, final boolean ops, final String permissionMessage) { + final Set checked = new HashSet(); + for (String label : ignoredCommands) { checked.add(CommandUtil.getCommandLabel(label, false)); } - PluginManager pm = Bukkit.getPluginManager(); + final PluginManager pm = Bukkit.getPluginManager(); Permission rootPerm = pm.getPermission(permissionBase); - if (rootPerm == null){ + if (rootPerm == null) { rootPerm = new Permission(permissionBase); pm.addPermission(rootPerm); } - List changed = new LinkedList(); - for (Command command : CommandUtil.getCommands()){ - String lcLabel = command.getLabel().trim().toLowerCase(); - if (checked != null){ - if (checked.contains(lcLabel)){ - if (!invertIgnored) continue; + final List changed = new LinkedList(); + // Apply protection based on white-list or black-list. + for (final Command command : CommandUtil.getCommands()) { + final String lcLabel = command.getLabel().trim().toLowerCase(); + if (checked.contains(lcLabel) || containsAnyAliases(checked, command)) { + if (!invertIgnored) { + continue; } - else if (invertIgnored) continue; + } + else if (invertIgnored) { + continue; } // Set the permission for the command. String cmdPermName = command.getPermission(); boolean cmdHadPerm; - if (cmdPermName == null){ + if (cmdPermName == null) { // Set a permission. cmdPermName = permissionBase + "." + lcLabel; command.setPermission(cmdPermName); @@ -125,22 +133,45 @@ public class PermissionUtil { } // Set permission default behavior. Permission cmdPerm = pm.getPermission(cmdPermName); - if (cmdPerm == null){ - if (!cmdHadPerm){ + if (cmdPerm == null) { + if (!cmdHadPerm) { cmdPerm = new Permission(cmdPermName); cmdPerm.addParent(rootPerm, true); pm.addPermission(cmdPerm); } } // Create change history entry. - if (cmdHadPerm) changed.add(new CommandProtectionEntry(command, lcLabel, cmdPermName, cmdPerm.getDefault(), command.getPermissionMessage())); - else changed.add(new CommandProtectionEntry(command, lcLabel, null, null, command.getPermissionMessage())); + if (cmdHadPerm) { + // TODO: Find (local?) cause of NullPointerException... + changed.add(new CommandProtectionEntry(command, lcLabel, cmdPermName, cmdPerm.getDefault(), command.getPermissionMessage())); + } + else { + changed.add(new CommandProtectionEntry(command, lcLabel, null, null, command.getPermissionMessage())); + } // Change cmdPerm.setDefault(ops ? PermissionDefault.OP : PermissionDefault.FALSE); command.setPermissionMessage(permissionMessage); } return changed; } + + /** + * Check if the checked set contains any trim+lower-case alias of the command. + * @param checked + * @param command + * @return + */ + private static final boolean containsAnyAliases(final Set checked, final Command command) { + final Collection aliases = command.getAliases(); + if (aliases != null) { + for (final String alias : aliases) { + if (checked.contains(alias.trim().toLowerCase())) { + return true; + } + } + } + return false; + } /** * Set a permission as child for all the other permissions given in a Collection. @@ -150,17 +181,17 @@ public class PermissionUtil { public static void addChildPermission(final Collection permissions, final String childPermissionName, final PermissionDefault permissionDefault) { final PluginManager pm = Bukkit.getPluginManager(); Permission childPermission = pm.getPermission(childPermissionName); - if (childPermission == null){ + if (childPermission == null) { childPermission = new Permission(childPermissionName, "auto-generated child permission (NoCheatPlus)", permissionDefault); pm.addPermission(childPermission); } - for (final String permissionName : permissions){ + for (final String permissionName : permissions) { Permission permission = pm.getPermission(permissionName); - if (permission == null){ + if (permission == null) { permission = new Permission(permissionName, "auto-generated permission (NoCheatPlus)", permissionDefault); pm.addPermission(permission); } - if (!permission.getChildren().containsKey(childPermissionName)){ + if (!permission.getChildren().containsKey(childPermissionName)) { childPermission.addParent(permission, true); } } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/players/DataManager.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/players/DataManager.java index 64d5deb8..c7ff2a0e 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/players/DataManager.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/players/DataManager.java @@ -482,6 +482,15 @@ public class DataManager implements Listener, INotifyReload, INeedConfig, Compon } } + /** + * Convenience method, also hiding how player data is stored for a Player instance - always creates a PlayerData instance, if not already present. + * @param player + * @return + */ + public static PlayerData getPlayerData(final Player player) { + return getPlayerData(player.getName(), true); + } + /** * * @param playerName diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/players/PlayerTask.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/players/PlayerTask.java index c8c82386..8aea0559 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/players/PlayerTask.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/players/PlayerTask.java @@ -11,39 +11,55 @@ import fr.neatmonster.nocheatplus.utilities.OnDemandTickListener; */ public class PlayerTask extends OnDemandTickListener { + // TODO: Consider overriding some logic, because it is used in the main thread only (context: isRegisterd + register). + public final String lcName; protected boolean updateInventory = false; + +// protected boolean correctDirection = false; /** * * @param name Not demanded to be case sensitive. */ - public PlayerTask(final String name){ + public PlayerTask(final String name) { this.lcName = name.toLowerCase(); } + @SuppressWarnings("deprecation") @Override public boolean delegateTick(final int tick, final long timeLast) { final Player player = DataManager.getPlayer(lcName); - if (player != null){ - if (player.isOnline()){ - if (updateInventory){ + if (player != null) { + if (player.isOnline()) { +// if (correctDirection) { +// final MCAccess access = NCPAPIProvider.getNoCheatPlusAPI().getMCAccess(); +// access.correctDirection(player); +// } + if (updateInventory) { player.updateInventory(); - updateInventory = false; } } } + // Reset values (players logging back in should be fine or handled differently). + updateInventory = false; +// correctDirection = false; return false; } - public void updateInventory(){ + public void updateInventory() { // TODO: Might not allow registering every tick. updateInventory = true; register(); } +// public void correctDirection() { +// correctDirection = true; +// register(); +// } + // TODO: updateHunger } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/BlockCache.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/BlockCache.java index ca01b482..d129bda8 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/BlockCache.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/BlockCache.java @@ -14,6 +14,10 @@ import fr.neatmonster.nocheatplus.utilities.ds.CoordMap; */ public abstract class BlockCache { + // TODO: New concepts (Might switch to material, inspect MC+CB code for reliability and performance of block-ids during runtime). + + private static final int ID_AIR = 0; + /** * Convenience method to check if the bounds as returned by getBounds cover a whole block. * @param bounds Can be null, must have 6 fields. @@ -21,7 +25,7 @@ public abstract class BlockCache { */ public static final boolean isFullBounds(final double[] bounds) { if (bounds == null) return false; - for (int i = 0; i < 3; i ++){ + for (int i = 0; i < 3; i ++) { if (bounds[i] > 0.0) return false; if (bounds[i + 3] < 1.0) return false; } @@ -43,9 +47,9 @@ public abstract class BlockCache { final int maxX = Location.locToBlock(x + xzMargin) / 16; final int minZ = Location.locToBlock(z - xzMargin) / 16; final int maxZ = Location.locToBlock(z + xzMargin) / 16; - for (int cx = minX; cx <= maxX; cx ++){ - for (int cz = minZ; cz <= maxZ; cz ++){ - if (!world.isChunkLoaded(cx, cz)){ + for (int cx = minX; cx <= maxX; cx ++) { + for (int cz = minZ; cz <= maxZ; cz ++) { + if (!world.isChunkLoaded(cx, cz)) { world.loadChunk(cx, cz); loaded ++; } @@ -63,7 +67,7 @@ public abstract class BlockCache { /** Cached shape values. */ private final CoordMap boundsMap = new CoordMap(23); - private int maxBlockY = 255; + protected int maxBlockY = 255; // TODO: switch to nodes with all details on, store a working node ? @@ -71,10 +75,10 @@ public abstract class BlockCache { // private int[] id = null; // private int[] data = null; - public BlockCache(){ + public BlockCache() { } - public BlockCache(final World world){ + public BlockCache(final World world) { setAccess(world); } @@ -122,7 +126,7 @@ public abstract class BlockCache { * Remove references.
    * NOTE: You must delete world references with this one. */ - public void cleanup(){ + public void cleanup() { idMap.clear(); dataMap.clear(); boundsMap.clear(); @@ -157,8 +161,10 @@ public abstract class BlockCache { */ public int getTypeId(final int x, final int y, final int z) { final Integer pId = idMap.get(x, y, z); - if (pId != null) return pId; - final Integer nId = fetchTypeId(x, y, z); + if (pId != null) { + return pId; + } + final Integer nId = (y < 0 || y > maxBlockY) ? ID_AIR : fetchTypeId(x, y, z); idMap.put(x, y, z, nId); return nId; } @@ -172,8 +178,10 @@ public abstract class BlockCache { */ public int getData(final int x, final int y, final int z) { final Integer pData = dataMap.get(x, y, z); - if (pData != null) return pData; - final Integer nData = fetchData(x, y, z); + if (pData != null) { + return pData; + } + final Integer nData = (y < 0 || y > maxBlockY) ? 0 : fetchData(x, y, z); dataMap.put(x, y, z, nData); return nData; } @@ -185,10 +193,12 @@ public abstract class BlockCache { * @param z * @return Array of floats (minX, minY, minZ, maxX, maxY, maxZ), may be null theoretically. Do not change these in place, because they might get cached. */ - public double[] getBounds(final int x, final int y, final int z){ + public double[] getBounds(final int x, final int y, final int z) { final double[] pBounds = boundsMap.get(x, y, z); - if (pBounds != null) return pBounds; - final double[] nBounds = fetchBounds(x, y, z); + if (pBounds != null) { + return pBounds; + } + final double[] nBounds = (y < 0 || y > maxBlockY) ? null : fetchBounds(x, y, z); boundsMap.put(x, y, z, nBounds); return nBounds; } @@ -200,7 +210,7 @@ public abstract class BlockCache { * @param z * @return */ - public boolean isFullBounds(final int x, final int y, final int z){ + public boolean isFullBounds(final int x, final int y, final int z) { return isFullBounds(getBounds(x, y, z)); } @@ -208,7 +218,7 @@ public abstract class BlockCache { * Get the maximal y coordinate a block can be at (non air). * @return */ - public int getMaxBlockY(){ + public int getMaxBlockY() { return maxBlockY; } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/BlockFlags.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/BlockFlags.java index a101274c..b6b59513 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/BlockFlags.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/BlockFlags.java @@ -15,8 +15,8 @@ public class BlockFlags { * @param id * @param mat */ - public static void setFlagsAs(int id, Material mat){ - setFlagsAs(id, mat.getId()); + public static void setFlagsAs(int id, Material mat) { + BlockProperties.setBlockFlags(id, BlockProperties.getBlockFlags(mat)); } /** @@ -24,7 +24,7 @@ public class BlockFlags { * @param id * @param mat */ - public static void setFlagsAs(int id, int otherId){ + public static void setFlagsAs(int id, int otherId) { BlockProperties.setBlockFlags(id, BlockProperties.getBlockFlags(otherId)); } @@ -33,7 +33,7 @@ public class BlockFlags { * @param id * @param flags */ - public static void addFlags(int id, long flags){ + public static void addFlags(int id, long flags) { BlockProperties.setBlockFlags(id, BlockProperties.getBlockFlags(id) | flags); } @@ -42,7 +42,7 @@ public class BlockFlags { * @param id * @param flags */ - public static void removeFlags(int id, long flags){ + public static void removeFlags(int id, long flags) { BlockProperties.setBlockFlags(id, BlockProperties.getBlockFlags(id) & ~flags); } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/BlockProperties.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/BlockProperties.java index f2cfe2ca..cb1b433d 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/BlockProperties.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/BlockProperties.java @@ -36,6 +36,7 @@ import fr.neatmonster.nocheatplus.logging.LogUtil; * - reading (all) the default properties from a file too. * */ +@SuppressWarnings("deprecation") public class BlockProperties { /** @@ -344,7 +345,10 @@ public class BlockProperties { public static final long F_ICE = 0x20000; /** LEAVES */ - public static final long F_LEAVES = 0x40000; + public static final long F_LEAVES = 0x40000; + + /** THIN FENCE (glass panes, iron fence) */ + public static final long F_THIN_FENCE = 0x80000; /** * Map flag to names. @@ -355,6 +359,8 @@ public class BlockProperties { */ private static final Map nameFlagMap = new LinkedHashMap(); + private static final Location useLoc = new Location(null, 0, 0, 0); + static{ // Use reflection to get a flag -> name mapping and vice versa. for (Field field : BlockProperties.class.getDeclaredFields()){ @@ -442,8 +448,8 @@ public class BlockProperties { tools.put(359, new ToolProps(ToolType.SHEARS, MaterialBase.NONE)); } - - private static void initBlocks(final MCAccess mcAccess, final WorldConfigProvider worldConfigProvider) { + + private static void initBlocks(final MCAccess mcAccess, final WorldConfigProvider worldConfigProvider) { Arrays.fill(blocks, null); // Initalize block flags // Generic initialization. @@ -499,14 +505,6 @@ public class BlockProperties { // Snow (1.4.x) blockFlags[Material.SNOW.getId()] |= F_HEIGHT_8SIM_INC; - // 1.5 block high. - for (final Material mat : new Material[]{ - Material.FENCE, Material.FENCE_GATE, - Material.NETHER_FENCE, - }){ - blockFlags[mat.getId()] |= F_HEIGHT150; - } - // Climbable for (final Material mat : new Material[]{ Material.VINE, Material.LADDER, @@ -570,17 +568,23 @@ public class BlockProperties { blockFlags[mat.getId()] |= F_IGN_PASSABLE; } - // Blocks changing depending on neighbor blocks. - for (final Material mat : new Material[]{ - Material.FENCE, Material.FENCE_GATE, Material.COBBLE_WALL, - Material.NETHER_FENCE, - Material.IRON_FENCE, Material.THIN_GLASS, - - }){ - blockFlags[mat.getId()] |= F_VARIABLE; - } // ? Extra flag for COCOA, ANVIL: depends on data value (other issue) + // Fences, 1.5 block high. + for (final Material mat : new Material[]{ + Material.FENCE, Material.FENCE_GATE, + Material.NETHER_FENCE, Material.COBBLE_WALL, + }){ + blockFlags[mat.getId()] |= F_HEIGHT150 | F_VARIABLE; + } + + // Thin fences (iron fence, glass panes). + for (final Material mat : new Material[]{ + Material.IRON_FENCE, Material.THIN_GLASS, + }){ + blockFlags[mat.getId()] |= F_THIN_FENCE | F_VARIABLE; + } + // Flexible ground (height): for (final Material mat : new Material[]{ // Strictly needed (multiple boxes otherwise). @@ -623,7 +627,7 @@ public class BlockProperties { }){ blocks[mat.getId()] = glassType; } - blocks[102] = glassType; // glass panes + blocks[Material.THIN_GLASS.getId()] = glassType; blocks[Material.NETHERRACK.getId()] = new BlockProps(woodPickaxe, 0.4f, secToMs(2, 0.3, 0.15, 0.1, 0.1, 0.05)); blocks[Material.LADDER.getId()] = new BlockProps(noTool, 0.4f, secToMs(0.6), 2.5f); blocks[Material.CACTUS.getId()] = new BlockProps(noTool, 0.4f, secToMs(0.6)); @@ -873,6 +877,16 @@ public class BlockProperties { else return blocks[blockId]; } + /** + * Convenience method. + * @param blockType + * @param player + * @return + */ + public static long getBreakingDuration(final Material BlockType, final Player player){ + return getBreakingDuration(BlockType.getId(), player); + } + /** * Convenience method. * @param blockId @@ -880,7 +894,9 @@ public class BlockProperties { * @return */ public static long getBreakingDuration(final int blockId, final Player player){ - return getBreakingDuration(blockId, player.getItemInHand(), player.getInventory().getHelmet(), player, player.getLocation()); + final long res = getBreakingDuration(blockId, player.getItemInHand(), player.getInventory().getHelmet(), player, player.getLocation(useLoc)); + useLoc.setWorld(null); + return res; } /** @@ -1143,8 +1159,11 @@ public class BlockProperties { if (blockId < 0 || blockId >= blocks.length) throw new IllegalArgumentException("The blockId is outside of supported range: " + blockId); blocks[blockId] = blockProps; } - - + + public static boolean isValidTool(final Material blockType, final ItemStack itemInHand) { + return isValidTool(blockType.getId(), itemInHand); + } + public static boolean isValidTool(final int blockId, final ItemStack itemInHand) { final BlockProps blockProps = getBlockProps(blockId); final ToolProps toolProps = getToolProps(itemInHand); @@ -1212,6 +1231,24 @@ public class BlockProperties { pLoc.cleanup(); return res; } + + /** + * Straw-man-method to hide warnings. Rather intended for display in debug/alert messages. + * @param blockType + * @return + */ + public static int getId(final Material blockType) { + return blockType.getId(); + } + + /** + * Straw-man method to hide warnings. + * @param id + * @return + */ + public static Material getMaterial(final int id) { + return Material.getMaterial(id); + } /** * @deprecated Typo in method name. @@ -1222,9 +1259,17 @@ public class BlockProperties { return blockFlags[id]; } + public static final long getBlockFlags(final Material blockType){ + return getBlockFlags(blockType.getId()); + } + public static final long getBlockFlags(final int id){ return blockFlags[id]; } + + public static final void setBlockFlags(final Material blockType, final long flags){ + setBlockFlags(blockType.getId(), flags); + } public static final void setBlockFlags(final int id, final long flags){ blockFlags[id] = flags; @@ -1253,12 +1298,25 @@ public class BlockProperties { public static final boolean isClimbable(final int id) { return (blockFlags[id] & F_CLIMBABLE) != 0; } + + /** + * Climbable material that needs to be attached to a block, to allow players to climb up.
    + * Currently only applies to vines. There is no flag for such, yet. + * @param id + * @return + */ + public static final boolean isAttachedClimbable(final int id) { + return id == Material.VINE.getId(); + } public static final boolean isStairs(final int id) { return (blockFlags[id] & F_STAIRS) != 0; } - - + + public static final boolean isLiquid(final Material blockType) { + return isLiquid(blockType.getId()); + } + public static final boolean isLiquid(final int id) { return (blockFlags[id] & F_LIQUID) != 0; } @@ -1271,6 +1329,15 @@ public class BlockProperties { return (blockFlags[id] & F_LEAVES) != 0; } + /** + * Might hold true for liquids too. + * @param blockType + * @return + */ + public static final boolean isSolid(final Material blockType){ + return isSolid(blockType.getId()); + } + /** * Might hold true for liquids too. * @param id @@ -1280,6 +1347,15 @@ public class BlockProperties { return (blockFlags[id] & F_SOLID) != 0; } + /** + * Might hold true for liquids too. + * @param blockType + * @return + */ + public static final boolean isGround(final Material blockType){ + return isGround(blockType.getId()); + } + /** * Might hold true for liquids too. * @param id @@ -1288,6 +1364,16 @@ public class BlockProperties { public static final boolean isGround(final int id){ return (blockFlags[id] & F_GROUND) != 0; } + + /** + * Just check if a position is not inside of a block that has a bounding box.
    + * This is an inaccurate check, it also returns false for doors etc. + * @param blockType + * @return + */ + public static final boolean isPassable(final Material blockType){ + return isPassable(blockType.getId()); + } /** * Just check if a position is not inside of a block that has a bounding box.
    @@ -1423,6 +1509,7 @@ public class BlockProperties { */ public static final boolean isPassableWorkaround(final BlockCache access, final int bx, final int by, final int bz, final double fx, final double fy, final double fz, final int id, final double dX, final double dY, final double dZ, final double dT){ // Note: Since this is only called if the bounding box collides, out-of-bounds checks should not be necessary. + // TODO: Add a flag if a workaround exists (!), might store the type of workaround extra (generic!), or extra flags. final long flags = blockFlags[id]; if ((flags & F_STAIRS) != 0){ if ((access.getData(bx, by, bz) & 0x4) != 0){ @@ -1436,10 +1523,11 @@ public class BlockProperties { else if (id == Material.SOUL_SAND.getId()){ if (Math.min(fy, fy + dY * dT) >= 0.875) return true; // 0.125 } - else if (id == Material.IRON_FENCE.getId() || id == Material.THIN_GLASS.getId()){ + else if ((flags & F_THIN_FENCE) != 0){ if (!collidesFence(fx, fz, dX, dZ, dT, 0.05)) return true; } else if (id == Material.FENCE.getId() || id == Material.NETHER_FENCE.getId()){ + // TODO: Re-check if cobble fence is now like this. if (!collidesFence(fx, fz, dX, dZ, dT, 0.425)) return true; } else if (id == Material.FENCE_GATE.getId()){ @@ -1642,7 +1730,8 @@ public class BlockProperties { */ public static final boolean isPassable(final Location from, final Location to){ blockCache.setAccess(from.getWorld()); - PassableRayTracing rt = new PassableRayTracing(); + final PassableRayTracing rt = new PassableRayTracing(); + rt.setMaxSteps(60); // TODO: Configurable ? rt.setBlockCache(blockCache); rt.set(from.getX(), from.getY(), from.getZ(), to.getX(), to.getY(), to.getZ()); rt.loop(); @@ -1810,6 +1899,22 @@ public class BlockProperties { return false; } + /** + * Convenience method for Material instead of block id. + * @param access + * @param minX + * @param minY + * @param minZ + * @param maxX + * @param maxY + * @param maxZ + * @param mat + * @return + */ + public static final boolean collidesId(final BlockCache access, final double minX, double minY, final double minZ, final double maxX, final double maxY, final double maxZ, final Material mat){ + return collidesId(access, minX, minY, minZ, maxX, maxY, maxZ, mat.getId()); + } + /** * Check if a block with the given id is overlapping with the bounds, this does not check the blocks actual bounds (All bounds of the block are seen as 0...1, for exact box match use collidesBlock). * @param access @@ -2063,7 +2168,10 @@ public class BlockProperties { final int iMinX = Location.locToBlock(minX); final int iMaxX = Location.locToBlock(maxX); final int iMinY = Location.locToBlock(minY - 0.5626); - if (iMinY > maxBlockY) return false; + if (iMinY > maxBlockY) { + // TODO: Keep checking this, does not apply for biger values of yOnGround. + return false; + } final int iMaxY = Math.min(Location.locToBlock(maxY), maxBlockY); final int iMinZ = Location.locToBlock(minZ); final int iMaxZ = Location.locToBlock(maxZ); diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/IdUtil.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/IdUtil.java new file mode 100644 index 00000000..67911b6d --- /dev/null +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/IdUtil.java @@ -0,0 +1,78 @@ +package fr.neatmonster.nocheatplus.utilities; + +import java.util.UUID; + +/** + * Utility for UUIDs, player names. + * @author mc_dev + * + */ +public class IdUtil { + + /** + * Valid user name check (Minecraft).
    + * (Taken from TrustCore.) + * @param name Allows null input. + * @return + */ + public static boolean isValidMinecraftUserName(final String name) { + return name != null && name.matches("[\\w]{2,16}"); + } + + /** + * Safe method to parse a UUID, using UUIDFromString internally. + * @param input + * @return + */ + public static UUID UUIDFromStringSafe(final String input) { + if (input == null) { + return null; + } + try { + return UUIDFromString(input); + } + catch (IllegalArgumentException e1) {} + return null; + } + + /** + * More flexible UUID parsing.
    + * (Taken from TrustCore.) + * @param input + * @return + */ + public static UUID UUIDFromString(final String input) { + // TODO: Add unit tests. + final int len = input.length(); + if (len == 36) { + return UUID.fromString(input); + } else if (len == 32) { + // TODO: Might better translate to longs right away !? + // Fill in '-' + char[] chars = input.toCharArray(); + char[] newChars = new char[36]; + int index = 0; + int targetIndex = 0; + while (targetIndex < 36) { + newChars[targetIndex] = chars[index]; + index ++; + targetIndex ++; + switch (index) { + case 8: + case 12: + case 16: + case 20: + newChars[targetIndex] = '-'; + targetIndex ++; + break; + default: + break; + } + } + return UUID.fromString(new String(newChars)); + } else { + throw new IllegalArgumentException("Unexpected length (" + len + ") for uuid: " + input); + } + } + +} diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/InventoryUtil.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/InventoryUtil.java index eeb9239a..1df3188a 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/InventoryUtil.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/InventoryUtil.java @@ -1,5 +1,6 @@ package fr.neatmonster.nocheatplus.utilities; +import org.bukkit.Material; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryType; @@ -14,6 +15,10 @@ import org.bukkit.inventory.ItemStack; */ public class InventoryUtil { + private static boolean isEmpty(Material mat) { + return mat == null || mat == Material.AIR; + } + /** * Does not account for special slots like armor. * @param inventory @@ -24,7 +29,7 @@ public class InventoryUtil { int count = 0; for (int i = 0; i < contents.length; i++){ final ItemStack stack = contents[i]; - if (stack == null || stack.getTypeId() == 0){ + if (stack == null || isEmpty(stack.getType())){ count ++; } } @@ -40,7 +45,7 @@ public class InventoryUtil { public static int getStackCount(final Inventory inventory, final ItemStack reference) { if (inventory == null) return 0; if (reference == null) return getFreeSlots(inventory); - final int id = reference.getTypeId(); + final Material mat = reference.getType(); final int durability = reference.getDurability(); final ItemStack[] contents = inventory.getContents(); int count = 0; @@ -49,7 +54,7 @@ public class InventoryUtil { if (stack == null){ continue; } - else if (stack.getTypeId() == id && stack.getDurability() == durability){ + else if (stack.getType() == mat && stack.getDurability() == durability){ count ++; } } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/PlayerLocation.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/PlayerLocation.java index 091c3da1..cf3d6f29 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/PlayerLocation.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/PlayerLocation.java @@ -36,8 +36,17 @@ public class PlayerLocation { /** Bounding box of the player. */ private double minX, maxX, minY, maxY, minZ, maxZ; - // Object members (reset to null) // - + // TODO: Check if onGround can be completely replaced by onGroundMinY and notOnGroundMaxY. + /** Minimal yOnGround for which the player is on ground. No extra xz/y margin.*/ + private double onGroundMinY = Double.MAX_VALUE; + /** Maximal yOnGround for which the player is not on ground. No extra xz/y margin.*/ + private double notOnGroundMaxY = Double.MIN_VALUE; + + // "Light" object members (reset to null) // + + // TODO: The following should be changed to primitive types, add one long for "checked"-flags. Booleans can be compressed into a long. + // TODO: All properties that can be set should have a "checked" flag, thus resetting the flag suffices. + /** Type id of the block at the position. */ private Integer typeId = null; @@ -46,7 +55,19 @@ public class PlayerLocation { /** Data value of the block this position is on. */ private Integer data = null; + + /** All block flags collected for maximum used bounds. */ + private Long blockFlags = null; + + /** Is the player on ice? */ + private Boolean onIce = null; + /** Is the player on ladder? */ + private Boolean onClimbable = null; + + /** Simple test if the exact position is passable. */ + private Boolean passable = null; + /** Is the player above stairs? */ private Boolean aboveStairs = null; @@ -61,38 +82,22 @@ public class PlayerLocation { /** Is the player on the ground? */ private Boolean onGround = null; - - // TODO: Check if onGround can be completely replaced by onGroundMinY and notOnGroundMaxY. - /** Minimal yOnGround for which the player is on ground. No extra xz/y margin.*/ - private double onGroundMinY = Double.MAX_VALUE; - /** Maximal yOnGround for which the player is not on ground. No extra xz/y margin.*/ - private double notOnGroundMaxY = Double.MIN_VALUE; - /** Is the player on ice? */ - private Boolean onIce = null; - - /** Is the player on ladder? */ - private Boolean onClimbable = null; - - /** Simple test if the exact position is passable. */ - private Boolean passable = null; - - /** All block flags collected for maximum used bounds. */ - private Long blockFlags = null; - - // "Heavy" members (should be reset to null or cleaned up at some point) // + // "Heavy" object members (reset to null for cleanup). // /** The player ! */ private Player player = null; /** Bukkit world. */ private World world = null; + + // "Heavy" object members (further cleanup call needed + set to null for cleanup) // /** Optional block property cache. */ private BlockCache blockCache = null; - public PlayerLocation(final MCAccess mcAccess, final BlockCache blockCache){ + public PlayerLocation(final MCAccess mcAccess, final BlockCache blockCache) { this.mcAccess = mcAccess; this.blockCache = blockCache; } @@ -105,8 +110,12 @@ public class PlayerLocation { * Gets the location. * * @return the location + * @throws NullPointerException, if the world stored internally is null. */ public Location getLocation() { + if (this.world == null) { + throw new NullPointerException("World is null."); + } return new Location(world, x, y, z); } @@ -114,7 +123,7 @@ public class PlayerLocation { * Get the world! * @return */ - public World getWorld(){ + public World getWorld() { return world; } @@ -218,7 +227,7 @@ public class PlayerLocation { * @param loc * @return */ - public boolean isBlockAbove(final PlayerLocation loc){ + public boolean isBlockAbove(final PlayerLocation loc) { return blockY == loc.getBlockY() + 1 && blockX == loc.getBlockX() && blockZ == loc.getBlockZ(); } @@ -227,7 +236,7 @@ public class PlayerLocation { * @param loc * @return */ - public boolean isBlockAbove(final Location loc){ + public boolean isBlockAbove(final Location loc) { return blockY == loc.getBlockY() + 1 && blockX == loc.getBlockX() && blockZ == loc.getBlockZ(); } @@ -256,7 +265,7 @@ public class PlayerLocation { * @param other * @return */ - public int manhattan(final PlayerLocation other){ + public int manhattan(final PlayerLocation other) { // TODO: Consider using direct field access from other methods as well. return TrigUtil.manhattan(this.blockX, this.blockY, this.blockZ, other.blockX, other.blockY, other.blockZ); } @@ -266,7 +275,7 @@ public class PlayerLocation { * @param other * @return */ - public int maxBlockDist(final PlayerLocation other){ + public int maxBlockDist(final PlayerLocation other) { // TODO: Consider using direct field access from other methods as well. return TrigUtil.maxDistance(this.blockX, this.blockY, this.blockZ, other.blockX, other.blockY, other.blockZ); } @@ -278,7 +287,7 @@ public class PlayerLocation { */ public boolean isAboveStairs() { if (aboveStairs == null) { - if (blockFlags != null && (blockFlags.longValue() & BlockProperties.F_STAIRS) == 0 ){ + if (blockFlags != null && (blockFlags.longValue() & BlockProperties.F_STAIRS) == 0 ) { aboveStairs = false; return false; } @@ -297,7 +306,7 @@ public class PlayerLocation { */ public boolean isInLava() { if (inLava == null) { - if (blockFlags != null && (blockFlags.longValue() & BlockProperties.F_LAVA) == 0 ){ + if (blockFlags != null && (blockFlags.longValue() & BlockProperties.F_LAVA) == 0 ) { inLava = false; return false; } @@ -318,7 +327,7 @@ public class PlayerLocation { */ public boolean isInWater() { if (inWater == null) { - if (blockFlags != null && (blockFlags.longValue() & BlockProperties.F_WATER) == 0 ){ + if (blockFlags != null && (blockFlags.longValue() & BlockProperties.F_WATER) == 0 ) { inWater = false; return false; } @@ -379,7 +388,7 @@ public class PlayerLocation { public boolean isOnClimbable() { if (onClimbable == null) { // Climbable blocks. - if (blockFlags != null && (blockFlags.longValue() & BlockProperties.F_CLIMBABLE) == 0 ){ + if (blockFlags != null && (blockFlags.longValue() & BlockProperties.F_CLIMBABLE) == 0 ) { onClimbable = false; return false; } @@ -397,25 +406,25 @@ public class PlayerLocation { * @param jumpHeigth Height the player is allowed to have jumped. * @return */ - public boolean canClimbUp(double jumpHeigth){ + public boolean canClimbUp(double jumpHeigth) { // TODO: distinguish vines. - if (getTypeId() == Material.VINE.getId()){ + if (BlockProperties.isAttachedClimbable(getTypeId())) { // Check if vine is attached to something solid - if (BlockProperties.canClimbUp(blockCache, blockX, blockY, blockZ)){ + if (BlockProperties.canClimbUp(blockCache, blockX, blockY, blockZ)) { return true; } // Check the block at head height. final int headY = Location.locToBlock(y + player.getEyeHeight()); - if (headY > blockY){ - for (int cy = blockY + 1; cy <= headY; cy ++){ - if (BlockProperties.canClimbUp(blockCache, blockX, cy, blockZ)){ + if (headY > blockY) { + for (int cy = blockY + 1; cy <= headY; cy ++) { + if (BlockProperties.canClimbUp(blockCache, blockX, cy, blockZ)) { return true; } } } // Finally check possible jump height. // TODO: This too is inaccurate. - if (isOnGround(jumpHeigth)){ + if (isOnGround(jumpHeigth)) { // Here ladders are ok. return true; } @@ -445,7 +454,7 @@ public class PlayerLocation { if (inWeb == null) { // TODO: inset still needed ? final double inset = 0.001d; - inWeb = BlockProperties.collidesId(blockCache, minX + inset, minY + inset, minZ + inset, maxX - inset, maxY - inset, maxZ - inset, Material.WEB.getId()); + inWeb = BlockProperties.collidesId(blockCache, minX + inset, minY + inset, minZ + inset, maxX - inset, maxY - inset, maxZ - inset, Material.WEB); } return inWeb; } @@ -456,34 +465,34 @@ public class PlayerLocation { * @return true, if the player is on ground */ public boolean isOnGround() { - if (onGround != null){ + if (onGround != null) { return onGround; } // Check cached values and simplifications. if (notOnGroundMaxY >= yOnGround) onGround = false; else if (onGroundMinY <= yOnGround) onGround = true; - else{ + else { // Shortcut check (currently needed for being stuck + sf). - if (blockFlags == null || (blockFlags.longValue() & BlockProperties.F_GROUND) != 0){ + if (blockFlags == null || (blockFlags.longValue() & BlockProperties.F_GROUND) != 0) { // TODO: Consider dropping this shortcut. final int bY = Location.locToBlock(y - yOnGround); final int id = bY == blockY ? getTypeId() : (bY == blockY -1 ? getTypeIdBelow() : blockCache.getTypeId(blockX, bY, blockZ)); final long flags = BlockProperties.getBlockFlags(id); // TODO: Might remove check for variable ? - if ((flags & BlockProperties.F_GROUND) != 0 && (flags & BlockProperties.F_VARIABLE) == 0){ + if ((flags & BlockProperties.F_GROUND) != 0 && (flags & BlockProperties.F_VARIABLE) == 0) { final double[] bounds = blockCache.getBounds(blockX, bY, blockZ); // Check collision if not inside of the block. [Might be a problem for cauldron or similar + something solid above.] // TODO: Might need more refinement. - if (bounds != null && y - bY >= bounds[4] && BlockProperties.collidesBlock(blockCache, x, minY - yOnGround, z, x, minY, z, blockX, bY, blockZ, id, bounds, flags)){ + if (bounds != null && y - bY >= bounds[4] && BlockProperties.collidesBlock(blockCache, x, minY - yOnGround, z, x, minY, z, blockX, bY, blockZ, id, bounds, flags)) { // TODO: BlockHeight is needed for fences, use right away (above)? if (!BlockProperties.isPassableWorkaround(blockCache, blockX, bY, blockZ, minX - blockX, minY - yOnGround - bY, minZ - blockZ, id, maxX - minX, yOnGround, maxZ - minZ, 1.0) - || (flags & BlockProperties.F_GROUND_HEIGHT) != 0 && BlockProperties.getGroundMinHeight(blockCache, blockX, bY, blockZ, id, bounds, flags) <= y - bY){ + || (flags & BlockProperties.F_GROUND_HEIGHT) != 0 && BlockProperties.getGroundMinHeight(blockCache, blockX, bY, blockZ, id, bounds, flags) <= y - bY) { // System.out.println("*** onground SHORTCUT"); onGround = true; } } } - if (onGround == null){ + if (onGround == null) { // System.out.println("*** fetch onground std"); // Full on-ground check (blocks). // Note: Might check for half-block height too (getTypeId), but that is much more seldom. @@ -493,7 +502,7 @@ public class PlayerLocation { else onGround = false; } if (onGround) onGroundMinY = Math.min(onGroundMinY, yOnGround); - else{ + else { // System.out.println("*** onground check entities"); // TODO: further confine this ? notOnGroundMaxY = Math.max(notOnGroundMaxY, yOnGround); @@ -508,7 +517,7 @@ public class PlayerLocation { * @param yOnGround Margin below the player. * @return */ - public boolean isOnGround(final double yOnGround){ + public boolean isOnGround(final double yOnGround) { if (notOnGroundMaxY >= yOnGround) return false; else if (onGroundMinY <= yOnGround) return true; return isOnGround(yOnGround, 0D, 0D, 0L); @@ -521,7 +530,7 @@ public class PlayerLocation { * @return */ public boolean isOnGround(final double yOnGround, final long ignoreFlags) { - if (ignoreFlags == 0){ + if (ignoreFlags == 0) { if (notOnGroundMaxY >= yOnGround) return false; else if (onGroundMinY <= yOnGround) return true; } @@ -538,7 +547,7 @@ public class PlayerLocation { */ public boolean isOnGround(final double yOnGround, final double xzMargin, final double yMargin) { if (xzMargin >= 0 && onGroundMinY <= yOnGround) return true; - if (xzMargin <= 0 && yMargin == 0){ + if (xzMargin <= 0 && yMargin == 0) { if (notOnGroundMaxY >= yOnGround) return false; } return isOnGround(yOnGround, xzMargin, yMargin, 0); @@ -553,20 +562,24 @@ public class PlayerLocation { * @return */ public boolean isOnGround(final double yOnGround, final double xzMargin, final double yMargin, final long ignoreFlags) { - if (ignoreFlags == 0){ + if (ignoreFlags == 0) { if (xzMargin >= 0 && onGroundMinY <= yOnGround) return true; - if (xzMargin <= 0 && yMargin == 0){ + if (xzMargin <= 0 && yMargin == 0) { if (notOnGroundMaxY >= yOnGround) return false; } } // System.out.println("*** Fetch on-ground: yOnGround=" + yOnGround + " xzM=" + xzMargin + " yM=" + yMargin + " ign=" + ignoreFlags); final boolean onGround = BlockProperties.isOnGround(blockCache, minX - xzMargin, minY - yOnGround - yMargin, minZ - xzMargin, maxX + xzMargin, minY + yMargin, maxZ + xzMargin, ignoreFlags); - if (ignoreFlags == 0){ - if (onGround){ - if (xzMargin <= 0 && yMargin == 0) onGroundMinY = Math.min(onGroundMinY, yOnGround); + if (ignoreFlags == 0) { + if (onGround) { + if (xzMargin <= 0 && yMargin == 0) { + onGroundMinY = Math.min(onGroundMinY, yOnGround); + } } - else{ - if (xzMargin >= 0) notOnGroundMaxY = Math.max(notOnGroundMaxY, yOnGround); + else { + if (xzMargin >= 0) { + notOnGroundMaxY = Math.max(notOnGroundMaxY, yOnGround); + } } } return onGround; @@ -579,7 +592,7 @@ public class PlayerLocation { * @param yMargin Extra margin added below and above. * @return */ - public boolean standsOnEntity(final double yOnGround, final double xzMargin, final double yMargin){ + public boolean standsOnEntity(final double yOnGround, final double xzMargin, final double yMargin) { return blockCache.standsOnEntity(player, minX - xzMargin, minY - yOnGround - yMargin, minZ - xzMargin, maxX + xzMargin, minY + yMargin, maxZ + xzMargin); } @@ -589,7 +602,7 @@ public class PlayerLocation { * @param yMargin * @return */ - public boolean isNextToSolid(final double xzMargin, final double yMargin){ + public boolean isNextToSolid(final double xzMargin, final double yMargin) { // TODO: Adjust to check block flags ? return BlockProperties.collides(blockCache, minX - xzMargin, minY - yMargin, minZ - xzMargin, maxX + xzMargin, maxY + yMargin, maxZ + xzMargin, BlockProperties.F_SOLID); } @@ -600,7 +613,7 @@ public class PlayerLocation { * @param yMargin * @return */ - public boolean isNextToGround(final double xzMargin, final double yMargin){ + public boolean isNextToGround(final double xzMargin, final double yMargin) { // TODO: Adjust to check block flags ? return BlockProperties.collides(blockCache, minX - xzMargin, minY - yMargin, minZ - xzMargin, maxX + xzMargin, maxY + yMargin, maxZ + xzMargin, BlockProperties.F_GROUND); } @@ -609,7 +622,7 @@ public class PlayerLocation { * Reset condition for flying checks (sf + nofall): liquids, web, ladder (not on-ground, though). * @return */ - public boolean isResetCond(){ + public boolean isResetCond() { // NOTE: if optimizing, setYOnGround has to be kept in mind. return isInLiquid() || isOnClimbable() || isInWeb(); } @@ -634,7 +647,7 @@ public class PlayerLocation { * @return */ public boolean isPassable() { - if (passable == null){ + if (passable == null) { passable = BlockProperties.isPassable(blockCache, x, y, z, getTypeId()); // passable = BlockProperties.isPassableExact(blockCache, x, y, z, getTypeId()); } @@ -716,6 +729,7 @@ public class PlayerLocation { * the location * @param player * the player + * @throws NullPointerException, if player.getLocation.getWorld() returns null. */ public void set(final Location location, final Player player) { set(location, player, 0.001); @@ -728,6 +742,7 @@ public class PlayerLocation { * the location * @param player * the player + * @throws NullPointerException, if Location.getWorld() returns null. */ public void set(final Location location, final Player player, final double yOnGround) { @@ -758,6 +773,10 @@ public class PlayerLocation { // Set world / block access. world = location.getWorld(); + + if (world == null) { + throw new NullPointerException("World is null."); + } // Reset cached values. typeId = typeIdBelow = data = null; @@ -774,7 +793,7 @@ public class PlayerLocation { * to have flags ready for faster denial. * @param maxYonGround */ - public void collectBlockFlags(double maxYonGround){ + public void collectBlockFlags(double maxYonGround) { maxYonGround = Math.max(yOnGround, maxYonGround); // TODO: Clearly refine this for 1.5 high blocks. // TODO: Check which checks need blocks below. @@ -817,7 +836,9 @@ public class PlayerLocation { */ public boolean isIllegal() { final AlmostBoolean spec = mcAccess.isIllegalBounds(player); - if (spec != AlmostBoolean.MAYBE) return spec.decide(); + if (spec != AlmostBoolean.MAYBE) { + return spec.decide(); + } else if (Math.abs(minX) > 3.2E7D || Math.abs(maxX) > 3.2E7D || Math.abs(minY) > 3.2E7D || Math.abs(maxY) > 3.2E7D || Math.abs(minZ) > 3.2E7D || Math.abs(maxZ) > 3.2E7D) return true; // if (Math.abs(box.a) > 3.2E7D || Math.abs(box.b) > 3.2E7D || Math.abs(box.c) > 3.2E7D || Math.abs(box.d) > 3.2E7D || Math.abs(box.e) > 3.2E7D || Math.abs(box.f) > 3.2E7D) return true; else return false; @@ -835,7 +856,7 @@ public class PlayerLocation { * Set the block flags which are usually collected on base of bounding box, yOnGround and other considerations, such as 1.5 high blocks. * @param blockFlags */ - public void setBlockFlags(Long blockFlags){ + public void setBlockFlags(Long blockFlags) { this.blockFlags = blockFlags; } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/RayTracing.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/RayTracing.java index f0cbf43e..98947a21 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/RayTracing.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/RayTracing.java @@ -76,7 +76,7 @@ public abstract class RayTracing { private static final double tDiff(final double dTotal, final double offset){ if (dTotal > 0.0){ - return (1 - offset) / dTotal; + return (1.0 - offset) / dTotal; } else if (dTotal < 0.0){ return offset / -dTotal; @@ -101,11 +101,22 @@ public abstract class RayTracing { tY = tDiff(dY, oY); tZ = tDiff(dZ, oZ); tMin = Math.min(tX, Math.min(tY, tZ)); - if (tMin == Double.MAX_VALUE || t + tMin > 1.0) tMin = 1.0 - t; + if (tMin == Double.MAX_VALUE) { + // All differences are 0 (no progress). + break; + } + if (t + tMin > 1.0) { + // Set to the remaining distance (does trigger). + tMin = 1.0 - t; + } // Call step with appropriate arguments. step ++; - if (!step(blockX, blockY, blockZ, oX, oY, oZ, tMin)) break; // || tMin == 0) break; - if (t + tMin >= 1.0 - tol) break; + if (!step(blockX, blockY, blockZ, oX, oY, oZ, tMin)) { + break; // || tMin == 0) break; + } + if (t + tMin >= 1.0 - tol) { + break; + } // Advance (add to t etc.). changed = false; // TODO: Some not handled cases would mean errors. @@ -113,23 +124,23 @@ public abstract class RayTracing { oX += tMin * dX; if (tX == tMin){ if (dX < 0){ - oX = 1; + oX = 1.0; blockX --; changed = true; } else if (dX > 0){ - oX = 0; + oX = 0.0; blockX ++; changed = true; } } - else if (oX >= 1 && dX > 0.0){ - oX -= 1; + else if (oX >= 1.0 && dX > 0.0){ + oX -= 1.0; blockX ++; changed = true; } else if (oX < 0 && dX < 0.0){ - oX += 1; + oX += 1.0; blockX --; changed = true; } @@ -137,23 +148,23 @@ public abstract class RayTracing { oY += tMin * dY; if (tY == tMin){ if (dY < 0){ - oY = 1; + oY = 1.0; blockY --; changed = true; } else if (dY > 0){ - oY = 0; + oY = 0.0; blockY ++; changed = true; } } else if (oY >= 1 && dY > 0.0){ - oY -= 1; + oY -= 1.0; blockY ++; changed = true; } else if (oY < 0 && dY < 0.0){ - oY += 1; + oY += 1.0; blockY --; changed = true; } @@ -161,23 +172,23 @@ public abstract class RayTracing { oZ += tMin * dZ; if (tZ == tMin){ if (dZ < 0){ - oZ = 1; + oZ = 1.0; blockZ --; changed = true; } else if (dZ > 0){ - oZ = 0; + oZ = 0.0; blockZ ++; changed = true; } } else if (oZ >= 1 && dZ > 0.0){ - oZ -= 1; + oZ -= 1.0; blockZ ++; changed = true; } else if (oZ < 0 && dZ < 0.0){ - oZ += 1; + oZ += 1.0; blockZ --; changed = true; } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/TeleportUtil.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/TeleportUtil.java index 2d03d208..7905dd44 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/TeleportUtil.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/TeleportUtil.java @@ -6,6 +6,9 @@ import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; public class TeleportUtil { + + /** Temp use. LocUtil.clone on passing. setWorld(null) after use. */ + private static final Location useLoc = new Location(null, 0, 0, 0); /** * Teleport the player with vehicle, temporarily eject the passenger and set teleported in MovingData. @@ -20,6 +23,7 @@ public class TeleportUtil { final boolean vehicleTeleported; final boolean playerIsPassenger = player.equals(passenger); if (playerIsPassenger && !vehicle.isDead()){ // && vehicle.equals(player.getVehicle). + // TODO: Does VehicleExit fire here !? Consequences? vehicle.eject(); vehicleTeleported = vehicle.teleport(location, TeleportCause.PLUGIN); @@ -29,13 +33,15 @@ public class TeleportUtil { } else vehicleTeleported = false; final boolean playerTeleported = player.teleport(location); - if (playerIsPassenger && playerTeleported && vehicleTeleported && player.getLocation().distance(vehicle.getLocation()) < 1.0){ + if (playerIsPassenger && playerTeleported && vehicleTeleported && player.getLocation().distance(vehicle.getLocation(useLoc)) < 1.0){ // Somewhat check against tp showing something wrong (< 1.0). + // TODO: Does VehicleEnter fire here !? Consequences? vehicle.setPassenger(player); } if (debug){ System.out.println(player.getName() + " vehicle set back: " + location); } + useLoc.setWorld(null); } /** diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/TrigUtil.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/TrigUtil.java index b9449557..53eba0a0 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/TrigUtil.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/TrigUtil.java @@ -20,6 +20,8 @@ public class TrigUtil { public static final double fRadToGrad = 360.0 / (2.0 * Math.PI); /** Some default precision value for the directionCheck method. */ public static final double DIRECTION_PRECISION = 2.6; + + private static final Location useLoc = new Location(null, 0, 0, 0); /** * Check if a player looks at a target of a specific size, with a specific @@ -43,9 +45,11 @@ public class TrigUtil { */ public static double directionCheck(final Player player, final double targetX, final double targetY, final double targetZ, final double targetWidth, final double targetHeight, final double precision) { - final Location loc = player.getLocation(); + final Location loc = player.getLocation(useLoc); final Vector dir = loc.getDirection(); - return directionCheck(loc.getX(), loc.getY() + player.getEyeHeight(), loc.getZ(), dir.getX(), dir.getY(), dir.getZ(), targetX, targetY, targetZ, targetWidth, targetHeight, precision); + final double res = directionCheck(loc.getX(), loc.getY() + player.getEyeHeight(), loc.getZ(), dir.getX(), dir.getY(), dir.getZ(), targetX, targetY, targetZ, targetWidth, targetHeight, precision); + useLoc.setWorld(null); + return res; } /** diff --git a/NCPCore/src/test/java/fr/neatmonster/nocheatplus/test/TestConfig.java b/NCPCore/src/test/java/fr/neatmonster/nocheatplus/test/TestConfig.java new file mode 100644 index 00000000..b7d11566 --- /dev/null +++ b/NCPCore/src/test/java/fr/neatmonster/nocheatplus/test/TestConfig.java @@ -0,0 +1,40 @@ +package fr.neatmonster.nocheatplus.test; + +import static org.junit.Assert.fail; + +import org.bukkit.Material; +import org.junit.Test; + +import fr.neatmonster.nocheatplus.config.RawConfigFile; + +public class TestConfig { + + private void testReadMaterial(String input, Material expectedMat) { + Material mat = RawConfigFile.parseMaterial(input); + if (expectedMat != mat) { + fail("Expected " + expectedMat + " for input '" + input + "', got instead: " + mat); + } + } + + @SuppressWarnings("deprecation") + @Test + public void testReadMaterial() { + // Some really needed parts first. + testReadMaterial("water lily", Material.WATER_LILY); + testReadMaterial("water-lily", Material.WATER_LILY); + testReadMaterial("watEr_lily", Material.WATER_LILY); + + testReadMaterial("flint and steel", Material.FLINT_AND_STEEL); + testReadMaterial("259", Material.FLINT_AND_STEEL); + + // Generic test. + for (final Material mat : Material.values()) { + if (mat.name().equalsIgnoreCase("LOCKED_CHEST")) { + continue; + } + testReadMaterial(mat.name(), mat); + testReadMaterial(Integer.toString(mat.getId()), mat); + } + + } +} diff --git a/NCPCore/src/test/java/fr/neatmonster/nocheatplus/test/TestLocationTrace.java b/NCPCore/src/test/java/fr/neatmonster/nocheatplus/test/TestLocationTrace.java new file mode 100644 index 00000000..7a411463 --- /dev/null +++ b/NCPCore/src/test/java/fr/neatmonster/nocheatplus/test/TestLocationTrace.java @@ -0,0 +1,269 @@ +package fr.neatmonster.nocheatplus.test; + +import static org.junit.Assert.fail; + +import java.util.Iterator; +import java.util.Random; + +import org.junit.Test; + +import fr.neatmonster.nocheatplus.checks.moving.LocationTrace; +import fr.neatmonster.nocheatplus.checks.moving.LocationTrace.TraceEntry; +import fr.neatmonster.nocheatplus.checks.moving.LocationTrace.TraceIterator; + + +public class TestLocationTrace { + + protected static final Random random = new Random(System.nanoTime() + 133345691); + + /** + * +- radius around 0.0. + * @param radius + * @return + */ + public static double rand(double radius) { + return rand(0.0, radius); + } + + /** + * +- radius around center. + * @param center + * @param radius + * @return + */ + public static double rand(double center, double radius) { + return center + 2.0 * radius * (random.nextDouble() - 0.5); + } + + /** + * center +- step + * @param center + * @param step + * @return + */ + public static double randStep(double center, double step) { + return center + (random.nextBoolean() ? step : -step); + } + + @Test + public void testSize() { + int size = 80; + double mergeDist = -0.1; + LocationTrace trace = new LocationTrace(size, mergeDist); + // Empty + if (!trace.isEmpty()) { + fail("Trace must be empty on start."); + } + if (trace.size() != 0) { + fail("Size must be 0 at start."); + } + // Adding up to size elements. + for (int i = 0; i < size ; i++) { + trace.addEntry(i, i, i, i); + if (trace.size() != i + 1) { + fail("Wrong size, expect " + (i + 1) + ", got instead: " + trace.size()); + } + } + // Adding a lot of elements. + for (int i = 0; i < 1000; i ++) { + trace.addEntry(i + size, i, i, i); + if (trace.size() != size) { + fail("Wrong size, expect " + size + ", got instead: " + trace.size()); + } + } + } + + @Test + public void testMergeZeroDist() { + int size = 80; + double mergeDist = 0.0; + LocationTrace trace = new LocationTrace(size, mergeDist); + for (int i = 0; i < 1000; i ++) { + trace.addEntry(i + size, 0 , 0, 0); + if (trace.size() != 1) { + fail("Wrong size, expect 1, got instead: " + trace.size()); + } + } + } + + @Test + public void testMergeUpdateAlwaysDist() { + // Extreme merge dist. + int size = 80; + double mergeDist = 1000.0; + LocationTrace trace = new LocationTrace(size, mergeDist); + double x = 0; + double y = 0; + double z = 0; + trace.addEntry(0 , x, y, z); + // Note that entries might get split, if the distance to the second last gets too big, so the maximum number of steps must be limited. + for (int i = 0; i < 1000; i ++) { + x = randStep(x, 0.5); + y = randStep(y, 0.5); + z = randStep(z, 0.5); + trace.addEntry(i + 1, x, y, z); + if (trace.size() != 2) { + fail("Wrong size, expect 2, got instead: " + trace.size()); + } + } + } + + @Test + public void testMergeDist() { + // Deterministic steps => calculatable size. + int size = 80; + double mergeDist = 0.5; + LocationTrace trace = new LocationTrace(size, mergeDist); + double x = 0; + double y = 0; + double z = 0; + trace.addEntry(0 , x, y, z); + for (int i = 0; i < size * 2; i ++) { + x += 0.5; + trace.addEntry(i + 1, x, y, z); + if (Math.abs(trace.size() - (1 + i / 2)) > 1 ) { + fail("Wrong size, expect roughly half of " + (i + 1) + ", got instead: " + trace.size()); + } + } + Iterator it = trace.oldestIterator(); + while (it.hasNext()) { + if (it.next().lastDistSq > 1.0) { + fail("Spacing should be smaller than 1.0 (sq / actual)."); + } + } + + } + + @Test + public void testEmptyIterator() { + // Expected to fail. + int size = 80; + double mergeDist = -0.1; + LocationTrace trace = new LocationTrace(size, mergeDist); + try { + trace.oldestIterator(); + fail("Expect an exception on trying to get an empty iterator (oldest)."); + } catch (IllegalArgumentException ex) { + + } + try { + trace.latestIterator(); + fail("Expect an exception on trying to get an empty iterator (latest)."); + } catch (IllegalArgumentException ex) { + + } + try { + trace.maxAgeIterator(0); + fail("Expect an exception on trying to get an empty iterator (maxAge)."); + } catch (IllegalArgumentException ex) { + + } + } + + @Test + public void testIteratorSizeAndOrder() { + int size = 80; + double mergeDist = -0.1; // Never merge. + LocationTrace trace = new LocationTrace(size, mergeDist); + // Adding up to size elements. + for (int i = 0; i < size; i++) { + trace.addEntry(i, i, i, i); + } + // Test size with one time filled up. + testIteratorSizeAndOrder(trace); + // Add size / 2 elements, to test cross-boundary iteration. + for (int i = 0; i < size / 2; i++) { + trace.addEntry(i + size, i, i, i); + } + // Test size again. + testIteratorSizeAndOrder(trace); + } + + private void testIteratorSizeAndOrder(LocationTrace trace) { + int size = trace.size(); + TraceIterator[] iterators = new TraceIterator[] { + trace.oldestIterator(), + trace.latestIterator(), + trace.maxAgeIterator(0) // Asserts entries to start at time >= 0. + }; + String[] iteratorNames = new String[]{ + "oldest", + "latest", + "maxAge" + }; + int[] increments = new int[] { + 1, + -1, + 1 + }; + for (int i = 0; i < iterators.length; i++) { + int n = 0; + TraceIterator it = iterators[i]; + TraceEntry last = null; + TraceEntry current = null; + while (it.hasNext()) { + current = it.next(); + n ++; + if (n > size) { + fail("Iterator (" + iteratorNames[i] + ") iterates too long."); + } + if (last != null) { + if (current.time - last.time != increments[i]) { + fail("Bad time increment (" + iteratorNames[i] + "). Expected " + increments[i] + ", got instead: " + (current.time - last.time)); + } + } + last = current; + } + if (n != size) { + fail("Iterator (" + iteratorNames[i] + ") should have " + size + " elements, found instead: " + n); + } + } + } + + @Test + public void testMaxAgeIterator() { + int size = 80; + double mergeDist = -0.1; // Never merge. + LocationTrace trace = new LocationTrace(size, mergeDist); + // Adding up to size elements. + for (int i = 0; i < size; i++) { + trace.addEntry(i, i, i, i); + } + for (int i = 0; i < size; i++) { + Iterator it = trace.maxAgeIterator(i); + long got = it.next().time; + if (got != i) { + fail("Bad entry point for iterator (maxAge), expected " + i + ", got instead: " + got); + } + int n = 1; + while (it.hasNext()) { + it.next(); + n ++; + } + if (n != size - i) { + fail("Bad number of elements for iterator (maxAge), expected " + (size - i) + ", got instead: " + n); + } + } + } + + @Test + public void testMaxAgeFirstElementAnyway() { + int size = 80; + double mergeDist = -0.1; // Never merge. + LocationTrace trace = new LocationTrace(size, mergeDist); + trace.addEntry(0, 0, 0, 0); + if (!trace.maxAgeIterator(1000).hasNext()) { + fail("Expect iterator (maxAge) to always contain the latest element."); + } + trace.addEntry(1, 0, 0, 0); + final Iterator it = trace.maxAgeIterator(2); + if (!it.hasNext()) { + fail("Expect iterator (maxAge) to always contain the latest element."); + } + it.next(); + if (it.hasNext()) { + fail("Expect iterator (maxAge) to have only the latest element for all out of range entries."); + } + } + +} diff --git a/NCPPlugin/pom.xml b/NCPPlugin/pom.xml index a684dc6e..0c2f447b 100644 --- a/NCPPlugin/pom.xml +++ b/NCPPlugin/pom.xml @@ -33,11 +33,6 @@ ncpcompatbukkit static
    - - fr.neatmonster - ncpcompatcb2511 - static - fr.neatmonster ncpcompatcb2512 @@ -83,11 +78,31 @@ ncpcompatcb2882 static + + fr.neatmonster + ncpcompatcb2922 + static + + + fr.neatmonster + ncpcompatcb3026 + static + + + fr.neatmonster + ncpcompatcb3043 + static + fr.neatmonster ncpcompatcbdev static + + fr.neatmonster + ncpcompatprotocollib + static + diff --git a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/NoCheatPlus.java b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/NoCheatPlus.java index c64fb721..50361d61 100644 --- a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/NoCheatPlus.java +++ b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/NoCheatPlus.java @@ -655,8 +655,12 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI { changedCommands.addAll(PermissionUtil.protectCommands(Permissions.FILTER_COMMAND, noCommand, true, false, noCommandMsg)); } // Add to changes history for undoing. - if (this.changedCommands == null) this.changedCommands = changedCommands; - else this.changedCommands.addAll(changedCommands); + if (this.changedCommands == null) { + this.changedCommands = changedCommands; + } + else { + this.changedCommands.addAll(changedCommands); + } } /* (non-Javadoc) @@ -665,8 +669,6 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI { @Override public void onLoad() { NCPAPIProvider.setNoCheatPlusAPI(this); - // TODO: Can not set to null in onDisable... - super.onLoad(); // TODO: Check what this does / order. } /* (non-Javadoc) @@ -764,7 +766,7 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI { // Register optional default components. final DefaultComponentFactory dcf = new DefaultComponentFactory(); - for (final Object obj : dcf.getAvailableComponentsOnEnable()){ + for (final Object obj : dcf.getAvailableComponentsOnEnable(this)){ addComponent(obj); // Register sub-components to enable registries for optional components. processQueuedSubComponentHolders(); @@ -888,7 +890,9 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI { initBlockProperties(config); // Reset Command protection. undoCommandChanges(); - if (config.getBoolean(ConfPaths.PROTECT_PLUGINS_HIDE_ACTIVE)) setupCommandProtection(); + if (config.getBoolean(ConfPaths.PROTECT_PLUGINS_HIDE_ACTIVE)) { + setupCommandProtection(); + } // (Re-) schedule consistency checking. scheduleConsistencyCheckers(); // Cache some things. diff --git a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/compat/DefaultComponentFactory.java b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/compat/DefaultComponentFactory.java index 33fb9da6..883dd949 100644 --- a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/compat/DefaultComponentFactory.java +++ b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/compat/DefaultComponentFactory.java @@ -4,8 +4,10 @@ import java.util.Collection; import java.util.LinkedList; import java.util.List; +import fr.neatmonster.nocheatplus.NoCheatPlus; import fr.neatmonster.nocheatplus.checks.inventory.FastConsume; import fr.neatmonster.nocheatplus.logging.LogUtil; +import fr.neatmonster.nocheatplus.net.protocollib.ProtocolLibComponent; /** * Default factory for add-in components which might only be available under certain circumstances. @@ -16,14 +18,16 @@ public class DefaultComponentFactory { /** * This will be called from within the plugin in onEnable, after registration of all core listeners and components. After each components addition processQueuedSubComponentHolders() will be called to allow registries for further optional components. + * @param plugin * @return */ - public Collection getAvailableComponentsOnEnable(){ + public Collection getAvailableComponentsOnEnable(NoCheatPlus plugin){ final List available = new LinkedList(); // Add components (try-catch). // Check: inventory.fastconsume. try{ + // TODO: Static test methods !? FastConsume.testAvailability(); available.add(new FastConsume()); } @@ -31,6 +35,13 @@ public class DefaultComponentFactory { LogUtil.logInfo("[NoCheatPlus] Inventory checks: FastConsume is not available."); } + // ProtocolLib dependencies. + try { + available.add(new ProtocolLibComponent(plugin)); + } catch (Throwable t){ + LogUtil.logInfo("[NoCheatPlus] Packet level access: ProtocolLib is not available."); + } + return available; } } diff --git a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/compat/MCAccessFactory.java b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/compat/MCAccessFactory.java index 939d11dd..94f7fcef 100644 --- a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/compat/MCAccessFactory.java +++ b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/compat/MCAccessFactory.java @@ -3,8 +3,9 @@ package fr.neatmonster.nocheatplus.compat; import java.util.ArrayList; import java.util.List; +import org.bukkit.Bukkit; + import fr.neatmonster.nocheatplus.compat.bukkit.MCAccessBukkit; -import fr.neatmonster.nocheatplus.compat.cb2511.MCAccessCB2511; import fr.neatmonster.nocheatplus.compat.cb2512.MCAccessCB2512; import fr.neatmonster.nocheatplus.compat.cb2545.MCAccessCB2545; import fr.neatmonster.nocheatplus.compat.cb2602.MCAccessCB2602; @@ -14,6 +15,9 @@ import fr.neatmonster.nocheatplus.compat.cb2763.MCAccessCB2763; import fr.neatmonster.nocheatplus.compat.cb2794.MCAccessCB2794; import fr.neatmonster.nocheatplus.compat.cb2808.MCAccessCB2808; import fr.neatmonster.nocheatplus.compat.cb2882.MCAccessCB2882; +import fr.neatmonster.nocheatplus.compat.cb2922.MCAccessCB2922; +import fr.neatmonster.nocheatplus.compat.cb3026.MCAccessCB3026; +import fr.neatmonster.nocheatplus.compat.cb3043.MCAccessCB3043; import fr.neatmonster.nocheatplus.compat.cbdev.MCAccessCBDev; import fr.neatmonster.nocheatplus.config.ConfPaths; import fr.neatmonster.nocheatplus.config.ConfigManager; @@ -27,8 +31,8 @@ import fr.neatmonster.nocheatplus.logging.LogUtil; public class MCAccessFactory { private final String[] updateLocs = new String[]{ - "[NoCheatPlus] Check for updates at BukkitDev: http://dev.bukkit.org/server-mods/nocheatplus/", - "[NoCheatPlus] Development builds: http://ci.ecocitycraft.com/job/NoCheatPlus/", + "[NoCheatPlus] Check for updates and support at BukkitDev: http://dev.bukkit.org/server-mods/nocheatplus/", + "[NoCheatPlus] Development builds (unsupported by the Bukkit Staff, at your own risk): http://ci.md-5.net/job/NoCheatPlus/changes", }; /** @@ -36,7 +40,7 @@ public class MCAccessFactory { * @return MCAccess instance. * @throws RuntimeException if no access can be set. */ - public MCAccess getMCAccess(){ + public MCAccess getMCAccess() { return getMCAccess(ConfigManager.getConfigFile().getBoolean(ConfPaths.COMPATIBILITY_BUKKITONLY)); } @@ -46,28 +50,52 @@ public class MCAccessFactory { * @return * @throws RuntimeException if no access can be set. */ - public MCAccess getMCAccess(final boolean bukkitOnly){ + public MCAccess getMCAccess(final boolean bukkitOnly) { final List throwables = new ArrayList(); // Try to set up native access. - if (!bukkitOnly){ + if (!bukkitOnly) { // TEMP // // Only add as long as no stable module has been added. - // 1.7.2 + // 1.7.10 try{ return new MCAccessCBDev(); } - catch(Throwable t){ + catch(Throwable t) { throwables.add(t); }; // TEMP END // + // 1.7.8|1.7.9 + try{ + return new MCAccessCB3043(); + } + catch(Throwable t) { + throwables.add(t); + }; + + // 1.7.5 + try{ + return new MCAccessCB3026(); + } + catch(Throwable t) { + throwables.add(t); + }; + + // 1.7.2 + try{ + return new MCAccessCB2922(); + } + catch(Throwable t) { + throwables.add(t); + }; + // 1.6.4 try{ return new MCAccessCB2882(); } - catch(Throwable t){ + catch(Throwable t) { throwables.add(t); }; @@ -75,7 +103,7 @@ public class MCAccessFactory { try{ return new MCAccessCB2808(); } - catch(Throwable t){ + catch(Throwable t) { throwables.add(t); }; @@ -83,7 +111,7 @@ public class MCAccessFactory { try{ return new MCAccessCB2794(); } - catch(Throwable t){ + catch(Throwable t) { throwables.add(t); }; @@ -91,7 +119,7 @@ public class MCAccessFactory { try{ return new MCAccessCB2763(); } - catch(Throwable t){ + catch(Throwable t) { throwables.add(t); }; @@ -99,7 +127,7 @@ public class MCAccessFactory { try{ return new MCAccessCB2691(); } - catch(Throwable t){ + catch(Throwable t) { throwables.add(t); }; @@ -107,7 +135,7 @@ public class MCAccessFactory { try{ return new MCAccessCB2645(); } - catch(Throwable t){ + catch(Throwable t) { throwables.add(t); }; @@ -115,7 +143,7 @@ public class MCAccessFactory { try{ return new MCAccessCB2602(); } - catch(Throwable t){ + catch(Throwable t) { throwables.add(t); }; @@ -123,7 +151,7 @@ public class MCAccessFactory { try{ return new MCAccessCB2545(); } - catch(Throwable t){ + catch(Throwable t) { throwables.add(t); }; @@ -131,50 +159,44 @@ public class MCAccessFactory { try{ return new MCAccessCB2512(); } - catch(Throwable t){ + catch(Throwable t) { throwables.add(t); }; - // 1.4.2 ... 1.4.5 (up to CB2511). - try{ - return new MCAccessCB2511(); - } - catch(Throwable t){ - throwables.add(t); - }; } // Try to set up api-only access (since 1.4.6). try{ final String msg; - if (bukkitOnly){ + if (bukkitOnly) { msg = "[NoCheatPlus] The plugin is configured for Bukkit-API-only access."; } else{ - msg = "[NoCheatPlus] Could not set up native access for your specific Minecraft + server-mod version."; + msg = "[NoCheatPlus] Could not set up native access for the server-mod (" + Bukkit.getServer().getVersion() + "). Please check for updates and consider to request support."; + for (String uMsg : updateLocs) { + LogUtil.logWarning(uMsg); + } } LogUtil.logWarning(msg); final MCAccess mcAccess = new MCAccessBukkit(); - LogUtil.logWarning("[NoCheatPlus] API-only MCAccess: Some features will likely not function properly, performance might suffer."); - for (String uMsg : updateLocs){ - LogUtil.logWarning(uMsg); - } + LogUtil.logWarning("[NoCheatPlus] Bukkit-API-only access: Some features will likely not function properly, performance might suffer."); return mcAccess; } - catch(Throwable t){ + catch(Throwable t) { throwables.add(t); }; // All went wrong. // TODO: Fall-back solution (disable plugin, disable checks). - LogUtil.logSevere("[NoCheatPlus] Your version of NoCheatPlus does not seem to provide support for either your Minecraft version or your specific server-mod."); - for (String msg : updateLocs){ + LogUtil.logSevere("[NoCheatPlus] Your version of NoCheatPlus is not compatible with the version of the server-mod (" + Bukkit.getServer().getVersion() + "). Please check for updates and consider to request support."); + for (String msg : updateLocs) { LogUtil.logSevere(msg); } - LogUtil.logSevere("[NoCheatPlus] Could not set up MC version specific access."); - for (Throwable t : throwables ){ + LogUtil.logSevere("[NoCheatPlus] >>> Failed to set up MCAccess <<<"); + for (Throwable t : throwables ) { LogUtil.logSevere(t); } - throw new RuntimeException("Could not set up access to Minecraft API."); + // TODO: Schedule disabling the plugin or running in circles. + throw new RuntimeException("Could not set up native access to the server mod, neither to the Bukkit-API."); } } diff --git a/NoCheatPlus/pom.xml b/NoCheatPlus/pom.xml index 1a5d8a8b..b7db03d1 100644 --- a/NoCheatPlus/pom.xml +++ b/NoCheatPlus/pom.xml @@ -4,12 +4,33 @@ nocheatplus NoCheatPlus - 3.10.9-SNAPSHOT + 3.10.13-SNAPSHOT Detect and fight the exploitation of various flaws/bugs in Minecraft. http://dev.bukkit.org/server-mods/nocheatplus jar + + + md_5-releases + http://repo.md-5.net/content/repositories/releases/ + + + md_5-snapshots + http://repo.md-5.net/content/repositories/snapshots/ + + + + + md_5-releases + http://repo.md-5.net/content/repositories/releases/ + + + md_5-snapshots + http://repo.md-5.net/content/repositories/snapshots/ + + + fr.neatmonster @@ -69,7 +90,6 @@ fr.neatmonster:ncpcommons fr.neatmonster:ncpcore fr.neatmonster:ncpcompatbukkit - fr.neatmonster:ncpcompatcb2511 fr.neatmonster:ncpcompatcb2512 fr.neatmonster:ncpcompatcb2545 fr.neatmonster:ncpcompatcb2602 @@ -79,7 +99,11 @@ fr.neatmonster:ncpcompatcb2794 fr.neatmonster:ncpcompatcb2808 fr.neatmonster:ncpcompatcb2882 + fr.neatmonster:ncpcompatcb2922 + fr.neatmonster:ncpcompatcb3026 + fr.neatmonster:ncpcompatcb3043 fr.neatmonster:ncpcompatcbdev + fr.neatmonster:ncpcompatprotocollib fr.neatmonster:ncpplugin diff --git a/README.md b/README.md index b291af1c..744a1d6c 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,13 @@ Compiling NoCheatPlus * We use [Maven] (http://maven.apache.org/download.cgi) 3 to handle the dependencies. * You can compile it with this Maven goal: `mvn clean package` * NoCheatPlus is compiled for Java 6. +* If you run into trouble with dependencies, because they have been removed from the repository for obscure reasons, or if the repository prevents you from accessing it, you can install the dependency manually. +Example for Eclipse with embedded maven: +Add a new maven build run configuration, name it appropriately, e.g. ```Install CB 1.7.5```. +Set goals to: ```install:install-file -Dfile= -DgroupId=org.bukkit -DartifactId=craftbukkit -Dversion=1.7.5-R0.1-SNAPSHOT -Dpackaging=jar``` +On Windows the might look like: ```X:\...\craftbukkit\3042\craftbukkit-1.7.5-R0.1-20140408.020329-16.jar``` +To let it run you might have to set the base directory, e.g. to ```${workspace_loc}```, it does not seem to have significance. +Do set the correct version alongside the file name. On newer version of maven, you might do with much simplified goals, because the pom inside the jars are parsed. Links --------- diff --git a/pom.xml b/pom.xml index 7a92a5a2..61b72a0f 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,6 @@ NCPCommons NCPCore NCPCompatBukkit - NCPCompatCB2511 NCPCompatCB2512 NCPCompatCB2545 NCPCompatCB2602 @@ -28,21 +27,15 @@ NCPCompatCB2794 NCPCompatCB2808 NCPCompatCB2882 + NCPCompatCB2922 + NCPCompatCB3026 + NCPCompatCB3043 NCPCompatCBDev + NCPCompatProtocolLib NCPPlugin NoCheatPlus - bukkit