mirror of
https://github.com/Updated-NoCheatPlus/NoCheatPlus.git
synced 2026-11-08 09:35:18 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a64a911486 | ||
|
|
15ae1001ee | ||
|
|
1ee3c1ff5e | ||
|
|
a801091438 | ||
|
|
02abcfc1c9 | ||
|
|
9ce8772fcf | ||
|
|
75ecd05ff3 | ||
|
|
c67e4a4808 | ||
|
|
524a48dec3 | ||
|
|
c96fdbb5aa | ||
|
|
ee3078aafa | ||
|
|
6f6a1e717a | ||
|
|
c8b3b14999 | ||
|
|
a10f890344 | ||
|
|
a5c174672a | ||
|
|
ed210aea3f | ||
|
|
09a751eecc | ||
|
|
5ed24b6545 | ||
|
|
4ecbdffa07 | ||
|
|
13a65cb9c4 | ||
|
|
9aef9c61cd | ||
|
|
46958110d2 | ||
|
|
1dbae29c4b | ||
|
|
4e4998e8ae | ||
|
|
991946290e | ||
|
|
70a6e6d7ad | ||
|
|
5a5f20b459 | ||
|
|
ddb128dbe8 | ||
|
|
f90a7c1b76 | ||
|
|
cedb9710a0 | ||
|
|
9048ea023b | ||
|
|
3ea3829ae9 | ||
|
|
20e5a7164e | ||
|
|
d886176eff | ||
|
|
402b4fd5c5 | ||
|
|
959f8151ab | ||
|
|
a35d8ffe61 | ||
|
|
5e6ee4b8cc | ||
|
|
3601db801a | ||
|
|
066d0268a3 | ||
|
|
e469c6727c | ||
|
|
3c54300831 | ||
|
|
042113e978 | ||
|
|
2d021ded3a | ||
|
|
2e6babb977 | ||
|
|
369f190116 | ||
|
|
7ef148301e | ||
|
|
1449067c17 | ||
|
|
127fbf725c | ||
|
|
25b9d27646 | ||
|
|
3198615873 | ||
|
|
125a23971a | ||
|
|
eff2d83139 | ||
|
|
e5df35dcce | ||
|
|
5987b4d381 | ||
|
|
1beb1d188f | ||
|
|
6c1f3caf41 | ||
|
|
c8850e7b83 | ||
|
|
dcdfbc83e0 | ||
|
|
c4ab7560a4 | ||
|
|
5f26653c7f | ||
|
|
112bdaa043 | ||
|
|
108435fd86 | ||
|
|
249da9c6e7 | ||
|
|
3817d568c6 | ||
|
|
a0e20f9c29 | ||
|
|
91aa3a9f33 | ||
|
|
7b1f19fc4b | ||
|
|
d84ea4dc92 | ||
|
|
1f0f9c972c | ||
|
|
c09af5be3b | ||
|
|
4c86d0a8bf | ||
|
|
8f904387e2 | ||
|
|
e474df86f4 | ||
|
|
3703a51529 | ||
|
|
c08cec4f9b | ||
|
|
da1058c7e4 | ||
|
|
c398a4988e | ||
|
|
dcda38716b | ||
|
|
5eafb1f506 | ||
|
|
df4f2079b4 | ||
|
|
264302636c | ||
|
|
ade49a577d | ||
|
|
3e8566de2a | ||
|
|
8bf59cd85b | ||
|
|
2839e6a40f | ||
|
|
f2a8cc6f64 | ||
|
|
e3560651c1 | ||
|
|
aa08284c03 | ||
|
|
60fa3bbdc7 | ||
|
|
7a8995cb2b | ||
|
|
5a7a6cc76b | ||
|
|
91692d8a5e | ||
|
|
0ec5bd9439 | ||
|
|
a635fccd77 | ||
|
|
048ac8f84a | ||
|
|
02766a11d3 | ||
|
|
0623d7592f | ||
|
|
7e5c65b828 | ||
|
|
e14a49e6e3 | ||
|
|
5b3bda5cb8 | ||
|
|
560ff53bac | ||
|
|
6a13a6b4ed | ||
|
|
ba2e189b8d | ||
|
|
67dd4bccda | ||
|
|
2ac8c27856 | ||
|
|
53ff9d299d | ||
|
|
53d14f3a36 | ||
|
|
d4181ce990 | ||
|
|
38d0486279 | ||
|
|
6143288a88 | ||
|
|
dd2136eea7 | ||
|
|
a2c81f7d9b | ||
|
|
e4126cdfa8 |
No files matched your search
@@ -6,13 +6,17 @@ labels: ''
|
|||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
#### *Complete* output of the `ncp version` command (please prefer text here)
|
#### *Complete* output of the `ncp version` command
|
||||||
|
|
||||||
#### Short description of the issue and how to reproduce (is it random / always happens / side conditions ?)
|
### Output of the `ncp inspect [playername]` command for in-game issues
|
||||||
|
|
||||||
|
#### Short description of the issue and how to reproduce
|
||||||
|
|
||||||
#### Have you made any (noteworthy) changes to the default configuration of NCP?
|
#### Have you made any (noteworthy) changes to the default configuration of NCP?
|
||||||
|
|
||||||
#### Do you run any other special plugins that affect game mechanics next to NC+ (skills, machines, adding/changing blocks, other anti-cheating)?
|
#### Do you run any other special plugins that affect game mechanics next to NC+ (skills, machines, adding/changing blocks, other anti-cheating)?
|
||||||
|
|
||||||
|
#### Do you use **client-side** mods that could interfere with vanilla game mechanics **_(such as Optifine's FastMath)_**?
|
||||||
|
|
||||||
#### Provide additional information: for bypasses, vulnerabilities and reproducible issues, we request a [debug log. ](https://github.com/Updated-NoCheatPlus/Docs/blob/master/Development/Debugging.md#on-the-fly-debug-output-for-individual-players) Use [GitHub gists](https://gist.github.com/) or [PasteBin](http://pastebin.com/) or similar for errors and huge logs, or attach a (zip-) file here.
|
#### Provide additional information: for bypasses, vulnerabilities and reproducible issues, we request a [debug log. ](https://github.com/Updated-NoCheatPlus/Docs/blob/master/Development/Debugging.md#on-the-fly-debug-output-for-individual-players) Use [GitHub gists](https://gist.github.com/) or [PasteBin](http://pastebin.com/) or similar for errors and huge logs, or attach a (zip-) file here.
|
||||||
|
|
||||||
+1
@@ -18,6 +18,7 @@ import java.util.Collection;
|
|||||||
|
|
||||||
import fr.neatmonster.nocheatplus.utilities.ds.bktree.SimpleTimedBKLevenshtein.STBKLResult;
|
import fr.neatmonster.nocheatplus.utilities.ds.bktree.SimpleTimedBKLevenshtein.STBKLResult;
|
||||||
import fr.neatmonster.nocheatplus.utilities.ds.bktree.TimedBKLevenshtein.SimpleTimedLevenNode;
|
import fr.neatmonster.nocheatplus.utilities.ds.bktree.TimedBKLevenshtein.SimpleTimedLevenNode;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.ds.bktree.BKModTree.LookupEntry;
|
||||||
|
|
||||||
public class SimpleTimedBKLevenshtein extends TimedBKLevenshtein<SimpleTimedLevenNode, STBKLResult> {
|
public class SimpleTimedBKLevenshtein extends TimedBKLevenshtein<SimpleTimedLevenNode, STBKLResult> {
|
||||||
|
|
||||||
|
|||||||
+125
-4
@@ -16,10 +16,20 @@ package fr.neatmonster.nocheatplus.utilities.ds.map;
|
|||||||
|
|
||||||
import fr.neatmonster.nocheatplus.utilities.Misc;
|
import fr.neatmonster.nocheatplus.utilities.Misc;
|
||||||
|
|
||||||
public class BlockCoord {
|
/**
|
||||||
private final int x;
|
* Integer block coordinate with support for in-place mutation.
|
||||||
private final int y;
|
*/
|
||||||
private final int z;
|
public class BlockCoord implements Cloneable {
|
||||||
|
/*
|
||||||
|
* NOTE: This class is now mutable to allow in-place addition/subtraction
|
||||||
|
* operations that do not create new instances. Because instances are
|
||||||
|
* mutable they are NOT safe to use as keys in hash-based collections if
|
||||||
|
* their coordinates may change while stored; use {@link #copy()} to get an
|
||||||
|
* immutable snapshot when needed.
|
||||||
|
*/
|
||||||
|
private int x;
|
||||||
|
private int y;
|
||||||
|
private int z;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a BlockCoord with integer coordinates.
|
* Constructs a BlockCoord with integer coordinates.
|
||||||
@@ -72,4 +82,115 @@ public class BlockCoord {
|
|||||||
BlockCoord bc = (BlockCoord) obj;
|
BlockCoord bc = (BlockCoord) obj;
|
||||||
return bc.getX() == x && bc.getY() == y && bc.getZ() == z;
|
return bc.getX() == x && bc.getY() == y && bc.getZ() == z;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the given BlockCoord to this one.
|
||||||
|
*
|
||||||
|
* @param other the other BlockCoord to add to this one
|
||||||
|
* @return this (after modification)
|
||||||
|
*/
|
||||||
|
public BlockCoord add(BlockCoord other) {
|
||||||
|
return add(other.x, other.y, other.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the given integers to this BlockCoord.
|
||||||
|
*
|
||||||
|
* @param dx delta x
|
||||||
|
* @param dy delta y
|
||||||
|
* @param dz delta z
|
||||||
|
* @return this (after modification)
|
||||||
|
*/
|
||||||
|
public BlockCoord add(int dx, int dy, int dz) {
|
||||||
|
this.x += dx;
|
||||||
|
this.y += dy;
|
||||||
|
this.z += dz;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subtract the given BlockCoord from this one.
|
||||||
|
*
|
||||||
|
* @param other the other BlockCoord to subtract
|
||||||
|
* @return this (after modification)
|
||||||
|
*/
|
||||||
|
public BlockCoord subtract(BlockCoord other) {
|
||||||
|
return subtract(other.x, other.y, other.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subtract the given integers from this BlockCoord.
|
||||||
|
*
|
||||||
|
* @param dx delta x
|
||||||
|
* @param dy delta y
|
||||||
|
* @param dz delta z
|
||||||
|
* @return this (after modification)
|
||||||
|
*/
|
||||||
|
public BlockCoord subtract(int dx, int dy, int dz) {
|
||||||
|
this.x -= dx;
|
||||||
|
this.y -= dy;
|
||||||
|
this.z -= dz;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the squared Euclidean distance between this block coordinate
|
||||||
|
* and another BlockCoord (integer arithmetic). Returns a double to match
|
||||||
|
* common expectations and avoid overflow for large distances.
|
||||||
|
*
|
||||||
|
* @param other the other block coordinate
|
||||||
|
* @return squared distance as a double
|
||||||
|
*/
|
||||||
|
public double distanceSquared(BlockCoord other) {
|
||||||
|
long dx = this.x - other.x;
|
||||||
|
long dy = this.y - other.y;
|
||||||
|
long dz = this.z - other.z;
|
||||||
|
return dx * dx + dy * dy + dz * dz;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the squared Euclidean distance between this block coordinate
|
||||||
|
* and the specified integer coordinates.
|
||||||
|
*
|
||||||
|
* @param x x coordinate to compare to
|
||||||
|
* @param y y coordinate to compare to
|
||||||
|
* @param z z coordinate to compare to
|
||||||
|
* @return squared distance as a double
|
||||||
|
*/
|
||||||
|
public double distanceSquared(int x, int y, int z) {
|
||||||
|
long dx = this.x - x;
|
||||||
|
long dy = this.y - y;
|
||||||
|
long dz = this.z - z;
|
||||||
|
return dx * dx + dy * dy + dz * dz;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a copy of this BlockCoord. Returns a new independent instance
|
||||||
|
* with the same coordinates (useful when you need an immutable snapshot
|
||||||
|
* while mutating the original).
|
||||||
|
*
|
||||||
|
* @return a new BlockCoord with identical coordinates
|
||||||
|
*/
|
||||||
|
public BlockCoord copy() {
|
||||||
|
return new BlockCoord(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clone this BlockCoord.
|
||||||
|
* <p>
|
||||||
|
* This implementation attempts to use Object#clone() (via
|
||||||
|
* super.clone()). If, for some reason,
|
||||||
|
* cloning via super is not supported (CloneNotSupportedException), we
|
||||||
|
* fall back to creating a manual copy via {@link #copy()}.
|
||||||
|
*
|
||||||
|
* @return a new BlockCoord with identical coordinates
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public BlockCoord clone() {
|
||||||
|
try {
|
||||||
|
return (BlockCoord) super.clone();
|
||||||
|
} catch (CloneNotSupportedException e) {
|
||||||
|
return copy();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+1
@@ -16,6 +16,7 @@ package fr.neatmonster.nocheatplus.utilities.ds.prefixtree;
|
|||||||
|
|
||||||
import fr.neatmonster.nocheatplus.utilities.ds.prefixtree.CharPrefixTree.SimpleCharNode;
|
import fr.neatmonster.nocheatplus.utilities.ds.prefixtree.CharPrefixTree.SimpleCharNode;
|
||||||
import fr.neatmonster.nocheatplus.utilities.ds.prefixtree.SimpleCharPrefixTree.SimpleCharLookupEntry;
|
import fr.neatmonster.nocheatplus.utilities.ds.prefixtree.SimpleCharPrefixTree.SimpleCharLookupEntry;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.ds.prefixtree.CharPrefixTree.CharLookupEntry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adding some convenience methods.
|
* Adding some convenience methods.
|
||||||
|
|||||||
+1
@@ -16,6 +16,7 @@ package fr.neatmonster.nocheatplus.utilities.ds.prefixtree;
|
|||||||
|
|
||||||
import fr.neatmonster.nocheatplus.utilities.ds.prefixtree.PrefixTree.SimpleNode;
|
import fr.neatmonster.nocheatplus.utilities.ds.prefixtree.PrefixTree.SimpleNode;
|
||||||
import fr.neatmonster.nocheatplus.utilities.ds.prefixtree.SimplePrefixTree.SimpleLookupEntry;
|
import fr.neatmonster.nocheatplus.utilities.ds.prefixtree.SimplePrefixTree.SimpleLookupEntry;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.ds.prefixtree.PrefixTree.LookupEntry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple prefix tree, for one given key type.
|
* Simple prefix tree, for one given key type.
|
||||||
|
|||||||
+1
@@ -16,6 +16,7 @@ package fr.neatmonster.nocheatplus.utilities.ds.prefixtree;
|
|||||||
|
|
||||||
import fr.neatmonster.nocheatplus.utilities.ds.prefixtree.SimpleTimedCharPrefixTree.SimpleTimedCharLookupEntry;
|
import fr.neatmonster.nocheatplus.utilities.ds.prefixtree.SimpleTimedCharPrefixTree.SimpleTimedCharLookupEntry;
|
||||||
import fr.neatmonster.nocheatplus.utilities.ds.prefixtree.TimedCharPrefixTree.SimpleTimedCharNode;
|
import fr.neatmonster.nocheatplus.utilities.ds.prefixtree.TimedCharPrefixTree.SimpleTimedCharNode;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.ds.prefixtree.TimedCharPrefixTree.TimedCharLookupEntry;
|
||||||
|
|
||||||
public class SimpleTimedCharPrefixTree extends TimedCharPrefixTree<SimpleTimedCharNode, SimpleTimedCharLookupEntry> {
|
public class SimpleTimedCharPrefixTree extends TimedCharPrefixTree<SimpleTimedCharNode, SimpleTimedCharLookupEntry> {
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,8 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.spigotmc</groupId>
|
<groupId>org.spigotmc</groupId>
|
||||||
<artifactId>spigot-api</artifactId>
|
<artifactId>spigot-api</artifactId>
|
||||||
<version>1.21.4-R0.1-SNAPSHOT</version>
|
<!-- Do not increase. 1.12 was the last version having its own native module -->
|
||||||
|
<version>1.21.11-R0.1-SNAPSHOT</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
+120
-4
@@ -14,6 +14,10 @@
|
|||||||
*/
|
*/
|
||||||
package fr.neatmonster.nocheatplus.compat.bukkit;
|
package fr.neatmonster.nocheatplus.compat.bukkit;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
@@ -21,6 +25,8 @@ import org.bukkit.entity.Entity;
|
|||||||
import org.bukkit.entity.EntityType;
|
import org.bukkit.entity.EntityType;
|
||||||
|
|
||||||
import fr.neatmonster.nocheatplus.compat.Bridge1_13;
|
import fr.neatmonster.nocheatplus.compat.Bridge1_13;
|
||||||
|
import fr.neatmonster.nocheatplus.compat.SchedulerHelper;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.CheckUtils;
|
||||||
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
||||||
import fr.neatmonster.nocheatplus.utilities.map.BlockFlags;
|
import fr.neatmonster.nocheatplus.utilities.map.BlockFlags;
|
||||||
import fr.neatmonster.nocheatplus.utilities.map.BlockProperties;
|
import fr.neatmonster.nocheatplus.utilities.map.BlockProperties;
|
||||||
@@ -28,6 +34,12 @@ import fr.neatmonster.nocheatplus.utilities.map.MaterialUtil;
|
|||||||
|
|
||||||
public class BlockCacheBukkit extends BlockCache {
|
public class BlockCacheBukkit extends BlockCache {
|
||||||
|
|
||||||
|
private static final long FOLIA_FALLBACK_LOG_INTERVAL_MS = 10000L;
|
||||||
|
private static final int FOLIA_FALLBACK_CONSOLE_LOG_MIN_COUNT = 2;
|
||||||
|
private static final AtomicInteger foliaFallbackCount = new AtomicInteger();
|
||||||
|
private static final AtomicInteger foliaFallbackResolvedCount = new AtomicInteger();
|
||||||
|
private static final AtomicLong nextFoliaFallbackLog = new AtomicLong();
|
||||||
|
|
||||||
protected World world;
|
protected World world;
|
||||||
|
|
||||||
/** Temporary use. Use LocUtil.clone before passing on. Call setWorld(null) after use. */
|
/** Temporary use. Use LocUtil.clone before passing on. Call setWorld(null) after use. */
|
||||||
@@ -50,21 +62,97 @@ public class BlockCacheBukkit extends BlockCache {
|
|||||||
@Override
|
@Override
|
||||||
public Material fetchTypeId(final int x, final int y, final int z) {
|
public Material fetchTypeId(final int x, final int y, final int z) {
|
||||||
// TODO: consider setting type id and data at once.
|
// TODO: consider setting type id and data at once.
|
||||||
return world.getBlockAt(x, y, z).getType();
|
if (world == null) {
|
||||||
|
return Material.AIR;
|
||||||
|
}
|
||||||
|
if (SchedulerHelper.isOwnedByCurrentRegion(world, x, z)) {
|
||||||
|
return world.getBlockAt(x, y, z).getType();
|
||||||
|
}
|
||||||
|
final Material fallbackType = fetchTypeIdWithLocationFallback(x, y, z);
|
||||||
|
return fallbackType == null ? Material.AIR : fallbackType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@Override
|
@Override
|
||||||
public int fetchData(final int x, final int y, final int z) {
|
public int fetchData(final int x, final int y, final int z) {
|
||||||
// TODO: consider setting type id and data at once.
|
// TODO: consider setting type id and data at once.
|
||||||
return Bridge1_13.hasIsSwimming() ? 0 : world.getBlockAt(x, y, z).getData();
|
if (world == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (SchedulerHelper.isOwnedByCurrentRegion(world, x, z)) {
|
||||||
|
return Bridge1_13.hasIsSwimming() ? 0 : world.getBlockAt(x, y, z).getData();
|
||||||
|
}
|
||||||
|
final Integer fallbackData = fetchDataWithLocationFallback(x, y, z);
|
||||||
|
return fallbackData == null ? 0 : fallbackData.intValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Material fetchTypeIdWithLocationFallback(final int x, final int y, final int z) {
|
||||||
|
// Folia compatibility: if the chunk-level ownership check cannot prove safety, retry the exact location.
|
||||||
|
// If that also fails, keep AIR as the safe final fallback instead of touching another region's block state.
|
||||||
|
boolean resolved = false;
|
||||||
|
try {
|
||||||
|
if (SchedulerHelper.isOwnedByCurrentRegion(new Location(world, x, y, z))) {
|
||||||
|
resolved = true;
|
||||||
|
return world.getBlockAt(x, y, z).getType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable t) {
|
||||||
|
// Keep the final AIR fallback for servers that throw while checking ownership or block state.
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
logFoliaBlockFallback("type", resolved);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
private Integer fetchDataWithLocationFallback(final int x, final int y, final int z) {
|
||||||
|
// Folia compatibility: retry exact-location ownership before returning legacy data 0 as the final fallback.
|
||||||
|
boolean resolved = false;
|
||||||
|
try {
|
||||||
|
if (SchedulerHelper.isOwnedByCurrentRegion(new Location(world, x, y, z))) {
|
||||||
|
resolved = true;
|
||||||
|
return Integer.valueOf(Bridge1_13.hasIsSwimming() ? 0 : world.getBlockAt(x, y, z).getData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable t) {
|
||||||
|
// Keep the final data 0 fallback for servers that throw while checking ownership or block state.
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
logFoliaBlockFallback("data", resolved);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void logFoliaBlockFallback(final String kind, final boolean resolved) {
|
||||||
|
if (!CheckUtils.shouldLogDebugToConsole()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final int count = foliaFallbackCount.incrementAndGet();
|
||||||
|
if (resolved) {
|
||||||
|
foliaFallbackResolvedCount.incrementAndGet();
|
||||||
|
}
|
||||||
|
final long now = System.currentTimeMillis();
|
||||||
|
final long next = nextFoliaFallbackLog.get();
|
||||||
|
if (now < next || !nextFoliaFallbackLog.compareAndSet(next, now + FOLIA_FALLBACK_LOG_INTERVAL_MS)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final int resolvedCount = foliaFallbackResolvedCount.getAndSet(0);
|
||||||
|
final int periodCount = foliaFallbackCount.getAndSet(0);
|
||||||
|
// Folia diagnostic: a single safe fallback during teleport/chunk handoff is expected and not useful console noise.
|
||||||
|
if (periodCount < FOLIA_FALLBACK_CONSOLE_LOG_MIN_COUNT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Bukkit.getLogger().info("[NCP][Folia][BlockCache] safe " + kind + " block fallback used "
|
||||||
|
+ periodCount + " times in the last 10s, resolved=" + resolvedCount
|
||||||
|
+ ", finalSafeFallback=" + (periodCount - resolvedCount) + ".");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double[] fetchBounds(final int x, final int y, final int z){
|
public double[] fetchBounds(final int x, final int y, final int z){
|
||||||
Material mat = getType(x, y, z);
|
Material mat = getType(x, y, z);
|
||||||
long flags = BlockFlags.getBlockFlags(mat);
|
long flags = BlockFlags.getBlockFlags(mat);
|
||||||
if (flags == BlockFlags.F_IGN_PASSABLE) {
|
if (flags == BlockFlags.F_IGN_PASSABLE_CHECK) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// TODO: Want to maintain a list with manual entries or at least half / full blocks ?
|
// TODO: Want to maintain a list with manual entries or at least half / full blocks ?
|
||||||
@@ -74,11 +162,17 @@ public class BlockCacheBukkit extends BlockCache {
|
|||||||
|
|
||||||
@Override
|
@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){
|
public boolean standsOnEntity(final Entity entity, final double minX, final double minY, final double minZ, final double maxX, final double maxY, final double maxZ){
|
||||||
|
if (!SchedulerHelper.isOwnedByCurrentRegion(entity)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
try{
|
try{
|
||||||
// TODO: Probably check other ids too before doing this ?
|
// TODO: Probably check other ids too before doing this ?
|
||||||
for (final Entity other : entity.getNearbyEntities(2.0, 2.0, 2.0)){
|
for (final Entity other : entity.getNearbyEntities(2.0, 2.0, 2.0)){
|
||||||
|
if (!SchedulerHelper.isOwnedByCurrentRegion(other)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
final EntityType type = other.getType();
|
final EntityType type = other.getType();
|
||||||
if (!MaterialUtil.isBoat(type) && type != EntityType.SHULKER){ // && !(other instanceof Minecart))
|
if (!MaterialUtil.isBoat(type) && type != EntityType.SHULKER){ // && !(other instanceof Minecart))
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
final double locY = entity.getLocation(useLoc).getY();
|
final double locY = entity.getLocation(useLoc).getY();
|
||||||
@@ -101,4 +195,26 @@ public class BlockCacheBukkit extends BlockCache {
|
|||||||
super.cleanup();
|
super.cleanup();
|
||||||
world = null;
|
world = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] fetchVisualBounds(int x, int y, int z) {
|
||||||
|
Material mat = getType(x, y, z);
|
||||||
|
long flags = BlockFlags.getBlockFlags(mat);
|
||||||
|
if (flags == BlockFlags.F_IGN_PASSABLE_CHECK) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// TODO: Want to maintain a list with manual entries or at least half / full blocks ?
|
||||||
|
// Always return full bounds, needs extra adaption to BlockProperties (!).
|
||||||
|
return new double[]{0D, 0D, 0D, 1D, 1D, 1D};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long fetchExtendedData(int x, int y, int z) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+65
-1
@@ -16,12 +16,19 @@ package fr.neatmonster.nocheatplus.compat.bukkit;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.block.data.BlockData;
|
||||||
|
import org.bukkit.block.data.Waterlogged;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.entity.EntityType;
|
import org.bukkit.entity.EntityType;
|
||||||
|
|
||||||
|
import fr.neatmonster.nocheatplus.compat.SchedulerHelper;
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitShapeModel;
|
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitShapeModel;
|
||||||
|
import fr.neatmonster.nocheatplus.compat.versions.ClientVersion;
|
||||||
|
import fr.neatmonster.nocheatplus.players.IPlayerData;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.map.BlockFlags;
|
||||||
import fr.neatmonster.nocheatplus.utilities.map.MaterialUtil;
|
import fr.neatmonster.nocheatplus.utilities.map.MaterialUtil;
|
||||||
|
|
||||||
|
|
||||||
@@ -56,6 +63,37 @@ public class BlockCacheBukkitModern extends BlockCacheBukkit {
|
|||||||
}
|
}
|
||||||
return super.fetchData(x, y, z);
|
return super.fetchData(x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long fetchExtendedData(final int x, final int y, final int z) {
|
||||||
|
if (world == null) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
// Folia support: only query live Bukkit block data from the owning region thread.
|
||||||
|
if (SchedulerHelper.isOwnedByCurrentRegion(world, x, z)) {
|
||||||
|
return fetchWaterloggedFlags(x, y, z);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (SchedulerHelper.isOwnedByCurrentRegion(new Location(world, x, y, z))) {
|
||||||
|
return fetchWaterloggedFlags(x, y, z);
|
||||||
|
}
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
// Keep the cache fallback conservative if the scheduler API is unavailable.
|
||||||
|
}
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long fetchWaterloggedFlags(final int x, final int y, final int z) {
|
||||||
|
final IPlayerData pData = getPlayerData();
|
||||||
|
if (pData != null && pData.getClientVersion().isLowerThan(ClientVersion.V_1_13)) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
final BlockData data = world.getBlockAt(x, y, z).getBlockData();
|
||||||
|
if (data instanceof Waterlogged && ((Waterlogged) data).isWaterlogged()) {
|
||||||
|
return BlockFlags.F_WATER | BlockFlags.F_LIQUID | BlockFlags.F_WATERLOGGED;
|
||||||
|
}
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double[] fetchBounds(int x, int y, int z) {
|
public double[] fetchBounds(int x, int y, int z) {
|
||||||
@@ -69,11 +107,37 @@ public class BlockCacheBukkitModern extends BlockCacheBukkit {
|
|||||||
return shapeModel.getShape(this, world, x, y, z);
|
return shapeModel.getShape(this, world, x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] fetchVisualBounds(int x, int y, int z) {
|
||||||
|
Material mat = getType(x, y, z);
|
||||||
|
final BukkitShapeModel shapeModel = shapeModels.get(mat);
|
||||||
|
if (shapeModel == null) {
|
||||||
|
return super.fetchVisualBounds(x, y, z);
|
||||||
|
}
|
||||||
|
return shapeModel.getVisualShape(this, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(int x, int y, int z) {
|
||||||
|
Material mat = getType(x, y, z);
|
||||||
|
final BukkitShapeModel shapeModel = shapeModels.get(mat);
|
||||||
|
if (shapeModel == null) {
|
||||||
|
return super.isCollisionSameVisual(x, y, z);
|
||||||
|
}
|
||||||
|
return shapeModel.isCollisionSameVisual(this, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@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) {
|
public boolean standsOnEntity(final Entity entity, final double minX, final double minY, final double minZ, final double maxX, final double maxY, final double maxZ) {
|
||||||
|
if (!SchedulerHelper.isOwnedByCurrentRegion(entity)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
// TODO: Probably check vehicle ids too before doing this ?
|
// TODO: Probably check vehicle ids too before doing this ?
|
||||||
for (final Entity vehicle : entity.getNearbyEntities(0.1, 2.0, 0.1)) {
|
for (final Entity vehicle : entity.getNearbyEntities(0.1, 2.0, 0.1)) {
|
||||||
|
if (!SchedulerHelper.isOwnedByCurrentRegion(vehicle)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
final EntityType type = vehicle.getType();
|
final EntityType type = vehicle.getType();
|
||||||
if (!MaterialUtil.isBoat(type) && type != EntityType.SHULKER) { // && !(vehicle instanceof Minecart))
|
if (!MaterialUtil.isBoat(type) && type != EntityType.SHULKER) { // && !(vehicle instanceof Minecart))
|
||||||
continue;
|
continue;
|
||||||
@@ -89,4 +153,4 @@ public class BlockCacheBukkitModern extends BlockCacheBukkit {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+13
-11
@@ -27,6 +27,7 @@ import fr.neatmonster.nocheatplus.checks.moving.model.PlayerMoveData;
|
|||||||
import fr.neatmonster.nocheatplus.compat.AttribUtil;
|
import fr.neatmonster.nocheatplus.compat.AttribUtil;
|
||||||
import fr.neatmonster.nocheatplus.compat.Bridge1_13;
|
import fr.neatmonster.nocheatplus.compat.Bridge1_13;
|
||||||
import fr.neatmonster.nocheatplus.compat.BridgeMisc;
|
import fr.neatmonster.nocheatplus.compat.BridgeMisc;
|
||||||
|
import fr.neatmonster.nocheatplus.compat.registry.BukkitAPIAccessFactory;
|
||||||
import fr.neatmonster.nocheatplus.compat.versions.ClientVersion;
|
import fr.neatmonster.nocheatplus.compat.versions.ClientVersion;
|
||||||
import fr.neatmonster.nocheatplus.compat.versions.ServerVersion;
|
import fr.neatmonster.nocheatplus.compat.versions.ServerVersion;
|
||||||
import fr.neatmonster.nocheatplus.components.modifier.IAttributeAccess;
|
import fr.neatmonster.nocheatplus.components.modifier.IAttributeAccess;
|
||||||
@@ -66,6 +67,7 @@ public class BukkitAttributeAccess implements IAttributeAccess {
|
|||||||
* @param id
|
* @param id
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings({"deprecation", "removal"})
|
||||||
private AttributeModifier getModifier(final AttributeInstance attrInst, final UUID id) {
|
private AttributeModifier getModifier(final AttributeInstance attrInst, final UUID id) {
|
||||||
for (final AttributeModifier mod : attrInst.getModifiers()) {
|
for (final AttributeModifier mod : attrInst.getModifiers()) {
|
||||||
if (id.equals(mod.getUniqueId())) {
|
if (id.equals(mod.getUniqueId())) {
|
||||||
@@ -87,7 +89,7 @@ public class BukkitAttributeAccess implements IAttributeAccess {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getSpeedMultiplier(final Player player) {
|
public double getSpeedMultiplier(final Player player) {
|
||||||
final AttributeInstance attrInst = BridgeBukkitAPI.getSpeedAttributeInstance(player);
|
final AttributeInstance attrInst = BukkitAPIAccessFactory.getBukkitAccess().getSpeedAttributeInstance(player);
|
||||||
final double val = attrInst.getValue() / attrInst.getBaseValue();
|
final double val = attrInst.getValue() / attrInst.getBaseValue();
|
||||||
final AttributeModifier mod = getModifier(attrInst, AttribUtil.ID_SPRINT_BOOST);
|
final AttributeModifier mod = getModifier(attrInst, AttribUtil.ID_SPRINT_BOOST);
|
||||||
return mod == null ? val : (val / getMultiplier(mod));
|
return mod == null ? val : (val / getMultiplier(mod));
|
||||||
@@ -95,7 +97,7 @@ public class BukkitAttributeAccess implements IAttributeAccess {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getSprintMultiplier(final Player player) {
|
public double getSprintMultiplier(final Player player) {
|
||||||
final AttributeInstance attrInst = BridgeBukkitAPI.getSpeedAttributeInstance(player);
|
final AttributeInstance attrInst = BukkitAPIAccessFactory.getBukkitAccess().getSpeedAttributeInstance(player);
|
||||||
final AttributeModifier mod = getModifier(attrInst, AttribUtil.ID_SPRINT_BOOST);
|
final AttributeModifier mod = getModifier(attrInst, AttribUtil.ID_SPRINT_BOOST);
|
||||||
return mod == null ? 1.0 : getMultiplier(mod);
|
return mod == null ? 1.0 : getMultiplier(mod);
|
||||||
}
|
}
|
||||||
@@ -112,7 +114,7 @@ public class BukkitAttributeAccess implements IAttributeAccess {
|
|||||||
gravity = thisMove.yDistance <= 0.0 && !Double.isInfinite(Bridge1_13.getSlowfallingAmplifier(player)) ? Magic.SLOW_FALL_GRAVITY : Magic.DEFAULT_GRAVITY;
|
gravity = thisMove.yDistance <= 0.0 && !Double.isInfinite(Bridge1_13.getSlowfallingAmplifier(player)) ? Magic.SLOW_FALL_GRAVITY : Magic.DEFAULT_GRAVITY;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
final AttributeInstance attrInst = BridgeBukkitAPI.getGravityAttributeInstance(player);
|
final AttributeInstance attrInst = BukkitAPIAccessFactory.getBukkitAccess().getGravityAttributeInstance(player);
|
||||||
// Fail-safe.
|
// Fail-safe.
|
||||||
if (attrInst == null) {
|
if (attrInst == null) {
|
||||||
gravity = thisMove.yDistance <= 0.0 && !Double.isInfinite(Bridge1_13.getSlowfallingAmplifier(player)) ? Magic.SLOW_FALL_GRAVITY : Magic.DEFAULT_GRAVITY;
|
gravity = thisMove.yDistance <= 0.0 && !Double.isInfinite(Bridge1_13.getSlowfallingAmplifier(player)) ? Magic.SLOW_FALL_GRAVITY : Magic.DEFAULT_GRAVITY;
|
||||||
@@ -131,7 +133,7 @@ public class BukkitAttributeAccess implements IAttributeAccess {
|
|||||||
// Doesn't exist. Calculate manually.
|
// Doesn't exist. Calculate manually.
|
||||||
return Magic.FALL_DAMAGE_DIST;
|
return Magic.FALL_DAMAGE_DIST;
|
||||||
}
|
}
|
||||||
final AttributeInstance attrInst = BridgeBukkitAPI.getSafeFallAttributeInstance(player);
|
final AttributeInstance attrInst = BukkitAPIAccessFactory.getBukkitAccess().getSafeFallAttributeInstance(player);
|
||||||
// Fail-safe
|
// Fail-safe
|
||||||
if (attrInst == null) return Magic.FALL_DAMAGE_DIST;
|
if (attrInst == null) return Magic.FALL_DAMAGE_DIST;
|
||||||
return MathUtil.clamp(attrInst.getValue(), -1024.0, 1024.0);
|
return MathUtil.clamp(attrInst.getValue(), -1024.0, 1024.0);
|
||||||
@@ -143,7 +145,7 @@ public class BukkitAttributeAccess implements IAttributeAccess {
|
|||||||
// Doesn't exist. Calculate manually.
|
// Doesn't exist. Calculate manually.
|
||||||
return 1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
final AttributeInstance attrInst = BridgeBukkitAPI.getFallMultAttributeInstance(player);
|
final AttributeInstance attrInst = BukkitAPIAccessFactory.getBukkitAccess().getFallMultAttributeInstance(player);
|
||||||
// Fail-safe
|
// Fail-safe
|
||||||
if (attrInst == null) return 1.0;
|
if (attrInst == null) return 1.0;
|
||||||
return MathUtil.clamp(attrInst.getValue(), 1.0, 100.0);
|
return MathUtil.clamp(attrInst.getValue(), 1.0, 100.0);
|
||||||
@@ -155,7 +157,7 @@ public class BukkitAttributeAccess implements IAttributeAccess {
|
|||||||
// Doesn't exist. Calculate manually.
|
// Doesn't exist. Calculate manually.
|
||||||
return 1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
final AttributeInstance attrInst = BridgeBukkitAPI.getBreakSpeedAttributeInstance(player);
|
final AttributeInstance attrInst = BukkitAPIAccessFactory.getBukkitAccess().getBreakSpeedAttributeInstance(player);
|
||||||
// Fail-safe
|
// Fail-safe
|
||||||
if (attrInst == null) return 1.0;
|
if (attrInst == null) return 1.0;
|
||||||
return MathUtil.clamp(attrInst.getValue(), 1.0, 1024.0);
|
return MathUtil.clamp(attrInst.getValue(), 1.0, 1024.0);
|
||||||
@@ -167,7 +169,7 @@ public class BukkitAttributeAccess implements IAttributeAccess {
|
|||||||
// Doesn't exist. Calculate manually.
|
// Doesn't exist. Calculate manually.
|
||||||
return 1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
final AttributeInstance attrInst = BridgeBukkitAPI.getJumpPowerAttributeInstance(player);
|
final AttributeInstance attrInst = BukkitAPIAccessFactory.getBukkitAccess().getJumpPowerAttributeInstance(player);
|
||||||
// Fail-safe
|
// Fail-safe
|
||||||
if (attrInst == null) return 1.0;
|
if (attrInst == null) return 1.0;
|
||||||
final double val = attrInst.getValue() / attrInst.getBaseValue();
|
final double val = attrInst.getValue() / attrInst.getBaseValue();
|
||||||
@@ -186,7 +188,7 @@ public class BukkitAttributeAccess implements IAttributeAccess {
|
|||||||
// Doesn't exist. Calculate manually.
|
// Doesn't exist. Calculate manually.
|
||||||
return 4.5;
|
return 4.5;
|
||||||
}
|
}
|
||||||
final AttributeInstance attrInst = BridgeBukkitAPI.getBlockInteractionRangeAttributeInstance(player);
|
final AttributeInstance attrInst = BukkitAPIAccessFactory.getBukkitAccess().getBlockInteractionRangeAttributeInstance(player);
|
||||||
// Fail-safe
|
// Fail-safe
|
||||||
if (attrInst == null) return 4.5;
|
if (attrInst == null) return 4.5;
|
||||||
return MathUtil.clamp(attrInst.getValue(), 0.0, 64.0);
|
return MathUtil.clamp(attrInst.getValue(), 0.0, 64.0);
|
||||||
@@ -198,7 +200,7 @@ public class BukkitAttributeAccess implements IAttributeAccess {
|
|||||||
// Doesn't exist. Calculate manually.
|
// Doesn't exist. Calculate manually.
|
||||||
return 3.0;
|
return 3.0;
|
||||||
}
|
}
|
||||||
final AttributeInstance attrInst = BridgeBukkitAPI.getEntityInteractionRangeAttributeInstance(player);
|
final AttributeInstance attrInst = BukkitAPIAccessFactory.getBukkitAccess().getEntityInteractionRangeAttributeInstance(player);
|
||||||
// Fail-safe
|
// Fail-safe
|
||||||
if (attrInst == null) return 3.0;
|
if (attrInst == null) return 3.0;
|
||||||
return MathUtil.clamp(attrInst.getValue(), 0.0, 64.0);
|
return MathUtil.clamp(attrInst.getValue(), 0.0, 64.0);
|
||||||
@@ -219,7 +221,7 @@ public class BukkitAttributeAccess implements IAttributeAccess {
|
|||||||
final MovingConfig cc = DataManager.getPlayerData(player).getGenericInstance(MovingConfig.class);
|
final MovingConfig cc = DataManager.getPlayerData(player).getGenericInstance(MovingConfig.class);
|
||||||
return cc.sfStepHeight;
|
return cc.sfStepHeight;
|
||||||
}
|
}
|
||||||
final AttributeInstance attrInst = BridgeBukkitAPI.getStepHeightAttributeInstance(player);
|
final AttributeInstance attrInst = BukkitAPIAccessFactory.getBukkitAccess().getStepHeightAttributeInstance(player);
|
||||||
if (attrInst == null) {
|
if (attrInst == null) {
|
||||||
// Fail-safe.
|
// Fail-safe.
|
||||||
if (player.isInsideVehicle()) {
|
if (player.isInsideVehicle()) {
|
||||||
@@ -277,7 +279,7 @@ public class BukkitAttributeAccess implements IAttributeAccess {
|
|||||||
if (ServerVersion.isLowerThan("1.20.5")) {
|
if (ServerVersion.isLowerThan("1.20.5")) {
|
||||||
return 1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
final AttributeInstance attrInst = BridgeBukkitAPI.getScaleAttributeInstance(player);
|
final AttributeInstance attrInst = BukkitAPIAccessFactory.getBukkitAccess().getScaleAttributeInstance(player);
|
||||||
// Fail-safe.
|
// Fail-safe.
|
||||||
if (attrInst == null) return 1.0;
|
if (attrInst == null) return 1.0;
|
||||||
return MathUtil.clamp(attrInst.getValue(), 0.0625, 16.0);
|
return MathUtil.clamp(attrInst.getValue(), 0.0625, 16.0);
|
||||||
|
|||||||
+1
-1
@@ -41,7 +41,7 @@ public class MCAccessBukkit extends MCAccessBukkitBase implements BlockPropertie
|
|||||||
}
|
}
|
||||||
else if (guessItchyBlock(mat)) {
|
else if (guessItchyBlock(mat)) {
|
||||||
// Uncertain bounding-box, allow passing through.
|
// Uncertain bounding-box, allow passing through.
|
||||||
long flags = BlockFlags.F_IGN_PASSABLE;
|
long flags = BlockFlags.F_IGN_PASSABLE_CHECK;
|
||||||
if ((BlockFlags.hasAnyFlag(flags, BlockFlags.SOLID_GROUND))) {
|
if ((BlockFlags.hasAnyFlag(flags, BlockFlags.SOLID_GROUND))) {
|
||||||
// Block can be ground, so allow standing on any height.
|
// Block can be ground, so allow standing on any height.
|
||||||
flags |= BlockFlags.F_GROUND | BlockFlags.F_GROUND_HEIGHT;
|
flags |= BlockFlags.F_GROUND | BlockFlags.F_GROUND_HEIGHT;
|
||||||
|
|||||||
+1
-1
@@ -80,7 +80,7 @@ public class MCAccessBukkitBase implements MCAccess {
|
|||||||
* Skip fully passable blocks (partially passable blocks may be itchy,
|
* Skip fully passable blocks (partially passable blocks may be itchy,
|
||||||
* though slabs will be easy to handle).
|
* though slabs will be easy to handle).
|
||||||
*/
|
*/
|
||||||
if (BlockFlags.hasAnyFlag(flags, BlockFlags.F_IGN_PASSABLE)) {
|
if (BlockFlags.hasAnyFlag(flags, BlockFlags.F_IGN_PASSABLE_CHECK)) {
|
||||||
// TODO: Blocks with min_height may actually be ok, if xz100 and some height are set.
|
// TODO: Blocks with min_height may actually be ok, if xz100 and some height are set.
|
||||||
if (BlockFlags.hasNoFlags(flags,
|
if (BlockFlags.hasNoFlags(flags,
|
||||||
BlockFlags.F_GROUND_HEIGHT
|
BlockFlags.F_GROUND_HEIGHT
|
||||||
|
|||||||
+73
-53
@@ -22,43 +22,7 @@ import org.bukkit.entity.Player;
|
|||||||
|
|
||||||
import fr.neatmonster.nocheatplus.compat.AlmostBoolean;
|
import fr.neatmonster.nocheatplus.compat.AlmostBoolean;
|
||||||
import fr.neatmonster.nocheatplus.compat.blocks.init.BlockInit;
|
import fr.neatmonster.nocheatplus.compat.blocks.init.BlockInit;
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitAnvil;
|
import fr.neatmonster.nocheatplus.compat.bukkit.model.*;
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitBamboo;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitBell;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitCake;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitCauldron;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitChorusPlant;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitCocoa;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitDirectionalCentered;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitDoor;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitDripLeaf;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitDripStone;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitEndPortalFrame;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitFence;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitFetchableBound;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitFetchableBounds;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitGate;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitGrindStone;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitHopper;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitLadder;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitLantern;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitLevelled;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitPiston;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitPistonHead;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitPowderSnow;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitRail;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitSeaPickle;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitShapeModel;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitShulkerBox;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitSlab;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitSnow;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitStairs;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitStatic;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitTrapDoor;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitTurtleEgg;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitWall;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitWallHead;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.model.BukkitWaterPlant;
|
|
||||||
import fr.neatmonster.nocheatplus.compat.cbreflect.reflect.ReflectBase;
|
import fr.neatmonster.nocheatplus.compat.cbreflect.reflect.ReflectBase;
|
||||||
import fr.neatmonster.nocheatplus.compat.cbreflect.reflect.ReflectDamageSource;
|
import fr.neatmonster.nocheatplus.compat.cbreflect.reflect.ReflectDamageSource;
|
||||||
import fr.neatmonster.nocheatplus.compat.cbreflect.reflect.ReflectDamageSources;
|
import fr.neatmonster.nocheatplus.compat.cbreflect.reflect.ReflectDamageSources;
|
||||||
@@ -101,8 +65,8 @@ public class MCAccessBukkitModern extends MCAccessBukkit {
|
|||||||
0.25, 0.125, 0.25, 0.75, 0.875, 0.75
|
0.25, 0.125, 0.25, 0.75, 0.875, 0.75
|
||||||
);
|
);
|
||||||
private static final BukkitShapeModel MODEL_HOPPER = new BukkitHopper();
|
private static final BukkitShapeModel MODEL_HOPPER = new BukkitHopper();
|
||||||
private static final BukkitShapeModel MODEL_CAULDRON = new BukkitCauldron(0.1875, 0.125, 0.8125, 0.0625);
|
private static final BukkitShapeModel MODEL_CAULDRON = new BukkitCauldron();
|
||||||
private static final BukkitShapeModel MODEL_COMPOSTER = new BukkitCauldron(0.0, 0.125, 1.0, 0.125);
|
private static final BukkitShapeModel MODEL_COMPOSTER = new BukkitComposter(0.0, 0.125, 1.0, 0.125);
|
||||||
private static final BukkitShapeModel MODEL_PISTON_HEAD = new BukkitPistonHead();
|
private static final BukkitShapeModel MODEL_PISTON_HEAD = new BukkitPistonHead();
|
||||||
private static final BukkitShapeModel MODEL_BELL = new BukkitBell();
|
private static final BukkitShapeModel MODEL_BELL = new BukkitBell();
|
||||||
private static final BukkitShapeModel MODEL_ANVIL = new BukkitAnvil();
|
private static final BukkitShapeModel MODEL_ANVIL = new BukkitAnvil();
|
||||||
@@ -135,8 +99,8 @@ public class MCAccessBukkitModern extends MCAccessBukkit {
|
|||||||
private static final BukkitShapeModel MODEL_END_ROD = new BukkitDirectionalCentered(0.375, 1.0, false);
|
private static final BukkitShapeModel MODEL_END_ROD = new BukkitDirectionalCentered(0.375, 1.0, false);
|
||||||
|
|
||||||
// Blocks that have a different shape with neighbor blocks (bukkit takes care though).
|
// Blocks that have a different shape with neighbor blocks (bukkit takes care though).
|
||||||
private static final BukkitShapeModel MODEL_THIN_FENCE = new BukkitFence(0.4375, 1.0);
|
private static final BukkitShapeModel MODEL_THIN_FENCE = new BukkitFence(0.4375, 1.0, true);
|
||||||
private static final BukkitShapeModel MODEL_THICK_FENCE = new BukkitFence(0.375, 1.5);
|
private static final BukkitShapeModel MODEL_THICK_FENCE = new BukkitFence(0.375, 1.5, false);
|
||||||
private static final BukkitShapeModel MODEL_THICK_FENCE2 = new BukkitWall(0.25, 1.5, 0.3125); // .75 .25 0 max: .25 .75 .5
|
private static final BukkitShapeModel MODEL_THICK_FENCE2 = new BukkitWall(0.25, 1.5, 0.3125); // .75 .25 0 max: .25 .75 .5
|
||||||
private static final BukkitShapeModel MODEL_WALL_HEAD = new BukkitWallHead();
|
private static final BukkitShapeModel MODEL_WALL_HEAD = new BukkitWallHead();
|
||||||
|
|
||||||
@@ -144,15 +108,17 @@ public class MCAccessBukkitModern extends MCAccessBukkit {
|
|||||||
private static final BukkitShapeModel MODEL_CAMPFIRE = new BukkitStatic(0.0, 0.4375);
|
private static final BukkitShapeModel MODEL_CAMPFIRE = new BukkitStatic(0.0, 0.4375);
|
||||||
private static final BukkitShapeModel MODEL_BAMBOO = new BukkitBamboo();
|
private static final BukkitShapeModel MODEL_BAMBOO = new BukkitBamboo();
|
||||||
private static final BukkitShapeModel MODEL_POINTED_DRIPSTONE = new BukkitDripStone();
|
private static final BukkitShapeModel MODEL_POINTED_DRIPSTONE = new BukkitDripStone();
|
||||||
|
private static final BukkitShapeModel MODEL_SCAFFOLDING = new BukkitScaffolding();
|
||||||
private static final BukkitShapeModel MODEL_WATER_PLANTS = new BukkitWaterPlant();
|
private static final BukkitShapeModel MODEL_WATER_PLANTS = new BukkitWaterPlant();
|
||||||
private static final BukkitShapeModel MODEL_LILY_PAD = new BukkitStatic(0.0625, 0.09375);
|
private static final BukkitShapeModel MODEL_LILY_PAD = new BukkitLilyPad();
|
||||||
private static final BukkitShapeModel MODEL_FLOWER_POT = new BukkitStatic(0.3125, 0.375);
|
private static final BukkitShapeModel MODEL_FLOWER_POT = new BukkitStatic(0.3125, 0.375);
|
||||||
private static final BukkitShapeModel MODEL_LANTERN = new BukkitLantern();
|
private static final BukkitShapeModel MODEL_LANTERN = new BukkitLantern();
|
||||||
private static final BukkitShapeModel MODEL_CONDUIT = new BukkitStatic(0.3125, 0.3125, 0.3125, 0.6875, 0.6875, 0.6875);
|
private static final BukkitShapeModel MODEL_CONDUIT = new BukkitStatic(0.3125, 0.3125, 0.3125, 0.6875, 0.6875, 0.6875);
|
||||||
private static final BukkitShapeModel MODEL_GROUND_HEAD = new BukkitStatic(0.25, 0.5);
|
private static final BukkitShapeModel MODEL_GROUND_HEAD = new BukkitStatic(0.25, 0.5);
|
||||||
private static final BukkitShapeModel MODEL_SINGLE_CHEST = new BukkitStatic(0.0625, 0.875);
|
private static final BukkitShapeModel MODEL_CHEST = new BukkitChest();
|
||||||
private static final BukkitShapeModel MODEL_HONEY_BLOCK = new BukkitStatic(0.0625, 0.9375);
|
private static final BukkitShapeModel MODEL_HONEY_BLOCK = new BukkitStatic(0.0625, 0.9375);
|
||||||
private static final BukkitShapeModel MODEL_SCULK_SHRIEKER = new BukkitStatic(0.0, 0.5);
|
private static final BukkitShapeModel MODEL_SCULK_SHRIEKER = new BukkitStatic(0.0, 0.5);
|
||||||
|
private static final BukkitShapeModel MODEL_DRIED_GHAST = new BukkitStatic(0.1875f, 0.0f, 0.1875f, 0.8125f, 0.625f, 0.8125f); // NMS bounds: x1,y1,z1,x2,y2,y2 (y2 = 10/16 = 0.625)
|
||||||
|
|
||||||
// Static blocks with full height sorted by inset.
|
// Static blocks with full height sorted by inset.
|
||||||
private static final BukkitShapeModel MODEL_INSET16_1_HEIGHT100 = new BukkitStatic(0.0625, 1.0);
|
private static final BukkitShapeModel MODEL_INSET16_1_HEIGHT100 = new BukkitStatic(0.0625, 1.0);
|
||||||
@@ -163,7 +129,7 @@ public class MCAccessBukkitModern extends MCAccessBukkit {
|
|||||||
private static final BukkitShapeModel MODEL_XZ100_HEIGHT8_3 = new BukkitStatic(0.375);
|
private static final BukkitShapeModel MODEL_XZ100_HEIGHT8_3 = new BukkitStatic(0.375);
|
||||||
private static final BukkitShapeModel MODEL_XZ100_HEIGHT16_9 = new BukkitStatic(0.5625);
|
private static final BukkitShapeModel MODEL_XZ100_HEIGHT16_9 = new BukkitStatic(0.5625);
|
||||||
private static final BukkitShapeModel MODEL_XZ100_HEIGHT4_3 = new BukkitStatic(0.75);
|
private static final BukkitShapeModel MODEL_XZ100_HEIGHT4_3 = new BukkitStatic(0.75);
|
||||||
private static final BukkitShapeModel MODEL_XZ100_HEIGHT16_15 = new BukkitStatic(0.9375);
|
private static final BukkitShapeModel MODEL_XZ100_HEIGHT16_15 = new BukkitDirtLike();
|
||||||
private static final BukkitShapeModel MODEL_XZ100_HEIGHT8_7 = new BukkitStatic(0.875);
|
private static final BukkitShapeModel MODEL_XZ100_HEIGHT8_7 = new BukkitStatic(0.875);
|
||||||
|
|
||||||
public MCAccessBukkitModern() {
|
public MCAccessBukkitModern() {
|
||||||
@@ -183,7 +149,7 @@ public class MCAccessBukkitModern extends MCAccessBukkit {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMCVersion() {
|
public String getMCVersion() {
|
||||||
return "1.13-1.21";
|
return "1.13-26";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -233,11 +199,18 @@ public class MCAccessBukkitModern extends MCAccessBukkit {
|
|||||||
processedBlocks.add(mat);
|
processedBlocks.add(mat);
|
||||||
}
|
}
|
||||||
for (final Material mat : BridgeMaterial.getAllBlocks(
|
for (final Material mat : BridgeMaterial.getAllBlocks(
|
||||||
"light", "glow_lichen", "big_dripleaf_stem",
|
"light", "glow_lichen", "big_dripleaf_stem")) {
|
||||||
// TODO: Not fully tested
|
|
||||||
"scaffolding")) {
|
|
||||||
processedBlocks.add(mat);
|
processedBlocks.add(mat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shelf mushrooms
|
||||||
|
for (final Material mat : BridgeMaterial.getAllBlocks("shelf_mushroom")) {
|
||||||
|
addModel(mat, MODEL_AUTO_FETCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Material mat : MaterialUtil.STRAW_BEDS) {
|
||||||
|
addModel(mat, MODEL_AUTO_FETCH);
|
||||||
|
}
|
||||||
|
|
||||||
// Candle
|
// Candle
|
||||||
for (Material mat : MaterialUtil.ALL_CANDLES) {
|
for (Material mat : MaterialUtil.ALL_CANDLES) {
|
||||||
@@ -248,13 +221,23 @@ public class MCAccessBukkitModern extends MCAccessBukkit {
|
|||||||
for (Material mat : MaterialUtil.AMETHYST) {
|
for (Material mat : MaterialUtil.AMETHYST) {
|
||||||
addModel(mat, MODEL_AUTO_FETCH);
|
addModel(mat, MODEL_AUTO_FETCH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shelves
|
||||||
|
for (Material mat : MaterialUtil.WOODEN_SHELVES) {
|
||||||
|
addModel(mat, MODEL_AUTO_FETCH);
|
||||||
|
}
|
||||||
|
|
||||||
// new flower, and others
|
// new flower, and others
|
||||||
for (Material mat : BridgeMaterial.getAllBlocks(
|
for (Material mat : BridgeMaterial.getAllBlocks(
|
||||||
"azalea", "flowering_azalea",
|
"azalea", "flowering_azalea",
|
||||||
"sculk_sensor", "frogspawn",
|
"sculk_sensor", "frogspawn",
|
||||||
"sniffer_egg", "decorated_pot", "pitcher_crop", "calibrated_sculk_sensor", "bubble_column",
|
"sniffer_egg", "decorated_pot", "pitcher_crop", "calibrated_sculk_sensor",
|
||||||
"closed_eyeblossom", "open_eyeblossom", "pale_hanging_moss", "resin_clump")) {
|
"closed_eyeblossom", "open_eyeblossom", "pale_hanging_moss", "resin_clump","cactus_flower",
|
||||||
|
"short_dry_grass", "tall_dry_grass", "golden_dandelion")) {
|
||||||
|
addModel(mat, MODEL_AUTO_FETCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final Material mat : BridgeMaterial.getAllBlocks("pale_moss_carpet")) {
|
||||||
addModel(mat, MODEL_AUTO_FETCH);
|
addModel(mat, MODEL_AUTO_FETCH);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,10 +247,26 @@ public class MCAccessBukkitModern extends MCAccessBukkit {
|
|||||||
addModel(mat, MODEL_AUTO_FETCH);
|
addModel(mat, MODEL_AUTO_FETCH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stonecutter and legacy chains
|
||||||
for (Material mat : BridgeMaterial.getAllBlocks(
|
for (Material mat : BridgeMaterial.getAllBlocks(
|
||||||
"stonecutter", "chain")) {
|
"stonecutter", "chain")) {
|
||||||
addModel(mat, MODEL_AUTO_FETCH_LEGACY);
|
addModel(mat, MODEL_AUTO_FETCH_LEGACY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Modern iron chains
|
||||||
|
if (BridgeMaterial.IRON_CHAIN != null) {
|
||||||
|
addModel(BridgeMaterial.IRON_CHAIN, MODEL_AUTO_FETCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copper chains
|
||||||
|
for (Material mat : MaterialUtil.COPPER_CHAINS) {
|
||||||
|
addModel(mat, MODEL_AUTO_FETCH_LEGACY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copper golem statues.
|
||||||
|
for (Material mat : MaterialUtil.COPPER_GOLEM_STATUES) {
|
||||||
|
addModel(mat, MODEL_AUTO_FETCH);
|
||||||
|
}
|
||||||
|
|
||||||
// Camp fire
|
// Camp fire
|
||||||
for (Material mat : BridgeMaterial.getAllBlocks(
|
for (Material mat : BridgeMaterial.getAllBlocks(
|
||||||
@@ -299,6 +298,7 @@ public class MCAccessBukkitModern extends MCAccessBukkit {
|
|||||||
addModel(BridgeMaterial.CAKE, MODEL_CAKE);
|
addModel(BridgeMaterial.CAKE, MODEL_CAKE);
|
||||||
|
|
||||||
// End Rod / Lightning Rod.
|
// End Rod / Lightning Rod.
|
||||||
|
// Do they actually have the same model? Doesn't the lightning rod have a slightly different box/bulb at the top? <- No, purely visual.
|
||||||
for (Material mat : MaterialUtil.RODS) {
|
for (Material mat : MaterialUtil.RODS) {
|
||||||
addModel(mat, MODEL_END_ROD);
|
addModel(mat, MODEL_END_ROD);
|
||||||
}
|
}
|
||||||
@@ -353,12 +353,15 @@ public class MCAccessBukkitModern extends MCAccessBukkit {
|
|||||||
addModel(mat, MODEL_XZ100_HEIGHT16_15);
|
addModel(mat, MODEL_XZ100_HEIGHT16_15);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Thin fence: Glass panes, iron bars.
|
// Thin fence: Glass panes, iron bars, copper bars.
|
||||||
for (final Material mat : MaterialUtil.addBlocks(
|
for (final Material mat : MaterialUtil.addBlocks(
|
||||||
MaterialUtil.GLASS_PANES,
|
MaterialUtil.GLASS_PANES,
|
||||||
BridgeMaterial.IRON_BARS)) {
|
BridgeMaterial.IRON_BARS)) {
|
||||||
addModel(mat, MODEL_THIN_FENCE);
|
addModel(mat, MODEL_THIN_FENCE);
|
||||||
}
|
}
|
||||||
|
for (final Material mat : MaterialUtil.COPPER_BARS) {
|
||||||
|
addModel(mat, MODEL_THIN_FENCE);
|
||||||
|
}
|
||||||
|
|
||||||
// Slabs
|
// Slabs
|
||||||
for (final Material mat : MaterialUtil.SLABS) {
|
for (final Material mat : MaterialUtil.SLABS) {
|
||||||
@@ -371,10 +374,12 @@ public class MCAccessBukkitModern extends MCAccessBukkit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Chests.
|
// Chests.
|
||||||
// TOOD: Might add a facing/directional extension for double chests.
|
|
||||||
for (Material mat : BridgeMaterial.getAllBlocks(
|
for (Material mat : BridgeMaterial.getAllBlocks(
|
||||||
"chest", "trapped_chest", "ender_chest")) {
|
"chest", "trapped_chest", "ender_chest")) {
|
||||||
addModel(mat, MODEL_SINGLE_CHEST);
|
addModel(mat, MODEL_CHEST);
|
||||||
|
}
|
||||||
|
for (Material mat : MaterialUtil.COPPER_CHESTS) {
|
||||||
|
addModel(mat, MODEL_CHEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Beds
|
// Beds
|
||||||
@@ -447,6 +452,9 @@ public class MCAccessBukkitModern extends MCAccessBukkit {
|
|||||||
"lantern", "soul_lantern")) {
|
"lantern", "soul_lantern")) {
|
||||||
addModel(mat, MODEL_LANTERN);
|
addModel(mat, MODEL_LANTERN);
|
||||||
}
|
}
|
||||||
|
for (Material mat : MaterialUtil.COPPER_LANTERNS) {
|
||||||
|
addModel(mat, MODEL_LANTERN);
|
||||||
|
}
|
||||||
|
|
||||||
// Piston.
|
// Piston.
|
||||||
for (Material mat : BridgeMaterial.getAllBlocks(
|
for (Material mat : BridgeMaterial.getAllBlocks(
|
||||||
@@ -496,9 +504,17 @@ public class MCAccessBukkitModern extends MCAccessBukkit {
|
|||||||
mt = BridgeMaterial.getBlock("bamboo");
|
mt = BridgeMaterial.getBlock("bamboo");
|
||||||
if (mt != null) addModel(mt, MODEL_BAMBOO);
|
if (mt != null) addModel(mt, MODEL_BAMBOO);
|
||||||
|
|
||||||
|
// Scaffolding
|
||||||
|
mt = BridgeMaterial.getBlock("scaffolding");
|
||||||
|
if (mt != null) addModel(mt, MODEL_SCAFFOLDING);
|
||||||
|
|
||||||
// Dripstone.
|
// Dripstone.
|
||||||
mt = BridgeMaterial.getBlock("pointed_dripstone");
|
mt = BridgeMaterial.getBlock("pointed_dripstone");
|
||||||
if (mt != null) addModel(mt, MODEL_POINTED_DRIPSTONE);
|
if (mt != null) addModel(mt, MODEL_POINTED_DRIPSTONE);
|
||||||
|
|
||||||
|
// Sulfur spike
|
||||||
|
mt = BridgeMaterial.getBlock("sulfur_spike");
|
||||||
|
if (mt != null) addModel(mt, MODEL_POINTED_DRIPSTONE);
|
||||||
|
|
||||||
// Bell.
|
// Bell.
|
||||||
mt = BridgeMaterial.getBlock("bell");
|
mt = BridgeMaterial.getBlock("bell");
|
||||||
@@ -531,6 +547,10 @@ public class MCAccessBukkitModern extends MCAccessBukkit {
|
|||||||
// Heavy core
|
// Heavy core
|
||||||
mt = BridgeMaterial.getBlock("heavy_core");
|
mt = BridgeMaterial.getBlock("heavy_core");
|
||||||
if (mt != null) addModel(mt, MODEL_GROUND_HEAD);
|
if (mt != null) addModel(mt, MODEL_GROUND_HEAD);
|
||||||
|
|
||||||
|
// Dried ghast
|
||||||
|
mt = BridgeMaterial.getBlock("dried_ghast");
|
||||||
|
if (mt != null) addModel(mt, MODEL_DRIED_GHAST);
|
||||||
|
|
||||||
// Sort to processed by flags.
|
// Sort to processed by flags.
|
||||||
for (final Material mat : Material.values()) {
|
for (final Material mat : Material.values()) {
|
||||||
|
|||||||
+10
@@ -76,4 +76,14 @@ public abstract class AbstractBukkitCentered implements BukkitShapeModel {
|
|||||||
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+17
-1
@@ -20,6 +20,8 @@ import org.bukkit.block.BlockFace;
|
|||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
import org.bukkit.block.data.Directional;
|
import org.bukkit.block.data.Directional;
|
||||||
|
|
||||||
|
import fr.neatmonster.nocheatplus.compat.versions.ClientVersion;
|
||||||
|
import fr.neatmonster.nocheatplus.players.IPlayerData;
|
||||||
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
||||||
|
|
||||||
public class BukkitAnvil implements BukkitShapeModel {
|
public class BukkitAnvil implements BukkitShapeModel {
|
||||||
@@ -28,11 +30,14 @@ public class BukkitAnvil implements BukkitShapeModel {
|
|||||||
public double[] getShape(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public double[] getShape(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
final Block block = world.getBlockAt(x, y, z);
|
final Block block = world.getBlockAt(x, y, z);
|
||||||
final BlockData blockData = block.getBlockData();
|
final BlockData blockData = block.getBlockData();
|
||||||
|
final IPlayerData data = blockCache.getPlayerData();
|
||||||
|
final boolean legacy = data != null && data.getClientVersion().isLowerThan(ClientVersion.V_1_13);
|
||||||
if (blockData instanceof Directional) {
|
if (blockData instanceof Directional) {
|
||||||
BlockFace face = ((Directional) blockData).getFacing();
|
BlockFace face = ((Directional) blockData).getFacing();
|
||||||
switch (face) {
|
switch (face) {
|
||||||
case NORTH:
|
case NORTH:
|
||||||
case SOUTH:
|
case SOUTH:
|
||||||
|
if (legacy) return new double[] {0.125, 0.0, 0.0, 0.875, 1.0, 1.0};
|
||||||
return new double[] {
|
return new double[] {
|
||||||
// Bottom
|
// Bottom
|
||||||
0.125, 0.0, 0.125, 0.875, 0.25, 0.875,
|
0.125, 0.0, 0.125, 0.875, 0.25, 0.875,
|
||||||
@@ -42,6 +47,7 @@ public class BukkitAnvil implements BukkitShapeModel {
|
|||||||
};
|
};
|
||||||
case WEST:
|
case WEST:
|
||||||
case EAST:
|
case EAST:
|
||||||
|
if (legacy) return new double[] {0.0, 0.0, 0.125, 1.0, 1.0, 0.875};
|
||||||
return new double[] {
|
return new double[] {
|
||||||
// Bottom
|
// Bottom
|
||||||
0.125, 0.0, 0.125, 0.875, 0.25, 0.875,
|
0.125, 0.0, 0.125, 0.875, 0.25, 0.875,
|
||||||
@@ -59,5 +65,15 @@ public class BukkitAnvil implements BukkitShapeModel {
|
|||||||
@Override
|
@Override
|
||||||
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+10
@@ -63,4 +63,14 @@ public class BukkitBamboo implements BukkitShapeModel {
|
|||||||
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+10
@@ -123,4 +123,14 @@ public class BukkitBell implements BukkitShapeModel {
|
|||||||
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+10
@@ -45,4 +45,14 @@ public class BukkitCake implements BukkitShapeModel {
|
|||||||
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+43
-4
@@ -16,13 +16,32 @@ package fr.neatmonster.nocheatplus.compat.bukkit.model;
|
|||||||
|
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
|
|
||||||
|
import fr.neatmonster.nocheatplus.compat.versions.ClientVersion;
|
||||||
|
import fr.neatmonster.nocheatplus.players.IPlayerData;
|
||||||
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
||||||
|
|
||||||
public class BukkitCauldron implements BukkitShapeModel {
|
public class BukkitCauldron implements BukkitShapeModel {
|
||||||
private final double[] bounds;
|
private final static double[] new1_13_2Bounds = {
|
||||||
|
0.0, 0.0, 0.0, 0.125, 1.0, 0.25,
|
||||||
|
0.0, 0.0, 0.75, 0.125, 1.0, 1.0,
|
||||||
|
0.125, 0.0, 0.0, 0.25, 1.0, 0.125,
|
||||||
|
0.125, 0.0, 0.875, 0.25, 1.0, 1.0,
|
||||||
|
0.75, 0.0, 0.0, 1.0, 1.0, 0.125,
|
||||||
|
0.75, 0.0, 0.875, 1.0, 1.0, 1.0,
|
||||||
|
0.875, 0.0, 0.125, 1.0, 1.0, 0.25,
|
||||||
|
0.875, 0.0, 0.75, 1.0, 1.0, 0.875,
|
||||||
|
0.0, 0.1875, 0.25, 1.0, 0.25, 0.75,
|
||||||
|
0.125, 0.1875, 0.125, 0.875, 0.25, 0.25,
|
||||||
|
0.125, 0.1875, 0.75, 0.875, 0.25, 0.875,
|
||||||
|
0.25, 0.1875, 0.0, 0.75, 1.0, 0.125,
|
||||||
|
0.25, 0.1875, 0.875, 0.75, 1.0, 1.0,
|
||||||
|
0.0, 0.25, 0.25, 0.125, 1.0, 0.75,
|
||||||
|
0.875, 0.25, 0.25, 1.0, 1.0, 0.75};
|
||||||
|
private final static double[] new1_13Bounds = makeCauldron(0.1875, 0.125, 0.8125, 0.0625);
|
||||||
|
private final static double[] legacyBounds = makeCauldron(0.0, 0.125, 1.0, 0.3125);
|
||||||
|
|
||||||
public BukkitCauldron(double minY, double sideWidth, double sideHeight, double coreHeight) {
|
private static double[] makeCauldron(double minY, double sideWidth, double sideHeight, double coreHeight) {
|
||||||
bounds = new double[] {
|
return new double[] {
|
||||||
// Core
|
// Core
|
||||||
sideWidth, minY, sideWidth, 1 - sideWidth, minY + coreHeight, 1 - sideWidth,
|
sideWidth, minY, sideWidth, 1 - sideWidth, minY + coreHeight, 1 - sideWidth,
|
||||||
// 4 side
|
// 4 side
|
||||||
@@ -35,11 +54,31 @@ public class BukkitCauldron implements BukkitShapeModel {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double[] getShape(BlockCache blockCache, World world, int x, int y, int z) {
|
public double[] getShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
return bounds;
|
IPlayerData pData = blockCache.getPlayerData();
|
||||||
|
if (pData != null) {
|
||||||
|
if (pData.getClientVersion().isLowerThan(ClientVersion.V_1_13)) {
|
||||||
|
return legacyBounds;
|
||||||
|
} else if (pData.getClientVersion().isLowerThan(ClientVersion.V_1_13_2)) {
|
||||||
|
return new1_13Bounds;
|
||||||
|
} else return new1_13_2Bounds;
|
||||||
|
}
|
||||||
|
return new1_13Bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return new double[] {0.0, 0.1875, 0.0, 1.0, 1.0, 1.0};
|
||||||
|
//return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
// Should be true , but current implementation of Visible check of NCP can't do complex ray-trace for performance wise and technical limitations
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+99
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package fr.neatmonster.nocheatplus.compat.bukkit.model;
|
||||||
|
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.block.BlockFace;
|
||||||
|
import org.bukkit.block.BlockState;
|
||||||
|
import org.bukkit.block.data.BlockData;
|
||||||
|
import org.bukkit.block.data.type.Chest;
|
||||||
|
import org.bukkit.block.data.type.Chest.Type;
|
||||||
|
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
||||||
|
|
||||||
|
public class BukkitChest implements BukkitShapeModel {
|
||||||
|
//TODO: With/without this, NCP still throw false when nearby doubled chest. Something else was wrong. Do note flags are correct too: (CHEST / SOLID+GROUND) BlockProps(2.5 / ToolProps(AXE/WOOD) / [3750, 1900, 950, 750, 600, 450, 400])
|
||||||
|
private final double[] NORTH_AABB = new double[] {0.0625, 0.0, 0.0, 0.9375, 0.875, 0.9375};
|
||||||
|
private final double[] SOUTH_AABB = new double[] {0.0625, 0.0, 0.0625, 0.9375, 0.875, 1.0};
|
||||||
|
private final double[] WEST_AABB = new double[] {0.0, 0.0, 0.0625, 0.9375, 0.875, 0.9375};
|
||||||
|
private final double[] EAST_AABB = new double[] {0.0625, 0.0, 0.0625, 1.0, 0.875, 0.9375};
|
||||||
|
private final double[] SINGLE = new double[] {0.0625, 0.0, 0.0625, 0.9375, 0.875, 0.9375};
|
||||||
|
|
||||||
|
public static BlockFace getConnectedDirection(BlockFace facing, Type chestType) {
|
||||||
|
return chestType == Type.LEFT ? getClockWise(facing) : getCounterClockWise(facing);
|
||||||
|
}
|
||||||
|
public static BlockFace getCounterClockWise(BlockFace dir) {
|
||||||
|
switch (dir) {
|
||||||
|
case NORTH: return BlockFace.WEST;
|
||||||
|
case SOUTH: return BlockFace.EAST;
|
||||||
|
case WEST: return BlockFace.SOUTH;
|
||||||
|
case EAST: return BlockFace.NORTH;
|
||||||
|
default: throw new IllegalStateException("Unable to get CCW facing of " + dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlockFace getClockWise(BlockFace dir) {
|
||||||
|
switch (dir) {
|
||||||
|
case NORTH: return BlockFace.EAST;
|
||||||
|
case SOUTH: return BlockFace.WEST;
|
||||||
|
case WEST: return BlockFace.NORTH;
|
||||||
|
case EAST: return BlockFace.SOUTH;
|
||||||
|
default: throw new IllegalStateException("Unable to get Y-rotated facing of " + dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
final Block block = world.getBlockAt(x, y, z);
|
||||||
|
final BlockState state = block.getState();
|
||||||
|
final BlockData blockData = state.getBlockData();
|
||||||
|
if (blockData instanceof Chest) {
|
||||||
|
final Chest chest = (Chest) blockData;
|
||||||
|
final Type chestType = chest.getType();
|
||||||
|
if (chestType == Type.SINGLE) {
|
||||||
|
return SINGLE;
|
||||||
|
}
|
||||||
|
switch (getConnectedDirection(chest.getFacing(), chestType)) {
|
||||||
|
case NORTH:
|
||||||
|
default:
|
||||||
|
return NORTH_AABB;
|
||||||
|
case SOUTH:
|
||||||
|
return SOUTH_AABB;
|
||||||
|
case WEST:
|
||||||
|
return WEST_AABB;
|
||||||
|
case EAST:
|
||||||
|
return EAST_AABB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SINGLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+101
-32
@@ -14,6 +14,10 @@
|
|||||||
*/
|
*/
|
||||||
package fr.neatmonster.nocheatplus.compat.bukkit.model;
|
package fr.neatmonster.nocheatplus.compat.bukkit.model;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.block.BlockFace;
|
import org.bukkit.block.BlockFace;
|
||||||
@@ -22,52 +26,117 @@ import org.bukkit.block.data.MultipleFacing;
|
|||||||
import org.bukkit.util.BoundingBox;
|
import org.bukkit.util.BoundingBox;
|
||||||
|
|
||||||
import fr.neatmonster.nocheatplus.compat.Bridge1_13;
|
import fr.neatmonster.nocheatplus.compat.Bridge1_13;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.collision.AxisAlignedBBUtils;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.collision.ShapeUtils;
|
||||||
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
||||||
|
|
||||||
public class BukkitChorusPlant implements BukkitShapeModel {
|
public class BukkitChorusPlant implements BukkitShapeModel {
|
||||||
|
|
||||||
|
private static final BlockFace[] directions = new BlockFace[]{BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST, BlockFace.UP, BlockFace.DOWN};
|
||||||
|
private static final double[][] modernShapes = makeShapes();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double[] getShape(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public double[] getShape(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
final Block block = world.getBlockAt(x, y, z);
|
final Block block = world.getBlockAt(x, y, z);
|
||||||
if (Bridge1_13.hasBoundingBox()) {
|
// Server is 1.13.2 higher
|
||||||
BoundingBox bd = block.getBoundingBox();
|
if (Bridge1_13.hasBuiltInRayTracing()) {
|
||||||
return new double[] {bd.getMinX()-x, 0.1875, bd.getMinZ()-z, bd.getMaxX()-x, bd.getMaxY()-y, bd.getMaxZ()-z};
|
double[] res = {};
|
||||||
|
for (BoundingBox box : block.getCollisionShape().getBoundingBoxes()) {
|
||||||
|
res = ShapeUtils.add(res, AxisAlignedBBUtils.toArray(box));
|
||||||
|
}
|
||||||
|
if (res.length == 0) return null;
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
// Server is 1.13
|
||||||
|
final BlockData blockData = block.getBlockData();
|
||||||
|
if (blockData instanceof MultipleFacing) {
|
||||||
|
final MultipleFacing chorusplant = (MultipleFacing) blockData;
|
||||||
|
return modernShapes[getAABBIndex(chorusplant.getFaces())];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return new double[] {0,0,0,1.0,1.0,1.0};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double[][] makeShapes() {
|
||||||
|
float min = 0.5F - (float) 0.3125;
|
||||||
|
float max = 0.5F + (float) 0.3125;
|
||||||
|
double[] base = {min, min, min, max, max, max};
|
||||||
|
double[][] cs = new double[directions.length][];
|
||||||
|
|
||||||
|
for (int i = 0; i < directions.length; i++) {
|
||||||
|
BlockFace dir = directions[i];
|
||||||
|
cs[i] = new double[] {0.5D + Math.min(-(float) 0.3125, (double) dir.getModX() * 0.5D), 0.5D + Math.min(-(float) 0.3125, (double) dir.getModY() * 0.5D), 0.5D + Math.min(-(float) 0.3125, (double) dir.getModZ() * 0.5D),
|
||||||
|
0.5D + Math.max((float) 0.3125, (double) dir.getModX() * 0.5D), 0.5D + Math.max((float) 0.3125, (double) dir.getModY() * 0.5D), 0.5D + Math.max((float) 0.3125, (double) dir.getModZ() * 0.5D)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
double[][] cs2 = new double[64][];
|
||||||
|
for (int k = 0; k < 64; k++) {
|
||||||
|
double[] tmp = base;
|
||||||
|
|
||||||
|
for (int j = 0; j < directions.length; j++) {
|
||||||
|
if ((k & 1 << j) != 0) {
|
||||||
|
tmp = ShapeUtils.merge(tmp, cs[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cs2[k] = tmp;
|
||||||
|
}
|
||||||
|
return cs2;
|
||||||
|
}
|
||||||
|
|
||||||
final BlockData blockData = block.getBlockData();
|
private Set<BlockFace> getLegacyFaces(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
double[] res = new double[] {0.187, 0.188, 0.187, 1.0 - 0.187, 0.8125, 1.0 - 0.187};
|
Set<BlockFace> faces = new HashSet<>();
|
||||||
if (blockData instanceof MultipleFacing) {
|
Material upBlock = blockCache.getType(x, y+1, z);
|
||||||
final MultipleFacing chorusplant = (MultipleFacing) blockData;
|
Material downBlock = blockCache.getType(x, y-1, z);
|
||||||
for (final BlockFace face : chorusplant.getFaces()) {
|
Material northBlock = blockCache.getType(x, y, z-1);
|
||||||
switch (face) {
|
Material southBlock = blockCache.getType(x, y, z+1);
|
||||||
//case DOWN:
|
Material westBlock = blockCache.getType(x-1, y, z);
|
||||||
// res[1]=0.0;
|
Material eastBlock = blockCache.getType(x+1, y, z);
|
||||||
// break;
|
if (downBlock == Material.CHORUS_PLANT || downBlock == Material.END_STONE || downBlock == Material.CHORUS_FLOWER) {
|
||||||
case UP:
|
faces.add(BlockFace.DOWN);
|
||||||
res[4]=1.0;
|
|
||||||
break;
|
|
||||||
case SOUTH:
|
|
||||||
res[5]=1.0;
|
|
||||||
break;
|
|
||||||
case NORTH:
|
|
||||||
res[2]=0.0;
|
|
||||||
break;
|
|
||||||
case WEST:
|
|
||||||
res[0]=0.0;
|
|
||||||
break;
|
|
||||||
case EAST:
|
|
||||||
res[3]=1.0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return res;
|
if (upBlock == Material.CHORUS_PLANT || upBlock == Material.CHORUS_FLOWER) {
|
||||||
|
faces.add(BlockFace.UP);
|
||||||
|
}
|
||||||
|
if (northBlock == Material.CHORUS_PLANT || northBlock == Material.CHORUS_FLOWER) {
|
||||||
|
faces.add(BlockFace.NORTH);
|
||||||
|
}
|
||||||
|
if (southBlock == Material.CHORUS_PLANT || southBlock == Material.CHORUS_FLOWER) {
|
||||||
|
faces.add(BlockFace.SOUTH);
|
||||||
|
}
|
||||||
|
if (westBlock == Material.CHORUS_PLANT || westBlock == Material.CHORUS_FLOWER) {
|
||||||
|
faces.add(BlockFace.WEST);
|
||||||
|
}
|
||||||
|
if (eastBlock == Material.CHORUS_PLANT || eastBlock == Material.CHORUS_FLOWER) {
|
||||||
|
faces.add(BlockFace.EAST);
|
||||||
|
}
|
||||||
|
return faces;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getAABBIndex(Set<BlockFace> faces) {
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (int j = 0; j < directions.length; ++j) {
|
||||||
|
if (faces.contains(directions[j])) {
|
||||||
|
i |= 1 << j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+10
@@ -93,4 +93,14 @@ public class BukkitCocoa implements BukkitShapeModel {
|
|||||||
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package fr.neatmonster.nocheatplus.compat.bukkit.model;
|
||||||
|
|
||||||
|
import org.bukkit.World;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
||||||
|
|
||||||
|
public class BukkitComposter implements BukkitShapeModel {
|
||||||
|
private final double[] bounds;
|
||||||
|
|
||||||
|
public BukkitComposter(double minY, double sideWidth, double sideHeight, double coreHeight) {
|
||||||
|
bounds = new double[] {
|
||||||
|
// Core
|
||||||
|
sideWidth, minY, sideWidth, 1 - sideWidth, minY + coreHeight, 1 - sideWidth,
|
||||||
|
// 4 side
|
||||||
|
0.0, minY, 0.0, 1.0, minY + sideHeight, sideWidth,
|
||||||
|
0.0, minY, 1.0 - sideWidth, 1.0, minY + sideHeight, 1.0,
|
||||||
|
0.0, minY, 0.0, sideWidth, minY + sideHeight, 1.0,
|
||||||
|
1.0 - sideWidth, minY, 0.0, 1.0, minY + sideHeight, 1.0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return bounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package fr.neatmonster.nocheatplus.compat.bukkit.model;
|
||||||
|
|
||||||
|
import org.bukkit.World;
|
||||||
|
|
||||||
|
import fr.neatmonster.nocheatplus.compat.versions.ClientVersion;
|
||||||
|
import fr.neatmonster.nocheatplus.players.IPlayerData;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grass path and farm land shape model.
|
||||||
|
*/
|
||||||
|
public class BukkitDirtLike implements BukkitShapeModel {
|
||||||
|
@Override
|
||||||
|
public double[] getShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
// Farmland changed the height from 1.10 but too lazy and not many clients on this version. Taking shortcut!
|
||||||
|
final IPlayerData data = blockCache.getPlayerData();
|
||||||
|
if (data != null && data.getClientVersion().isLowerThan(ClientVersion.V_1_9)) {
|
||||||
|
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
||||||
|
}
|
||||||
|
return new double[] {0.0, 0.0, 0.0, 1.0, 0.9375, 1.0};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -111,8 +111,18 @@ public class BukkitDoor implements BukkitShapeModel {
|
|||||||
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+12
-4
@@ -34,20 +34,28 @@ public class BukkitDripLeaf implements BukkitShapeModel {
|
|||||||
case PARTIAL:
|
case PARTIAL:
|
||||||
res[4] -= 0.125;
|
res[4] -= 0.125;
|
||||||
return res;
|
return res;
|
||||||
case UNSTABLE:
|
|
||||||
// WORKAROUND: Zero this bounding box if the tilt is unstable [NCP should take care of ground judgement then]
|
|
||||||
return new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
|
|
||||||
case NONE:
|
case NONE:
|
||||||
|
case UNSTABLE:
|
||||||
return res;
|
return res;
|
||||||
default:
|
default:
|
||||||
break;
|
return new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+14
-3
@@ -14,15 +14,16 @@
|
|||||||
*/
|
*/
|
||||||
package fr.neatmonster.nocheatplus.compat.bukkit.model;
|
package fr.neatmonster.nocheatplus.compat.bukkit.model;
|
||||||
|
|
||||||
import fr.neatmonster.nocheatplus.utilities.location.LocUtil;
|
|
||||||
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
|
||||||
import fr.neatmonster.nocheatplus.utilities.math.MathUtil;
|
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.block.BlockFace;
|
import org.bukkit.block.BlockFace;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
import org.bukkit.block.data.type.PointedDripstone;
|
import org.bukkit.block.data.type.PointedDripstone;
|
||||||
|
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.location.LocUtil;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.math.MathUtil;
|
||||||
|
|
||||||
public class BukkitDripStone implements BukkitShapeModel {
|
public class BukkitDripStone implements BukkitShapeModel {
|
||||||
private static final double[] TIP_MERGE = {0.3125, 0.0, 0.3125, 0.6875, 1.0, 0.6875};
|
private static final double[] TIP_MERGE = {0.3125, 0.0, 0.3125, 0.6875, 1.0, 0.6875};
|
||||||
private static final double[] TIP_DOWN = {0.3125, 0.3125, 0.3125, 0.6875, 1.0, 0.6875};
|
private static final double[] TIP_DOWN = {0.3125, 0.3125, 0.3125, 0.6875, 1.0, 0.6875};
|
||||||
@@ -65,6 +66,11 @@ public class BukkitDripStone implements BukkitShapeModel {
|
|||||||
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
private float calcOffset(long x, boolean shift, boolean bedrock) {
|
private float calcOffset(long x, boolean shift, boolean bedrock) {
|
||||||
if (shift) x >>=8;
|
if (shift) x >>=8;
|
||||||
@@ -84,4 +90,9 @@ public class BukkitDripStone implements BukkitShapeModel {
|
|||||||
private double[] offset(double[] input, double x, double y, double z) {
|
private double[] offset(double[] input, double x, double y, double z) {
|
||||||
return new double[] {input[0] + x, input[1] + y, input[2] + z, input[3] + x, input[4] + y, input[5] + z};
|
return new double[] {input[0] + x, input[1] + y, input[2] + z, input[3] + x, input[4] + y, input[5] + z};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+10
@@ -35,6 +35,11 @@ public class BukkitEndPortalFrame implements BukkitShapeModel {
|
|||||||
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
private double[] getShape(boolean hasEye) {
|
private double[] getShape(boolean hasEye) {
|
||||||
return hasEye ? new double[] {0.0, 0.0, 0.0, 1.0, 0.8125, 1.0, 0.25, 0.8125, 0.25, 0.75, 1.0, 0.75}
|
return hasEye ? new double[] {0.0, 0.0, 0.0, 1.0, 0.8125, 1.0, 0.25, 0.8125, 0.25, 0.75, 1.0, 0.75}
|
||||||
: new double[] {0.0, 0.0, 0.0, 1.0, 0.8125, 1.0};
|
: new double[] {0.0, 0.0, 0.0, 1.0, 0.8125, 1.0};
|
||||||
@@ -44,4 +49,9 @@ public class BukkitEndPortalFrame implements BukkitShapeModel {
|
|||||||
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+116
-39
@@ -20,65 +20,142 @@ import org.bukkit.block.BlockFace;
|
|||||||
import org.bukkit.block.BlockState;
|
import org.bukkit.block.BlockState;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
import org.bukkit.block.data.MultipleFacing;
|
import org.bukkit.block.data.MultipleFacing;
|
||||||
|
import org.bukkit.util.BoundingBox;
|
||||||
|
|
||||||
|
import fr.neatmonster.nocheatplus.compat.Bridge1_13;
|
||||||
|
import fr.neatmonster.nocheatplus.compat.versions.ClientVersion;
|
||||||
|
import fr.neatmonster.nocheatplus.players.IPlayerData;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.collision.AxisAlignedBBUtils;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.collision.ShapeUtils;
|
||||||
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
||||||
|
|
||||||
public class BukkitFence implements BukkitShapeModel {
|
public class BukkitFence implements BukkitShapeModel {
|
||||||
|
private final double[] northplane;
|
||||||
|
private final double[] southplane;
|
||||||
|
private final double[] westplane;
|
||||||
|
private final double[] eastplane;
|
||||||
|
private final double[] northplaneB;
|
||||||
|
private final double[] southplaneB;
|
||||||
|
private final double[] westplaneB;
|
||||||
|
private final double[] eastplaneB;
|
||||||
|
private final double[] fullplane;
|
||||||
|
private final double[] eastwest;
|
||||||
|
private final double[] southnorth;
|
||||||
|
private final double[] baseState;
|
||||||
|
private final boolean cutEdgeOnLowVersions;
|
||||||
|
|
||||||
private final double minXZ;
|
public BukkitFence(double inset, double height, boolean cutEdgeOnLowVersions) {
|
||||||
private final double maxXZ;
|
this(inset, 1.0 - inset, height, cutEdgeOnLowVersions);
|
||||||
private final double height;
|
|
||||||
|
|
||||||
public BukkitFence(double inset, double height) {
|
|
||||||
this(inset, 1.0 - inset, height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BukkitFence(double minXZ, double maxXZ, double height) {
|
public BukkitFence(double minXZ, double maxXZ, double height, boolean cutEdgeOnLowVersions) {
|
||||||
this.minXZ = minXZ;
|
this.cutEdgeOnLowVersions = cutEdgeOnLowVersions;
|
||||||
this.maxXZ = maxXZ;
|
this.northplane = new double[] {minXZ, 0.0, 0.0, maxXZ, height, maxXZ};
|
||||||
this.height = height;
|
this.southplane = new double[] {minXZ, 0.0, minXZ, maxXZ, height, 1.0};
|
||||||
|
this.westplane = new double[] {0.0, 0.0, minXZ, maxXZ, height, maxXZ};
|
||||||
|
this.eastplane = new double[] {minXZ, 0.0, minXZ, 1.0, height, maxXZ};
|
||||||
|
this.northplaneB = new double[] {minXZ, 0.0, 0.0, maxXZ, height, maxXZ - 0.0625};
|
||||||
|
this.southplaneB = new double[] {minXZ, 0.0, minXZ + 0.0625, maxXZ, height, 1.0};
|
||||||
|
this.westplaneB = new double[] {0.0, 0.0, minXZ, maxXZ - 0.0625, height, maxXZ};
|
||||||
|
this.eastplaneB = new double[] {minXZ + 0.0625, 0.0, minXZ, 1.0, height, maxXZ};
|
||||||
|
this.eastwest = ShapeUtils.merge(westplane, eastplane);
|
||||||
|
this.southnorth = ShapeUtils.merge(southplane, northplane);
|
||||||
|
this.fullplane = ShapeUtils.add(eastwest, southnorth);
|
||||||
|
this.baseState = new double[] {minXZ, 0.0, minXZ, maxXZ, height, maxXZ};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double[] getShape(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public double[] getShape(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
// 13749998807909
|
|
||||||
// 86250001192093
|
|
||||||
// 0.1375, 0.8625
|
|
||||||
final Block block = world.getBlockAt(x, y, z);
|
final Block block = world.getBlockAt(x, y, z);
|
||||||
final BlockState state = block.getState();
|
final IPlayerData data = blockCache.getPlayerData();
|
||||||
final BlockData blockData = state.getBlockData();
|
final boolean cutEdge = data != null && cutEdgeOnLowVersions && data.getClientVersion().isLowerThan(ClientVersion.V_1_9);
|
||||||
|
// Server is 1.13.2 higher and player not on 1.8
|
||||||
if (blockData instanceof MultipleFacing) {
|
if (Bridge1_13.hasBuiltInRayTracing() && !cutEdge) {
|
||||||
// Note isPassableWorkaround for these (no voxel shapes / multi cuboid yet).
|
double[] res = {};
|
||||||
final MultipleFacing fence = (MultipleFacing) blockData;
|
for (BoundingBox box : block.getCollisionShape().getBoundingBoxes()) {
|
||||||
// TODO: If height > 1.0, check if it needs to be capped, provided relevant.
|
res = ShapeUtils.add(res, AxisAlignedBBUtils.toArray(box));
|
||||||
double[] res = new double[] {minXZ, 0.0, minXZ, maxXZ, height, maxXZ};
|
}
|
||||||
for (final BlockFace face : fence.getFaces()) {
|
if (res.length == 0) return null;
|
||||||
switch (face) {
|
return res;
|
||||||
case EAST:
|
} else {
|
||||||
res[3] = 1.0;
|
boolean east = false;
|
||||||
break;
|
boolean north = false;
|
||||||
case NORTH:
|
boolean west = false;
|
||||||
res[2] = 0.0;
|
boolean south = false;
|
||||||
break;
|
final BlockState state = block.getState();
|
||||||
case WEST:
|
final BlockData blockData = state.getBlockData();
|
||||||
res[0] = 0.0;
|
if (blockData instanceof MultipleFacing) {
|
||||||
break;
|
final MultipleFacing fence = (MultipleFacing) blockData;
|
||||||
case SOUTH:
|
for (final BlockFace face : fence.getFaces()) {
|
||||||
res[5] = 1.0;
|
switch (face) {
|
||||||
break;
|
case EAST:
|
||||||
default:
|
east = true;
|
||||||
break;
|
break;
|
||||||
|
case NORTH:
|
||||||
|
north = true;
|
||||||
|
break;
|
||||||
|
case WEST:
|
||||||
|
west = true;
|
||||||
|
break;
|
||||||
|
case SOUTH:
|
||||||
|
south = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res;
|
if (east && north && west && south) {
|
||||||
|
return fullplane;
|
||||||
|
}
|
||||||
|
double tmp[] = new double[0];
|
||||||
|
if (east && west) {
|
||||||
|
tmp = eastwest;
|
||||||
|
east = west = false;
|
||||||
|
} else if (south && north) {
|
||||||
|
tmp = southnorth;
|
||||||
|
south = north = false;
|
||||||
|
}
|
||||||
|
if (south) {
|
||||||
|
tmp = ShapeUtils.add(tmp, cutEdge && isNextDir(east, north, west, south) ? southplaneB : southplane);
|
||||||
|
}
|
||||||
|
if (north) {
|
||||||
|
tmp = ShapeUtils.add(tmp, cutEdge && isNextDir(east, north, west, south) ? northplaneB : northplane);
|
||||||
|
}
|
||||||
|
if (east) {
|
||||||
|
tmp = ShapeUtils.add(tmp, cutEdge && isNextDir(east, north, west, south) ? eastplaneB : eastplane);
|
||||||
|
}
|
||||||
|
if (west) {
|
||||||
|
tmp = ShapeUtils.add(tmp, cutEdge && isNextDir(east, north, west, south) ? westplaneB : westplane);
|
||||||
|
}
|
||||||
|
if (tmp.length == 0) {
|
||||||
|
tmp = ShapeUtils.add(tmp, cutEdge ? fullplane : baseState);
|
||||||
|
}
|
||||||
|
return tmp;
|
||||||
}
|
}
|
||||||
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
}
|
||||||
|
|
||||||
|
private boolean isNextDir(boolean east, boolean north, boolean west, boolean south) {
|
||||||
|
return (north || south) && (west || east);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
double a[] = getShape(blockCache, world, x, y, z);
|
||||||
|
for (int i = 1; i < (int)a.length / 6; i++) a[i*6-2] = 1.0;
|
||||||
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
// TODO: Which one is better, return true and cap the height or return false and fetch twice?
|
||||||
|
// NOTE: Should be no different as current implementation doesn't do exact ray trace check
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+18
@@ -20,6 +20,14 @@ import org.bukkit.util.BoundingBox;
|
|||||||
|
|
||||||
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches a single BoundingBox from Bukkit's Block#getBoundingBox().
|
||||||
|
* Returns a six-element double[] {minX, minY, minZ, maxX, maxY, maxZ}
|
||||||
|
* relative to the block position (world coords minus the block's x,y,z).
|
||||||
|
*
|
||||||
|
* This class returns one bounding box; BukkitFetchableBounds reads the VoxelShape and may return multiple boxes for complex
|
||||||
|
* collision shapes.
|
||||||
|
*/
|
||||||
public class BukkitFetchableBound implements BukkitShapeModel {
|
public class BukkitFetchableBound implements BukkitShapeModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -29,8 +37,18 @@ public class BukkitFetchableBound implements BukkitShapeModel {
|
|||||||
return new double[] {box.getMinX()-x, box.getMinY()-y, box.getMinZ()-z, box.getMaxX()-x, box.getMaxY()-y, box.getMaxZ()-z};
|
return new double[] {box.getMinX()-x, box.getMinY()-y, box.getMinZ()-z, box.getMaxX()-x, box.getMaxY()-y, box.getMaxZ()-z};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+21
-8
@@ -20,31 +20,44 @@ import org.bukkit.util.BoundingBox;
|
|||||||
import org.bukkit.util.VoxelShape;
|
import org.bukkit.util.VoxelShape;
|
||||||
|
|
||||||
import fr.neatmonster.nocheatplus.utilities.collision.AxisAlignedBBUtils;
|
import fr.neatmonster.nocheatplus.utilities.collision.AxisAlignedBBUtils;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.collision.ShapeUtils;
|
||||||
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the block's collision VoxelShape and returns one or more bounding boxes
|
||||||
|
* as a concatenated double[] containing {minX,minY,minZ,maxX,maxY,maxZ} for each
|
||||||
|
* box. Coordinates are expressed relative to the block position.
|
||||||
|
*
|
||||||
|
* Difference from BukkitFetchableBound: this class may return multiple boxes
|
||||||
|
* (preserving complex/multi-part collision geometry); BukkitFetchableBound
|
||||||
|
* returns a single BoundingBox from Block#getBoundingBox().
|
||||||
|
*/
|
||||||
public class BukkitFetchableBounds implements BukkitShapeModel {
|
public class BukkitFetchableBounds implements BukkitShapeModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double[] getShape(BlockCache blockCache, World world, int x, int y, int z) {
|
public double[] getShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
final Block block = world.getBlockAt(x, y, z);
|
final Block block = world.getBlockAt(x, y, z);
|
||||||
final VoxelShape blockshape = block.getCollisionShape();
|
final VoxelShape blockShape = block.getCollisionShape();
|
||||||
double[] res = {};
|
double[] res = {};
|
||||||
for (BoundingBox box : blockshape.getBoundingBoxes()) {
|
for (BoundingBox box : blockShape.getBoundingBoxes()) {
|
||||||
res = add(res, AxisAlignedBBUtils.toArray(box));
|
res = ShapeUtils.add(res, AxisAlignedBBUtils.toArray(box));
|
||||||
}
|
}
|
||||||
if (res.length == 0) return null;
|
if (res.length == 0) return null;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
private double[] add(final double[] array1, final double[] array2) {
|
@Override
|
||||||
final double[] newArray = new double[array1.length + array2.length];
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
System.arraycopy(array1, 0, newArray, 0, array1.length);
|
return getShape(blockCache, world, x, y, z);
|
||||||
System.arraycopy(array2, 0, newArray, array1.length, array2.length);
|
|
||||||
return newArray;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+10
@@ -61,6 +61,11 @@ public class BukkitGate implements BukkitShapeModel {
|
|||||||
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
final Block block = world.getBlockAt(x, y, z);
|
final Block block = world.getBlockAt(x, y, z);
|
||||||
@@ -71,4 +76,9 @@ public class BukkitGate implements BukkitShapeModel {
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+10
@@ -106,8 +106,18 @@ public class BukkitGrindStone implements BukkitShapeModel {
|
|||||||
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+47
-30
@@ -20,12 +20,24 @@ import org.bukkit.block.BlockFace;
|
|||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
import org.bukkit.block.data.Directional;
|
import org.bukkit.block.data.Directional;
|
||||||
|
|
||||||
|
import fr.neatmonster.nocheatplus.compat.versions.ClientVersion;
|
||||||
|
import fr.neatmonster.nocheatplus.players.IPlayerData;
|
||||||
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
||||||
|
|
||||||
public class BukkitHopper implements BukkitShapeModel {
|
public class BukkitHopper implements BukkitShapeModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double[] getShape(BlockCache blockCache, World world, int x, int y, int z) {
|
public double[] getShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
IPlayerData pData = blockCache.getPlayerData();
|
||||||
|
if (pData != null && pData.getClientVersion().isLowerThan(ClientVersion.V_1_13)) {
|
||||||
|
return new double[] {
|
||||||
|
0, 0, 0, 1, 0.625, 1,
|
||||||
|
0, 0.625, 0, 0.125, 1, 1,
|
||||||
|
0.875, 0.625, 0, 1, 1, 1,
|
||||||
|
0, 0.625, 0, 1, 1, 0.125,
|
||||||
|
0, 0.625, 0.875, 1, 1, 1
|
||||||
|
};
|
||||||
|
}
|
||||||
final Block block = world.getBlockAt(x, y, z);
|
final Block block = world.getBlockAt(x, y, z);
|
||||||
final BlockData blockData = block.getBlockData();
|
final BlockData blockData = block.getBlockData();
|
||||||
if (blockData instanceof Directional) {
|
if (blockData instanceof Directional) {
|
||||||
@@ -40,13 +52,11 @@ public class BukkitHopper implements BukkitShapeModel {
|
|||||||
0.25, 0.25, 0.25, 0.75, 0.625, 0.75,
|
0.25, 0.25, 0.25, 0.75, 0.625, 0.75,
|
||||||
// Bottom
|
// Bottom
|
||||||
0.375, 0.25, 0.0, 0.625, 0.5, 0.25,
|
0.375, 0.25, 0.0, 0.625, 0.5, 0.25,
|
||||||
// Top
|
|
||||||
0.0, 0.6875, 0.0, 1.0, 1.0, 1.0
|
|
||||||
// 4 sides of hopper (top)
|
// 4 sides of hopper (top)
|
||||||
//0.0, 0.6875, 0.0, 1.0, 1.0, 0.125,
|
0.0, 0.6875, 0.0, 1.0, 1.0, 0.125,
|
||||||
//0.0, 0.6875, 0.875, 1.0, 1.0, 1.0,
|
0.0, 0.6875, 0.875, 1.0, 1.0, 1.0,
|
||||||
//0.0, 0.6875, 0.0, 0.125, 1.0, 1.0,
|
0.0, 0.6875, 0.0, 0.125, 1.0, 1.0,
|
||||||
//0.875, 0.6875, 0.0, 1.0, 1.0, 1.0,
|
0.875, 0.6875, 0.0, 1.0, 1.0, 1.0,
|
||||||
};
|
};
|
||||||
case SOUTH:
|
case SOUTH:
|
||||||
return new double[] {
|
return new double[] {
|
||||||
@@ -56,13 +66,11 @@ public class BukkitHopper implements BukkitShapeModel {
|
|||||||
0.25, 0.25, 0.25, 0.75, 0.625, 0.75,
|
0.25, 0.25, 0.25, 0.75, 0.625, 0.75,
|
||||||
// Bottom
|
// Bottom
|
||||||
0.375, 0.25, 0.75, 0.625, 0.5, 1.0,
|
0.375, 0.25, 0.75, 0.625, 0.5, 1.0,
|
||||||
// Top
|
|
||||||
0.0, 0.6875, 0.0, 1.0, 1.0, 1.0
|
|
||||||
// 4 sides of hopper (top)
|
// 4 sides of hopper (top)
|
||||||
//0.0, 0.6875, 0.0, 1.0, 1.0, 0.125,
|
0.0, 0.6875, 0.0, 1.0, 1.0, 0.125,
|
||||||
//0.0, 0.6875, 0.875, 1.0, 1.0, 1.0,
|
0.0, 0.6875, 0.875, 1.0, 1.0, 1.0,
|
||||||
//0.0, 0.6875, 0.0, 0.125, 1.0, 1.0,
|
0.0, 0.6875, 0.0, 0.125, 1.0, 1.0,
|
||||||
//0.875, 0.6875, 0.0, 1.0, 1.0, 1.0,
|
0.875, 0.6875, 0.0, 1.0, 1.0, 1.0,
|
||||||
};
|
};
|
||||||
case WEST:
|
case WEST:
|
||||||
return new double[] {
|
return new double[] {
|
||||||
@@ -72,13 +80,11 @@ public class BukkitHopper implements BukkitShapeModel {
|
|||||||
0.25, 0.25, 0.25, 0.75, 0.625, 0.75,
|
0.25, 0.25, 0.25, 0.75, 0.625, 0.75,
|
||||||
// Bottom
|
// Bottom
|
||||||
0.0, 0.25, 0.375, 0.25, 0.5, 0.625,
|
0.0, 0.25, 0.375, 0.25, 0.5, 0.625,
|
||||||
// Top
|
|
||||||
0.0, 0.6875, 0.0, 1.0, 1.0, 1.0
|
|
||||||
// 4 sides of hopper (top)
|
// 4 sides of hopper (top)
|
||||||
//0.0, 0.6875, 0.0, 1.0, 1.0, 0.125,
|
0.0, 0.6875, 0.0, 1.0, 1.0, 0.125,
|
||||||
//0.0, 0.6875, 0.875, 1.0, 1.0, 1.0,
|
0.0, 0.6875, 0.875, 1.0, 1.0, 1.0,
|
||||||
//0.0, 0.6875, 0.0, 0.125, 1.0, 1.0,
|
0.0, 0.6875, 0.0, 0.125, 1.0, 1.0,
|
||||||
//0.875, 0.6875, 0.0, 1.0, 1.0, 1.0,
|
0.875, 0.6875, 0.0, 1.0, 1.0, 1.0,
|
||||||
};
|
};
|
||||||
case EAST:
|
case EAST:
|
||||||
return new double[] {
|
return new double[] {
|
||||||
@@ -88,13 +94,11 @@ public class BukkitHopper implements BukkitShapeModel {
|
|||||||
0.25, 0.25, 0.25, 0.75, 0.625, 0.75,
|
0.25, 0.25, 0.25, 0.75, 0.625, 0.75,
|
||||||
// Bottom
|
// Bottom
|
||||||
0.75, 0.25, 0.375, 1.0, 0.5, 0.625,
|
0.75, 0.25, 0.375, 1.0, 0.5, 0.625,
|
||||||
// Top
|
|
||||||
0.0, 0.6875, 0.0, 1.0, 1.0, 1.0
|
|
||||||
// 4 sides of hopper (top)
|
// 4 sides of hopper (top)
|
||||||
//0.0, 0.6875, 0.0, 1.0, 1.0, 0.125,
|
0.0, 0.6875, 0.0, 1.0, 1.0, 0.125,
|
||||||
//0.0, 0.6875, 0.875, 1.0, 1.0, 1.0,
|
0.0, 0.6875, 0.875, 1.0, 1.0, 1.0,
|
||||||
//0.0, 0.6875, 0.0, 0.125, 1.0, 1.0,
|
0.0, 0.6875, 0.0, 0.125, 1.0, 1.0,
|
||||||
//0.875, 0.6875, 0.0, 1.0, 1.0, 1.0,
|
0.875, 0.6875, 0.0, 1.0, 1.0, 1.0,
|
||||||
};
|
};
|
||||||
case DOWN:
|
case DOWN:
|
||||||
return new double[] {
|
return new double[] {
|
||||||
@@ -104,13 +108,11 @@ public class BukkitHopper implements BukkitShapeModel {
|
|||||||
0.25, 0.25, 0.25, 0.75, 0.625, 0.75,
|
0.25, 0.25, 0.25, 0.75, 0.625, 0.75,
|
||||||
// Bottom
|
// Bottom
|
||||||
0.375, 0.0, 0.375, 0.625, 0.25, 0.625,
|
0.375, 0.0, 0.375, 0.625, 0.25, 0.625,
|
||||||
// Top
|
|
||||||
0.0, 0.6875, 0.0, 1.0, 1.0, 1.0
|
|
||||||
// 4 sides of hopper (top)
|
// 4 sides of hopper (top)
|
||||||
//0.0, 0.6875, 0.0, 1.0, 1.0, 0.125,
|
0.0, 0.6875, 0.0, 1.0, 1.0, 0.125,
|
||||||
//0.0, 0.6875, 0.875, 1.0, 1.0, 1.0,
|
0.0, 0.6875, 0.875, 1.0, 1.0, 1.0,
|
||||||
//0.0, 0.6875, 0.0, 0.125, 1.0, 1.0,
|
0.0, 0.6875, 0.0, 0.125, 1.0, 1.0,
|
||||||
//0.875, 0.6875, 0.0, 1.0, 1.0, 1.0,
|
0.875, 0.6875, 0.0, 1.0, 1.0, 1.0,
|
||||||
};
|
};
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -119,8 +121,23 @@ public class BukkitHopper implements BukkitShapeModel {
|
|||||||
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
IPlayerData pData = blockCache.getPlayerData();
|
||||||
|
if (pData != null && pData.getClientVersion().isLowerThan(ClientVersion.V_1_13)) {
|
||||||
|
return new double[] {0, 0, 0, 1, 1, 1};
|
||||||
|
}
|
||||||
|
return new double[] {0.0, 0.625, 0.0, 1.0, 1.0, 1.0, 0.25, 0.25, 0.25, 0.75, 0.625, 0.75};
|
||||||
|
//return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+10
@@ -32,6 +32,16 @@ public class BukkitLadder implements BukkitShapeModel {
|
|||||||
return new double[] {box.getMinX()-x, box.getMinY()-y, box.getMinZ()-z, box.getMaxX()-x, box.getMaxY()-y, box.getMaxZ()-z};
|
return new double[] {box.getMinX()-x, box.getMinY()-y, box.getMinZ()-z, box.getMaxX()-x, box.getMaxY()-y, box.getMaxZ()-z};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
final Block block = world.getBlockAt(x, y, z);
|
final Block block = world.getBlockAt(x, y, z);
|
||||||
|
|||||||
+10
@@ -38,6 +38,16 @@ public class BukkitLantern implements BukkitShapeModel {
|
|||||||
return new double[] {xz, 0.0, xz, 1.0-xz, 0.4375, 1.0-xz, 0.375, 0.4375, 0.375, 0.625, 0.5625, 0.625};
|
return new double[] {xz, 0.0, xz, 1.0-xz, 0.4375, 1.0-xz, 0.375, 0.4375, 0.375, 0.625, 0.5625, 0.625};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+10
@@ -37,6 +37,16 @@ public class BukkitLevelled implements BukkitShapeModel {
|
|||||||
return new double[] {0.0, 0.0, 0.0, 1.0, 8 / 9f, 1.0};
|
return new double[] {0.0, 0.0, 0.0, 1.0, 8 / 9f, 1.0};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
final Block block = world.getBlockAt(x, y, z);
|
final Block block = world.getBlockAt(x, y, z);
|
||||||
|
|||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package fr.neatmonster.nocheatplus.compat.bukkit.model;
|
||||||
|
|
||||||
|
import org.bukkit.World;
|
||||||
|
|
||||||
|
import fr.neatmonster.nocheatplus.compat.versions.ClientVersion;
|
||||||
|
import fr.neatmonster.nocheatplus.players.IPlayerData;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Water Lily shape model.
|
||||||
|
*/
|
||||||
|
public class BukkitLilyPad implements BukkitShapeModel {
|
||||||
|
@Override
|
||||||
|
public double[] getShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
final IPlayerData data = blockCache.getPlayerData();
|
||||||
|
if (data != null && data.getClientVersion().isLowerThan(ClientVersion.V_1_9)) {
|
||||||
|
return new double[] {0.0625, 0.0, 0.0625, 0.9375, 0.125, 0.9375};
|
||||||
|
}
|
||||||
|
return new double[] {0.0625, 0.0, 0.0625, 0.9375, 0.09375, 0.9375};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -71,6 +71,16 @@ public class BukkitPiston implements BukkitShapeModel {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+10
@@ -81,6 +81,16 @@ public class BukkitPistonHead implements BukkitShapeModel {
|
|||||||
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+19
-8
@@ -22,21 +22,32 @@ import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
|||||||
|
|
||||||
public class BukkitPowderSnow implements BukkitShapeModel {
|
public class BukkitPowderSnow implements BukkitShapeModel {
|
||||||
|
|
||||||
|
// Some what behave like ladder
|
||||||
|
double[] REDUCED_HEIGHT = {0.0, 0.0, 0.0, 1.0, 0.9, 1.0};
|
||||||
|
double[] FULL_HEIGHT = {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double[] getShape(BlockCache blockCache, World world, int x, int y, int z) {
|
public double[] getShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
IPlayerData pData = blockCache.getPlayerData();
|
IPlayerData pData = blockCache.getPlayerData();
|
||||||
if (pData != null) {
|
if (pData != null) {
|
||||||
MovingData data = pData.getGenericInstance(MovingData.class);
|
MovingData data = pData.getGenericInstance(MovingData.class);
|
||||||
// TODO: Make NoFall no dealing damage on this block
|
if (data.noFallFallDistance > 2.5) {
|
||||||
if (data.lastY > (y + 1 - 1e-5) && data.hasLeatherBoots && !pData.isShiftKeyPressed()) {
|
return REDUCED_HEIGHT;
|
||||||
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
} else if (data.lastY > y + 1 - 1e-5 && data.hasLeatherBoots && !pData.isShiftKeyPressed()) {
|
||||||
|
return FULL_HEIGHT;
|
||||||
}
|
}
|
||||||
// Give up, too hard to properly implement, workaround instead
|
|
||||||
//if (data.noFallFallDistance > 2.5) {
|
|
||||||
// return new double[] {0.0, 0.0, 0.0, 1.0, 0.9, 1.0};
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
return new double[] {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return FULL_HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+11
@@ -39,4 +39,15 @@ public class BukkitRail implements BukkitShapeModel {
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
// TODO Hold on, this model doesn't seem right, will fix later
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+123
@@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package fr.neatmonster.nocheatplus.compat.bukkit.model;
|
||||||
|
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.block.data.BlockData;
|
||||||
|
import org.bukkit.block.data.type.Scaffolding;
|
||||||
|
|
||||||
|
import fr.neatmonster.nocheatplus.checks.moving.MovingData;
|
||||||
|
import fr.neatmonster.nocheatplus.players.IPlayerData;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scaffolding shape model.
|
||||||
|
*/
|
||||||
|
public class BukkitScaffolding implements BukkitShapeModel {
|
||||||
|
|
||||||
|
// Stable visual shape (top slab + four posts)
|
||||||
|
private static final double[] STABLE_SHAPE = new double[] {
|
||||||
|
// top slab: 0,14,0 -> 16,16,16
|
||||||
|
0.0, 0.875, 0.0, 1.0, 1.0, 1.0,
|
||||||
|
// post (0..2 x, 0..2 z)
|
||||||
|
0.0, 0.0, 0.0, 0.125, 1.0, 0.125,
|
||||||
|
// post (14..16 x, 0..2 z)
|
||||||
|
0.875, 0.0, 0.0, 1.0, 1.0, 0.125,
|
||||||
|
// post (0..2 x, 14..16 z)
|
||||||
|
0.0, 0.0, 0.875, 0.125, 1.0, 1.0,
|
||||||
|
// post (14..16 x, 14..16 z)
|
||||||
|
0.875, 0.0, 0.875, 1.0, 1.0, 1.0
|
||||||
|
};
|
||||||
|
|
||||||
|
// Unstable visual shape includes a thin bottom plate plus the stable parts
|
||||||
|
private static final double[] UNSTABLE_SHAPE = new double[] {
|
||||||
|
// bottom full thin plate: 0,0,0 -> 16,2,16
|
||||||
|
0.0, 0.0, 0.0, 1.0, 0.125, 1.0,
|
||||||
|
// include the stable shape boxes as well
|
||||||
|
0.0, 0.875, 0.0, 1.0, 1.0, 1.0,
|
||||||
|
0.0, 0.0, 0.0, 0.125, 1.0, 0.125,
|
||||||
|
0.875, 0.0, 0.0, 1.0, 1.0, 0.125,
|
||||||
|
0.0, 0.0, 0.875, 0.125, 1.0, 1.0,
|
||||||
|
0.875, 0.0, 0.875, 1.0, 1.0, 1.0
|
||||||
|
};
|
||||||
|
// Just define ground plate, no need full boxes as Minecraft handle climbable if player within 0.3125 inset to the center, and was handled by NCP somewhere else
|
||||||
|
private static final double[] BOTTOM_PLATE = new double[] {0.0, 0.0, 0.0, 1.0, 0.125, 1.0};
|
||||||
|
private static final double[] UPPER_PLATE = new double[] {0.0, 0.875, 0.0, 1.0, 1.0, 1.0};
|
||||||
|
private static final double[] ALL_PLATE = new double[] {0.0, 0.875, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.125, 1.0};
|
||||||
|
// (empty box)
|
||||||
|
private static final double[] NO_COLLISION = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
final Block block = world.getBlockAt(x, y, z);
|
||||||
|
final BlockData data = block.getBlockData();
|
||||||
|
// If the player is above the block and not sneaking, return the stable full shape.
|
||||||
|
IPlayerData pData = blockCache.getPlayerData();
|
||||||
|
if (data instanceof Scaffolding) {
|
||||||
|
Scaffolding scaff = (Scaffolding) data;
|
||||||
|
if (pData != null) {
|
||||||
|
MovingData mData = pData.getGenericInstance(MovingData.class);
|
||||||
|
boolean hasbottom = false;
|
||||||
|
// If scaffolding has distance != 0 and is bottom, and player is just above or within tolerance return bottom plate collision
|
||||||
|
if (scaff.getDistance() != 0 && scaff.isBottom()) {
|
||||||
|
hasbottom = true;
|
||||||
|
}
|
||||||
|
// If player was above the block and not sneaking -> stable collision
|
||||||
|
// Vanilla: return full stable collision only when the entity is above the block AND not descending.
|
||||||
|
// 'isDescending' in vanilla is actually the entity's "isShiftKeyDown" (sneak) flag, not a vertical-velocity test.
|
||||||
|
//System.out.println("scf 1 " + mData.lastY + ">" + (y + 1 - 1e-5) + " " + !pData.isShiftKeyPressed() + " " + y);
|
||||||
|
if (mData.lastY > (y + 1 - 1e-5) // p_56071_.isAbove(Shapes.block(), p_56070_, true)
|
||||||
|
&& !pData.isShiftKeyPressed()) {
|
||||||
|
return hasbottom ? ALL_PLATE : UPPER_PLATE;
|
||||||
|
}
|
||||||
|
if (hasbottom && mData.lastY > y + 0.125 - 1e-5) {
|
||||||
|
//System.out.println("scf 2");
|
||||||
|
return BOTTOM_PLATE;
|
||||||
|
}
|
||||||
|
//System.out.println("scf 3");
|
||||||
|
return NO_COLLISION;
|
||||||
|
// TODO: This should be the VISUAL shape (aka: hitbox), not the COLLISION shape.
|
||||||
|
// We need to distinguish shapes from collision shapes in the API.
|
||||||
|
// return scaff.isBottom() ? UNSTABLE_SHAPE : STABLE_SHAPE;
|
||||||
|
}
|
||||||
|
// Shortcut Visible haven't set pData...
|
||||||
|
return (scaff.getDistance() != 0 && scaff.isBottom()) ? UNSTABLE_SHAPE : STABLE_SHAPE;
|
||||||
|
}
|
||||||
|
// Fallback to a full block. Should never happen.
|
||||||
|
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
final Block block = world.getBlockAt(x, y, z);
|
||||||
|
final BlockData data = block.getBlockData();
|
||||||
|
if (data instanceof Scaffolding) {
|
||||||
|
Scaffolding scaff = (Scaffolding) data;
|
||||||
|
return (scaff.getDistance() != 0 && scaff.isBottom()) ? UNSTABLE_SHAPE : STABLE_SHAPE;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -54,8 +54,18 @@ public class BukkitSeaPickle implements BukkitShapeModel {
|
|||||||
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+65
-8
@@ -16,30 +16,87 @@ package fr.neatmonster.nocheatplus.compat.bukkit.model;
|
|||||||
|
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.block.BlockFace;
|
||||||
import org.bukkit.block.BlockState;
|
import org.bukkit.block.BlockState;
|
||||||
import org.bukkit.block.Container;
|
import org.bukkit.block.ShulkerBox;
|
||||||
|
import org.bukkit.block.data.BlockData;
|
||||||
|
import org.bukkit.block.data.Directional;
|
||||||
|
|
||||||
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
||||||
|
|
||||||
public class BukkitShulkerBox implements BukkitShapeModel {
|
public class BukkitShulkerBox implements BukkitShapeModel {
|
||||||
|
|
||||||
|
double FULL_BLOCK[] = {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double[] getShape(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public double[] getShape(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
|
|
||||||
final Block block = world.getBlockAt(x, y, z);
|
final Block block = world.getBlockAt(x, y, z);
|
||||||
final BlockState state = block.getState();
|
final BlockState state = block.getState();
|
||||||
//final BlockData blockData = state.getBlockData();
|
|
||||||
|
// Base full block
|
||||||
if (state instanceof Container) {
|
double minX = 0.0, minY = 0.0, minZ = 0.0;
|
||||||
if (!((Container) state).getInventory().getViewers().isEmpty()) {
|
double maxX = 1.0, maxY = 1.0, maxZ = 1.0;
|
||||||
return new double[] {0.0, 0.0, 0.0, 1.0, 1.5, 1.0};
|
|
||||||
|
if ((state instanceof ShulkerBox)) {
|
||||||
|
final ShulkerBox shulker = (ShulkerBox) state;
|
||||||
|
|
||||||
|
// Closed shulker. Return full block
|
||||||
|
if (shulker.getInventory().getViewers().isEmpty()) {
|
||||||
|
return FULL_BLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Facing is stored in BlockData, not in ShulkerBox
|
||||||
|
BlockFace face = BlockFace.UP;
|
||||||
|
BlockData data = block.getBlockData();
|
||||||
|
|
||||||
|
if (data instanceof Directional) {
|
||||||
|
face = ((Directional) data).getFacing();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open shulker: extend 0.5 blocks in facing direction
|
||||||
|
switch (face) {
|
||||||
|
case UP:
|
||||||
|
maxY = 1.5;
|
||||||
|
break;
|
||||||
|
case DOWN:
|
||||||
|
minY = -0.5;
|
||||||
|
break;
|
||||||
|
case NORTH:
|
||||||
|
minZ = -0.5;
|
||||||
|
break;
|
||||||
|
case SOUTH:
|
||||||
|
maxZ = 1.5;
|
||||||
|
break;
|
||||||
|
case WEST:
|
||||||
|
minX = -0.5;
|
||||||
|
break;
|
||||||
|
case EAST:
|
||||||
|
maxX = 1.5;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Should not happen, but just in case... Treat as UP
|
||||||
|
maxY = 1.5;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
|
||||||
|
return new double[]{minX, minY, minZ, maxX, maxY, maxZ};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+10
@@ -46,6 +46,16 @@ public class BukkitSlab implements BukkitShapeModel {
|
|||||||
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+11
-1
@@ -25,7 +25,7 @@ import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
|||||||
public class BukkitSnow implements BukkitShapeModel {
|
public class BukkitSnow implements BukkitShapeModel {
|
||||||
|
|
||||||
private static final double[][] SNOW_LAYERS = {
|
private static final double[][] SNOW_LAYERS = {
|
||||||
{0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
null,
|
||||||
{0.0, 0.0, 0.0, 1.0, 0.125, 1.0},
|
{0.0, 0.0, 0.0, 1.0, 0.125, 1.0},
|
||||||
{0.0, 0.0, 0.0, 1.0, 0.250, 1.0},
|
{0.0, 0.0, 0.0, 1.0, 0.250, 1.0},
|
||||||
{0.0, 0.0, 0.0, 1.0, 0.375, 1.0},
|
{0.0, 0.0, 0.0, 1.0, 0.375, 1.0},
|
||||||
@@ -48,6 +48,16 @@ public class BukkitSnow implements BukkitShapeModel {
|
|||||||
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
final Block block = world.getBlockAt(x, y, z);
|
final Block block = world.getBlockAt(x, y, z);
|
||||||
|
|||||||
+16
-81
@@ -14,8 +14,6 @@
|
|||||||
*/
|
*/
|
||||||
package fr.neatmonster.nocheatplus.compat.bukkit.model;
|
package fr.neatmonster.nocheatplus.compat.bukkit.model;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
@@ -25,8 +23,7 @@ import org.bukkit.block.data.Bisected.Half;
|
|||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
import org.bukkit.block.data.type.Stairs;
|
import org.bukkit.block.data.type.Stairs;
|
||||||
|
|
||||||
import fr.neatmonster.nocheatplus.utilities.collision.Axis;
|
import fr.neatmonster.nocheatplus.utilities.collision.ShapeUtils;
|
||||||
import fr.neatmonster.nocheatplus.utilities.collision.AxisAlignedBBUtils;
|
|
||||||
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
||||||
|
|
||||||
public class BukkitStairs implements BukkitShapeModel {
|
public class BukkitStairs implements BukkitShapeModel {
|
||||||
@@ -66,6 +63,16 @@ public class BukkitStairs implements BukkitShapeModel {
|
|||||||
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
//final Block block = world.getBlockAt(x, y, z);
|
//final Block block = world.getBlockAt(x, y, z);
|
||||||
@@ -95,16 +102,16 @@ public class BukkitStairs implements BukkitShapeModel {
|
|||||||
private double[] makeStairShape(int flags, double[] slab, double[] octet_nn, double[] octet_pn, double[] octet_np, double[] octet_pp) {
|
private double[] makeStairShape(int flags, double[] slab, double[] octet_nn, double[] octet_pn, double[] octet_np, double[] octet_pp) {
|
||||||
double[] res = slab;
|
double[] res = slab;
|
||||||
if ((flags & 1) != 0) {
|
if ((flags & 1) != 0) {
|
||||||
res = merge(res, octet_nn);
|
res = ShapeUtils.merge(res, octet_nn);
|
||||||
}
|
}
|
||||||
if ((flags & 2) != 0) {
|
if ((flags & 2) != 0) {
|
||||||
res = merge(res, octet_pn);
|
res = ShapeUtils.merge(res, octet_pn);
|
||||||
}
|
}
|
||||||
if ((flags & 4) != 0) {
|
if ((flags & 4) != 0) {
|
||||||
res = merge(res, octet_np);
|
res = ShapeUtils.merge(res, octet_np);
|
||||||
}
|
}
|
||||||
if ((flags & 8) != 0) {
|
if ((flags & 8) != 0) {
|
||||||
res = merge(res, octet_pp);
|
res = ShapeUtils.merge(res, octet_pp);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -130,77 +137,5 @@ public class BukkitStairs implements BukkitShapeModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Poorly designed, Will recode better version later
|
|
||||||
private double[] merge(double[] bounds, double[] octet) {
|
|
||||||
double[] res = bounds;
|
|
||||||
final double minX = octet[0];
|
|
||||||
final double minY = octet[1];
|
|
||||||
final double minZ = octet[2];
|
|
||||||
final double maxX = octet[3];
|
|
||||||
final double maxY = octet[4];
|
|
||||||
final double maxZ = octet[5];
|
|
||||||
for (int i = 2; i <= AxisAlignedBBUtils.getNumberOfAABBs(bounds); i++) {
|
|
||||||
|
|
||||||
final double tminX = bounds[i*6-6];
|
|
||||||
final double tminY = bounds[i*6-5];
|
|
||||||
final double tminZ = bounds[i*6-4];
|
|
||||||
final double tmaxX = bounds[i*6-3];
|
|
||||||
final double tmaxY = bounds[i*6-2];
|
|
||||||
final double tmaxZ = bounds[i*6-1];
|
|
||||||
if (sameshape(minX, minY, minZ, maxX, maxY, maxZ, tminX, tminY, tminZ, tmaxX, tmaxY, tmaxZ)) {
|
|
||||||
final List<Axis> a = getRelative(minX, minY, minZ, maxX, maxY, maxZ, tminX, tminY, tminZ, tmaxX, tmaxY, tmaxZ);
|
|
||||||
if (a.size() == 1) {
|
|
||||||
Axis axis = a.get(0);
|
|
||||||
switch (axis) {
|
|
||||||
case X_AXIS:
|
|
||||||
res[i*6-6] = Math.min(tminX, minX);
|
|
||||||
res[i*6-3] = Math.max(tmaxX, maxX);
|
|
||||||
return res;
|
|
||||||
//case Y_AXIS:
|
|
||||||
// break;
|
|
||||||
case Z_AXIS:
|
|
||||||
res[i*6-4] = Math.min(tminZ, minZ);
|
|
||||||
res[i*6-1] = Math.max(tmaxZ, maxZ);
|
|
||||||
return res;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return add(res, octet);
|
|
||||||
}
|
|
||||||
|
|
||||||
private double[] add(final double[] array1, final double[] array2) {
|
|
||||||
final double[] newArray = new double[array1.length + array2.length];
|
|
||||||
System.arraycopy(array1, 0, newArray, 0, array1.length);
|
|
||||||
System.arraycopy(array2, 0, newArray, array1.length, array2.length);
|
|
||||||
return newArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Axis> getRelative(double minX, double minY, double minZ, double maxX, double maxY, double maxZ,
|
|
||||||
double tminX, double tminY, double tminZ, double tmaxX, double tmaxY, double tmaxZ) {
|
|
||||||
final List<Axis> list = new ArrayList<Axis>();
|
|
||||||
if (minX == tmaxX || maxX == tminX) {
|
|
||||||
list.add(Axis.X_AXIS);
|
|
||||||
}
|
|
||||||
if (minY == tmaxY || maxY == tminY) {
|
|
||||||
list.add(Axis.Y_AXIS);
|
|
||||||
}
|
|
||||||
if (minZ == tmaxZ || maxZ == tminZ) {
|
|
||||||
list.add(Axis.Z_AXIS);
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean sameshape(double minX, double minY, double minZ, double maxX, double maxY, double maxZ, double tminX,
|
|
||||||
double tminY, double tminZ, double tmaxX, double tmaxY, double tmaxZ) {
|
|
||||||
final double dx = maxX - minX;
|
|
||||||
final double dy = maxY - minY;
|
|
||||||
final double dz = maxZ - minZ;
|
|
||||||
final double tdx = tmaxX - tminX;
|
|
||||||
final double tdy = tmaxY - tminY;
|
|
||||||
final double tdz = tmaxZ - tminZ;
|
|
||||||
return dx == tdx && dy == tdy && dz == tdz;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+26
-9
@@ -23,27 +23,34 @@ public class BukkitStatic implements BukkitShapeModel {
|
|||||||
private final double[] bounds;
|
private final double[] bounds;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize with the given height and with full xz-bounds.
|
* Constructs a {@code BukkitStatic} block model with full width (no inset) and the specified height.
|
||||||
*
|
* This is typically used for regular blocks that occupy the entire block space in the X and Z axes.
|
||||||
* @param height
|
*
|
||||||
|
* @param height the height of the block, corresponding to the Y axis
|
||||||
*/
|
*/
|
||||||
public BukkitStatic(double height) {
|
public BukkitStatic(double height) {
|
||||||
this(0.0, height);
|
this(0.0, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize with the given height and xz-inset.
|
* Constructs a {@code BukkitStatic} block model with a specified inset on the X and Z axes and the given height.
|
||||||
*
|
* This is useful for blocks that do not occupy the full width and height of a block.
|
||||||
* @param xzInset
|
*
|
||||||
* @param height
|
* @param xzInset the inset on the X and Z axes, reducing the block's width and height
|
||||||
|
* @param height the height of the block, corresponding to the Y axis
|
||||||
*/
|
*/
|
||||||
public BukkitStatic(double xzInset, double height) {
|
public BukkitStatic(double xzInset, double height) {
|
||||||
this(xzInset, 0.0, xzInset, 1.0 - xzInset, height, 1.0 - xzInset);
|
this(xzInset, 0.0, xzInset, 1.0 - xzInset, height, 1.0 - xzInset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize with the given bounds
|
* Constructs a {@code BukkitStatic} block model using explicit bounding box values.
|
||||||
*
|
* The {@code bounds} array must have a length that is a multiple of six, where each group of six values
|
||||||
|
* represents the minimum and maximum coordinates for X, Y, and Z ({@code minX, minY, minZ, maxX, maxY, maxZ}).
|
||||||
|
* This constructor allows for the definition of complex or custom-shaped blocks.
|
||||||
|
*
|
||||||
|
* @param bounds the bounding box values, in groups of six for each box: minX, minY, minZ, maxX, maxY, maxZ
|
||||||
|
* @throws IllegalArgumentException if the length of {@code bounds} is not a multiple of six
|
||||||
*/
|
*/
|
||||||
public BukkitStatic(double ...bounds) {
|
public BukkitStatic(double ...bounds) {
|
||||||
if (bounds.length % 6 != 0) {
|
if (bounds.length % 6 != 0) {
|
||||||
@@ -58,6 +65,16 @@ public class BukkitStatic implements BukkitShapeModel {
|
|||||||
return bounds;
|
return bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+10
@@ -55,6 +55,16 @@ public class BukkitTrapDoor implements BukkitShapeModel {
|
|||||||
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
final Block block = world.getBlockAt(x, y, z);
|
final Block block = world.getBlockAt(x, y, z);
|
||||||
|
|||||||
+10
@@ -52,6 +52,16 @@ public class BukkitTurtleEgg implements BukkitShapeModel {
|
|||||||
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+139
-87
@@ -23,132 +23,184 @@ import org.bukkit.block.BlockState;
|
|||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
import org.bukkit.block.data.MultipleFacing;
|
import org.bukkit.block.data.MultipleFacing;
|
||||||
import org.bukkit.block.data.type.Wall;
|
import org.bukkit.block.data.type.Wall;
|
||||||
|
import org.bukkit.util.BoundingBox;
|
||||||
|
|
||||||
|
import fr.neatmonster.nocheatplus.compat.Bridge1_13;
|
||||||
|
import fr.neatmonster.nocheatplus.compat.versions.ClientVersion;
|
||||||
|
import fr.neatmonster.nocheatplus.players.IPlayerData;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.collision.AxisAlignedBBUtils;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.collision.ShapeUtils;
|
||||||
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
||||||
|
|
||||||
public class BukkitWall implements BukkitShapeModel {
|
public class BukkitWall implements BukkitShapeModel {
|
||||||
|
|
||||||
private final double minXZ;
|
|
||||||
private final double maxXZ;
|
|
||||||
private final double height;
|
|
||||||
private final double sideInset;
|
|
||||||
private final double[] east;
|
private final double[] east;
|
||||||
private final double[] north;
|
private final double[] north;
|
||||||
private final double[] west;
|
private final double[] west;
|
||||||
private final double[] south;
|
private final double[] south;
|
||||||
|
private final double[] eastB;
|
||||||
|
private final double[] northB;
|
||||||
|
private final double[] westB;
|
||||||
|
private final double[] southB;
|
||||||
private final double[] eastwest;
|
private final double[] eastwest;
|
||||||
private final double[] southnorth;
|
private final double[] southnorth;
|
||||||
|
private final double[] baseState;
|
||||||
|
|
||||||
public BukkitWall(double inset, double height) {
|
public BukkitWall(double inset, double height) {
|
||||||
this(inset, 1.0 - inset, height, 0.0);
|
this(inset, height, inset);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BukkitWall(double inset, double height, double sideInset) {
|
public BukkitWall(double inset, double height, double sideInset) {
|
||||||
this(inset, 1.0 - inset, height, sideInset);
|
this(inset, 1.0 - inset, height, sideInset, 1.0 - sideInset);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BukkitWall(double minXZ, double maxXZ, double height, double sideInset) {
|
public BukkitWall(double minXZ, double maxXZ, double height, double minSideXZ, double maxSideXZ) {
|
||||||
this.minXZ = minXZ;
|
east = new double[] {maxXZ, 0.0, minSideXZ, 1.0, height, maxSideXZ};
|
||||||
this.maxXZ = maxXZ;
|
north = new double[] {minSideXZ, 0.0, 0.0, maxSideXZ, height, minXZ};
|
||||||
this.height = height;
|
west = new double[] {0.0, 0.0, minSideXZ, minXZ, height, maxSideXZ};
|
||||||
this.sideInset = sideInset;
|
south = new double[] {minSideXZ, 0.0, maxXZ, maxSideXZ, height, 1.0};
|
||||||
east = new double[] {maxXZ, 0.0, sideInset, 1.0, height, 1.0 - sideInset};
|
eastB = new double[] {maxXZ, 0.0, minXZ, 1.0, height, maxXZ};
|
||||||
north = new double[] {sideInset, 0.0, 0.0, 1.0 - sideInset, height, minXZ};
|
northB = new double[] {minXZ, 0.0, 0.0, maxXZ, height, minXZ};
|
||||||
west = new double[] {0.0, 0.0, sideInset, minXZ, height, 1.0 - sideInset};
|
westB = new double[] {0.0, 0.0, minXZ, minXZ, height, maxXZ};
|
||||||
south = new double[] {sideInset, 0.0, maxXZ, 1.0 - sideInset, height, 1.0};
|
southB = new double[] {minXZ, 0.0, maxXZ, maxXZ, height, 1.0};
|
||||||
eastwest = new double[] {0.0, 0.0, sideInset, 1.0, height, 1.0 - sideInset};
|
eastwest = new double[] {0.0, 0.0, minSideXZ, 1.0, height, maxSideXZ};
|
||||||
southnorth = new double[] {sideInset, 0.0, 0.0, 1.0 - sideInset, height, 1.0};
|
southnorth = new double[] {minSideXZ, 0.0, 0.0, maxSideXZ, height, 1.0};
|
||||||
|
baseState = new double[] {minXZ, 0.0, minXZ, maxXZ, height, maxXZ};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double[] getShape(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public double[] getShape(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
// Relevant: https://bugs.mojang.com/browse/MC-9565
|
// Relevant: https://bugs.mojang.com/browse/MC-9565
|
||||||
final Block block = world.getBlockAt(x, y, z);
|
final Block block = world.getBlockAt(x, y, z);
|
||||||
final BlockState state = block.getState();
|
final IPlayerData data = blockCache.getPlayerData();
|
||||||
final BlockData blockData = state.getBlockData();
|
final boolean legacy = data != null && data.getClientVersion().isLowerThan(ClientVersion.V_1_13);
|
||||||
|
if (Bridge1_13.hasBuiltInRayTracing() && !legacy) {
|
||||||
if (blockData instanceof MultipleFacing) {
|
double[] res = {};
|
||||||
MultipleFacing fence = (MultipleFacing) blockData;
|
for (BoundingBox box : block.getCollisionShape().getBoundingBoxes()) {
|
||||||
double[] res = new double[] {minXZ, 0.0, minXZ, maxXZ, height, maxXZ};
|
res = ShapeUtils.add(res, AxisAlignedBBUtils.toArray(box));
|
||||||
Set<BlockFace> faces = fence.getFaces();
|
|
||||||
if (!faces.contains(BlockFace.UP) && faces.size() == 2) {
|
|
||||||
if (faces.contains(BlockFace.SOUTH)) {
|
|
||||||
return new double[] {sideInset, 0.0, 0.0, 1.0 - sideInset, height, 1.0};
|
|
||||||
}
|
|
||||||
if (faces.contains(BlockFace.WEST)) {
|
|
||||||
return new double[] {0.0, 0.0, sideInset, 1.0, height, 1.0 - sideInset};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (final BlockFace face : fence.getFaces()) {
|
|
||||||
switch (face) {
|
|
||||||
case EAST:
|
|
||||||
res = add(res, east);
|
|
||||||
break;
|
|
||||||
case NORTH:
|
|
||||||
res = add(res, north);
|
|
||||||
break;
|
|
||||||
case WEST:
|
|
||||||
res = add(res, west);
|
|
||||||
break;
|
|
||||||
case SOUTH:
|
|
||||||
res = add(res, south);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (res.length == 0) return null;
|
||||||
return res;
|
return res;
|
||||||
}
|
} else {
|
||||||
else if (blockData instanceof Wall) {
|
final BlockState state = block.getState();
|
||||||
final Wall wall = (Wall) blockData;
|
final BlockData blockData = state.getBlockData();
|
||||||
if (wall.isUp()) {
|
boolean east = false;
|
||||||
double[] res = new double[] {minXZ, 0.0, minXZ, maxXZ, height, maxXZ};
|
boolean north = false;
|
||||||
|
boolean west = false;
|
||||||
|
boolean south = false;
|
||||||
|
boolean up = false;
|
||||||
|
|
||||||
|
if (blockData instanceof MultipleFacing) {
|
||||||
|
MultipleFacing fence = (MultipleFacing) blockData;
|
||||||
|
Set<BlockFace> faces = fence.getFaces();
|
||||||
|
up = faces.contains(BlockFace.UP);
|
||||||
|
for (final BlockFace face : fence.getFaces()) {
|
||||||
|
switch (face) {
|
||||||
|
case EAST:
|
||||||
|
east = true;
|
||||||
|
break;
|
||||||
|
case NORTH:
|
||||||
|
north = true;
|
||||||
|
break;
|
||||||
|
case WEST:
|
||||||
|
west = true;
|
||||||
|
break;
|
||||||
|
case SOUTH:
|
||||||
|
south = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (blockData instanceof Wall) {
|
||||||
|
final Wall wall = (Wall) blockData;
|
||||||
|
up = wall.isUp();
|
||||||
if (!wall.getHeight(BlockFace.WEST).equals(Wall.Height.NONE)) {
|
if (!wall.getHeight(BlockFace.WEST).equals(Wall.Height.NONE)) {
|
||||||
res = add(res, west);
|
west = true;
|
||||||
}
|
}
|
||||||
if (!wall.getHeight(BlockFace.EAST).equals(Wall.Height.NONE)) {
|
if (!wall.getHeight(BlockFace.EAST).equals(Wall.Height.NONE)) {
|
||||||
res = add(res, east);
|
east = true;
|
||||||
}
|
}
|
||||||
if (!wall.getHeight(BlockFace.NORTH).equals(Wall.Height.NONE)) {
|
if (!wall.getHeight(BlockFace.NORTH).equals(Wall.Height.NONE)) {
|
||||||
res = add(res, north);
|
north = true;
|
||||||
}
|
}
|
||||||
if (!wall.getHeight(BlockFace.SOUTH).equals(Wall.Height.NONE)) {
|
if (!wall.getHeight(BlockFace.SOUTH).equals(Wall.Height.NONE)) {
|
||||||
res = add(res, south);
|
south = true;
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
double[] res = null;
|
|
||||||
if (!wall.getHeight(BlockFace.WEST).equals(Wall.Height.NONE)
|
|
||||||
&& !wall.getHeight(BlockFace.EAST).equals(Wall.Height.NONE)) {
|
|
||||||
if (res == null) {
|
|
||||||
res = eastwest;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!wall.getHeight(BlockFace.NORTH).equals(Wall.Height.NONE)
|
|
||||||
&& !wall.getHeight(BlockFace.SOUTH).equals(Wall.Height.NONE)) {
|
|
||||||
if (res == null) {
|
|
||||||
res = southnorth;
|
|
||||||
}
|
|
||||||
else res = add(res, southnorth);
|
|
||||||
}
|
|
||||||
if (res != null) {
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (legacy && east && west && south && north) {
|
||||||
|
return new double[] {0.0, 0.0, 0.0, 1.0, 1.5, 1.0};
|
||||||
|
}
|
||||||
|
|
||||||
|
double tmp[] = new double[0];
|
||||||
|
if (east && west) {
|
||||||
|
if (legacy) {
|
||||||
|
if (south) {
|
||||||
|
return new double[] {0.0, 0.0, 0.25, 1.0, 1.5, 1.0};
|
||||||
|
}
|
||||||
|
if (north) {
|
||||||
|
return new double[] {0.0, 0.0, 0.0, 1.0, 1.5, 0.75};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmp = eastwest;
|
||||||
|
east = west = false;
|
||||||
|
} else if (south && north) {
|
||||||
|
if (legacy) {
|
||||||
|
if (west) {
|
||||||
|
return new double[] {0.0, 0.0, 0.0, 0.75, 1.5, 1.0};
|
||||||
|
}
|
||||||
|
if (east) {
|
||||||
|
return new double[] {0.25, 0.0, 0.0, 1.0, 1.5, 1.0};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmp = southnorth;
|
||||||
|
south = north = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (legacy) {
|
||||||
|
if (south && west) {return new double[] {0.0, 0.0, 0.25, 0.75, 1.5, 1.0};}
|
||||||
|
if (south && east) {return new double[] {0.25, 0.0, 0.25, 1.0, 1.5, 1.0};}
|
||||||
|
if (north && west) {return new double[] {0.0, 0.0, 0.0, 0.75, 1.5, 0.75};}
|
||||||
|
if (north && east) {return new double[] {0.25, 0.0, 0.0, 1.0, 1.5, 0.75};}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (south) {
|
||||||
|
tmp = ShapeUtils.add(tmp, legacy ? this.southB : this.south);
|
||||||
|
}
|
||||||
|
if (north) {
|
||||||
|
tmp = ShapeUtils.add(tmp, legacy ? this.northB : this.north);
|
||||||
|
}
|
||||||
|
if (east) {
|
||||||
|
tmp = ShapeUtils.add(tmp, legacy ? this.eastB : this.east);
|
||||||
|
}
|
||||||
|
if (west) {
|
||||||
|
tmp = ShapeUtils.add(tmp, legacy ? this.westB : this.west);
|
||||||
|
}
|
||||||
|
if (tmp.length == 0 || up) {
|
||||||
|
tmp = ShapeUtils.add(tmp, this.baseState);
|
||||||
|
}
|
||||||
|
return tmp;
|
||||||
}
|
}
|
||||||
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
double a[] = getShape(blockCache, world, x, y, z);
|
||||||
|
for (int i = 1; i < (int)a.length / 6; i++) a[i*6-2] = 1.0;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
// TODO: Which one is better, return true and cap the height or return false and fetch twice?
|
||||||
|
// NOTE: Should be no different as current implementation doesn't do exact ray trace check
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private double[] add(final double[] array1, final double[] array2) {
|
|
||||||
final double[] newArray = new double[array1.length + array2.length];
|
|
||||||
System.arraycopy(array1, 0, newArray, 0, array1.length);
|
|
||||||
System.arraycopy(array2, 0, newArray, array1.length, array2.length);
|
|
||||||
return newArray;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+10
@@ -47,6 +47,16 @@ public class BukkitWallHead implements BukkitShapeModel {
|
|||||||
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
public int getFakeData(final BlockCache blockCache, final World world, final int x, final int y, final int z) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+10
@@ -33,6 +33,16 @@ public class BukkitWaterPlant implements BukkitShapeModel {
|
|||||||
return new double[] {0.0, 0.0, 0.0, 1.0, 8 / 9f, 1.0};
|
return new double[] {0.0, 0.0, 0.0, 1.0, 8 / 9f, 1.0};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return getShape(blockCache, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
public int getFakeData(BlockCache blockCache, World world, int x, int y, int z) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+43
-2
@@ -19,10 +19,51 @@ import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
|
|||||||
public interface ShapeModel<W> {
|
public interface ShapeModel<W> {
|
||||||
|
|
||||||
// TODO: Rather fill in all into node directly (data as well), avoid redundant casting etc.
|
// TODO: Rather fill in all into node directly (data as well), avoid redundant casting etc.
|
||||||
// TODO: Best route passable workaround through here too (base on a flag), + getGroundMinHeight?.
|
|
||||||
|
|
||||||
// TODO: Refine +- might have BukkitBlockCacheNode etc.
|
// TODO: Refine +- might have BukkitBlockCacheNode etc.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the <i>collision</i> shape of the block at the given position. <br>
|
||||||
|
* <strong> Do note that not all blocks have a collision shape, like all plants/farmable blocks, which do have a visual shape that can be interacted with, but don't have a collision one;
|
||||||
|
* other have a collision shape that exceeds their visual one, like fences, which have a height of 1.0 block visually, but 1.5 collision-wise and such. </strong> <br>
|
||||||
|
* Most of the time, the collision shape will be the same as its visual shape, but not always. <br>
|
||||||
|
* To check for any divergence, use {@link #isCollisionSameVisual(BlockCache, W, int, int, int)}. <br>
|
||||||
|
*
|
||||||
|
* @param blockCache The block cache.
|
||||||
|
* @param world The world.
|
||||||
|
* @param x The x coordinate of the block.
|
||||||
|
* @param y The y coordinate of the block.
|
||||||
|
* @param z The z coordinate of the block.
|
||||||
|
* @return An array of doubles representing the collision shape, in the format
|
||||||
|
* {minX, minY, minZ, maxX, maxY, maxZ, ...} for each box in the shape.
|
||||||
|
* Coordinates are in the range [0.0, 1.0].
|
||||||
|
*/
|
||||||
public double[] getShape(BlockCache blockCache, W world, int x, int y, int z);
|
public double[] getShape(BlockCache blockCache, W world, int x, int y, int z);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tell if collision shape is the same as visual shape <br>
|
||||||
|
*
|
||||||
|
* @param blockCache
|
||||||
|
* @param world
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
* @param z
|
||||||
|
* @return if true
|
||||||
|
*/
|
||||||
|
public boolean isCollisionSameVisual(BlockCache blockCache, W world, int x, int y, int z);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the visual shape of the block at the given position. <br>
|
||||||
|
* Call this method if dealing with visual interactions, like ray tracing for line of sight, block targeting, etc. <br>
|
||||||
|
* @See {@link #getShape(BlockCache, W, int, int, int)} for collision shape. <br>
|
||||||
|
*
|
||||||
|
* @param blockCache
|
||||||
|
* @param world
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
* @param z
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public double[] getVisualShape(BlockCache blockCache, W world, int x, int y, int z);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allow faking data.
|
* Allow faking data.
|
||||||
|
|||||||
+20
-1
@@ -49,7 +49,26 @@ public class BlockCacheCBReflect extends BlockCacheBukkit {
|
|||||||
final double[] shape = LegacyBlocks.getShape(this, mat, x, y, z, true);
|
final double[] shape = LegacyBlocks.getShape(this, mat, x, y, z, true);
|
||||||
if (shape != null) return shape;
|
if (shape != null) return shape;
|
||||||
try {
|
try {
|
||||||
return helper.nmsWorld_fetchBlockShape(this.nmsWorld, this.getType(x, y, z), x, y, z);
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, helper.nmsWorld_fetchBlockShape(this.nmsWorld, this.getType(x, y, z), x, y, z));
|
||||||
|
}
|
||||||
|
catch (ReflectFailureException ex) {
|
||||||
|
return super.fetchBounds(x, y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(int x, int y, int z) {
|
||||||
|
final org.bukkit.Material mat = getType(x, y, z);
|
||||||
|
return LegacyBlocks.isCollisionSameVisual(this, mat, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] fetchVisualBounds(int x, int y, int z) {
|
||||||
|
final org.bukkit.Material mat = getType(x, y, z);
|
||||||
|
final double[] shape = LegacyBlocks.getVisualShape(this, mat, x, y, z);
|
||||||
|
if (shape != null) return shape;
|
||||||
|
try {
|
||||||
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, helper.nmsWorld_fetchBlockShape(this.nmsWorld, this.getType(x, y, z), x, y, z));
|
||||||
}
|
}
|
||||||
catch (ReflectFailureException ex) {
|
catch (ReflectFailureException ex) {
|
||||||
return super.fetchBounds(x, y, z);
|
return super.fetchBounds(x, y, z);
|
||||||
|
|||||||
+25
-14
@@ -20,9 +20,13 @@ import java.util.UUID;
|
|||||||
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import fr.neatmonster.nocheatplus.checks.moving.MovingConfig;
|
||||||
import fr.neatmonster.nocheatplus.compat.AttribUtil;
|
import fr.neatmonster.nocheatplus.compat.AttribUtil;
|
||||||
import fr.neatmonster.nocheatplus.components.modifier.IAttributeAccess;
|
import fr.neatmonster.nocheatplus.components.modifier.IAttributeAccess;
|
||||||
|
import fr.neatmonster.nocheatplus.players.DataManager;
|
||||||
import fr.neatmonster.nocheatplus.utilities.ReflectionUtil;
|
import fr.neatmonster.nocheatplus.utilities.ReflectionUtil;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.map.MaterialUtil;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.moving.Magic;
|
||||||
|
|
||||||
public class ReflectAttributeAccess implements IAttributeAccess {
|
public class ReflectAttributeAccess implements IAttributeAccess {
|
||||||
|
|
||||||
@@ -216,71 +220,78 @@ public class ReflectAttributeAccess implements IAttributeAccess {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getGravity(Player player) {
|
public double getGravity(Player player) {
|
||||||
return Double.MAX_VALUE;
|
return Magic.DEFAULT_GRAVITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getSafeFallDistance(Player player) {
|
public double getSafeFallDistance(Player player) {
|
||||||
return Double.MAX_VALUE;
|
return Magic.FALL_DAMAGE_DIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getFallDamageMultiplier(Player player) {
|
public double getFallDamageMultiplier(Player player) {
|
||||||
return Double.MAX_VALUE;
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getBreakingSpeedMultiplier(Player player) {
|
public double getBreakingSpeedMultiplier(Player player) {
|
||||||
return Double.MAX_VALUE;
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getJumpGainMultiplier(Player player) {
|
public double getJumpGainMultiplier(Player player) {
|
||||||
return Double.MAX_VALUE;
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getPlayerSneakingFactor(Player player) {
|
public double getPlayerSneakingFactor(Player player) {
|
||||||
return Double.MAX_VALUE;
|
return Magic.SNEAK_MULTIPLIER;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getPlayerMaxBlockReach(Player player) {
|
public double getPlayerMaxBlockReach(Player player) {
|
||||||
return Double.MAX_VALUE;
|
return 4.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getPlayerMaxAttackReach(Player player) {
|
public double getPlayerMaxAttackReach(Player player) {
|
||||||
return Double.MAX_VALUE;
|
return 3.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getMaxStepUp(Player player) {
|
public double getMaxStepUp(Player player) {
|
||||||
return Double.MAX_VALUE;
|
if (player.isInsideVehicle()) {
|
||||||
|
if (player.getVehicle() != null && MaterialUtil.isBoat(player.getVehicle().getType())) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
final MovingConfig cc = DataManager.getPlayerData(player).getGenericInstance(MovingConfig.class);
|
||||||
|
return cc.sfStepHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getMovementEfficiency(Player player) {
|
public float getMovementEfficiency(Player player) {
|
||||||
return Float.MAX_VALUE;
|
return 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getWaterMovementEfficiency(Player player) {
|
public float getWaterMovementEfficiency(Player player) {
|
||||||
return Float.MAX_VALUE;
|
return 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getSubmergedMiningSpeedMultiplier(Player player) {
|
public double getSubmergedMiningSpeedMultiplier(Player player) {
|
||||||
return Double.MAX_VALUE;
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getMiningEfficiency(Player player) {
|
public double getMiningEfficiency(Player player) {
|
||||||
return Double.MAX_VALUE;
|
return 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getEntityScale(Player player) {
|
public double getEntityScale(Player player) {
|
||||||
return Double.MAX_VALUE;
|
return 1.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+7
@@ -60,6 +60,13 @@ public class ReflectDamageSources {
|
|||||||
nmsDamageSources = ReflectionUtil.getMethodNoArgs(nmsEntityClass, "dX", nmsClass);
|
nmsDamageSources = ReflectionUtil.getMethodNoArgs(nmsEntityClass, "dX", nmsClass);
|
||||||
nmsfall = ReflectionUtil.getMethodNoArgs(nmsClass, "l", reflectDamageSource.nmsClass);
|
nmsfall = ReflectionUtil.getMethodNoArgs(nmsClass, "l", reflectDamageSource.nmsClass);
|
||||||
}
|
}
|
||||||
|
// 1.21.8 and possibly earlier down to .4
|
||||||
|
if (nmsDamageSources == null) {
|
||||||
|
nmsDamageSources = ReflectionUtil.getMethodNoArgs(nmsEntityClass, "ea", nmsClass);
|
||||||
|
// TODO: Check
|
||||||
|
// nmsfall = ReflectionUtil.getMethodNoArgs(nmsClass, "l", reflectDamageSource.nmsClass);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object getDamageSource(Object handle) {
|
public Object getDamageSource(Object handle) {
|
||||||
|
|||||||
+3
-2
@@ -86,7 +86,8 @@ public class ReflectEntity extends ReflectGetHandleBase<Entity> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Method seekClearActiveItemMethod(Class<?> nmsClass) {
|
private Method seekClearActiveItemMethod(Class<?> nmsClass) {
|
||||||
// void releaseUsingItem() -> fE(1.21.2-1.21.3)
|
// void releaseUsingItem() -> fL (1.21.8 and possibly down to .4)
|
||||||
|
// fE(1.21.2-1.21.3)
|
||||||
// fw(1.21-1.21.1)
|
// fw(1.21-1.21.1)
|
||||||
// fA(1.20.5-1.20.6)
|
// fA(1.20.5-1.20.6)
|
||||||
// fs(1.20.4)
|
// fs(1.20.4)
|
||||||
@@ -95,7 +96,7 @@ public class ReflectEntity extends ReflectGetHandleBase<Entity> {
|
|||||||
// fj(1.19-19.4)
|
// fj(1.19-19.4)
|
||||||
// eR(???)
|
// eR(???)
|
||||||
// clearActiveItem(1.13-???)
|
// clearActiveItem(1.13-???)
|
||||||
String[] methodname = {"fE", "fw", "fA", "fs", "fr", "fn", "fj", "eR", "clearActiveItem", "releaseUsingItem"};
|
String[] methodname = {"fL", "fE", "fw", "fA", "fs", "fr", "fn", "fj", "eR", "clearActiveItem", "releaseUsingItem"};
|
||||||
for (String name : methodname) {
|
for (String name : methodname) {
|
||||||
Method method = ReflectionUtil.getMethodNoArgs(nmsClass, name);
|
Method method = ReflectionUtil.getMethodNoArgs(nmsClass, name);
|
||||||
if (method != null) return method;
|
if (method != null) return method;
|
||||||
|
|||||||
+27
-1
@@ -109,7 +109,29 @@ public class BlockCacheCB2922 extends BlockCache {
|
|||||||
block.updateShape(iBlockAccess, x, y, z); // data from cache.
|
block.updateShape(iBlockAccess, x, y, z); // data from cache.
|
||||||
|
|
||||||
// minX, minY, minZ, maxX, maxY, maxZ
|
// minX, minY, minZ, maxX, maxY, maxZ
|
||||||
return new double[]{block.x(), block.z(), block.B(), block.y(), block.A(), block.C()};
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, new double[]{block.x(), block.z(), block.B(), block.y(), block.A(), block.C()});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(int x, int y, int z) {
|
||||||
|
final Material mat = getType(x, y, z);
|
||||||
|
return LegacyBlocks.isCollisionSameVisual(this, mat, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] fetchVisualBounds(final int x, final int y, final int z){
|
||||||
|
// TODO: change api for this / use nodes (!)
|
||||||
|
final Material mat = getType(x, y, z);
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
final int id = mat.getId();
|
||||||
|
final net.minecraft.server.v1_7_R1.Block block = net.minecraft.server.v1_7_R1.Block.e(id);
|
||||||
|
if (block == null) return null;
|
||||||
|
final double[] shape = LegacyBlocks.getVisualShape(this, mat, x, y, z);
|
||||||
|
if (shape != null) return shape;
|
||||||
|
block.updateShape(iBlockAccess, x, y, z); // data from cache.
|
||||||
|
|
||||||
|
// minX, minY, minZ, maxX, maxY, maxZ
|
||||||
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, new double[]{block.x(), block.z(), block.B(), block.y(), block.A(), block.C()});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -156,4 +178,8 @@ public class BlockCacheCB2922 extends BlockCache {
|
|||||||
bukkitWorld = null;
|
bukkitWorld = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long fetchExtendedData(int x, int y, int z) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+27
-2
@@ -104,7 +104,29 @@ public class BlockCacheCB3026 extends BlockCache {
|
|||||||
block.updateShape(iBlockAccess, x, y, z); // getData from cache.
|
block.updateShape(iBlockAccess, x, y, z); // getData from cache.
|
||||||
|
|
||||||
// minX, minY, minZ, maxX, maxY, maxZ
|
// minX, minY, minZ, maxX, maxY, maxZ
|
||||||
return new double[]{block.x(), block.z(), block.B(), block.y(), block.A(), block.C()};
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, new double[]{block.x(), block.z(), block.B(), block.y(), block.A(), block.C()});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(int x, int y, int z) {
|
||||||
|
final Material mat = getType(x, y, z);
|
||||||
|
return LegacyBlocks.isCollisionSameVisual(this, mat, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] fetchVisualBounds(final int x, final int y, final int z){
|
||||||
|
// TODO: change api for this / use nodes (!)
|
||||||
|
final Material mat = getType(x, y, z);
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
final int id = mat.getId();
|
||||||
|
final net.minecraft.server.v1_7_R2.Block block = net.minecraft.server.v1_7_R2.Block.e(id);
|
||||||
|
if (block == null) return null;
|
||||||
|
final double[] shape = LegacyBlocks.getVisualShape(this, mat, x, y, z);
|
||||||
|
if (shape != null) return shape;
|
||||||
|
block.updateShape(iBlockAccess, x, y, z); // getData from cache.
|
||||||
|
|
||||||
|
// minX, minY, minZ, maxX, maxY, maxZ
|
||||||
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, new double[]{block.x(), block.z(), block.B(), block.y(), block.A(), block.C()});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -150,5 +172,8 @@ public class BlockCacheCB3026 extends BlockCache {
|
|||||||
world = null;
|
world = null;
|
||||||
bukkitWorld = null;
|
bukkitWorld = null;
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
|
public long fetchExtendedData(int x, int y, int z) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+27
-2
@@ -104,7 +104,29 @@ public class BlockCacheCB3043 extends BlockCache {
|
|||||||
block.updateShape(iBlockAccess, x, y, z); // getData from cache.
|
block.updateShape(iBlockAccess, x, y, z); // getData from cache.
|
||||||
|
|
||||||
// minX, minY, minZ, maxX, maxY, maxZ
|
// minX, minY, minZ, maxX, maxY, maxZ
|
||||||
return new double[]{block.x(), block.z(), block.B(), block.y(), block.A(), block.C()};
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, new double[]{block.x(), block.z(), block.B(), block.y(), block.A(), block.C()});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(int x, int y, int z) {
|
||||||
|
final Material mat = getType(x, y, z);
|
||||||
|
return LegacyBlocks.isCollisionSameVisual(this, mat, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] fetchVisualBounds(final int x, final int y, final int z){
|
||||||
|
// TODO: change api for this / use nodes (!)
|
||||||
|
final Material mat = getType(x, y, z);
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
final int id = mat.getId();
|
||||||
|
final net.minecraft.server.v1_7_R3.Block block = net.minecraft.server.v1_7_R3.Block.e(id);
|
||||||
|
if (block == null) return null;
|
||||||
|
final double[] shape = LegacyBlocks.getVisualShape(this, mat, x, y, z);
|
||||||
|
if (shape != null) return shape;
|
||||||
|
block.updateShape(iBlockAccess, x, y, z); // getData from cache.
|
||||||
|
|
||||||
|
// minX, minY, minZ, maxX, maxY, maxZ
|
||||||
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, new double[]{block.x(), block.z(), block.B(), block.y(), block.A(), block.C()});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -150,5 +172,8 @@ public class BlockCacheCB3043 extends BlockCache {
|
|||||||
world = null;
|
world = null;
|
||||||
bukkitWorld = null;
|
bukkitWorld = null;
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
|
public long fetchExtendedData(int x, int y, int z) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+29
-2
@@ -105,7 +105,31 @@ public class BlockCacheCB3100 extends BlockCache {
|
|||||||
block.updateShape(iBlockAccess, x, y, z); // getData from cache.
|
block.updateShape(iBlockAccess, x, y, z); // getData from cache.
|
||||||
|
|
||||||
// minX, minY, minZ, maxX, maxY, maxZ
|
// minX, minY, minZ, maxX, maxY, maxZ
|
||||||
return new double[]{block.x(), block.z(), block.B(), block.y(), block.A(), block.C()};
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, new double[]{block.x(), block.z(), block.B(), block.y(), block.A(), block.C()});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(int x, int y, int z) {
|
||||||
|
final Material mat = getType(x, y, z);
|
||||||
|
return LegacyBlocks.isCollisionSameVisual(this, mat, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] fetchVisualBounds(final int x, final int y, final int z){
|
||||||
|
final Material mat = getType(x, y, z);
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
final int id = mat.getId();
|
||||||
|
final net.minecraft.server.v1_7_R4.Block block = net.minecraft.server.v1_7_R4.Block.getById(id);
|
||||||
|
if (block == null) {
|
||||||
|
// TODO: Convention for null bounds -> full ?
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final double[] shape = LegacyBlocks.getVisualShape(this, mat, x, y, z);
|
||||||
|
if (shape != null) return shape;
|
||||||
|
block.updateShape(iBlockAccess, x, y, z); // getData from cache.
|
||||||
|
|
||||||
|
// minX, minY, minZ, maxX, maxY, maxZ
|
||||||
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, new double[]{block.x(), block.z(), block.B(), block.y(), block.A(), block.C()});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -153,5 +177,8 @@ public class BlockCacheCB3100 extends BlockCache {
|
|||||||
world = null;
|
world = null;
|
||||||
bukkitWorld = null;
|
bukkitWorld = null;
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
|
public long fetchExtendedData(int x, int y, int z) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+35
-2
@@ -85,7 +85,37 @@ public class BlockCacheSpigotCB1_10_R1 extends BlockCache {
|
|||||||
//return null;
|
//return null;
|
||||||
}
|
}
|
||||||
// minX, minY, minZ, maxX, maxY, maxZ
|
// minX, minY, minZ, maxX, maxY, maxZ
|
||||||
return new double[]{bb.a, bb.b, bb.c, bb.d, bb.e, bb.f};
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, new double[]{bb.a, bb.b, bb.c, bb.d, bb.e, bb.f});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(int x, int y, int z) {
|
||||||
|
final Material mat = getType(x, y, z);
|
||||||
|
return LegacyBlocks.isCollisionSameVisual(this, mat, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] fetchVisualBounds(final int x, final int y, final int z){
|
||||||
|
final Material mat = getType(x, y, z);
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
final int id = mat.getId();
|
||||||
|
final net.minecraft.server.v1_10_R1.Block block = net.minecraft.server.v1_10_R1.Block.getById(id);
|
||||||
|
if (block == null) {
|
||||||
|
// TODO: Convention for null blocks -> full ?
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final double[] shape = LegacyBlocks.getVisualShape(this, mat, x, y, z);
|
||||||
|
if (shape != null) return shape;
|
||||||
|
final BlockPosition pos = new BlockPosition(x, y, z);
|
||||||
|
// TODO: Deprecation warning below (reason / substitute?).
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
final AxisAlignedBB bb = block.a(world.getType(pos), world, pos);
|
||||||
|
if (bb == null) {
|
||||||
|
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0}; // Special case.
|
||||||
|
//return null;
|
||||||
|
}
|
||||||
|
// minX, minY, minZ, maxX, maxY, maxZ
|
||||||
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, new double[]{bb.a, bb.b, bb.c, bb.d, bb.e, bb.f});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -133,5 +163,8 @@ public class BlockCacheSpigotCB1_10_R1 extends BlockCache {
|
|||||||
world = null;
|
world = null;
|
||||||
bukkitWorld = null;
|
bukkitWorld = null;
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
|
public long fetchExtendedData(int x, int y, int z) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+35
-2
@@ -115,7 +115,37 @@ public class BlockCacheSpigotCB1_11_R1 extends BlockCache {
|
|||||||
//return null;
|
//return null;
|
||||||
}
|
}
|
||||||
// minX, minY, minZ, maxX, maxY, maxZ
|
// minX, minY, minZ, maxX, maxY, maxZ
|
||||||
return new double[]{bb.a, bb.b, bb.c, bb.d, bb.e, bb.f};
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, new double[]{bb.a, bb.b, bb.c, bb.d, bb.e, bb.f});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(int x, int y, int z) {
|
||||||
|
final Material mat = getType(x, y, z);
|
||||||
|
return LegacyBlocks.isCollisionSameVisual(this, mat, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] fetchVisualBounds(final int x, final int y, final int z){
|
||||||
|
final Material mat = getType(x, y, z);
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
final int id = mat.getId();
|
||||||
|
final net.minecraft.server.v1_11_R1.Block block = net.minecraft.server.v1_11_R1.Block.getById(id);
|
||||||
|
if (block == null) {
|
||||||
|
// TODO: Convention for null blocks -> full ?
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final double[] shape = LegacyBlocks.getVisualShape(this, mat, x, y, z);
|
||||||
|
if (shape != null) return shape;
|
||||||
|
final BlockPosition pos = new BlockPosition(x, y, z);
|
||||||
|
// TODO: Deprecation warning below (reason / substitute?).
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
final AxisAlignedBB bb = block.b(iBlockAccess.getType(pos), iBlockAccess, pos);
|
||||||
|
if (bb == null) {
|
||||||
|
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0}; // Special case.
|
||||||
|
//return null;
|
||||||
|
}
|
||||||
|
// minX, minY, minZ, maxX, maxY, maxZ
|
||||||
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, new double[]{bb.a, bb.b, bb.c, bb.d, bb.e, bb.f});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -163,5 +193,8 @@ public class BlockCacheSpigotCB1_11_R1 extends BlockCache {
|
|||||||
world = null;
|
world = null;
|
||||||
bukkitWorld = null;
|
bukkitWorld = null;
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
|
public long fetchExtendedData(int x, int y, int z) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+35
-2
@@ -85,7 +85,37 @@ public class BlockCacheSpigotCB1_12_R1 extends BlockCache {
|
|||||||
//return null;
|
//return null;
|
||||||
}
|
}
|
||||||
// minX, minY, minZ, maxX, maxY, maxZ
|
// minX, minY, minZ, maxX, maxY, maxZ
|
||||||
return new double[]{bb.a, bb.b, bb.c, bb.d, bb.e, bb.f};
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, new double[]{bb.a, bb.b, bb.c, bb.d, bb.e, bb.f});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(int x, int y, int z) {
|
||||||
|
final Material mat = getType(x, y, z);
|
||||||
|
return LegacyBlocks.isCollisionSameVisual(this, mat, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] fetchVisualBounds(final int x, final int y, final int z){
|
||||||
|
final Material mat = getType(x, y, z);
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
final int id = mat.getId();
|
||||||
|
final net.minecraft.server.v1_12_R1.Block block = net.minecraft.server.v1_12_R1.Block.getById(id);
|
||||||
|
if (block == null) {
|
||||||
|
// TODO: Convention for null blocks -> full ?
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final double[] shape = LegacyBlocks.getVisualShape(this, mat, x, y, z);
|
||||||
|
if (shape != null) return shape;
|
||||||
|
final BlockPosition pos = new BlockPosition(x, y, z);
|
||||||
|
// TODO: Deprecation warning below (reason / substitute?).
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
final AxisAlignedBB bb = block.b(world.getType(pos), world, pos);
|
||||||
|
if (bb == null) {
|
||||||
|
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0}; // Special case.
|
||||||
|
//return null;
|
||||||
|
}
|
||||||
|
// minX, minY, minZ, maxX, maxY, maxZ
|
||||||
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, new double[]{bb.a, bb.b, bb.c, bb.d, bb.e, bb.f});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -133,5 +163,8 @@ public class BlockCacheSpigotCB1_12_R1 extends BlockCache {
|
|||||||
world = null;
|
world = null;
|
||||||
bukkitWorld = null;
|
bukkitWorld = null;
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
|
public long fetchExtendedData(int x, int y, int z) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+29
-2
@@ -78,7 +78,31 @@ public class BlockCacheSpigotCB1_8_R1 extends BlockCache {
|
|||||||
block.updateShape(world, new BlockPosition(x, y, z));
|
block.updateShape(world, new BlockPosition(x, y, z));
|
||||||
|
|
||||||
// minX, minY, minZ, maxX, maxY, maxZ
|
// minX, minY, minZ, maxX, maxY, maxZ
|
||||||
return new double[]{block.z(), block.B(), block.D(), block.A(), block.C(), block.E()};
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, new double[]{block.z(), block.B(), block.D(), block.A(), block.C(), block.E()});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(int x, int y, int z) {
|
||||||
|
final Material mat = getType(x, y, z);
|
||||||
|
return LegacyBlocks.isCollisionSameVisual(this, mat, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] fetchVisualBounds(final int x, final int y, final int z){
|
||||||
|
final Material mat = getType(x, y, z);
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
final int id = mat.getId();
|
||||||
|
final net.minecraft.server.v1_8_R1.Block block = net.minecraft.server.v1_8_R1.Block.getById(id);
|
||||||
|
if (block == null) {
|
||||||
|
// TODO: Convention for null bounds -> full ?
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final double[] shape = LegacyBlocks.getVisualShape(this, mat, x, y, z);
|
||||||
|
if (shape != null) return shape;
|
||||||
|
block.updateShape(world, new BlockPosition(x, y, z));
|
||||||
|
|
||||||
|
// minX, minY, minZ, maxX, maxY, maxZ
|
||||||
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, new double[]{block.z(), block.B(), block.D(), block.A(), block.C(), block.E()});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -126,5 +150,8 @@ public class BlockCacheSpigotCB1_8_R1 extends BlockCache {
|
|||||||
world = null;
|
world = null;
|
||||||
bukkitWorld = null;
|
bukkitWorld = null;
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
|
public long fetchExtendedData(int x, int y, int z) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+29
-2
@@ -78,7 +78,31 @@ public class BlockCacheSpigotCB1_8_R2 extends BlockCache {
|
|||||||
block.updateShape(world, new BlockPosition(x, y, z));
|
block.updateShape(world, new BlockPosition(x, y, z));
|
||||||
|
|
||||||
// minX, minY, minZ, maxX, maxY, maxZ
|
// minX, minY, minZ, maxX, maxY, maxZ
|
||||||
return new double[]{block.B(), block.D(), block.F(), block.C(), block.E(), block.G()};
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, new double[]{block.B(), block.D(), block.F(), block.C(), block.E(), block.G()});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(int x, int y, int z) {
|
||||||
|
final Material mat = getType(x, y, z);
|
||||||
|
return LegacyBlocks.isCollisionSameVisual(this, mat, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] fetchVisualBounds(final int x, final int y, final int z){
|
||||||
|
final Material mat = getType(x, y, z);
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
final int id = mat.getId();
|
||||||
|
final net.minecraft.server.v1_8_R2.Block block = net.minecraft.server.v1_8_R2.Block.getById(id);
|
||||||
|
if (block == null) {
|
||||||
|
// TODO: Convention for null bounds -> full ?
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final double[] shape = LegacyBlocks.getVisualShape(this, mat, x, y, z);
|
||||||
|
if (shape != null) return shape;
|
||||||
|
block.updateShape(world, new BlockPosition(x, y, z));
|
||||||
|
|
||||||
|
// minX, minY, minZ, maxX, maxY, maxZ
|
||||||
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, new double[]{block.B(), block.D(), block.F(), block.C(), block.E(), block.G()});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -126,5 +150,8 @@ public class BlockCacheSpigotCB1_8_R2 extends BlockCache {
|
|||||||
world = null;
|
world = null;
|
||||||
bukkitWorld = null;
|
bukkitWorld = null;
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
|
public long fetchExtendedData(int x, int y, int z) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+29
-2
@@ -78,7 +78,31 @@ public class BlockCacheSpigotCB1_8_R3 extends BlockCache {
|
|||||||
block.updateShape(world, new BlockPosition(x, y, z));
|
block.updateShape(world, new BlockPosition(x, y, z));
|
||||||
|
|
||||||
// minX, minY, minZ, maxX, maxY, maxZ
|
// minX, minY, minZ, maxX, maxY, maxZ
|
||||||
return new double[]{block.B(), block.D(), block.F(), block.C(), block.E(), block.G()};
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, new double[]{block.B(), block.D(), block.F(), block.C(), block.E(), block.G()});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(int x, int y, int z) {
|
||||||
|
final Material mat = getType(x, y, z);
|
||||||
|
return LegacyBlocks.isCollisionSameVisual(this, mat, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] fetchVisualBounds(final int x, final int y, final int z){
|
||||||
|
final Material mat = getType(x, y, z);
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
final int id = mat.getId();
|
||||||
|
final net.minecraft.server.v1_8_R3.Block block = net.minecraft.server.v1_8_R3.Block.getById(id);
|
||||||
|
if (block == null) {
|
||||||
|
// TODO: Convention for null bounds -> full ?
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final double[] shape = LegacyBlocks.getVisualShape(this, mat, x, y, z);
|
||||||
|
if (shape != null) return shape;
|
||||||
|
block.updateShape(world, new BlockPosition(x, y, z));
|
||||||
|
|
||||||
|
// minX, minY, minZ, maxX, maxY, maxZ
|
||||||
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, new double[]{block.B(), block.D(), block.F(), block.C(), block.E(), block.G()});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -126,5 +150,8 @@ public class BlockCacheSpigotCB1_8_R3 extends BlockCache {
|
|||||||
world = null;
|
world = null;
|
||||||
bukkitWorld = null;
|
bukkitWorld = null;
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
|
public long fetchExtendedData(int x, int y, int z) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+33
-2
@@ -83,7 +83,35 @@ public class BlockCacheSpigotCB1_9_R1 extends BlockCache {
|
|||||||
//return null;
|
//return null;
|
||||||
}
|
}
|
||||||
// minX, minY, minZ, maxX, maxY, maxZ
|
// minX, minY, minZ, maxX, maxY, maxZ
|
||||||
return new double[]{bb.a, bb.b, bb.c, bb.d, bb.e, bb.f};
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, new double[]{bb.a, bb.b, bb.c, bb.d, bb.e, bb.f});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(int x, int y, int z) {
|
||||||
|
final Material mat = getType(x, y, z);
|
||||||
|
return LegacyBlocks.isCollisionSameVisual(this, mat, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] fetchVisualBounds(final int x, final int y, final int z){
|
||||||
|
final Material mat = getType(x, y, z);
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
final int id = mat.getId();
|
||||||
|
final net.minecraft.server.v1_9_R1.Block block = net.minecraft.server.v1_9_R1.Block.getById(id);
|
||||||
|
if (block == null) {
|
||||||
|
// TODO: Convention for null blocks -> full ?
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final double[] shape = LegacyBlocks.getVisualShape(this, mat, x, y, z);
|
||||||
|
if (shape != null) return shape;
|
||||||
|
final BlockPosition pos = new BlockPosition(x, y, z);
|
||||||
|
final AxisAlignedBB bb = block.a(world.getType(pos), world, pos);
|
||||||
|
if (bb == null) {
|
||||||
|
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0}; // Special case.
|
||||||
|
//return null;
|
||||||
|
}
|
||||||
|
// minX, minY, minZ, maxX, maxY, maxZ
|
||||||
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, new double[]{bb.a, bb.b, bb.c, bb.d, bb.e, bb.f});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -131,5 +159,8 @@ public class BlockCacheSpigotCB1_9_R1 extends BlockCache {
|
|||||||
world = null;
|
world = null;
|
||||||
bukkitWorld = null;
|
bukkitWorld = null;
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
|
public long fetchExtendedData(int x, int y, int z) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+35
-2
@@ -85,7 +85,37 @@ public class BlockCacheSpigotCB1_9_R2 extends BlockCache {
|
|||||||
//return null;
|
//return null;
|
||||||
}
|
}
|
||||||
// minX, minY, minZ, maxX, maxY, maxZ
|
// minX, minY, minZ, maxX, maxY, maxZ
|
||||||
return new double[]{bb.a, bb.b, bb.c, bb.d, bb.e, bb.f};
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, new double[]{bb.a, bb.b, bb.c, bb.d, bb.e, bb.f});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCollisionSameVisual(int x, int y, int z) {
|
||||||
|
final Material mat = getType(x, y, z);
|
||||||
|
return LegacyBlocks.isCollisionSameVisual(this, mat, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] fetchVisualBounds(final int x, final int y, final int z){
|
||||||
|
final Material mat = getType(x, y, z);
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
final int id = mat.getId();
|
||||||
|
final net.minecraft.server.v1_9_R2.Block block = net.minecraft.server.v1_9_R2.Block.getById(id);
|
||||||
|
if (block == null) {
|
||||||
|
// TODO: Convention for null blocks -> full ?
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final double[] shape = LegacyBlocks.getVisualShape(this, mat, x, y, z);
|
||||||
|
if (shape != null) return shape;
|
||||||
|
final BlockPosition pos = new BlockPosition(x, y, z);
|
||||||
|
// TODO: Deprecation warning below (reason / substitute?).
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
final AxisAlignedBB bb = block.a(world.getType(pos), world, pos);
|
||||||
|
if (bb == null) {
|
||||||
|
return new double[] {0.0, 0.0, 0.0, 1.0, 1.0, 1.0}; // Special case.
|
||||||
|
//return null;
|
||||||
|
}
|
||||||
|
// minX, minY, minZ, maxX, maxY, maxZ
|
||||||
|
return LegacyBlocks.adjustBounds(this, mat, x, y, z, new double[]{bb.a, bb.b, bb.c, bb.d, bb.e, bb.f});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -133,5 +163,8 @@ public class BlockCacheSpigotCB1_9_R2 extends BlockCache {
|
|||||||
world = null;
|
world = null;
|
||||||
bukkitWorld = null;
|
bukkitWorld = null;
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
|
public long fetchExtendedData(int x, int y, int z) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -20,8 +20,8 @@
|
|||||||
|
|
||||||
<repositories>
|
<repositories>
|
||||||
<repository>
|
<repository>
|
||||||
<id>dmulloy2-repo</id>
|
<id>dmulloy2-public</id>
|
||||||
<url>https://repo.dmulloy2.net/nexus/repository/public/</url>
|
<url>https://repo.dmulloy2.net/repository/public/</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.spigotmc</groupId>
|
<groupId>org.spigotmc</groupId>
|
||||||
<artifactId>spigot-api</artifactId>
|
<artifactId>spigot-api</artifactId>
|
||||||
<version>1.21.4-R0.1-SNAPSHOT</version>
|
<version>26.3-R0.1-SNAPSHOT</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -39,9 +39,9 @@
|
|||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.comphenix.protocol</groupId>
|
<groupId>net.dmulloy2</groupId>
|
||||||
<artifactId>ProtocolLib</artifactId>
|
<artifactId>ProtocolLib</artifactId>
|
||||||
<version>5.3.0</version>
|
<version>5.5.0-SNAPSHOT</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|||||||
+869
@@ -0,0 +1,869 @@
|
|||||||
|
/*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package fr.neatmonster.nocheatplus.checks.net.entity;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.OptionalInt;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
|
import com.comphenix.protocol.reflect.StructureModifier;
|
||||||
|
import com.comphenix.protocol.wrappers.BlockPosition;
|
||||||
|
import com.comphenix.protocol.wrappers.WrappedDataValue;
|
||||||
|
import com.comphenix.protocol.wrappers.WrappedWatchableObject;
|
||||||
|
|
||||||
|
import fr.neatmonster.nocheatplus.compat.versions.ServerVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProtocolLib field layout for the entity packets the tracker follows.
|
||||||
|
* Relative deltas are left in their packet units (1/32 before 1.9, 1/4096 after).
|
||||||
|
*/
|
||||||
|
public final class EntityPacketReader {
|
||||||
|
|
||||||
|
private static boolean loggedUnknownFirework;
|
||||||
|
private static boolean loggedUnreadAbsolute;
|
||||||
|
private static final boolean AT_LEAST_1_9 = ServerVersion.isAtLeast("1.9");
|
||||||
|
private static final boolean AT_LEAST_1_14 = ServerVersion.isAtLeast("1.14");
|
||||||
|
private static final boolean AT_LEAST_26_3 = ServerVersion.isAtLeast("26.3");
|
||||||
|
|
||||||
|
public static final class Spawn {
|
||||||
|
public int id;
|
||||||
|
public UUID uuid;
|
||||||
|
public String typeName;
|
||||||
|
public int typeId = -1;
|
||||||
|
public double x;
|
||||||
|
public double y;
|
||||||
|
public double z;
|
||||||
|
public boolean positionKnown;
|
||||||
|
public Float yaw;
|
||||||
|
public Float pitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Relative {
|
||||||
|
public int id;
|
||||||
|
public boolean hasPosition;
|
||||||
|
public long dx;
|
||||||
|
public long dy;
|
||||||
|
public long dz;
|
||||||
|
public Float yaw;
|
||||||
|
public Float pitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Absolute {
|
||||||
|
public int id;
|
||||||
|
public boolean positionKnown;
|
||||||
|
public double x;
|
||||||
|
public double y;
|
||||||
|
public double z;
|
||||||
|
/** When set, that axis is a delta added to the last position the client was given. */
|
||||||
|
public boolean relativeX;
|
||||||
|
public boolean relativeY;
|
||||||
|
public boolean relativeZ;
|
||||||
|
public Float yaw;
|
||||||
|
public Float pitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Mount {
|
||||||
|
public int vehicleId;
|
||||||
|
public int[] passengers = new int[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Attach {
|
||||||
|
public int passengerId;
|
||||||
|
public int vehicleId;
|
||||||
|
public boolean mount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private EntityPacketReader() {}
|
||||||
|
|
||||||
|
public static Spawn readSpawn(PacketContainer packet) {
|
||||||
|
Integer id = readInt(packet, 0);
|
||||||
|
if (id == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Spawn spawn = new Spawn();
|
||||||
|
spawn.id = id.intValue();
|
||||||
|
spawn.uuid = readUuid(packet);
|
||||||
|
spawn.typeName = readTypeName(packet);
|
||||||
|
spawn.typeId = readTypeId(packet);
|
||||||
|
fillAbsolute(packet, spawn);
|
||||||
|
return spawn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Relative readRelative(PacketContainer packet, boolean hasPosition, boolean hasLook) {
|
||||||
|
Integer id = readInt(packet, 0);
|
||||||
|
if (id == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Relative relative = new Relative();
|
||||||
|
relative.id = id.intValue();
|
||||||
|
if (hasPosition) {
|
||||||
|
long[] delta = readDelta(packet);
|
||||||
|
if (delta != null) {
|
||||||
|
relative.hasPosition = true;
|
||||||
|
relative.dx = delta[0];
|
||||||
|
relative.dy = delta[1];
|
||||||
|
relative.dz = delta[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasLook) {
|
||||||
|
readAngles(packet, relative);
|
||||||
|
}
|
||||||
|
return relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Absolute readAbsolute(PacketContainer packet) {
|
||||||
|
Integer id = readInt(packet, 0);
|
||||||
|
if (id == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Absolute absolute = new Absolute();
|
||||||
|
absolute.id = id.intValue();
|
||||||
|
fillNestedPosition(packet, absolute);
|
||||||
|
if (!absolute.positionKnown) {
|
||||||
|
try {
|
||||||
|
StructureModifier<Double> doubles = packet.getDoubles();
|
||||||
|
Double x = doubles.readSafely(0);
|
||||||
|
Double y = doubles.readSafely(1);
|
||||||
|
Double z = doubles.readSafely(2);
|
||||||
|
if (x != null && y != null && z != null) {
|
||||||
|
absolute.positionKnown = true;
|
||||||
|
absolute.x = x.doubleValue();
|
||||||
|
absolute.y = y.doubleValue();
|
||||||
|
absolute.z = z.doubleValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {}
|
||||||
|
}
|
||||||
|
if (!absolute.positionKnown) {
|
||||||
|
int[] legacy = legacyFixedPoint(packet);
|
||||||
|
if (legacy != null) {
|
||||||
|
absolute.positionKnown = true;
|
||||||
|
absolute.x = legacy[0] / 32.0D;
|
||||||
|
absolute.y = legacy[1] / 32.0D;
|
||||||
|
absolute.z = legacy[2] / 32.0D;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!absolute.positionKnown) {
|
||||||
|
noteUnreadAbsolute(packet);
|
||||||
|
}
|
||||||
|
readAngles(packet, absolute);
|
||||||
|
return absolute.positionKnown ? absolute : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1.21.2+ teleport and position-sync store the coordinates inside a position/rotation
|
||||||
|
* holder (position vector, velocity vector, yaw, pitch), not as packet-level doubles.
|
||||||
|
* A 1.20.6 client receives that holder already resolved to an absolute teleport.
|
||||||
|
*/
|
||||||
|
private static void fillNestedPosition(PacketContainer packet, Absolute absolute) {
|
||||||
|
Object handle;
|
||||||
|
try {
|
||||||
|
handle = packet.getHandle();
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (handle == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Object holder = positionHolder(handle);
|
||||||
|
double[] xyz = holder == null ? null : firstVector(holder);
|
||||||
|
if (xyz == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
absolute.positionKnown = true;
|
||||||
|
absolute.x = xyz[0];
|
||||||
|
absolute.y = xyz[1];
|
||||||
|
absolute.z = xyz[2];
|
||||||
|
float[] rotation = holder == null ? null : firstTwoFloats(holder);
|
||||||
|
if (rotation != null && absolute.yaw == null) {
|
||||||
|
absolute.yaw = Float.valueOf(rotation[0]);
|
||||||
|
absolute.pitch = Float.valueOf(rotation[1]);
|
||||||
|
}
|
||||||
|
markRelativeAxes(handle, absolute);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void noteUnreadAbsolute(PacketContainer packet) {
|
||||||
|
if (loggedUnreadAbsolute) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loggedUnreadAbsolute = true;
|
||||||
|
Object handle = null;
|
||||||
|
try {
|
||||||
|
handle = packet.getHandle();
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {}
|
||||||
|
System.out.println("entity position unread " + (handle == null ? "null" : handle.getClass().getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The nested holder, or the packet itself when the vector sits directly on it. */
|
||||||
|
private static Object positionHolder(Object packet) {
|
||||||
|
if (firstVector(packet) != null) {
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
Class<?> type = packet.getClass();
|
||||||
|
while (type != null && type != Object.class) {
|
||||||
|
Field[] fields = type.getDeclaredFields();
|
||||||
|
for (int index = 0; index < fields.length; index++) {
|
||||||
|
Field field = fields[index];
|
||||||
|
if (Modifier.isStatic(field.getModifiers()) || field.getType().isPrimitive()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Object value = readField(field, packet);
|
||||||
|
if (value != null && firstVector(value) != null) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type = type.getSuperclass();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** First vector on this object: its own three doubles, or the first nested vector field. */
|
||||||
|
private static double[] firstVector(Object object) {
|
||||||
|
if (object == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
double[] own = instanceDoubles(object);
|
||||||
|
if (own != null) {
|
||||||
|
return own;
|
||||||
|
}
|
||||||
|
Class<?> type = object.getClass();
|
||||||
|
while (type != null && type != Object.class) {
|
||||||
|
Field[] fields = type.getDeclaredFields();
|
||||||
|
for (int index = 0; index < fields.length; index++) {
|
||||||
|
Field field = fields[index];
|
||||||
|
if (Modifier.isStatic(field.getModifiers()) || field.getType().isPrimitive()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
double[] nested = instanceDoubles(readField(field, object));
|
||||||
|
if (nested != null) {
|
||||||
|
return nested;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type = type.getSuperclass();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double[] instanceDoubles(Object object) {
|
||||||
|
if (object == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Field[] fields = object.getClass().getDeclaredFields();
|
||||||
|
int count = 0;
|
||||||
|
for (int index = 0; index < fields.length; index++) {
|
||||||
|
Field field = fields[index];
|
||||||
|
if (!Modifier.isStatic(field.getModifiers()) && field.getType() == double.class) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count < 3) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
double[] values = new double[3];
|
||||||
|
int found = 0;
|
||||||
|
for (int index = 0; index < fields.length && found < 3; index++) {
|
||||||
|
Field field = fields[index];
|
||||||
|
if (Modifier.isStatic(field.getModifiers()) || field.getType() != double.class) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Object raw = readField(field, object);
|
||||||
|
if (!(raw instanceof Double)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
values[found++] = ((Double) raw).doubleValue();
|
||||||
|
}
|
||||||
|
return found == 3 ? values : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float[] firstTwoFloats(Object object) {
|
||||||
|
Field[] fields = object.getClass().getDeclaredFields();
|
||||||
|
float[] values = null;
|
||||||
|
int found = 0;
|
||||||
|
for (int index = 0; index < fields.length; index++) {
|
||||||
|
Field field = fields[index];
|
||||||
|
if (Modifier.isStatic(field.getModifiers()) || field.getType() != float.class) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Object raw = readField(field, object);
|
||||||
|
if (!(raw instanceof Float)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (values == null) {
|
||||||
|
values = new float[2];
|
||||||
|
}
|
||||||
|
values[found++] = ((Float) raw).floatValue();
|
||||||
|
if (found == 2) {
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Teleport packets name the axes that are deltas. Position sync has no such set,
|
||||||
|
* so every axis stays absolute. Names are the enum constants (X, Y, Z).
|
||||||
|
*/
|
||||||
|
private static void markRelativeAxes(Object packet, Absolute absolute) {
|
||||||
|
Class<?> type = packet.getClass();
|
||||||
|
while (type != null && type != Object.class) {
|
||||||
|
Field[] fields = type.getDeclaredFields();
|
||||||
|
for (int index = 0; index < fields.length; index++) {
|
||||||
|
Field field = fields[index];
|
||||||
|
if (Modifier.isStatic(field.getModifiers()) || !Set.class.isAssignableFrom(field.getType())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Object raw = readField(field, packet);
|
||||||
|
if (!(raw instanceof Set)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Set<?> flags = (Set<?>) raw;
|
||||||
|
java.util.Iterator<?> iterator = flags.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Object flag = iterator.next();
|
||||||
|
if (!(flag instanceof Enum)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String name = ((Enum<?>) flag).name();
|
||||||
|
if ("X".equals(name)) {
|
||||||
|
absolute.relativeX = true;
|
||||||
|
}
|
||||||
|
else if ("Y".equals(name)) {
|
||||||
|
absolute.relativeY = true;
|
||||||
|
}
|
||||||
|
else if ("Z".equals(name)) {
|
||||||
|
absolute.relativeZ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
type = type.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object readField(Field field, Object target) {
|
||||||
|
try {
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(target);
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int[] readDestroy(PacketContainer packet) {
|
||||||
|
try {
|
||||||
|
int[] ids = packet.getIntegerArrays().readSafely(0);
|
||||||
|
if (ids != null) {
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {}
|
||||||
|
try {
|
||||||
|
List<Integer> list = packet.getIntLists().readSafely(0);
|
||||||
|
if (list != null && !list.isEmpty()) {
|
||||||
|
int[] ids = new int[list.size()];
|
||||||
|
for (int index = 0; index < list.size(); index++) {
|
||||||
|
Integer id = list.get(index);
|
||||||
|
ids[index] = id == null ? -1 : id.intValue();
|
||||||
|
}
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {}
|
||||||
|
Integer one = readInt(packet, 0);
|
||||||
|
return one == null ? new int[0] : new int[] { one.intValue() };
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Mount readMount(PacketContainer packet) {
|
||||||
|
Integer vehicle = readInt(packet, 0);
|
||||||
|
if (vehicle == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Mount mount = new Mount();
|
||||||
|
mount.vehicleId = vehicle.intValue();
|
||||||
|
mount.passengers = readPassengerIds(packet);
|
||||||
|
return mount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Attach readAttach(PacketContainer packet) {
|
||||||
|
// 1.8: int 0 is 0 for mount and 1 for leash, then passenger, then vehicle.
|
||||||
|
Integer flag = readInt(packet, 0);
|
||||||
|
Integer passenger = readInt(packet, 1);
|
||||||
|
Integer vehicle = readInt(packet, 2);
|
||||||
|
if (flag == null || passenger == null || vehicle == null || flag.intValue() != 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Attach attach = new Attach();
|
||||||
|
attach.mount = true;
|
||||||
|
attach.passengerId = passenger.intValue();
|
||||||
|
attach.vehicleId = vehicle.intValue();
|
||||||
|
return attach;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entity id a firework rocket is attached to, from the metadata index vanilla uses.
|
||||||
|
* Null when that index is not in the packet. -1 when the optional is present but empty.
|
||||||
|
*/
|
||||||
|
public static Integer readFireworkAttached(PacketContainer packet) {
|
||||||
|
int index = fireworkAttachIndex();
|
||||||
|
try {
|
||||||
|
List<WrappedDataValue> values = packet.getDataValueCollectionModifier().readSafely(0);
|
||||||
|
Integer attached = entityIdAt(values, index);
|
||||||
|
if (attached != null || hasIndex(values, index)) {
|
||||||
|
return attached;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {}
|
||||||
|
try {
|
||||||
|
List<WrappedWatchableObject> values = packet.getWatchableCollectionModifier().readSafely(0);
|
||||||
|
if (values == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (int cursor = 0; cursor < values.size(); cursor++) {
|
||||||
|
WrappedWatchableObject value = values.get(cursor);
|
||||||
|
if (value != null && value.getIndex() == index) {
|
||||||
|
return entityIdOf(value.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int fireworkAttachIndex() {
|
||||||
|
// Grim's offset from index 9: 1.12.2 and older use 7, through 1.16.5 use 8, newer use 9.
|
||||||
|
if (!ServerVersion.isAtLeast("1.13")) {
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
if (!ServerVersion.isAtLeast("1.17")) {
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
return 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean hasIndex(List<WrappedDataValue> values, int index) {
|
||||||
|
if (values == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int cursor = 0; cursor < values.size(); cursor++) {
|
||||||
|
WrappedDataValue value = values.get(cursor);
|
||||||
|
if (value != null && value.getIndex() == index) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Integer entityIdAt(List<WrappedDataValue> values, int index) {
|
||||||
|
if (values == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (int cursor = 0; cursor < values.size(); cursor++) {
|
||||||
|
WrappedDataValue value = values.get(cursor);
|
||||||
|
if (value != null && value.getIndex() == index) {
|
||||||
|
return entityIdOf(value.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Integer entityIdOf(Object value) {
|
||||||
|
if (value instanceof Integer) {
|
||||||
|
return (Integer) value;
|
||||||
|
}
|
||||||
|
// 1.14+ firework "attached to" is an unsigned optional int, not Optional<Integer>.
|
||||||
|
if (value instanceof OptionalInt) {
|
||||||
|
OptionalInt optional = (OptionalInt) value;
|
||||||
|
return optional.isPresent() ? Integer.valueOf(optional.getAsInt()) : Integer.valueOf(-1);
|
||||||
|
}
|
||||||
|
if (value instanceof Optional) {
|
||||||
|
Optional<?> optional = (Optional<?>) value;
|
||||||
|
if (!optional.isPresent()) {
|
||||||
|
return Integer.valueOf(-1);
|
||||||
|
}
|
||||||
|
Object inner = optional.get();
|
||||||
|
if (inner instanceof Integer) {
|
||||||
|
return (Integer) inner;
|
||||||
|
}
|
||||||
|
return entityIdOf(inner);
|
||||||
|
}
|
||||||
|
if (value != null && value.getClass().getName().endsWith("OptionalInt")) {
|
||||||
|
try {
|
||||||
|
Method present = value.getClass().getMethod("isPresent");
|
||||||
|
Object isPresent = present.invoke(value);
|
||||||
|
if (!(isPresent instanceof Boolean) || !((Boolean) isPresent).booleanValue()) {
|
||||||
|
return Integer.valueOf(-1);
|
||||||
|
}
|
||||||
|
Method read = value.getClass().getMethod("getAsInt");
|
||||||
|
Object id = read.invoke(value);
|
||||||
|
if (id instanceof Integer) {
|
||||||
|
return (Integer) id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {}
|
||||||
|
}
|
||||||
|
if (value != null && !loggedUnknownFirework) {
|
||||||
|
loggedUnknownFirework = true;
|
||||||
|
System.out.println("firework meta type " + value.getClass().getName() + " value=" + value);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Byte readSharedFlags(PacketContainer packet) {
|
||||||
|
try {
|
||||||
|
List<WrappedDataValue> values = packet.getDataValueCollectionModifier().readSafely(0);
|
||||||
|
Byte flags = flagsAtIndexZero(values);
|
||||||
|
if (flags != null) {
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {}
|
||||||
|
try {
|
||||||
|
List<WrappedWatchableObject> values = packet.getWatchableCollectionModifier().readSafely(0);
|
||||||
|
if (values == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (int index = 0; index < values.size(); index++) {
|
||||||
|
WrappedWatchableObject value = values.get(index);
|
||||||
|
if (value != null && value.getIndex() == 0) {
|
||||||
|
Byte flags = asByte(value.getValue());
|
||||||
|
if (flags != null) {
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Byte flagsAtIndexZero(List<WrappedDataValue> values) {
|
||||||
|
if (values == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (int index = 0; index < values.size(); index++) {
|
||||||
|
WrappedDataValue value = values.get(index);
|
||||||
|
if (value != null && value.getIndex() == 0) {
|
||||||
|
Byte flags = asByte(value.getValue());
|
||||||
|
if (flags != null) {
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void fillAbsolute(PacketContainer packet, Spawn spawn) {
|
||||||
|
try {
|
||||||
|
StructureModifier<Double> doubles = packet.getDoubles();
|
||||||
|
Double x = doubles.readSafely(0);
|
||||||
|
Double y = doubles.readSafely(1);
|
||||||
|
Double z = doubles.readSafely(2);
|
||||||
|
if (x != null && y != null && z != null) {
|
||||||
|
spawn.positionKnown = true;
|
||||||
|
spawn.x = x.doubleValue();
|
||||||
|
spawn.y = y.doubleValue();
|
||||||
|
spawn.z = z.doubleValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {}
|
||||||
|
if (!spawn.positionKnown) {
|
||||||
|
try {
|
||||||
|
BlockPosition block = packet.getBlockPositionModifier().readSafely(0);
|
||||||
|
if (block != null) {
|
||||||
|
spawn.positionKnown = true;
|
||||||
|
spawn.x = block.getX();
|
||||||
|
spawn.y = block.getY();
|
||||||
|
spawn.z = block.getZ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {}
|
||||||
|
}
|
||||||
|
if (!spawn.positionKnown) {
|
||||||
|
int[] legacy = legacyFixedPoint(packet);
|
||||||
|
if (legacy != null) {
|
||||||
|
spawn.positionKnown = true;
|
||||||
|
spawn.x = legacy[0] / 32.0D;
|
||||||
|
spawn.y = legacy[1] / 32.0D;
|
||||||
|
spawn.z = legacy[2] / 32.0D;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Float[] angles = readAnglePair(packet);
|
||||||
|
if (angles != null) {
|
||||||
|
spawn.yaw = angles[0];
|
||||||
|
spawn.pitch = angles[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[] legacyFixedPoint(PacketContainer packet) {
|
||||||
|
try {
|
||||||
|
StructureModifier<Integer> integers = packet.getIntegers();
|
||||||
|
if (integers.size() >= 5) {
|
||||||
|
Integer x = integers.readSafely(2);
|
||||||
|
Integer y = integers.readSafely(3);
|
||||||
|
Integer z = integers.readSafely(4);
|
||||||
|
if (x != null && y != null && z != null) {
|
||||||
|
return new int[] { x.intValue(), y.intValue(), z.intValue() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (integers.size() >= 4) {
|
||||||
|
Integer x = integers.readSafely(1);
|
||||||
|
Integer y = integers.readSafely(2);
|
||||||
|
Integer z = integers.readSafely(3);
|
||||||
|
if (x != null && y != null && z != null) {
|
||||||
|
return new int[] { x.intValue(), y.intValue(), z.intValue() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long[] readDelta(PacketContainer packet) {
|
||||||
|
if (AT_LEAST_26_3) {
|
||||||
|
long[] modern = ModernDelta.read(packet);
|
||||||
|
if (modern != null) {
|
||||||
|
return modern;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (AT_LEAST_1_14) {
|
||||||
|
StructureModifier<Short> shorts = packet.getShorts();
|
||||||
|
if (shorts.size() >= 3) {
|
||||||
|
return new long[] { shorts.read(0).shortValue(), shorts.read(1).shortValue(), shorts.read(2).shortValue() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (AT_LEAST_1_9) {
|
||||||
|
StructureModifier<Integer> integers = packet.getIntegers();
|
||||||
|
if (integers.size() >= 4) {
|
||||||
|
return new long[] { integers.read(1).intValue(), integers.read(2).intValue(), integers.read(3).intValue() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
StructureModifier<Byte> bytes = packet.getBytes();
|
||||||
|
if (bytes.size() >= 3) {
|
||||||
|
return new long[] { bytes.read(0).byteValue(), bytes.read(1).byteValue(), bytes.read(2).byteValue() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {}
|
||||||
|
if (!AT_LEAST_26_3) {
|
||||||
|
return ModernDelta.read(packet);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void readAngles(PacketContainer packet, Relative relative) {
|
||||||
|
Float[] angles = readAnglePair(packet);
|
||||||
|
if (angles != null) {
|
||||||
|
relative.yaw = angles[0];
|
||||||
|
relative.pitch = angles[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void readAngles(PacketContainer packet, Absolute absolute) {
|
||||||
|
Float[] angles = readAnglePair(packet);
|
||||||
|
if (angles != null) {
|
||||||
|
absolute.yaw = angles[0];
|
||||||
|
absolute.pitch = angles[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Float[] readAnglePair(PacketContainer packet) {
|
||||||
|
try {
|
||||||
|
StructureModifier<Float> floats = packet.getFloat();
|
||||||
|
if (floats.size() >= 2) {
|
||||||
|
Float yaw = floats.readSafely(0);
|
||||||
|
Float pitch = floats.readSafely(1);
|
||||||
|
if (yaw != null && pitch != null) {
|
||||||
|
return new Float[] { yaw, pitch };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {}
|
||||||
|
try {
|
||||||
|
StructureModifier<Byte> bytes = packet.getBytes();
|
||||||
|
int size = bytes.size();
|
||||||
|
if (size >= 2) {
|
||||||
|
Byte yaw = bytes.readSafely(size - 2);
|
||||||
|
Byte pitch = bytes.readSafely(size - 1);
|
||||||
|
if (yaw != null && pitch != null) {
|
||||||
|
return new Float[] { Float.valueOf(angle(yaw.byteValue())), Float.valueOf(angle(pitch.byteValue())) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float angle(byte value) {
|
||||||
|
return value * 360.0f / 256.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[] readPassengerIds(PacketContainer packet) {
|
||||||
|
try {
|
||||||
|
int[] ids = packet.getIntegerArrays().readSafely(0);
|
||||||
|
if (ids != null) {
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {}
|
||||||
|
try {
|
||||||
|
List<Integer> list = packet.getIntLists().readSafely(0);
|
||||||
|
if (list != null) {
|
||||||
|
int[] ids = new int[list.size()];
|
||||||
|
for (int index = 0; index < list.size(); index++) {
|
||||||
|
Integer id = list.get(index);
|
||||||
|
ids[index] = id == null ? -1 : id.intValue();
|
||||||
|
}
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {}
|
||||||
|
return new int[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Integer readInt(PacketContainer packet, int index) {
|
||||||
|
try {
|
||||||
|
return packet.getIntegers().readSafely(index);
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static UUID readUuid(PacketContainer packet) {
|
||||||
|
try {
|
||||||
|
return packet.getUUIDs().readSafely(0);
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String readTypeName(PacketContainer packet) {
|
||||||
|
try {
|
||||||
|
EntityType type = packet.getEntityTypeModifier().readSafely(0);
|
||||||
|
return type == null ? null : type.name();
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int readTypeId(PacketContainer packet) {
|
||||||
|
try {
|
||||||
|
if (packet.getDoubles().size() < 3) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
Integer type = packet.getIntegers().readSafely(1);
|
||||||
|
return type == null ? -1 : type.intValue();
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Byte asByte(Object value) {
|
||||||
|
return value instanceof Byte ? (Byte) value : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 26.3 encodes a relative move as VecDelta instead of three shorts.
|
||||||
|
* Absent on older servers, in which case the reader falls through.
|
||||||
|
*/
|
||||||
|
private static final class ModernDelta {
|
||||||
|
private static final Class<?> DELTA;
|
||||||
|
private static final Class<?> LINEAR;
|
||||||
|
private static final Class<?> STEPPED;
|
||||||
|
private static final Method STEPS;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Class<?> delta = null;
|
||||||
|
Class<?> linear = null;
|
||||||
|
Class<?> stepped = null;
|
||||||
|
Method steps = null;
|
||||||
|
try {
|
||||||
|
delta = Class.forName("net.minecraft.network.protocol.game.VecDelta");
|
||||||
|
linear = Class.forName("net.minecraft.network.protocol.game.VecDelta$Linear");
|
||||||
|
stepped = Class.forName("net.minecraft.network.protocol.game.VecDelta$Stepped");
|
||||||
|
steps = stepped.getMethod("steps");
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {}
|
||||||
|
DELTA = delta;
|
||||||
|
LINEAR = linear;
|
||||||
|
STEPPED = stepped;
|
||||||
|
STEPS = steps;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long[] read(PacketContainer packet) {
|
||||||
|
if (DELTA == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Object delta = packet.getModifier().withType(DELTA).readSafely(0);
|
||||||
|
if (delta == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (LINEAR.isInstance(delta)) {
|
||||||
|
return axes(delta);
|
||||||
|
}
|
||||||
|
if (STEPPED.isInstance(delta) && STEPS != null) {
|
||||||
|
long dx = 0L;
|
||||||
|
long dy = 0L;
|
||||||
|
long dz = 0L;
|
||||||
|
Object raw = STEPS.invoke(delta);
|
||||||
|
if (raw instanceof List) {
|
||||||
|
List<?> list = (List<?>) raw;
|
||||||
|
for (int index = 0; index < list.size(); index++) {
|
||||||
|
long[] step = axes(list.get(index));
|
||||||
|
if (step == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
dx += step[0];
|
||||||
|
dy += step[1];
|
||||||
|
dz += step[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new long[] { dx, dy, dz };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long[] axes(Object value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
StructureModifier<Short> shorts = new StructureModifier<Short>(value.getClass()).withTarget(value).withType(short.class);
|
||||||
|
if (shorts.size() < 3) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new long[] { shorts.read(0).shortValue(), shorts.read(1).shortValue(), shorts.read(2).shortValue() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+260
@@ -0,0 +1,260 @@
|
|||||||
|
/*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package fr.neatmonster.nocheatplus.checks.net.protocollib;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.PacketType;
|
||||||
|
import com.comphenix.protocol.events.ListenerPriority;
|
||||||
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
|
import com.comphenix.protocol.events.PacketPostListener;
|
||||||
|
|
||||||
|
import fr.neatmonster.nocheatplus.checks.net.entity.ClientEntity;
|
||||||
|
import fr.neatmonster.nocheatplus.checks.net.entity.ClientEntityTracker;
|
||||||
|
import fr.neatmonster.nocheatplus.checks.net.entity.EntityPacketReader;
|
||||||
|
import fr.neatmonster.nocheatplus.compat.versions.ClientVersion;
|
||||||
|
import fr.neatmonster.nocheatplus.compat.versions.ServerVersion;
|
||||||
|
import fr.neatmonster.nocheatplus.logging.StaticLog;
|
||||||
|
import fr.neatmonster.nocheatplus.players.DataManager;
|
||||||
|
import fr.neatmonster.nocheatplus.players.IPlayerData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keeps {@link ClientEntityTracker} in step with the packets each client actually receives.
|
||||||
|
* The transaction clock is {@link fr.neatmonster.nocheatplus.checks.net.protocollib.TransactionAdapter}.
|
||||||
|
*/
|
||||||
|
public class EntityActivityAdapter extends BaseAdapter {
|
||||||
|
|
||||||
|
private static PacketType[] listenTypes() {
|
||||||
|
List<PacketType> types = new ArrayList<PacketType>();
|
||||||
|
add(types, PacketType.Play.Server.SPAWN_ENTITY);
|
||||||
|
add(types, PacketType.Play.Server.SPAWN_ENTITY_LIVING);
|
||||||
|
add(types, PacketType.Play.Server.NAMED_ENTITY_SPAWN);
|
||||||
|
add(types, PacketType.Play.Server.SPAWN_ENTITY_PAINTING);
|
||||||
|
add(types, PacketType.Play.Server.ENTITY_TELEPORT);
|
||||||
|
add(types, PacketType.Play.Server.ENTITY_POSITION_SYNC);
|
||||||
|
add(types, PacketType.Play.Server.REL_ENTITY_MOVE);
|
||||||
|
add(types, PacketType.Play.Server.REL_ENTITY_MOVE_LOOK);
|
||||||
|
add(types, PacketType.Play.Server.ENTITY_LOOK);
|
||||||
|
add(types, PacketType.Play.Server.ENTITY_DESTROY);
|
||||||
|
add(types, PacketType.Play.Server.ENTITY_METADATA);
|
||||||
|
add(types, PacketType.Play.Server.ENTITY_STATUS);
|
||||||
|
add(types, PacketType.Play.Server.MOUNT);
|
||||||
|
add(types, PacketType.Play.Server.ATTACH_ENTITY);
|
||||||
|
// 1.20.6 and older servers do not have this packet. The server tick steps entities there.
|
||||||
|
// It appears only when the server is 1.21.2 or newer, once per server tick.
|
||||||
|
if (ServerVersion.isAtLeast("1.21.2")) {
|
||||||
|
add(types, PacketType.Play.Client.CLIENT_TICK_END);
|
||||||
|
}
|
||||||
|
return types.toArray(new PacketType[types.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void add(List<PacketType> types, PacketType type) {
|
||||||
|
try {
|
||||||
|
if (type != null && type.isSupported()) {
|
||||||
|
types.add(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** True when this client actually sends tick-end. Older clients only see ViaVersion's fake copy, and only if the server protocol has the packet. */
|
||||||
|
private static boolean sendsRealTickEnd(Player player) {
|
||||||
|
if (!ServerVersion.isAtLeast("1.21.2")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
IPlayerData data = DataManager.getPlayerDataSafe(player);
|
||||||
|
if (data == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ClientVersion version = data.getClientVersion();
|
||||||
|
return version != null && version.isAtLeast(ClientVersion.V_1_21_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int failures;
|
||||||
|
|
||||||
|
public EntityActivityAdapter(Plugin plugin) {
|
||||||
|
super(plugin, ListenerPriority.LOW, listenTypes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPacketReceiving(PacketEvent event) {
|
||||||
|
if (!accept(event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
ClientEntityTracker tracker = tracker(player);
|
||||||
|
if (tracker == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PacketType type = event.getPacketType();
|
||||||
|
if (type == PacketType.Play.Client.CLIENT_TICK_END) {
|
||||||
|
// Registered only when the server is 1.21.2 or newer. ViaVersion still inserts a
|
||||||
|
// copy for older clients. That copy is not a client tick, so only a real 1.21.2+
|
||||||
|
// client steps here. Everyone else steps on the server tick.
|
||||||
|
if (sendsRealTickEnd(player)) {
|
||||||
|
tracker.onTickEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable throwable) {
|
||||||
|
fail(throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPacketSending(PacketEvent event) {
|
||||||
|
if (event.isCancelled() || !accept(event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
ClientEntityTracker tracker = tracker(player);
|
||||||
|
if (tracker == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PacketType type = event.getPacketType();
|
||||||
|
PacketContainer packet = event.getPacket();
|
||||||
|
if (type == PacketType.Play.Server.SPAWN_ENTITY || type == PacketType.Play.Server.SPAWN_ENTITY_LIVING
|
||||||
|
|| type == PacketType.Play.Server.NAMED_ENTITY_SPAWN || type == PacketType.Play.Server.SPAWN_ENTITY_PAINTING) {
|
||||||
|
EntityPacketReader.Spawn spawn = EntityPacketReader.readSpawn(packet);
|
||||||
|
if (spawn != null) {
|
||||||
|
tracker.onSpawn(player, spawn.id, spawn.uuid, kind(type, spawn.typeName), spawn.typeName, spawn.typeId,
|
||||||
|
spawn.x, spawn.y, spawn.z, spawn.positionKnown, spawn.yaw, spawn.pitch);
|
||||||
|
bracketAfter(event, player, tracker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == PacketType.Play.Server.REL_ENTITY_MOVE || type == PacketType.Play.Server.REL_ENTITY_MOVE_LOOK
|
||||||
|
|| type == PacketType.Play.Server.ENTITY_LOOK) {
|
||||||
|
boolean look = type != PacketType.Play.Server.REL_ENTITY_MOVE;
|
||||||
|
boolean move = type != PacketType.Play.Server.ENTITY_LOOK;
|
||||||
|
EntityPacketReader.Relative relative = EntityPacketReader.readRelative(packet, move, look);
|
||||||
|
if (relative != null) {
|
||||||
|
tracker.onRelativeMove(player, relative.id, relative.dx, relative.dy, relative.dz, relative.hasPosition, relative.yaw, relative.pitch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == PacketType.Play.Server.ENTITY_TELEPORT || type == PacketType.Play.Server.ENTITY_POSITION_SYNC) {
|
||||||
|
EntityPacketReader.Absolute absolute = EntityPacketReader.readAbsolute(packet);
|
||||||
|
if (absolute != null) {
|
||||||
|
tracker.onAbsoluteMove(player, absolute.id, absolute.x, absolute.y, absolute.z,
|
||||||
|
absolute.relativeX, absolute.relativeY, absolute.relativeZ, absolute.yaw, absolute.pitch,
|
||||||
|
type == PacketType.Play.Server.ENTITY_TELEPORT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == PacketType.Play.Server.ENTITY_DESTROY) {
|
||||||
|
tracker.onDestroy(player, EntityPacketReader.readDestroy(packet));
|
||||||
|
}
|
||||||
|
else if (type == PacketType.Play.Server.ENTITY_METADATA) {
|
||||||
|
Integer id = packet.getIntegers().readSafely(0);
|
||||||
|
if (id != null) {
|
||||||
|
tracker.onMetadata(player, id.intValue(), EntityPacketReader.readSharedFlags(packet),
|
||||||
|
EntityPacketReader.readFireworkAttached(packet));
|
||||||
|
bracketAfter(event, player, tracker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == PacketType.Play.Server.ENTITY_STATUS) {
|
||||||
|
Integer id = packet.getIntegers().readSafely(0);
|
||||||
|
Byte status = packet.getBytes().readSafely(0);
|
||||||
|
if (id != null && status != null) {
|
||||||
|
tracker.onStatus(player, id.intValue(), status.byteValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == PacketType.Play.Server.MOUNT) {
|
||||||
|
EntityPacketReader.Mount mount = EntityPacketReader.readMount(packet);
|
||||||
|
if (mount != null) {
|
||||||
|
tracker.onMount(player, mount.vehicleId, mount.passengers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == PacketType.Play.Server.ATTACH_ENTITY && !ServerVersion.isAtLeast("1.9")) {
|
||||||
|
EntityPacketReader.Attach attach = EntityPacketReader.readAttach(packet);
|
||||||
|
if (attach != null) {
|
||||||
|
tracker.onLegacyAttach(player, attach.passengerId, attach.vehicleId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable throwable) {
|
||||||
|
fail(throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bracketAfter(PacketEvent event, final Player player, final ClientEntityTracker tracker) {
|
||||||
|
if (!tracker.consumeBracketPing()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final Plugin owner = getPlugin();
|
||||||
|
try {
|
||||||
|
event.getNetworkMarker().addPostListener(new PacketPostListener() {
|
||||||
|
@Override
|
||||||
|
public Plugin getPlugin() {
|
||||||
|
return owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPostEvent(PacketEvent sent) {
|
||||||
|
tracker.bracketPing(player);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {
|
||||||
|
tracker.bracketPing(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ClientEntity.Kind kind(PacketType type, String typeName) {
|
||||||
|
if (type == PacketType.Play.Server.NAMED_ENTITY_SPAWN || "PLAYER".equals(typeName)) {
|
||||||
|
return ClientEntity.Kind.PLAYER;
|
||||||
|
}
|
||||||
|
if (type == PacketType.Play.Server.SPAWN_ENTITY_LIVING) {
|
||||||
|
return ClientEntity.Kind.LIVING;
|
||||||
|
}
|
||||||
|
if (type == PacketType.Play.Server.SPAWN_ENTITY_PAINTING) {
|
||||||
|
return ClientEntity.Kind.PAINTING;
|
||||||
|
}
|
||||||
|
return ClientEntity.Kind.OBJECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ClientEntityTracker tracker(Player player) {
|
||||||
|
if (player == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
IPlayerData data = DataManager.getPlayerDataSafe(player);
|
||||||
|
if (data == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return data.getGenericInstance(ClientEntityTracker.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean accept(PacketEvent event) {
|
||||||
|
try {
|
||||||
|
if (event.isPlayerTemporary()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (NoSuchMethodError ignored) {}
|
||||||
|
return event.getPlayer() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fail(Throwable throwable) {
|
||||||
|
if (failures++ < 3) {
|
||||||
|
StaticLog.logWarning("Entity tracker packet listener failed: " + throwable.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+84
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package fr.neatmonster.nocheatplus.checks.net.protocollib;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.PacketType;
|
||||||
|
import com.comphenix.protocol.events.ListenerPriority;
|
||||||
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
|
import com.comphenix.protocol.reflect.StructureModifier;
|
||||||
|
import com.comphenix.protocol.wrappers.AutoWrapper;
|
||||||
|
|
||||||
|
import fr.neatmonster.nocheatplus.checks.net.NetData;
|
||||||
|
import fr.neatmonster.nocheatplus.checks.net.model.DataPacketInput;
|
||||||
|
import fr.neatmonster.nocheatplus.players.DataManager;
|
||||||
|
import fr.neatmonster.nocheatplus.players.IPlayerData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen to the Steer_vehicle packet for extrapolating input information sent by the player (1.21.2+)
|
||||||
|
* The packet is actually named "Player_Input" in the Minecraft protocol, but ProtocolLib uses the former name for compatibility.
|
||||||
|
*/
|
||||||
|
public class InputsAdapter extends BaseAdapter {
|
||||||
|
|
||||||
|
private static AutoWrapper<DataPacketInput> INPUT_WRAPPER;
|
||||||
|
|
||||||
|
private static PacketType[] initPacketTypes() {
|
||||||
|
final List<PacketType> types = new LinkedList<PacketType>(Arrays.asList(PacketType.Play.Client.STEER_VEHICLE));
|
||||||
|
return types.toArray(new PacketType[types.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputsAdapter(Plugin plugin) {
|
||||||
|
super(plugin, ListenerPriority.MONITOR, initPacketTypes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPacketReceiving(final PacketEvent event) {
|
||||||
|
handleInputPacket(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleInputPacket(PacketEvent event) {
|
||||||
|
try {
|
||||||
|
if (event.isPlayerTemporary()) return;
|
||||||
|
}
|
||||||
|
catch (NoSuchMethodError e) {
|
||||||
|
if (event.getPlayer() == null) {
|
||||||
|
counters.add(ProtocolLibComponent.idNullPlayer, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (DataManager.getPlayerDataSafe(event.getPlayer()) == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final Player player = event.getPlayer();
|
||||||
|
final PacketContainer packet = event.getPacket();
|
||||||
|
final IPlayerData pData = DataManager.getPlayerData(player);
|
||||||
|
final NetData data = pData.getGenericInstance(NetData.class);
|
||||||
|
// Instead of a primitive, we're being handed the full Input object from Mojang’s code.
|
||||||
|
StructureModifier<Object> objs = packet.getModifier().withType(Object.class);
|
||||||
|
Object raw = objs.read(0);
|
||||||
|
if (INPUT_WRAPPER == null) {
|
||||||
|
INPUT_WRAPPER = AutoWrapper.wrap(DataPacketInput.class, raw.getClass());
|
||||||
|
}
|
||||||
|
final DataPacketInput in = INPUT_WRAPPER.wrap(raw);
|
||||||
|
data.addInputQueue(in);
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
-9
@@ -44,13 +44,11 @@ public class KeepAliveAdapter extends BaseAdapter {
|
|||||||
private final KeepAliveFrequency frequencyCheck = new KeepAliveFrequency();
|
private final KeepAliveFrequency frequencyCheck = new KeepAliveFrequency();
|
||||||
|
|
||||||
public KeepAliveAdapter(Plugin plugin) {
|
public KeepAliveAdapter(Plugin plugin) {
|
||||||
super(plugin, ListenerPriority.LOW, PacketType.Play.Client.KEEP_ALIVE);
|
super(plugin, ListenerPriority.LOW, PacketType.Play.Client.KEEP_ALIVE, PacketType.Play.Server.KEEP_ALIVE);
|
||||||
this.checkType = CheckType.NET_KEEPALIVEFREQUENCY;
|
this.checkType = CheckType.NET_KEEPALIVEFREQUENCY;
|
||||||
// Add feature tags for checks.
|
// Add feature tags for checks.
|
||||||
if (NCPAPIProvider.getNoCheatPlusAPI().getWorldDataManager().isActiveAnywhere(
|
if (NCPAPIProvider.getNoCheatPlusAPI().getWorldDataManager().isActiveAnywhere(CheckType.NET_KEEPALIVEFREQUENCY)) {
|
||||||
CheckType.NET_KEEPALIVEFREQUENCY)) {
|
NCPAPIProvider.getNoCheatPlusAPI().addFeatureTags("checks", Arrays.asList(KeepAliveFrequency.class.getSimpleName()));
|
||||||
NCPAPIProvider.getNoCheatPlusAPI().addFeatureTags(
|
|
||||||
"checks", Arrays.asList(KeepAliveFrequency.class.getSimpleName()));
|
|
||||||
}
|
}
|
||||||
NCPAPIProvider.getNoCheatPlusAPI().addComponent(frequencyCheck);
|
NCPAPIProvider.getNoCheatPlusAPI().addComponent(frequencyCheck);
|
||||||
}
|
}
|
||||||
@@ -72,11 +70,12 @@ public class KeepAliveAdapter extends BaseAdapter {
|
|||||||
final IPlayerData pData = DataManager.getPlayerDataSafe(player);
|
final IPlayerData pData = DataManager.getPlayerDataSafe(player);
|
||||||
if (pData == null) return;
|
if (pData == null) return;
|
||||||
final NetData data = pData.getGenericInstance(NetData.class);
|
final NetData data = pData.getGenericInstance(NetData.class);
|
||||||
|
recordKeepAlivePacketDetails(event, data, time);
|
||||||
data.lastKeepAliveTime = time;
|
data.lastKeepAliveTime = time;
|
||||||
final NetConfig cc = pData.getGenericInstance(NetConfig.class);
|
final NetConfig cc = pData.getGenericInstance(NetConfig.class);
|
||||||
|
|
||||||
// Run check(s).
|
// Run check(s).
|
||||||
// TODO: Match vs. outgoing keep alive requests.
|
// KeepAlive model: outgoing server ids are recorded in onPacketSending; matching replies are expected.
|
||||||
// TODO: Better modeling of actual packet sequences (flying vs. keep alive vs. request/ping).
|
// TODO: Better modeling of actual packet sequences (flying vs. keep alive vs. request/ping).
|
||||||
// TODO: Better integration with god-mode check / trigger reset ndt.
|
// TODO: Better integration with god-mode check / trigger reset ndt.
|
||||||
if (frequencyCheck.isEnabled(player, pData)
|
if (frequencyCheck.isEnabled(player, pData)
|
||||||
@@ -85,9 +84,25 @@ public class KeepAliveAdapter extends BaseAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void recordKeepAlivePacketDetails(final PacketEvent event, final NetData data, final long time) {
|
||||||
public void onPacketSending(PacketEvent event) {
|
final KeepAlivePacketInfo info = KeepAlivePacketInfo.read(event.getPacket());
|
||||||
// TODO: Maybe detect if keep alive wasn't asked for + allow cancel.
|
data.recordKeepAlivePacket(time, info.idAvailable, info.id, info.idType, info.longCount, info.intCount,
|
||||||
|
event.isAsync(), Thread.currentThread().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPacketSending(PacketEvent event) {
|
||||||
|
final Player player = event.getPlayer();
|
||||||
|
if (player == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final IPlayerData pData = DataManager.getPlayerDataSafe(player);
|
||||||
|
if (pData == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final KeepAlivePacketInfo info = KeepAlivePacketInfo.read(event.getPacket());
|
||||||
|
pData.getGenericInstance(NetData.class).recordOutgoingKeepAlivePacket(System.currentTimeMillis(),
|
||||||
|
info.idAvailable, info.id, info.idType, info.longCount, info.intCount,
|
||||||
|
event.isAsync(), Thread.currentThread().getName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+74
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package fr.neatmonster.nocheatplus.checks.net.protocollib;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
|
import com.comphenix.protocol.reflect.StructureModifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProtocolLib packet-shape extraction for keepalive request/response ids.
|
||||||
|
*/
|
||||||
|
final class KeepAlivePacketInfo {
|
||||||
|
|
||||||
|
final boolean idAvailable;
|
||||||
|
final long id;
|
||||||
|
final String idType;
|
||||||
|
final int longCount;
|
||||||
|
final int intCount;
|
||||||
|
|
||||||
|
private KeepAlivePacketInfo(final boolean idAvailable, final long id, final String idType,
|
||||||
|
final int longCount, final int intCount) {
|
||||||
|
this.idAvailable = idAvailable;
|
||||||
|
this.id = id;
|
||||||
|
this.idType = idType;
|
||||||
|
this.longCount = longCount;
|
||||||
|
this.intCount = intCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
static KeepAlivePacketInfo read(final PacketContainer packet) {
|
||||||
|
boolean idAvailable = false;
|
||||||
|
long id = 0L;
|
||||||
|
String idType = "none";
|
||||||
|
int longCount = -1;
|
||||||
|
int intCount = -1;
|
||||||
|
try {
|
||||||
|
final StructureModifier<Long> longs = packet.getLongs();
|
||||||
|
longCount = longs.size();
|
||||||
|
if (longCount > 0) {
|
||||||
|
final Long value = longs.read(0);
|
||||||
|
if (value != null) {
|
||||||
|
idAvailable = true;
|
||||||
|
id = value.longValue();
|
||||||
|
idType = "long";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {}
|
||||||
|
try {
|
||||||
|
final StructureModifier<Integer> integers = packet.getIntegers();
|
||||||
|
intCount = integers.size();
|
||||||
|
if (!idAvailable && intCount > 0) {
|
||||||
|
final Integer value = integers.read(0);
|
||||||
|
if (value != null) {
|
||||||
|
idAvailable = true;
|
||||||
|
id = value.longValue();
|
||||||
|
idType = "int";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {}
|
||||||
|
return new KeepAlivePacketInfo(idAvailable, id, idType, longCount, intCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
+68
-44
@@ -34,9 +34,11 @@ import com.comphenix.protocol.reflect.StructureModifier;
|
|||||||
|
|
||||||
import fr.neatmonster.nocheatplus.NCPAPIProvider;
|
import fr.neatmonster.nocheatplus.NCPAPIProvider;
|
||||||
import fr.neatmonster.nocheatplus.checks.CheckType;
|
import fr.neatmonster.nocheatplus.checks.CheckType;
|
||||||
|
import fr.neatmonster.nocheatplus.checks.moving.MovingData;
|
||||||
import fr.neatmonster.nocheatplus.checks.net.FlyingFrequency;
|
import fr.neatmonster.nocheatplus.checks.net.FlyingFrequency;
|
||||||
import fr.neatmonster.nocheatplus.checks.net.Moving;
|
import fr.neatmonster.nocheatplus.checks.net.Moving;
|
||||||
import fr.neatmonster.nocheatplus.checks.net.NetConfig;
|
import fr.neatmonster.nocheatplus.checks.net.NetConfig;
|
||||||
|
import fr.neatmonster.nocheatplus.checks.net.entity.ClientEntityTracker;
|
||||||
import fr.neatmonster.nocheatplus.checks.net.NetData;
|
import fr.neatmonster.nocheatplus.checks.net.NetData;
|
||||||
import fr.neatmonster.nocheatplus.checks.net.WrongTurn;
|
import fr.neatmonster.nocheatplus.checks.net.WrongTurn;
|
||||||
import fr.neatmonster.nocheatplus.checks.net.model.DataPacketFlying;
|
import fr.neatmonster.nocheatplus.checks.net.model.DataPacketFlying;
|
||||||
@@ -51,6 +53,7 @@ import fr.neatmonster.nocheatplus.players.IPlayerData;
|
|||||||
import fr.neatmonster.nocheatplus.time.monotonic.Monotonic;
|
import fr.neatmonster.nocheatplus.time.monotonic.Monotonic;
|
||||||
import fr.neatmonster.nocheatplus.utilities.CheckUtils;
|
import fr.neatmonster.nocheatplus.utilities.CheckUtils;
|
||||||
import fr.neatmonster.nocheatplus.utilities.StringUtil;
|
import fr.neatmonster.nocheatplus.utilities.StringUtil;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.TickTask;
|
||||||
import fr.neatmonster.nocheatplus.utilities.ds.count.ActionFrequency;
|
import fr.neatmonster.nocheatplus.utilities.ds.count.ActionFrequency;
|
||||||
import fr.neatmonster.nocheatplus.utilities.location.LocUtil;
|
import fr.neatmonster.nocheatplus.utilities.location.LocUtil;
|
||||||
|
|
||||||
@@ -62,7 +65,8 @@ import fr.neatmonster.nocheatplus.utilities.location.LocUtil;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class MovingFlying extends BaseAdapter {
|
public class MovingFlying extends BaseAdapter {
|
||||||
private static final boolean isServerAtLeast1_21_3 = ServerVersion.compareMinecraftVersion("1.21.3") >= 0;
|
|
||||||
|
private static final boolean isServerAtLeast1_21_3 = ServerVersion.isAtLeast("1.21.3");
|
||||||
// Setup for flying packets.
|
// Setup for flying packets.
|
||||||
public static final int indexOnGround = 0;
|
public static final int indexOnGround = 0;
|
||||||
public static final int indexhorizontalCollision = isServerAtLeast1_21_3 ? 1 : 0;
|
public static final int indexhorizontalCollision = isServerAtLeast1_21_3 ? 1 : 0;
|
||||||
@@ -71,26 +75,34 @@ public class MovingFlying extends BaseAdapter {
|
|||||||
public static final int indexX = 0;
|
public static final int indexX = 0;
|
||||||
public static final int indexY = 1;
|
public static final int indexY = 1;
|
||||||
public static final int indexZ = 2;
|
public static final int indexZ = 2;
|
||||||
/** 1.7.10 */
|
public static final int indexStance = 3; // 1.7.10
|
||||||
public static final int indexStance = 3;
|
|
||||||
public static final int indexYaw = 0;
|
public static final int indexYaw = 0;
|
||||||
public static final int indexPitch = 1;
|
public static final int indexPitch = 1;
|
||||||
|
|
||||||
|
// Setup for teleport accept packet.
|
||||||
|
private static PacketType confirmTeleportType;
|
||||||
|
private static boolean acceptConfirmTeleportPackets;
|
||||||
|
|
||||||
private final Plugin plugin = Bukkit.getPluginManager().getPlugin("NoCheatPlus");
|
private final Plugin plugin = Bukkit.getPluginManager().getPlugin("NoCheatPlus");
|
||||||
private static PacketType[] initPacketTypes() {
|
private static PacketType[] initPacketTypes() {
|
||||||
final List<PacketType> types = new LinkedList<PacketType>(Arrays.asList(PacketType.Play.Client.LOOK, PacketType.Play.Client.POSITION, PacketType.Play.Client.POSITION_LOOK));
|
final List<PacketType> types = new LinkedList<PacketType>(Arrays.asList(PacketType.Play.Client.LOOK, PacketType.Play.Client.POSITION, PacketType.Play.Client.POSITION_LOOK));
|
||||||
if (ServerVersion.compareMinecraftVersion("1.17") < 0) {
|
if (ServerVersion.isLowerThan("1.17")) {
|
||||||
types.add(PacketType.Play.Client.FLYING);
|
types.add(PacketType.Play.Client.FLYING);
|
||||||
StaticLog.logInfo("Add listener for legacy PlayInFlying packet.");
|
StaticLog.logInfo("Add listener for legacy PlayInFlying packet.");
|
||||||
}
|
}
|
||||||
else types.add(PacketType.Play.Client.GROUND);
|
else types.add(PacketType.Play.Client.GROUND);
|
||||||
// Add confirm teleport.
|
// Add confirm teleport.
|
||||||
// PacketPlayInTeleportAccept
|
// PacketPlayInTeleportAccept
|
||||||
PacketType confirmType = ProtocolLibComponent.findPacketTypeByName(Protocol.PLAY, Sender.CLIENT, "TeleportAccept");
|
confirmTeleportType = ProtocolLibComponent.findPacketTypeByName(Protocol.PLAY, Sender.CLIENT, "AcceptTeleportation");
|
||||||
if (confirmType != null && ServerVersion.compareMinecraftVersion("1.9") >= 0) {
|
if (confirmTeleportType == null) { // Fallback check for the old packet name.
|
||||||
StaticLog.logInfo("Confirm teleport packet available (via name): " + confirmType);
|
confirmTeleportType = ProtocolLibComponent.findPacketTypeByName(Protocol.PLAY, Sender.CLIENT, "TeleportAccept");
|
||||||
types.add(confirmType);
|
|
||||||
}
|
}
|
||||||
|
if (confirmTeleportType != null && ServerVersion.isAtLeast("1.9")) {
|
||||||
|
StaticLog.logInfo("Confirm teleport packet available (via name): " + confirmTeleportType);
|
||||||
|
types.add(confirmTeleportType);
|
||||||
|
acceptConfirmTeleportPackets = true;
|
||||||
|
}
|
||||||
|
else acceptConfirmTeleportPackets = false;
|
||||||
return types.toArray(new PacketType[types.size()]);
|
return types.toArray(new PacketType[types.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +114,6 @@ public class MovingFlying extends BaseAdapter {
|
|||||||
private final WrongTurn wrongTurn = new WrongTurn();
|
private final WrongTurn wrongTurn = new WrongTurn();
|
||||||
|
|
||||||
private final int idFlying = counters.registerKey("packet.flying");
|
private final int idFlying = counters.registerKey("packet.flying");
|
||||||
|
|
||||||
private final int idAsyncFlying = counters.registerKey("packet.flying.asynchronous");
|
private final int idAsyncFlying = counters.registerKey("packet.flying.asynchronous");
|
||||||
|
|
||||||
/** If a packet can't be parsed, this time stamp is set for occasional logging. */
|
/** If a packet can't be parsed, this time stamp is set for occasional logging. */
|
||||||
@@ -111,11 +122,7 @@ public class MovingFlying extends BaseAdapter {
|
|||||||
private long packetMismatchLogFrequency = 60000;
|
private long packetMismatchLogFrequency = 60000;
|
||||||
|
|
||||||
private final HashSet<PACKET_CONTENT> validContent = new LinkedHashSet<PACKET_CONTENT>();
|
private final HashSet<PACKET_CONTENT> validContent = new LinkedHashSet<PACKET_CONTENT>();
|
||||||
|
|
||||||
private final PacketType confirmTeleportType = ProtocolLibComponent.findPacketTypeByName(Protocol.PLAY, Sender.CLIENT, "TeleportAccept");
|
|
||||||
|
|
||||||
private boolean acceptConfirmTeleportPackets = confirmTeleportType != null;
|
|
||||||
|
|
||||||
public MovingFlying(Plugin plugin) {
|
public MovingFlying(Plugin plugin) {
|
||||||
// PacketPlayInFlying[3, legacy: 10]
|
// PacketPlayInFlying[3, legacy: 10]
|
||||||
super(plugin, ListenerPriority.LOW, initPacketTypes());
|
super(plugin, ListenerPriority.LOW, initPacketTypes());
|
||||||
@@ -219,28 +226,28 @@ public class MovingFlying extends BaseAdapter {
|
|||||||
// Interpret the packet content.
|
// Interpret the packet content.
|
||||||
final DataPacketFlying packetData = interpretPacket(event, time);
|
final DataPacketFlying packetData = interpretPacket(event, time);
|
||||||
// Early return tests, if the packet can be interpreted.
|
// Early return tests, if the packet can be interpreted.
|
||||||
boolean skipFlyingFrequency = false;
|
boolean skipChecks = false;
|
||||||
if (packetData != null) {
|
if (packetData != null) {
|
||||||
// Prevent processing packets with obviously malicious content.
|
// Prevent processing packets with obviously malicious content.
|
||||||
if (isInvalidContent(packetData)) {
|
if (isInvalidContent(packetData)) {
|
||||||
// TODO: extra actions: log and kick (cancel state is not evaluated)
|
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
|
// Do request and improbable update here for good measure.
|
||||||
|
TickTask.requestImprobableUpdate(player.getUniqueId(), 1.0f);
|
||||||
if (pData.isDebugActive(this.checkType)) {
|
if (pData.isDebugActive(this.checkType)) {
|
||||||
debug(player, "Incoming packet, cancel due to malicious content: " + packetData.toString());
|
debug(player, "Incoming flying packet, cancel due to malicious content: " + packetData.toString());
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(data.teleportQueue.processAck(packetData)) {
|
switch (data.teleportQueue.processAck(packetData)) {
|
||||||
case WAITING: {
|
case WAITING: {
|
||||||
if (pData.isDebugActive(this.checkType)) {
|
if (pData.isDebugActive(this.checkType)) {
|
||||||
debug(player, "Incoming packet, still waiting for ACK on outgoing position.");
|
debug(player, "Incoming flying packet, still waiting for ACK on outgoing position.");
|
||||||
}
|
}
|
||||||
if (confirmTeleportType != null && cc.supersededFlyingCancelWaiting) {
|
if (confirmTeleportType != null && cc.supersededFlyingCancelWaiting) {
|
||||||
// Don't add to the flying queue for now (assumed invalid).
|
// Don't add to the flying queue for now (assumed invalid).
|
||||||
final AckReference ackReference = data.teleportQueue.getLastAckReference();
|
final AckReference ackReference = data.teleportQueue.getLastAckReference();
|
||||||
if (ackReference.lastOutgoingId != Integer.MIN_VALUE
|
if (ackReference.lastOutgoingId != Integer.MIN_VALUE && ackReference.lastOutgoingId != ackReference.maxConfirmedId) {
|
||||||
&& ackReference.lastOutgoingId != ackReference.maxConfirmedId) {
|
|
||||||
// Still waiting for a 'confirm teleport' packet. More or less safe to cancel this out.
|
// Still waiting for a 'confirm teleport' packet. More or less safe to cancel this out.
|
||||||
/*
|
/*
|
||||||
* TODO: The actual issue with this, apart from
|
* TODO: The actual issue with this, apart from
|
||||||
@@ -255,18 +262,17 @@ public class MovingFlying extends BaseAdapter {
|
|||||||
* position-correction teleporting the server does, for
|
* position-correction teleporting the server does, for
|
||||||
* the cases a plugin can handle.
|
* the cases a plugin can handle.
|
||||||
*/
|
*/
|
||||||
// TODO: Timeout -> either skip cancel or schedule a set back (to last valid pos or other).
|
// See todo below on if (cancel) {...}
|
||||||
// TODO: Config?
|
cancel = true;
|
||||||
cancel = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ACK: {
|
case ACK: {
|
||||||
// Skip processing ACK packets, no cancel.
|
// Skip processing ACK packets, no cancel.
|
||||||
skipFlyingFrequency = true;
|
skipChecks = true;
|
||||||
if (pData.isDebugActive(this.checkType)) {
|
if (pData.isDebugActive(this.checkType)) {
|
||||||
debug(player, "Incoming packet, interpret as ACK for outgoing position.");
|
debug(player, "Incoming flying packet, interpret as ACK for outgoing position. Skip checks.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
@@ -276,36 +282,53 @@ public class MovingFlying extends BaseAdapter {
|
|||||||
}
|
}
|
||||||
// Add as valid packet (exclude invalid coordinates etc. for now).
|
// Add as valid packet (exclude invalid coordinates etc. for now).
|
||||||
validContent.add(packetData.getSimplifiedContentType());
|
validContent.add(packetData.getSimplifiedContentType());
|
||||||
|
if (packetData.hasPos && !skipChecks) {
|
||||||
|
ClientEntityTracker pushTracker = pData.getGenericInstance(ClientEntityTracker.class);
|
||||||
|
if (pushTracker != null) {
|
||||||
|
pushTracker.notePositionPacket(packetData.getX(), packetData.getY(), packetData.getZ());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actual packet frequency check.
|
// Actual packet frequency check.
|
||||||
// TODO: Consider using the NetStatic check.
|
// TODO: Consider using the NetStatic check.
|
||||||
if (!cancel && !skipFlyingFrequency
|
if (!cancel && !skipChecks
|
||||||
&& flyingFrequency.check(player, packetData, time, data, cc, pData)
|
&& flyingFrequency.check(player, packetData, time, data, cc, pData)
|
||||||
&& !pData.hasBypass(CheckType.NET_FLYINGFREQUENCY, player)) {
|
&& !pData.hasBypass(CheckType.NET_FLYINGFREQUENCY, player)) {
|
||||||
cancel = true;
|
cancel = true;
|
||||||
// Also request a set back here
|
|
||||||
data.requestSetBack(player, this, plugin, CheckType.NET_FLYINGFREQUENCY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// More packet checks.
|
// More packet checks.
|
||||||
if (!cancel && !skipFlyingFrequency && pData.isCheckActive(CheckType.NET_MOVING, player)
|
if (!cancel && !skipChecks && pData.isCheckActive(CheckType.NET_MOVING, player)
|
||||||
&& moving.check(player, packetData, data, cc, pData, plugin)
|
&& moving.check(player, packetData, data, cc, pData, plugin)
|
||||||
&& !pData.hasBypass(CheckType.NET_MOVING, player)) {
|
&& !pData.hasBypass(CheckType.NET_MOVING, player)) {
|
||||||
cancel = true;
|
cancel = true;
|
||||||
// Also request a set back here
|
|
||||||
data.requestSetBack(player, this, plugin, CheckType.NET_MOVING);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Illegal pitch check
|
// Illegal pitch check
|
||||||
if (!cancel && pData.isCheckActive(CheckType.NET_WRONGTURN, player)
|
if (!cancel && pData.isCheckActive(CheckType.NET_WRONGTURN, player)
|
||||||
&& wrongTurn.check(player, packetData.getPitch(), data, cc)
|
&& wrongTurn.check(player, packetData.getPitch(), data, cc)
|
||||||
&& !pData.hasBypass(CheckType.NET_WRONGTURN, player)) {
|
&& !pData.hasBypass(CheckType.NET_WRONGTURN, player)) {
|
||||||
cancel = true; // Is it a good idea to cancel or should we just reset the players pitch?
|
cancel = true; // Is it a good idea to cancel or should we just reset the players pitch?
|
||||||
}
|
}
|
||||||
|
if (!cancel && pData.isCheckActive(CheckType.MOVING_NOFALL, player)) {
|
||||||
|
final MovingData mData = pData.getGenericInstance(MovingData.class);
|
||||||
|
final float noFallFallDistance = mData.noFallFallDistance;
|
||||||
|
if (mData.requestTrueForGround) {
|
||||||
|
// If damage has already dealt
|
||||||
|
if (noFallFallDistance > 0.0) {
|
||||||
|
player.setFallDistance(noFallFallDistance);
|
||||||
|
event.getPacket().getBooleans().write(indexOnGround, mData.requestTrueForGround);
|
||||||
|
}
|
||||||
|
mData.requestTrueForGround = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Process cancel and debug log.
|
// Process cancel and debug log.
|
||||||
if (cancel) {
|
if (cancel) {
|
||||||
|
/*
|
||||||
|
* TODO: Test if this can fix packet fly exploits; it will also cause set backs on flying packets awaiting ACK(s)
|
||||||
|
* Previously, NCP would simply cancel these flying packets out, without really restricting movement.
|
||||||
|
*/
|
||||||
|
data.requestSetBack(player, this, plugin, CheckType.NET);
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,9 +336,15 @@ public class MovingFlying extends BaseAdapter {
|
|||||||
debug(player, (packetData == null ? "(Incompatible data)" : packetData.toString()) + (event.isCancelled() ? " CANCEL" : ""));
|
debug(player, (packetData == null ? "(Incompatible data)" : packetData.toString()) + (event.isCancelled() ? " CANCEL" : ""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given {@link DataPacketFlying} contains invalid position or look data. <br>
|
||||||
|
* Delegates to {@link LocUtil#isBadCoordinate(double...)} and {@link LocUtil#isBadCoordinate(float...)}.<br>
|
||||||
|
* If this returns true, invalid packets won't be added to the flying queue, and will be cancelled immediately.
|
||||||
|
*
|
||||||
|
* @param packetData the flying packet data to validate
|
||||||
|
* @return true if the packet contains invalid position or look data, false otherwise
|
||||||
|
*/
|
||||||
private boolean isInvalidContent(final DataPacketFlying packetData) {
|
private boolean isInvalidContent(final DataPacketFlying packetData) {
|
||||||
if (packetData.hasPos && LocUtil.isBadCoordinate(packetData.getX(), packetData.getY(), packetData.getZ())) {
|
if (packetData.hasPos && LocUtil.isBadCoordinate(packetData.getX(), packetData.getY(), packetData.getZ())) {
|
||||||
return true;
|
return true;
|
||||||
@@ -328,16 +357,11 @@ public class MovingFlying extends BaseAdapter {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Interpret the packet content and do with it whatever is suitable.
|
* Interpret the packet content and do with it whatever is suitable.
|
||||||
* @param player
|
|
||||||
* @param event
|
* @param event
|
||||||
* @param allScore
|
|
||||||
* @param time
|
* @param time
|
||||||
* @param data
|
|
||||||
* @param cc
|
|
||||||
* @return Packet data if successful, or null on packet mismatch.
|
* @return Packet data if successful, or null on packet mismatch.
|
||||||
*/
|
*/
|
||||||
private DataPacketFlying interpretPacket(final PacketEvent event, final long time) {
|
private DataPacketFlying interpretPacket(final PacketEvent event, final long time) {
|
||||||
|
|
||||||
final PacketContainer packet = event.getPacket();
|
final PacketContainer packet = event.getPacket();
|
||||||
final List<Boolean> booleans = packet.getBooleans().getValues();
|
final List<Boolean> booleans = packet.getBooleans().getValues();
|
||||||
if (booleans.size() != (isServerAtLeast1_21_3 ? 4 : 3)) {
|
if (booleans.size() != (isServerAtLeast1_21_3 ? 4 : 3)) {
|
||||||
@@ -345,7 +369,7 @@ public class MovingFlying extends BaseAdapter {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final boolean onGround = booleans.get(MovingFlying.indexOnGround).booleanValue();
|
final boolean onGround = booleans.get(MovingFlying.indexOnGround).booleanValue();
|
||||||
final boolean horizontalCollision = isServerAtLeast1_21_3 ? booleans.get(MovingFlying.indexhorizontalCollision).booleanValue() : false;
|
final boolean horizontalCollision = isServerAtLeast1_21_3 && booleans.get(MovingFlying.indexhorizontalCollision).booleanValue();
|
||||||
final boolean hasPos = booleans.get(MovingFlying.indexhasPos).booleanValue();
|
final boolean hasPos = booleans.get(MovingFlying.indexhasPos).booleanValue();
|
||||||
final boolean hasLook = booleans.get(MovingFlying.indexhasLook).booleanValue();
|
final boolean hasLook = booleans.get(MovingFlying.indexhasLook).booleanValue();
|
||||||
|
|
||||||
|
|||||||
+1
@@ -117,6 +117,7 @@ public class OutgoingPosition extends BaseAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final CountableLocation packetData = data.teleportQueue.onOutgoingTeleport(x, y, z, yaw, pitch, teleportId);
|
final CountableLocation packetData = data.teleportQueue.onOutgoingTeleport(x, y, z, yaw, pitch, teleportId);
|
||||||
|
data.recordOutgoingPosition(x, y, z, yaw, pitch, teleportId, packetData != null, time);
|
||||||
if (packetData == null) {
|
if (packetData == null) {
|
||||||
// Add counter for untracked (by Bukkit API) outgoing teleport.
|
// Add counter for untracked (by Bukkit API) outgoing teleport.
|
||||||
// TODO: There may be other cases which are indicated by Bukkit API events.
|
// TODO: There may be other cases which are indicated by Bukkit API events.
|
||||||
|
|||||||
+36
-18
@@ -24,6 +24,7 @@ import org.bukkit.entity.Player;
|
|||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||||
import org.bukkit.event.player.PlayerRespawnEvent;
|
import org.bukkit.event.player.PlayerRespawnEvent;
|
||||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
@@ -35,11 +36,12 @@ import com.comphenix.protocol.ProtocolLibrary;
|
|||||||
import com.comphenix.protocol.ProtocolManager;
|
import com.comphenix.protocol.ProtocolManager;
|
||||||
import com.comphenix.protocol.events.PacketAdapter;
|
import com.comphenix.protocol.events.PacketAdapter;
|
||||||
|
|
||||||
//import com.viaversion.viaversion.api.Via;
|
|
||||||
|
|
||||||
import fr.neatmonster.nocheatplus.NCPAPIProvider;
|
import fr.neatmonster.nocheatplus.NCPAPIProvider;
|
||||||
import fr.neatmonster.nocheatplus.checks.CheckType;
|
import fr.neatmonster.nocheatplus.checks.CheckType;
|
||||||
import fr.neatmonster.nocheatplus.checks.net.NetData;
|
import fr.neatmonster.nocheatplus.checks.net.NetData;
|
||||||
|
import fr.neatmonster.nocheatplus.checks.net.entity.ClientEntityTracker;
|
||||||
|
import fr.neatmonster.nocheatplus.checks.net.transaction.TransactionChannel;
|
||||||
|
import fr.neatmonster.nocheatplus.compat.BridgeMisc;
|
||||||
import fr.neatmonster.nocheatplus.compat.SchedulerHelper;
|
import fr.neatmonster.nocheatplus.compat.SchedulerHelper;
|
||||||
import fr.neatmonster.nocheatplus.compat.versions.ServerVersion;
|
import fr.neatmonster.nocheatplus.compat.versions.ServerVersion;
|
||||||
import fr.neatmonster.nocheatplus.components.NoCheatPlusAPI;
|
import fr.neatmonster.nocheatplus.components.NoCheatPlusAPI;
|
||||||
@@ -107,6 +109,9 @@ public class ProtocolLibComponent implements IDisableListener, INotifyReload, Jo
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StaticLog.logInfo("Adding packet level hooks for ProtocolLib (MC " + ProtocolLibrary.getProtocolManager().getMinecraftVersion().getVersion() + ")...");
|
StaticLog.logInfo("Adding packet level hooks for ProtocolLib (MC " + ProtocolLibrary.getProtocolManager().getMinecraftVersion().getVersion() + ")...");
|
||||||
|
// Transaction clock first, so the end-of-tick ping is registered before entity tracking.
|
||||||
|
register("fr.neatmonster.nocheatplus.checks.net.protocollib.TransactionAdapter", plugin);
|
||||||
|
register("fr.neatmonster.nocheatplus.checks.net.protocollib.EntityActivityAdapter", plugin);
|
||||||
//Special purpose.
|
//Special purpose.
|
||||||
if (ConfigManager.isTrueForAnyConfig(ConfPaths.NET + ConfPaths.SUB_DEBUG) || ConfigManager.isTrueForAnyConfig(ConfPaths.CHECKS_DEBUG) ) {
|
if (ConfigManager.isTrueForAnyConfig(ConfPaths.NET + ConfPaths.SUB_DEBUG) || ConfigManager.isTrueForAnyConfig(ConfPaths.CHECKS_DEBUG) ) {
|
||||||
// (Debug logging. Only activates if debug is set for checks or checks.net, not on the fly.)
|
// (Debug logging. Only activates if debug is set for checks or checks.net, not on the fly.)
|
||||||
@@ -121,9 +126,7 @@ public class ProtocolLibComponent implements IDisableListener, INotifyReload, Jo
|
|||||||
// (Also sets lastKeepAliveTime, if enabled.)
|
// (Also sets lastKeepAliveTime, if enabled.)
|
||||||
register("fr.neatmonster.nocheatplus.checks.net.protocollib.UseEntityAdapter", plugin);
|
register("fr.neatmonster.nocheatplus.checks.net.protocollib.UseEntityAdapter", plugin);
|
||||||
}
|
}
|
||||||
if (worldMan.isActiveAnywhere(CheckType.NET_FLYINGFREQUENCY)
|
if (worldMan.isActiveAnywhere(CheckType.MOVING) || worldMan.isActiveAnywhere(CheckType.NET)) {
|
||||||
|| worldMan.isActiveAnywhere(CheckType.NET_MOVING)
|
|
||||||
|| worldMan.isActiveAnywhere(CheckType.NET_WRONGTURN)) {
|
|
||||||
// (Also sets lastKeepAliveTime, if enabled.)
|
// (Also sets lastKeepAliveTime, if enabled.)
|
||||||
// If any check that uses flying packets is enabled, register all
|
// If any check that uses flying packets is enabled, register all
|
||||||
register("fr.neatmonster.nocheatplus.checks.net.protocollib.MovingFlying", plugin);
|
register("fr.neatmonster.nocheatplus.checks.net.protocollib.MovingFlying", plugin);
|
||||||
@@ -141,7 +144,7 @@ public class ProtocolLibComponent implements IDisableListener, INotifyReload, Jo
|
|||||||
register("fr.neatmonster.nocheatplus.checks.net.protocollib.CatchAllAdapter", plugin);
|
register("fr.neatmonster.nocheatplus.checks.net.protocollib.CatchAllAdapter", plugin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ServerVersion.compareMinecraftVersion("1.8") >= 0) {
|
if (ServerVersion.isAtLeast("1.8")) {
|
||||||
// if (!BridgeMisc.hasIsUsingItemMethod()) {
|
// if (!BridgeMisc.hasIsUsingItemMethod()) {
|
||||||
register("fr.neatmonster.nocheatplus.checks.net.protocollib.UseItemAdapter", plugin);
|
register("fr.neatmonster.nocheatplus.checks.net.protocollib.UseItemAdapter", plugin);
|
||||||
// }
|
// }
|
||||||
@@ -149,11 +152,15 @@ public class ProtocolLibComponent implements IDisableListener, INotifyReload, Jo
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Fuck 1.7.
|
// Fuck 1.7.
|
||||||
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().info(Streams.STATUS, "Disable UseItemAdapter due to incompatibilities. Using-item status won't be monitored.");
|
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().info(Streams.STATUS, "Disable UseItemAdapter and VelocityAdapter due to incompatibilities. Using-item status and explosion velocity won't be monitored.");
|
||||||
}
|
}
|
||||||
if (worldMan.isActiveAnywhere(CheckType.NET_TOGGLEFREQUENCY)) {
|
if (worldMan.isActiveAnywhere(CheckType.NET_TOGGLEFREQUENCY)) {
|
||||||
register("fr.neatmonster.nocheatplus.checks.net.protocollib.EntityActionAdapter", plugin);
|
register("fr.neatmonster.nocheatplus.checks.net.protocollib.EntityActionAdapter", plugin);
|
||||||
}
|
}
|
||||||
|
if (BridgeMisc.hasPlayerInputEvent()) {
|
||||||
|
register("fr.neatmonster.nocheatplus.checks.net.protocollib.InputsAdapter", plugin);
|
||||||
|
}
|
||||||
|
|
||||||
if (!registeredPacketAdapters.isEmpty()) {
|
if (!registeredPacketAdapters.isEmpty()) {
|
||||||
List<String> names = new ArrayList<String>(registeredPacketAdapters.size());
|
List<String> names = new ArrayList<String>(registeredPacketAdapters.size());
|
||||||
for (PacketAdapter adapter : registeredPacketAdapters) {
|
for (PacketAdapter adapter : registeredPacketAdapters) {
|
||||||
@@ -227,6 +234,7 @@ public class ProtocolLibComponent implements IDisableListener, INotifyReload, Jo
|
|||||||
|
|
||||||
}
|
}
|
||||||
registeredPacketAdapters.clear();
|
registeredPacketAdapters.clear();
|
||||||
|
TransactionChannel.setSender(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void unregister(PacketAdapter adapter) {
|
protected static void unregister(PacketAdapter adapter) {
|
||||||
@@ -252,23 +260,20 @@ public class ProtocolLibComponent implements IDisableListener, INotifyReload, Jo
|
|||||||
public void playerJoins(final Player player) {
|
public void playerJoins(final Player player) {
|
||||||
if (!registeredPacketAdapters.isEmpty()) {
|
if (!registeredPacketAdapters.isEmpty()) {
|
||||||
DataManager.getGenericInstance(player, NetData.class).onJoin(player);
|
DataManager.getGenericInstance(player, NetData.class).onJoin(player);
|
||||||
|
DataManager.getGenericInstance(player, TransactionChannel.class).onJoin();
|
||||||
|
DataManager.getGenericInstance(player, ClientEntityTracker.class).onJoin(player);
|
||||||
}
|
}
|
||||||
SchedulerHelper.runSyncDelayedTask(Bukkit.getPluginManager().getPlugin("NoCheatPlus"), (arg) -> {
|
|
||||||
// Ensure client-version checking doesn't yield UNKNOWN if multiprotocol plugins are not present on the server
|
|
||||||
if (Bukkit.getPluginManager().getPlugin("ViaVersion") == null && Bukkit.getPluginManager().getPlugin("ProtocolSupport") == null) {
|
|
||||||
// Assume clients to match the server protocol version - We do not support protocol-hack plugins other than PS/Via
|
|
||||||
// - If not even ProtocolLib is installed, do return UNKNOWN (that's on the server's owner). Time to make NCP full-on depend on ProtocolLib?
|
|
||||||
// - If the server has protocol plugins active but doesn't have CompatNoCheatPlus, that's on them as well :)
|
|
||||||
//DataManager.getPlayerData(player).setClientVersionID(Via.getAPI().getPlayerVersion(player.getUniqueId()));
|
|
||||||
DataManager.getPlayerData(player).setClientVersionID(ProtocolLibrary.getProtocolManager().getProtocolVersion(player));
|
|
||||||
}
|
|
||||||
}, 5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void playerLeaves(final Player player) {
|
public void playerLeaves(final Player player) {
|
||||||
if (!registeredPacketAdapters.isEmpty()) {
|
if (!registeredPacketAdapters.isEmpty()) {
|
||||||
DataManager.getGenericInstance(player, NetData.class).onLeave(player);
|
DataManager.getGenericInstance(player, NetData.class).onLeave(player);
|
||||||
|
final IPlayerData pData = DataManager.getPlayerDataSafe(player);
|
||||||
|
if (pData != null) {
|
||||||
|
pData.getGenericInstance(ClientEntityTracker.class).onLeave();
|
||||||
|
pData.getGenericInstance(TransactionChannel.class).onLeave();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// (On leaving the server, client data is reset in PlayerData)
|
// (On leaving the server, client data is reset in PlayerData)
|
||||||
}
|
}
|
||||||
@@ -279,6 +284,7 @@ public class ProtocolLibComponent implements IDisableListener, INotifyReload, Jo
|
|||||||
final Player player = event.getPlayer();
|
final Player player = event.getPlayer();
|
||||||
final NetData data = DataManager.getGenericInstance(player, NetData.class);
|
final NetData data = DataManager.getGenericInstance(player, NetData.class);
|
||||||
data.onJoin(player);
|
data.onJoin(player);
|
||||||
|
DataManager.getGenericInstance(player, ClientEntityTracker.class).invalidate();
|
||||||
final Location loc = event.getRespawnLocation();
|
final Location loc = event.getRespawnLocation();
|
||||||
data.teleportQueue.onTeleportEvent(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch());
|
data.teleportQueue.onTeleportEvent(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch());
|
||||||
}
|
}
|
||||||
@@ -287,7 +293,6 @@ public class ProtocolLibComponent implements IDisableListener, INotifyReload, Jo
|
|||||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||||
public void onPlayerTeleport(final PlayerTeleportEvent event) {
|
public void onPlayerTeleport(final PlayerTeleportEvent event) {
|
||||||
if (!registeredPacketAdapters.isEmpty()) {
|
if (!registeredPacketAdapters.isEmpty()) {
|
||||||
// TODO: Might move to MovingListener.
|
|
||||||
// TODO: Might still add cancelled UNKNOWN events. TEST IT
|
// TODO: Might still add cancelled UNKNOWN events. TEST IT
|
||||||
final Location to = event.getTo();
|
final Location to = event.getTo();
|
||||||
if (to == null) {
|
if (to == null) {
|
||||||
@@ -299,7 +304,20 @@ public class ProtocolLibComponent implements IDisableListener, INotifyReload, Jo
|
|||||||
// Register expected location for comparison with outgoing packets.
|
// Register expected location for comparison with outgoing packets.
|
||||||
data.teleportQueue.onTeleportEvent(to.getX(), to.getY(), to.getZ(), to.getYaw(), to.getPitch());
|
data.teleportQueue.onTeleportEvent(to.getX(), to.getY(), to.getZ(), to.getYaw(), to.getPitch());
|
||||||
data.clearFlyingQueue();
|
data.clearFlyingQueue();
|
||||||
|
if (event.getFrom() != null && event.getFrom().getWorld() != null && to.getWorld() != null
|
||||||
|
&& !event.getFrom().getWorld().getName().equals(to.getWorld().getName())) {
|
||||||
|
pData.getGenericInstance(ClientEntityTracker.class).invalidate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
|
public void onWorldChange(final PlayerChangedWorldEvent event) {
|
||||||
|
if (!registeredPacketAdapters.isEmpty()) {
|
||||||
|
final IPlayerData pData = DataManager.getPlayerDataSafe(event.getPlayer());
|
||||||
|
if (pData != null) {
|
||||||
|
pData.getGenericInstance(ClientEntityTracker.class).invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+21
-16
@@ -21,7 +21,6 @@ import java.util.List;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Sound;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
@@ -33,7 +32,7 @@ import com.comphenix.protocol.reflect.StructureModifier;
|
|||||||
|
|
||||||
import fr.neatmonster.nocheatplus.checks.CheckType;
|
import fr.neatmonster.nocheatplus.checks.CheckType;
|
||||||
import fr.neatmonster.nocheatplus.checks.net.NetConfig;
|
import fr.neatmonster.nocheatplus.checks.net.NetConfig;
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.BridgeBukkitAPI;
|
import fr.neatmonster.nocheatplus.compat.registry.BukkitAPIAccessFactory;
|
||||||
import fr.neatmonster.nocheatplus.compat.versions.ServerVersion;
|
import fr.neatmonster.nocheatplus.compat.versions.ServerVersion;
|
||||||
import fr.neatmonster.nocheatplus.players.DataManager;
|
import fr.neatmonster.nocheatplus.players.DataManager;
|
||||||
import fr.neatmonster.nocheatplus.players.IPlayerData;
|
import fr.neatmonster.nocheatplus.players.IPlayerData;
|
||||||
@@ -46,9 +45,9 @@ public class SoundDistance extends BaseAdapter {
|
|||||||
|
|
||||||
/** Partly by debugging, partly from various sources, possibly including wrong spelling. */
|
/** Partly by debugging, partly from various sources, possibly including wrong spelling. */
|
||||||
private static final Set<String> effectNames = new HashSet<String>(Arrays.asList(
|
private static final Set<String> effectNames = new HashSet<String>(Arrays.asList(
|
||||||
////////////
|
/////////////
|
||||||
// PRE 1.9
|
// PRE 1.9 //
|
||||||
////////////
|
/////////////
|
||||||
|
|
||||||
// Weather
|
// Weather
|
||||||
"ambient.weather.thunder",
|
"ambient.weather.thunder",
|
||||||
@@ -69,7 +68,7 @@ public class SoundDistance extends BaseAdapter {
|
|||||||
"game.neutral.die", // Enderdragon 1.8.7 (debug).
|
"game.neutral.die", // Enderdragon 1.8.7 (debug).
|
||||||
|
|
||||||
//////////////////
|
//////////////////
|
||||||
// 1.9 AND LATER
|
// 1.9 AND 1.20 //
|
||||||
//////////////////
|
//////////////////
|
||||||
// Weather
|
// Weather
|
||||||
"ENTITY_LIGHTNING_IMPACT",
|
"ENTITY_LIGHTNING_IMPACT",
|
||||||
@@ -88,9 +87,22 @@ public class SoundDistance extends BaseAdapter {
|
|||||||
"ENTITY_WITHER_DEATH",
|
"ENTITY_WITHER_DEATH",
|
||||||
"ENTITY_WITHER_HURT",
|
"ENTITY_WITHER_HURT",
|
||||||
"ENTITY_WITHER_SHOOT",
|
"ENTITY_WITHER_SHOOT",
|
||||||
"ENTITY_WITHER_SPAWN"
|
"ENTITY_WITHER_SPAWN",
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
// 1.21 AND LATER //
|
||||||
|
////////////////////
|
||||||
|
// Weather
|
||||||
|
"ENTITY_LIGHTNING_BOLT_IMPACT",
|
||||||
|
"ENTITY_LIGHTNING_BOLT_THUNDER",
|
||||||
|
// Enderdragon
|
||||||
|
"ENTITY_ENDER_DRAGON_AMBIENT",
|
||||||
|
"ENTITY_ENDER_DRAGON_DEATH",
|
||||||
|
"ENTITY_DRAGON_FIREBALL_EXPLODE",
|
||||||
|
"ENTITY_ENDER_DRAGON_FLAP",
|
||||||
|
"ENTITY_ENDER_DRAGON_GROWL",
|
||||||
|
"ENTITY_ENDER_DRAGON_HURT",
|
||||||
|
"ENTITY_ENDER_DRAGON_SHOOT"
|
||||||
));
|
));
|
||||||
|
|
||||||
private final Integer idSoundEffectCancel = counters.registerKey("packet.sound.cancel");
|
private final Integer idSoundEffectCancel = counters.registerKey("packet.sound.cancel");
|
||||||
@@ -122,14 +134,7 @@ public class SoundDistance extends BaseAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSoundMonitoredLatest(final PacketContainer packetContainer) {
|
private boolean isSoundMonitoredLatest(final PacketContainer packetContainer) {
|
||||||
StructureModifier<Sound> sounds = packetContainer.getSoundEffects();
|
return BukkitAPIAccessFactory.getBukkitAccess().matchSounds(packetContainer, effectNames);
|
||||||
for (final Sound sound : sounds.getValues()) {
|
|
||||||
if (sound != null && effectNames.contains(BridgeBukkitAPI.ConvertSoundToString(sound))) {
|
|
||||||
//debug(null, "MONITOR SOUND: " + sound);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSoundMonitored(final PacketContainer packetContainer) {
|
private boolean isSoundMonitored(final PacketContainer packetContainer) {
|
||||||
|
|||||||
+206
@@ -0,0 +1,206 @@
|
|||||||
|
/*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package fr.neatmonster.nocheatplus.checks.net.protocollib;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.PacketType;
|
||||||
|
import com.comphenix.protocol.ProtocolLibrary;
|
||||||
|
import com.comphenix.protocol.events.ListenerPriority;
|
||||||
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
|
|
||||||
|
import fr.neatmonster.nocheatplus.NCPAPIProvider;
|
||||||
|
import fr.neatmonster.nocheatplus.checks.net.entity.ClientEntityTracker;
|
||||||
|
import fr.neatmonster.nocheatplus.checks.net.transaction.TransactionChannel;
|
||||||
|
import fr.neatmonster.nocheatplus.compat.BridgeMisc;
|
||||||
|
import fr.neatmonster.nocheatplus.compat.versions.ServerVersion;
|
||||||
|
import fr.neatmonster.nocheatplus.components.registry.feature.TickListener;
|
||||||
|
import fr.neatmonster.nocheatplus.logging.StaticLog;
|
||||||
|
import fr.neatmonster.nocheatplus.players.DataManager;
|
||||||
|
import fr.neatmonster.nocheatplus.players.IPlayerData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends the transaction ping and accepts only the replies this plugin wrote.
|
||||||
|
* One ping goes out at the end of each server tick so queued confirmations flush.
|
||||||
|
* Entity tracking uses that same clock.
|
||||||
|
*/
|
||||||
|
public class TransactionAdapter extends BaseAdapter implements TickListener {
|
||||||
|
|
||||||
|
private static final boolean USE_PING;
|
||||||
|
|
||||||
|
static {
|
||||||
|
boolean ping = false;
|
||||||
|
try {
|
||||||
|
ping = ServerVersion.isAtLeast("1.17") && PacketType.Play.Server.PING.isSupported();
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {}
|
||||||
|
USE_PING = ping;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PacketType[] listenTypes() {
|
||||||
|
List<PacketType> types = new ArrayList<PacketType>();
|
||||||
|
add(types, PacketType.Play.Client.PONG);
|
||||||
|
add(types, PacketType.Play.Client.TRANSACTION);
|
||||||
|
return types.toArray(new PacketType[types.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void add(List<PacketType> types, PacketType type) {
|
||||||
|
try {
|
||||||
|
if (type != null && type.isSupported()) {
|
||||||
|
types.add(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int failures;
|
||||||
|
|
||||||
|
public TransactionAdapter(Plugin plugin) {
|
||||||
|
super(plugin, ListenerPriority.LOW, listenTypes());
|
||||||
|
TransactionChannel.setSender(new TransactionChannel.Sender() {
|
||||||
|
@Override
|
||||||
|
public boolean send(Player player, int wireId, boolean legacy) {
|
||||||
|
return write(player, wireId, legacy);
|
||||||
|
}
|
||||||
|
}, USE_PING);
|
||||||
|
NCPAPIProvider.getNoCheatPlusAPI().addComponent(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean write(Player player, int wireId, boolean legacy) {
|
||||||
|
try {
|
||||||
|
PacketContainer packet;
|
||||||
|
if (!legacy) {
|
||||||
|
packet = ProtocolLibrary.getProtocolManager().createPacket(PacketType.Play.Server.PING);
|
||||||
|
packet.getIntegers().write(0, Integer.valueOf(wireId));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
packet = ProtocolLibrary.getProtocolManager().createPacket(PacketType.Play.Server.TRANSACTION);
|
||||||
|
packet.getIntegers().write(0, Integer.valueOf(0));
|
||||||
|
packet.getShorts().write(0, Short.valueOf((short) wireId));
|
||||||
|
packet.getBooleans().write(0, Boolean.FALSE);
|
||||||
|
}
|
||||||
|
// filters = false: this ping must not re-enter the listener that is sending it
|
||||||
|
ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTick(int tick, long timeLast) {
|
||||||
|
Player[] players = BridgeMisc.getOnlinePlayers();
|
||||||
|
for (int index = 0; index < players.length; index++) {
|
||||||
|
Player player = players[index];
|
||||||
|
if (player == null || !player.isOnline()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ClientEntityTracker tracker = tracker(player);
|
||||||
|
if (tracker != null) {
|
||||||
|
// Also writes this tick's ping, which confirms tasks queued on lastSent + 1.
|
||||||
|
tracker.onEndOfTick(player);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
TransactionChannel channel = channel(player);
|
||||||
|
if (channel != null) {
|
||||||
|
channel.send(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPacketReceiving(PacketEvent event) {
|
||||||
|
if (!accept(event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
TransactionChannel channel = channel(player);
|
||||||
|
if (channel == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PacketType type = event.getPacketType();
|
||||||
|
Integer id = readId(event.getPacket(), type);
|
||||||
|
if (id != null && channel.onReply(id.intValue())) {
|
||||||
|
event.setCancelled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable throwable) {
|
||||||
|
fail(throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPacketSending(PacketEvent event) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Integer readId(PacketContainer packet, PacketType type) {
|
||||||
|
try {
|
||||||
|
if (type == PacketType.Play.Client.PONG) {
|
||||||
|
return packet.getIntegers().readSafely(0);
|
||||||
|
}
|
||||||
|
Short action = packet.getShorts().readSafely(0);
|
||||||
|
return action == null ? null : Integer.valueOf(action.shortValue());
|
||||||
|
}
|
||||||
|
catch (Throwable ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TransactionChannel channel(Player player) {
|
||||||
|
if (player == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
IPlayerData data = DataManager.getPlayerDataSafe(player);
|
||||||
|
if (data == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return data.getGenericInstance(TransactionChannel.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ClientEntityTracker tracker(Player player) {
|
||||||
|
if (player == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
IPlayerData data = DataManager.getPlayerDataSafe(player);
|
||||||
|
if (data == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return data.getGenericInstance(ClientEntityTracker.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean accept(PacketEvent event) {
|
||||||
|
try {
|
||||||
|
if (event.isPlayerTemporary()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (NoSuchMethodError ignored) {}
|
||||||
|
return event.getPlayer() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fail(Throwable throwable) {
|
||||||
|
if (failures++ < 3) {
|
||||||
|
StaticLog.logWarning("Transaction ping listener failed: " + throwable.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+32
-7
@@ -32,6 +32,8 @@ import fr.neatmonster.nocheatplus.checks.CheckType;
|
|||||||
import fr.neatmonster.nocheatplus.checks.net.AttackFrequency;
|
import fr.neatmonster.nocheatplus.checks.net.AttackFrequency;
|
||||||
import fr.neatmonster.nocheatplus.checks.net.NetConfig;
|
import fr.neatmonster.nocheatplus.checks.net.NetConfig;
|
||||||
import fr.neatmonster.nocheatplus.checks.net.NetData;
|
import fr.neatmonster.nocheatplus.checks.net.NetData;
|
||||||
|
import fr.neatmonster.nocheatplus.compat.versions.ServerVersion;
|
||||||
|
import fr.neatmonster.nocheatplus.logging.StaticLog;
|
||||||
import fr.neatmonster.nocheatplus.players.DataManager;
|
import fr.neatmonster.nocheatplus.players.DataManager;
|
||||||
import fr.neatmonster.nocheatplus.players.IPlayerData;
|
import fr.neatmonster.nocheatplus.players.IPlayerData;
|
||||||
import fr.neatmonster.nocheatplus.utilities.ReflectionUtil;
|
import fr.neatmonster.nocheatplus.utilities.ReflectionUtil;
|
||||||
@@ -40,6 +42,7 @@ import fr.neatmonster.nocheatplus.utilities.ReflectionUtil;
|
|||||||
* Adapter for the UseEntity NMS packet
|
* Adapter for the UseEntity NMS packet
|
||||||
*/
|
*/
|
||||||
public class UseEntityAdapter extends BaseAdapter {
|
public class UseEntityAdapter extends BaseAdapter {
|
||||||
|
private static final boolean isServerAtLeast1_13 = ServerVersion.isAtLeast("1.13");
|
||||||
|
|
||||||
private static class LegacyReflectionSet {
|
private static class LegacyReflectionSet {
|
||||||
/** Hacks. */
|
/** Hacks. */
|
||||||
@@ -102,6 +105,10 @@ public class UseEntityAdapter extends BaseAdapter {
|
|||||||
|
|
||||||
private final LegacyReflectionSet legacySet;
|
private final LegacyReflectionSet legacySet;
|
||||||
|
|
||||||
|
private volatile boolean protocolLibEntityUseActionBroken = false;
|
||||||
|
|
||||||
|
private volatile boolean protocolLibEntityUseActionWarningLogged = false;
|
||||||
|
|
||||||
public UseEntityAdapter(Plugin plugin) {
|
public UseEntityAdapter(Plugin plugin) {
|
||||||
super(plugin, PacketType.Play.Client.USE_ENTITY);
|
super(plugin, PacketType.Play.Client.USE_ENTITY);
|
||||||
this.checkType = CheckType.NET_ATTACKFREQUENCY;
|
this.checkType = CheckType.NET_ATTACKFREQUENCY;
|
||||||
@@ -167,12 +174,20 @@ public class UseEntityAdapter extends BaseAdapter {
|
|||||||
if (!packetInterpreted) {
|
if (!packetInterpreted) {
|
||||||
// Handle as if latest.
|
// Handle as if latest.
|
||||||
try {
|
try {
|
||||||
final StructureModifier<EntityUseAction> actions = packet.getEntityUseActions();
|
if (!protocolLibEntityUseActionBroken) {
|
||||||
final StructureModifier<WrappedEnumEntityUseAction> enumActions = packet.getEnumEntityUseActions();
|
final StructureModifier<EntityUseAction> actions = packet.getEntityUseActions();
|
||||||
if (actions.size() == 1 && actions.read(0) == EntityUseAction.ATTACK
|
if (actions.size() == 1) {
|
||||||
|| enumActions.size() == 1 && enumActions.read(0).equals(WrappedEnumEntityUseAction.attack())) {
|
packetInterpreted = true;
|
||||||
packetInterpreted = true;
|
isAttack = actions.read(0) == EntityUseAction.ATTACK;
|
||||||
isAttack = true;
|
}
|
||||||
|
// ProtocolLib compatibility: modern snapshots can throw while initializing enum wrappers.
|
||||||
|
if (!packetInterpreted && isServerAtLeast1_13) {
|
||||||
|
final StructureModifier<WrappedEnumEntityUseAction> enumActions = packet.getEnumEntityUseActions();
|
||||||
|
if (enumActions.size() == 1 && enumActions.read(0).equals(WrappedEnumEntityUseAction.attack())) {
|
||||||
|
packetInterpreted = true;
|
||||||
|
isAttack = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (NullPointerException e) {
|
catch (NullPointerException e) {
|
||||||
@@ -181,6 +196,16 @@ public class UseEntityAdapter extends BaseAdapter {
|
|||||||
* why doesn't the LegacyReflectionSet work here?
|
* why doesn't the LegacyReflectionSet work here?
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
catch (LinkageError | RuntimeException e) {
|
||||||
|
protocolLibEntityUseActionBroken = true;
|
||||||
|
if (!protocolLibEntityUseActionWarningLogged) {
|
||||||
|
protocolLibEntityUseActionWarningLogged = true;
|
||||||
|
// Diagnostic logging: name the failing branch so attack/combat packet issues are clear in console.
|
||||||
|
StaticLog.logWarning("ProtocolLib could not expose USE_ENTITY action data (branch=enumEntityUseActions, packet=" + packet.getType() + "). Skipping AttackFrequency interpretation for USE_ENTITY packets until restart.");
|
||||||
|
StaticLog.logWarning("ProtocolLib USE_ENTITY action failure: " + e.getClass().getName() + ": " + e.getMessage());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!packetInterpreted) {
|
if (!packetInterpreted) {
|
||||||
// StaticLog.logWarning("Attack packet couldn't be interpreted. Skipping AttackFrequency.");
|
// StaticLog.logWarning("Attack packet couldn't be interpreted. Skipping AttackFrequency.");
|
||||||
@@ -211,4 +236,4 @@ public class UseEntityAdapter extends BaseAdapter {
|
|||||||
return actionName == null ? 0 : (INTERPRETED | ("ATTACK".equals(actionName) ? ATTACK : 0));
|
return actionName == null ? 0 : (INTERPRETED | ("ATTACK".equals(actionName) ? ATTACK : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+5
@@ -350,6 +350,11 @@ public class UseItemAdapter extends BaseAdapter {
|
|||||||
data.offHandUse = e.getHand() == EquipmentSlot.OFF_HAND;
|
data.offHandUse = e.getHand() == EquipmentSlot.OFF_HAND;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 1.21.11: Spears...
|
||||||
|
if (MaterialUtil.isSpear(m)) {
|
||||||
|
pData.setItemInUse(m);
|
||||||
|
data.offHandUse = e.getHand() == EquipmentSlot.OFF_HAND;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// No item in hands, no deal.
|
// No item in hands, no deal.
|
||||||
|
|||||||
+51
-11
@@ -17,15 +17,18 @@ package fr.neatmonster.nocheatplus.checks.net.protocollib;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
import com.comphenix.protocol.PacketType;
|
import com.comphenix.protocol.PacketType;
|
||||||
import com.comphenix.protocol.events.ListenerPriority;
|
import com.comphenix.protocol.events.ListenerPriority;
|
||||||
import com.comphenix.protocol.events.PacketContainer;
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
import com.comphenix.protocol.events.PacketEvent;
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
import com.comphenix.protocol.reflect.StructureModifier;
|
import com.comphenix.protocol.reflect.StructureModifier;
|
||||||
|
import com.comphenix.protocol.wrappers.BukkitConverters;
|
||||||
|
|
||||||
import fr.neatmonster.nocheatplus.checks.CheckType;
|
import fr.neatmonster.nocheatplus.checks.CheckType;
|
||||||
import fr.neatmonster.nocheatplus.checks.moving.MovingConfig;
|
import fr.neatmonster.nocheatplus.checks.moving.MovingConfig;
|
||||||
@@ -34,25 +37,28 @@ import fr.neatmonster.nocheatplus.checks.moving.velocity.VelocityFlags;
|
|||||||
import fr.neatmonster.nocheatplus.players.DataManager;
|
import fr.neatmonster.nocheatplus.players.DataManager;
|
||||||
import fr.neatmonster.nocheatplus.players.IPlayerData;
|
import fr.neatmonster.nocheatplus.players.IPlayerData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter for handling velocity related packet
|
||||||
|
*/
|
||||||
public class VelocityAdapter extends BaseAdapter {
|
public class VelocityAdapter extends BaseAdapter {
|
||||||
private static PacketType[] initPacketTypes() {
|
private static PacketType[] initPacketTypes() {
|
||||||
final List<PacketType> types = new LinkedList<PacketType>(Arrays.asList(
|
final List<PacketType> types = new LinkedList<PacketType>(Arrays.asList(
|
||||||
PacketType.Play.Server.EXPLOSION
|
PacketType.Play.Server.EXPLOSION,
|
||||||
//PacketType.Play.Server.ENTITY_VELOCITY
|
PacketType.Play.Server.ENTITY_VELOCITY
|
||||||
));
|
));
|
||||||
return types.toArray(new PacketType[types.size()]);
|
return types.toArray(new PacketType[types.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public VelocityAdapter(Plugin plugin) {
|
public VelocityAdapter(Plugin plugin) {
|
||||||
super(plugin, ListenerPriority.MONITOR, initPacketTypes());
|
super(plugin, ListenerPriority.LOWEST, initPacketTypes());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPacketSending(final PacketEvent event) {
|
public void onPacketSending(final PacketEvent event) {
|
||||||
handleVelocityPacket(event);
|
handleOutgoingVelocityPacket(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleVelocityPacket(PacketEvent event) {
|
private void handleOutgoingVelocityPacket(PacketEvent event) {
|
||||||
try {
|
try {
|
||||||
if (event.isPlayerTemporary()) return;
|
if (event.isPlayerTemporary()) return;
|
||||||
}
|
}
|
||||||
@@ -65,13 +71,51 @@ public class VelocityAdapter extends BaseAdapter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (event.getPacketType() != PacketType.Play.Server.EXPLOSION) return;
|
if (event.getPacketType() == PacketType.Play.Server.EXPLOSION) {
|
||||||
|
handleExplosionPacket(event);
|
||||||
|
} else {
|
||||||
|
handleEntityVelocityPacket(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleEntityVelocityPacket(PacketEvent event) {
|
||||||
|
final Player player = event.getPlayer();
|
||||||
|
final PacketContainer packet = event.getPacket();
|
||||||
|
final StructureModifier<Integer> ints = packet.getIntegers();
|
||||||
|
Integer id = ints.readSafely(0);
|
||||||
|
id = (id == null) ? -1 : id;
|
||||||
|
final IPlayerData pData = DataManager.getPlayerData(player);
|
||||||
|
if (id != player.getEntityId() || !pData.isCheckActive(CheckType.MOVING, player)) return;
|
||||||
|
final MovingData data = pData.getGenericInstance(MovingData.class);
|
||||||
|
final MovingConfig cc = pData.getGenericInstance(MovingConfig.class);
|
||||||
|
if (ints.size() == 4) {
|
||||||
|
final Double velX = ints.read(1) / 8000D;
|
||||||
|
final Double velY = ints.read(2) / 8000D;
|
||||||
|
final Double velZ = ints.read(3) / 8000D;
|
||||||
|
data.addVelocity(player, cc, velX, velY, velZ);
|
||||||
|
} else {
|
||||||
|
final Vector vec = packet.getVectors().readSafely(0);
|
||||||
|
if (vec != null) {
|
||||||
|
data.addVelocity(player, cc, vec.getX(), vec.getY(), vec.getZ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleExplosionPacket(PacketEvent event) {
|
||||||
final Player player = event.getPlayer();
|
final Player player = event.getPlayer();
|
||||||
final PacketContainer packet = event.getPacket();
|
final PacketContainer packet = event.getPacket();
|
||||||
final StructureModifier<Float> floats = packet.getFloat();
|
final StructureModifier<Float> floats = packet.getFloat();
|
||||||
|
final IPlayerData pData = DataManager.getPlayerData(player);
|
||||||
|
if (!pData.isCheckActive(CheckType.MOVING, player)) return;
|
||||||
|
final MovingData data = pData.getGenericInstance(MovingData.class);
|
||||||
|
final MovingConfig cc = pData.getGenericInstance(MovingConfig.class);
|
||||||
|
|
||||||
if (floats.size() != 4) {
|
if (floats.size() != 4) {
|
||||||
// TODO : Warning
|
Optional<Vector> read = packet.getOptionals(BukkitConverters.getVectorConverter()).read(0);
|
||||||
|
if (read.isPresent()) {
|
||||||
|
final Vector vec = read.get();
|
||||||
|
data.addVelocity(player, cc, vec.getX(), vec.getY(), vec.getZ(), VelocityFlags.ORIGIN_EXPLOSION | VelocityFlags.ADDITIVE);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,10 +125,6 @@ public class VelocityAdapter extends BaseAdapter {
|
|||||||
final Float velY = floats.read(2);
|
final Float velY = floats.read(2);
|
||||||
final Float velZ = floats.read(3);
|
final Float velZ = floats.read(3);
|
||||||
if (Math.abs(velX) == 0.0 && Math.abs(velZ) == 0.0 && Math.abs(velY) == 0.0) return;
|
if (Math.abs(velX) == 0.0 && Math.abs(velZ) == 0.0 && Math.abs(velY) == 0.0) return;
|
||||||
final IPlayerData pData = DataManager.getPlayerData(player);
|
|
||||||
if (!pData.isCheckActive(CheckType.MOVING, player)) return;
|
|
||||||
final MovingData data = pData.getGenericInstance(MovingData.class);
|
|
||||||
final MovingConfig cc = pData.getGenericInstance(MovingConfig.class);
|
|
||||||
// Process velocity.
|
// Process velocity.
|
||||||
data.addVelocity(player, cc, velX, velY, velZ, VelocityFlags.ORIGIN_EXPLOSION | VelocityFlags.ADDITIVE);
|
data.addVelocity(player, cc, velX, velY, velZ, VelocityFlags.ORIGIN_EXPLOSION | VelocityFlags.ADDITIVE);
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -23,7 +23,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.spigotmc</groupId>
|
<groupId>org.spigotmc</groupId>
|
||||||
<artifactId>spigot-api</artifactId>
|
<artifactId>spigot-api</artifactId>
|
||||||
<version>1.21.4-R0.1-SNAPSHOT</version>
|
<version>26.3-R0.1-SNAPSHOT</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -33,8 +33,9 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>fr.neatmonster</groupId>
|
<groupId>fr.neatmonster</groupId>
|
||||||
<artifactId>ncplegacy</artifactId>
|
<artifactId>ncpbukkitapiaggregated</artifactId>
|
||||||
<version>1.1-SNAPSHOT</version>
|
<version>1.1-SNAPSHOT</version>
|
||||||
|
<type>pom</type>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
|
|||||||
@@ -14,10 +14,11 @@
|
|||||||
*/
|
*/
|
||||||
package fr.neatmonster.nocheatplus.actions.types;
|
package fr.neatmonster.nocheatplus.actions.types;
|
||||||
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
import fr.neatmonster.nocheatplus.actions.AbstractActionList;
|
import fr.neatmonster.nocheatplus.actions.AbstractActionList;
|
||||||
import fr.neatmonster.nocheatplus.actions.ParameterHolder;
|
import fr.neatmonster.nocheatplus.actions.ParameterHolder;
|
||||||
@@ -60,22 +61,35 @@ public class CommandAction<D extends ParameterHolder, L extends AbstractActionLi
|
|||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see fr.neatmonster.nocheatplus.actions.Action#execute(fr.neatmonster.nocheatplus.checks.ViolationData)
|
* @see fr.neatmonster.nocheatplus.actions.Action#execute(fr.neatmonster.nocheatplus.checks.ViolationData)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void execute(final D violationData) {
|
public void execute(final D violationData) {
|
||||||
final String command = getMessage(violationData);
|
final String command = getMessage(violationData);
|
||||||
SchedulerHelper.runSyncTask(plugin, (arg) -> {
|
final Runnable runCommand = () -> {
|
||||||
try {
|
try {
|
||||||
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), command);
|
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), command);
|
||||||
if (logDebug) {
|
if (logDebug) {
|
||||||
debug(violationData, command);
|
debug(violationData, command);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (final Exception e) {
|
catch (final Exception e) {
|
||||||
StaticLog.logOnce(Level.WARNING, "Failed to execute the command '" + command + "': " + e.getMessage()
|
StaticLog.logOnce(Level.WARNING, "Failed to execute the command '" + command + "': " + e.getMessage()
|
||||||
+ ", please check if everything is setup correct.", StringUtil.throwableToString(e));
|
+ ", please check if everything is setup correct.", StringUtil.throwableToString(e));
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
}
|
if (plugin == null) {
|
||||||
|
runCommand.run();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (SchedulerHelper.isFoliaServer() && violationData instanceof ViolationData) {
|
||||||
|
final Player player = ((ViolationData) violationData).player;
|
||||||
|
if (player != null) {
|
||||||
|
SchedulerHelper.runSyncTaskForEntity(player, plugin, (arg) -> runCommand.run(),
|
||||||
|
() -> SchedulerHelper.runSyncTask(plugin, (arg) -> runCommand.run()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SchedulerHelper.runSyncTask(plugin, (arg) -> runCommand.run());
|
||||||
|
}
|
||||||
|
|
||||||
private void debug(final D violationData, String command) {
|
private void debug(final D violationData, String command) {
|
||||||
final String prefix;
|
final String prefix;
|
||||||
|
|||||||
@@ -64,7 +64,6 @@ public enum CheckType {
|
|||||||
|
|
||||||
COMBINED(CheckTypeType.GROUP, CheckType.ALL, Permissions.COMBINED),
|
COMBINED(CheckTypeType.GROUP, CheckType.ALL, Permissions.COMBINED),
|
||||||
COMBINED_IMPROBABLE(CheckTypeType.CHECK, COMBINED, Permissions.COMBINED_IMPROBABLE),
|
COMBINED_IMPROBABLE(CheckTypeType.CHECK, COMBINED, Permissions.COMBINED_IMPROBABLE),
|
||||||
COMBINED_MUNCHHAUSEN(CheckTypeType.CHECK, COMBINED, Permissions.COMBINED_MUNCHHAUSEN),
|
|
||||||
/** Rather for data removal and exemption. */
|
/** Rather for data removal and exemption. */
|
||||||
COMBINED_YAWRATE(CheckTypeType.CHECK, COMBINED),
|
COMBINED_YAWRATE(CheckTypeType.CHECK, COMBINED),
|
||||||
|
|
||||||
|
|||||||
+3
-11
@@ -30,7 +30,6 @@ import fr.neatmonster.nocheatplus.NCPAPIProvider;
|
|||||||
import fr.neatmonster.nocheatplus.checks.CheckListener;
|
import fr.neatmonster.nocheatplus.checks.CheckListener;
|
||||||
import fr.neatmonster.nocheatplus.checks.CheckType;
|
import fr.neatmonster.nocheatplus.checks.CheckType;
|
||||||
import fr.neatmonster.nocheatplus.checks.combined.CombinedConfig;
|
import fr.neatmonster.nocheatplus.checks.combined.CombinedConfig;
|
||||||
import fr.neatmonster.nocheatplus.checks.moving.MovingData;
|
|
||||||
import fr.neatmonster.nocheatplus.checks.net.FlyingQueueHandle;
|
import fr.neatmonster.nocheatplus.checks.net.FlyingQueueHandle;
|
||||||
import fr.neatmonster.nocheatplus.checks.net.model.DataPacketFlying;
|
import fr.neatmonster.nocheatplus.checks.net.model.DataPacketFlying;
|
||||||
import fr.neatmonster.nocheatplus.compat.Bridge1_13;
|
import fr.neatmonster.nocheatplus.compat.Bridge1_13;
|
||||||
@@ -47,7 +46,6 @@ import fr.neatmonster.nocheatplus.players.PlayerFactoryArgument;
|
|||||||
import fr.neatmonster.nocheatplus.stats.Counters;
|
import fr.neatmonster.nocheatplus.stats.Counters;
|
||||||
import fr.neatmonster.nocheatplus.utilities.CheckUtils;
|
import fr.neatmonster.nocheatplus.utilities.CheckUtils;
|
||||||
import fr.neatmonster.nocheatplus.utilities.entity.InventoryUtil;
|
import fr.neatmonster.nocheatplus.utilities.entity.InventoryUtil;
|
||||||
import fr.neatmonster.nocheatplus.utilities.TickTask;
|
|
||||||
import fr.neatmonster.nocheatplus.utilities.location.LocUtil;
|
import fr.neatmonster.nocheatplus.utilities.location.LocUtil;
|
||||||
import fr.neatmonster.nocheatplus.utilities.map.BlockProperties;
|
import fr.neatmonster.nocheatplus.utilities.map.BlockProperties;
|
||||||
import fr.neatmonster.nocheatplus.utilities.moving.MovingUtil;
|
import fr.neatmonster.nocheatplus.utilities.moving.MovingUtil;
|
||||||
@@ -349,16 +347,10 @@ public class BlockInteractListener extends CheckListener {
|
|||||||
&& event.isCancelled() && event.useItemInHand() != Result.DENY) {
|
&& event.isCancelled() && event.useItemInHand() != Result.DENY) {
|
||||||
final ItemStack stack = Bridge1_9.getUsedItem(player, event);
|
final ItemStack stack = Bridge1_9.getUsedItem(player, event);
|
||||||
if (stack != null && BridgeMisc.maybeElytraBoost(player, stack.getType())) {
|
if (stack != null && BridgeMisc.maybeElytraBoost(player, stack.getType())) {
|
||||||
final int power = BridgeMisc.getFireworksPower(stack);
|
// The boost itself is the rocket entity the client still has. Its lifetime is random,
|
||||||
final MovingData mData = pData.getGenericInstance(MovingData.class);
|
// so a right-click timer does not match the ticks the client actually boosts.
|
||||||
final int ticks = Math.max((1 + power) * 20, 30);
|
|
||||||
mData.fireworksBoostDuration = ticks;
|
|
||||||
mData.fireworksBoostTickNeedCheck = ticks - 1;
|
|
||||||
// Expiration tick: not general latency, rather a minimum margin for sudden congestion.
|
|
||||||
mData.fireworksBoostTickExpire = TickTask.getTick() + ticks;
|
|
||||||
// TODO: Invalidation mechanics: by tick/time well ?
|
|
||||||
if (pData.isDebugActive(CheckType.MOVING)) {
|
if (pData.isDebugActive(CheckType.MOVING)) {
|
||||||
debug(player, "Elytra boost (power " + power + "): " + stack);
|
debug(player, "Elytra boost item: " + stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ public class Visible extends Check {
|
|||||||
// Initialize.
|
// Initialize.
|
||||||
final BlockCache blockCache = this.wrapBlockCache.getBlockCache();
|
final BlockCache blockCache = this.wrapBlockCache.getBlockCache();
|
||||||
blockCache.setAccess(loc.getWorld());
|
blockCache.setAccess(loc.getWorld());
|
||||||
|
blockCache.setPlayerData(pData);
|
||||||
rayTracing.setBlockCache(blockCache);
|
rayTracing.setBlockCache(blockCache);
|
||||||
//collides = !checker.checkFlyingQueue(eyeX, eyeY, eyeZ, loc.getYaw(), loc.getPitch(),
|
//collides = !checker.checkFlyingQueue(eyeX, eyeY, eyeZ, loc.getYaw(), loc.getPitch(),
|
||||||
// blockX, blockY, blockZ, flyingHandle, face, tags, debug, player);
|
// blockX, blockY, blockZ, flyingHandle, face, tags, debug, player);
|
||||||
@@ -160,7 +161,14 @@ public class Visible extends Check {
|
|||||||
else if (Math.abs(face.getModZ()) > 0) {
|
else if (Math.abs(face.getModZ()) > 0) {
|
||||||
axisData.priority = Axis.Z_AXIS;
|
axisData.priority = Axis.Z_AXIS;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Origin version of updated Visible check, also implement for Fight.Visiable.
|
||||||
|
* After lots of reports telling that randomly flagged with old one, this was made.
|
||||||
|
* Rather fixing look after interact packets, hidden micro-moves for in sight check.
|
||||||
|
* This just check with obstacles from player's eyes to the block they are interacting. Is it possible at all?
|
||||||
|
* Since it doesn't have correct line of sight, handle special blocks like stairs can be problematic.
|
||||||
|
* Bedrock Edition compatible! Players playing on touch screen and interact blocks ~60 degree off from their look.
|
||||||
|
*/
|
||||||
do {
|
do {
|
||||||
canContinue = false;
|
canContinue = false;
|
||||||
for (BlockCoord neighbor : MapUtil.getNeighborsInDirection(bc, direction, eyeX, eyeY, eyeZ, axisData)) {
|
for (BlockCoord neighbor : MapUtil.getNeighborsInDirection(bc, direction, eyeX, eyeY, eyeZ, axisData)) {
|
||||||
|
|||||||
+13
-17
@@ -199,15 +199,14 @@ public class BlockPlaceListener extends CheckListener {
|
|||||||
placedMat = BlockProperties.isAir(event.getItemInHand()) ? Material.AIR : event.getItemInHand().getType();
|
placedMat = BlockProperties.isAir(event.getItemInHand()) ? Material.AIR : event.getItemInHand().getType();
|
||||||
}
|
}
|
||||||
else placedMat = Bridge1_9.getItemInMainHand(player).getType(); // Safety first.
|
else placedMat = Bridge1_9.getItemInMainHand(player).getType(); // Safety first.
|
||||||
|
|
||||||
boolean cancelled = false;
|
boolean cancelled = false;
|
||||||
int skippedRedundantChecks = 0;
|
int skippedRedundantChecks = 0;
|
||||||
final IPlayerData pData = DataManager.getPlayerData(player);
|
final IPlayerData pData = DataManager.getPlayerData(player);
|
||||||
final BlockPlaceData data = pData.getGenericInstance(BlockPlaceData.class);
|
final BlockPlaceData data = pData.getGenericInstance(BlockPlaceData.class);
|
||||||
final BlockPlaceConfig cc = pData.getGenericInstance(BlockPlaceConfig.class);
|
final BlockPlaceConfig cc = pData.getGenericInstance(BlockPlaceConfig.class);
|
||||||
final BlockInteractData bdata = pData.getGenericInstance(BlockInteractData.class);
|
final BlockInteractData biData = pData.getGenericInstance(BlockInteractData.class);
|
||||||
// isInteractBlock - the block placed against is the block last interacted with.
|
// isInteractBlock - the block placed against is the block last interacted with.
|
||||||
final boolean isInteractBlock = !bdata.getLastIsCancelled() && bdata.matchesLastBlock(TickTask.getTick(), blockAgainst);
|
final boolean isInteractBlock = !biData.getLastIsCancelled() && biData.matchesLastBlock(TickTask.getTick(), blockAgainst);
|
||||||
final BlockFace placedFace = event.getBlock().getFace(blockAgainst);
|
final BlockFace placedFace = event.getBlock().getFace(blockAgainst);
|
||||||
final Block blockPlaced = event.getBlockPlaced();
|
final Block blockPlaced = event.getBlockPlaced();
|
||||||
final boolean shouldSkipSome;
|
final boolean shouldSkipSome;
|
||||||
@@ -260,18 +259,15 @@ public class BlockPlaceListener extends CheckListener {
|
|||||||
}
|
}
|
||||||
// Check for Improbable, whatever FastPlace says, provided the feature is enabled at all.
|
// Check for Improbable, whatever FastPlace says, provided the feature is enabled at all.
|
||||||
if (cc.fastPlaceImprobableWeight > 0.0f) {
|
if (cc.fastPlaceImprobableWeight > 0.0f) {
|
||||||
// Check only if frequency is decently high.
|
if (cc.fastPlaceImprobableFeedOnly) {
|
||||||
if (data.fastPlaceVL > 20) {
|
Improbable.feed(player, cc.fastPlaceImprobableWeight, System.currentTimeMillis());
|
||||||
// Don't check if set to only feed.
|
|
||||||
if (!cc.fastPlaceImprobableFeedOnly) {
|
|
||||||
if (Improbable.check(player, cc.fastPlaceImprobableWeight, System.currentTimeMillis(), "blockplace.fastplace", pData)) {
|
|
||||||
cancelled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else Improbable.feed(player, cc.fastPlaceImprobableWeight, System.currentTimeMillis());
|
|
||||||
}
|
}
|
||||||
// Feed only for lower frequencies.
|
else if (data.fastPlaceVL > 25 && Improbable.check(player, cc.fastPlaceImprobableWeight, System.currentTimeMillis(), "blockplace.fastplace", pData)) {
|
||||||
else Improbable.feed(player, cc.fastPlaceImprobableWeight, System.currentTimeMillis());
|
// Only check on decently high frequencies
|
||||||
|
cancelled = true;
|
||||||
|
}
|
||||||
|
// Feed for lower frequencies.
|
||||||
|
else Improbable.feed(player, cc.fastPlaceImprobableWeight, System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,7 +312,7 @@ public class BlockPlaceListener extends CheckListener {
|
|||||||
final double eyeHeight = MovingUtil.getEyeHeight(player);
|
final double eyeHeight = MovingUtil.getEyeHeight(player);
|
||||||
// Reach check (distance).
|
// Reach check (distance).
|
||||||
if (!cancelled && !shouldSkipSome) {
|
if (!cancelled && !shouldSkipSome) {
|
||||||
if (isInteractBlock && bdata.isPassedCheck(CheckType.BLOCKINTERACT_REACH)) {
|
if (isInteractBlock && biData.isPassedCheck(CheckType.BLOCKINTERACT_REACH)) {
|
||||||
skippedRedundantChecks++;
|
skippedRedundantChecks++;
|
||||||
}
|
}
|
||||||
else if (reachCheck && reach.check(player, eyeHeight, block, data, cc)) {
|
else if (reachCheck && reach.check(player, eyeHeight, block, data, cc)) {
|
||||||
@@ -325,7 +321,7 @@ public class BlockPlaceListener extends CheckListener {
|
|||||||
}
|
}
|
||||||
// Direction check.
|
// Direction check.
|
||||||
if (!cancelled && !shouldSkipSome) {
|
if (!cancelled && !shouldSkipSome) {
|
||||||
if (isInteractBlock && bdata.isPassedCheck(CheckType.BLOCKINTERACT_DIRECTION)) {
|
if (isInteractBlock && biData.isPassedCheck(CheckType.BLOCKINTERACT_DIRECTION)) {
|
||||||
skippedRedundantChecks++;
|
skippedRedundantChecks++;
|
||||||
}
|
}
|
||||||
else if (directionCheck) {
|
else if (directionCheck) {
|
||||||
@@ -507,7 +503,7 @@ public class BlockPlaceListener extends CheckListener {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
final Material mat = player.getLocation(useLoc2).getBlock().getType();
|
final Material mat = player.getLocation(useLoc2).getBlock().getType();
|
||||||
final long flags = BlockFlags.F_CLIMBABLE | BlockFlags.F_LIQUID | BlockFlags.F_IGN_PASSABLE;
|
final long flags = BlockFlags.F_CLIMBABLE | BlockFlags.F_LIQUID | BlockFlags.F_IGN_PASSABLE_CHECK;
|
||||||
if (!BlockProperties.isAir(mat) && (BlockFlags.getBlockFlags(mat) & flags) == 0 && !mcAccess.getHandle().hasGravity(mat)) {
|
if (!BlockProperties.isAir(mat) && (BlockFlags.getBlockFlags(mat) & flags) == 0 && !mcAccess.getHandle().hasGravity(mat)) {
|
||||||
// Still fails on piston traps etc.
|
// Still fails on piston traps etc.
|
||||||
if (!BlockProperties.isPassable(player.getLocation(), projectile.getLocation())
|
if (!BlockProperties.isPassable(player.getLocation(), projectile.getLocation())
|
||||||
|
|||||||
@@ -27,11 +27,12 @@ import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
|||||||
import org.bukkit.event.player.PlayerLoginEvent;
|
import org.bukkit.event.player.PlayerLoginEvent;
|
||||||
import org.bukkit.event.player.PlayerLoginEvent.Result;
|
import org.bukkit.event.player.PlayerLoginEvent.Result;
|
||||||
|
|
||||||
import fr.neatmonster.nocheatplus.NCPAPIProvider;
|
import fr.neatmonster.nocheatplus.NCPAPIProvider;
|
||||||
import fr.neatmonster.nocheatplus.checks.CheckListener;
|
import fr.neatmonster.nocheatplus.checks.CheckListener;
|
||||||
import fr.neatmonster.nocheatplus.checks.CheckType;
|
import fr.neatmonster.nocheatplus.checks.CheckType;
|
||||||
import fr.neatmonster.nocheatplus.checks.moving.MovingConfig;
|
import fr.neatmonster.nocheatplus.checks.moving.MovingConfig;
|
||||||
import fr.neatmonster.nocheatplus.command.CommandUtil;
|
import fr.neatmonster.nocheatplus.checks.net.NetData;
|
||||||
|
import fr.neatmonster.nocheatplus.command.CommandUtil;
|
||||||
import fr.neatmonster.nocheatplus.compat.BridgeMisc;
|
import fr.neatmonster.nocheatplus.compat.BridgeMisc;
|
||||||
import fr.neatmonster.nocheatplus.compat.SchedulerHelper;
|
import fr.neatmonster.nocheatplus.compat.SchedulerHelper;
|
||||||
import fr.neatmonster.nocheatplus.components.NoCheatPlusAPI;
|
import fr.neatmonster.nocheatplus.components.NoCheatPlusAPI;
|
||||||
@@ -160,11 +161,21 @@ public class ChatListener extends CheckListener implements INotifyReload, JoinLe
|
|||||||
else if (logins.isEnabled(player, pData) && logins.check(player, cc, data)) {
|
else if (logins.isEnabled(player, pData) && logins.check(player, cc, data)) {
|
||||||
event.disallow(Result.KICK_OTHER, cc.loginsKickMessage);
|
event.disallow(Result.KICK_OTHER, cc.loginsKickMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** We listen to PlayerCommandPreprocess events because commands can be used for spamming too. */
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||||
@EventHandler(priority = EventPriority.LOWEST)
|
public void onPlayerTeleportCommandMonitor(final PlayerCommandPreprocessEvent event) {
|
||||||
public void onPlayerCommandPreprocess(final PlayerCommandPreprocessEvent event) {
|
final String command = getTeleportLikeCommand(event.getMessage());
|
||||||
|
if (command != null) {
|
||||||
|
final Player player = event.getPlayer();
|
||||||
|
DataManager.getPlayerData(player).getGenericInstance(NetData.class)
|
||||||
|
.recordTeleportCommand(command, player.getLocation(), System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** We listen to PlayerCommandPreprocess events because commands can be used for spamming too. */
|
||||||
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
|
public void onPlayerCommandPreprocess(final PlayerCommandPreprocessEvent event) {
|
||||||
final Player player = event.getPlayer();
|
final Player player = event.getPlayer();
|
||||||
// Tell TickTask to update cached permissions.
|
// Tell TickTask to update cached permissions.
|
||||||
final IPlayerData pData = DataManager.getPlayerData(player);
|
final IPlayerData pData = DataManager.getPlayerData(player);
|
||||||
@@ -223,8 +234,47 @@ public class ChatListener extends CheckListener implements INotifyReload, JoinLe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getTeleportLikeCommand(final String message) {
|
||||||
|
if (message == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final String lcMessage = StringUtil.leftTrim(message).toLowerCase();
|
||||||
|
if (lcMessage.length() < 2 || lcMessage.charAt(0) != '/') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final String[] split = lcMessage.split(" ", 2);
|
||||||
|
String command = split[0].substring(1);
|
||||||
|
final int namespaceIndex = command.indexOf(':');
|
||||||
|
if (namespaceIndex >= 0 && namespaceIndex + 1 < command.length()) {
|
||||||
|
command = command.substring(namespaceIndex + 1);
|
||||||
|
}
|
||||||
|
return isTeleportLikeCommand(command) ? command : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isTeleportLikeCommand(final String command) {
|
||||||
|
return command.equals("rtp")
|
||||||
|
|| command.equals("randomtp")
|
||||||
|
|| command.equals("randomteleport")
|
||||||
|
|| command.equals("wild")
|
||||||
|
|| command.equals("wilderness")
|
||||||
|
|| command.equals("spawn")
|
||||||
|
|| command.equals("hub")
|
||||||
|
|| command.equals("lobby")
|
||||||
|
|| command.equals("home")
|
||||||
|
|| command.equals("homes")
|
||||||
|
|| command.equals("warp")
|
||||||
|
|| command.equals("warps")
|
||||||
|
|| command.equals("back")
|
||||||
|
|| command.equals("tpa")
|
||||||
|
|| command.equals("tpaccept")
|
||||||
|
|| command.equals("tpahere")
|
||||||
|
|| command.equals("tp")
|
||||||
|
|| command.equals("teleport")
|
||||||
|
|| command.equals("tphere");
|
||||||
|
}
|
||||||
|
|
||||||
private boolean checkUntrackedLocation(final Player player, final String message,
|
private boolean checkUntrackedLocation(final Player player, final String message,
|
||||||
final MovingConfig mcc, final IPlayerData pData) {
|
final MovingConfig mcc, final IPlayerData pData) {
|
||||||
|
|||||||
+2
-6
@@ -51,9 +51,7 @@ public class CombinedConfig extends ACheckConfig {
|
|||||||
public final int invulnerableModifierDefault;
|
public final int invulnerableModifierDefault;
|
||||||
public final boolean invulnerableTriggerAlways;
|
public final boolean invulnerableTriggerAlways;
|
||||||
public final boolean invulnerableTriggerFallDistance;
|
public final boolean invulnerableTriggerFallDistance;
|
||||||
|
|
||||||
public final ActionList munchHausenActions;
|
|
||||||
|
|
||||||
// Last yaw tracking
|
// Last yaw tracking
|
||||||
public final float yawRate;
|
public final float yawRate;
|
||||||
public final boolean yawRateImprobableFeedOnly;
|
public final boolean yawRateImprobableFeedOnly;
|
||||||
@@ -108,9 +106,7 @@ public class CombinedConfig extends ACheckConfig {
|
|||||||
if (error) StaticLog.logInfo("Damage causes can be: " + StringUtil.join(Arrays.asList(DamageCause.values()), ", "));
|
if (error) StaticLog.logInfo("Damage causes can be: " + StringUtil.join(Arrays.asList(DamageCause.values()), ", "));
|
||||||
invulnerableTriggerAlways = config.getBoolean(ConfPaths.COMBINED_INVULNERABLE_TRIGGERS_ALWAYS);
|
invulnerableTriggerAlways = config.getBoolean(ConfPaths.COMBINED_INVULNERABLE_TRIGGERS_ALWAYS);
|
||||||
invulnerableTriggerFallDistance = config.getBoolean(ConfPaths.COMBINED_INVULNERABLE_TRIGGERS_FALLDISTANCE);
|
invulnerableTriggerFallDistance = config.getBoolean(ConfPaths.COMBINED_INVULNERABLE_TRIGGERS_FALLDISTANCE);
|
||||||
|
|
||||||
munchHausenActions = config.getOptimizedActionList(ConfPaths.COMBINED_MUNCHHAUSEN_ACTIONS, Permissions.COMBINED_MUNCHHAUSEN);
|
|
||||||
|
|
||||||
yawRate = config.getInt(ConfPaths.COMBINED_YAWRATE_RATE);
|
yawRate = config.getInt(ConfPaths.COMBINED_YAWRATE_RATE);
|
||||||
yawRateImprobableFeedOnly = config.getBoolean(ConfPaths.COMBINED_YAWRATE_IMPROBABLE_FEEDONLY);
|
yawRateImprobableFeedOnly = config.getBoolean(ConfPaths.COMBINED_YAWRATE_IMPROBABLE_FEEDONLY);
|
||||||
yawRateImprobableWeight = (float) config.getDouble(ConfPaths.COMBINED_YAWRATE_IMPROBABLE_WEIGHT);
|
yawRateImprobableWeight = (float) config.getDouble(ConfPaths.COMBINED_YAWRATE_IMPROBABLE_WEIGHT);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import java.util.Collection;
|
|||||||
import fr.neatmonster.nocheatplus.checks.CheckType;
|
import fr.neatmonster.nocheatplus.checks.CheckType;
|
||||||
import fr.neatmonster.nocheatplus.checks.access.ACheckData;
|
import fr.neatmonster.nocheatplus.checks.access.ACheckData;
|
||||||
import fr.neatmonster.nocheatplus.components.data.IDataOnRemoveSubCheckData;
|
import fr.neatmonster.nocheatplus.components.data.IDataOnRemoveSubCheckData;
|
||||||
|
import fr.neatmonster.nocheatplus.players.PlayerData;
|
||||||
import fr.neatmonster.nocheatplus.utilities.PenaltyTime;
|
import fr.neatmonster.nocheatplus.utilities.PenaltyTime;
|
||||||
import fr.neatmonster.nocheatplus.utilities.ds.count.ActionFrequency;
|
import fr.neatmonster.nocheatplus.utilities.ds.count.ActionFrequency;
|
||||||
|
|
||||||
@@ -26,11 +27,25 @@ public class CombinedData extends ACheckData implements IDataOnRemoveSubCheckDat
|
|||||||
|
|
||||||
// VLs
|
// VLs
|
||||||
public double improbableVL = 0;
|
public double improbableVL = 0;
|
||||||
public double munchHausenVL = 0;
|
|
||||||
|
|
||||||
// Invulnerable management:
|
// Invulnerable management:
|
||||||
/** This is the tick from which on the player is vulnerable again. */
|
/** This is the tick from which on the player is vulnerable again. */
|
||||||
public int invulnerableTick = Integer.MIN_VALUE;
|
public int invulnerableTick = Integer.MIN_VALUE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last sprinting state of the player as set by {@link PlayerData#isSprinting()}. Set in SurvivalFly.
|
||||||
|
*/
|
||||||
|
public boolean wasSprinting;
|
||||||
|
/**
|
||||||
|
* Last shift state of the player as set by {@link PlayerData#isShiftKeyPressed()}. Set in SurvivalFly.
|
||||||
|
*/
|
||||||
|
public boolean wasPressingShift;
|
||||||
|
|
||||||
|
/** Last time the player was levitating */
|
||||||
|
public boolean wasLevitating;
|
||||||
|
|
||||||
|
/** Last time the player was slow falling */
|
||||||
|
public boolean wasSlowFalling;
|
||||||
|
|
||||||
// Yawrate check.
|
// Yawrate check.
|
||||||
public float lastYaw;
|
public float lastYaw;
|
||||||
@@ -46,7 +61,7 @@ public class CombinedData extends ACheckData implements IDataOnRemoveSubCheckDat
|
|||||||
public final PenaltyTime timeFreeze = new PenaltyTime();
|
public final PenaltyTime timeFreeze = new PenaltyTime();
|
||||||
|
|
||||||
// Improbable check
|
// Improbable check
|
||||||
public final ActionFrequency improbableCount = new ActionFrequency(20, 3000); // 20 buckets covering 3 seconds each (full time resolution of 1 minute)
|
public final ActionFrequency improbableCount = new ActionFrequency(20, 3000); // Sliding window of 20 buckets, each 3 seconds long (total span: ~1 minute, with 3s resolution)
|
||||||
|
|
||||||
// *----------No slowdown related data----------*
|
// *----------No slowdown related data----------*
|
||||||
/** Whether the player use the item on left hand */
|
/** Whether the player use the item on left hand */
|
||||||
@@ -57,8 +72,6 @@ public class CombinedData extends ACheckData implements IDataOnRemoveSubCheckDat
|
|||||||
public long releaseItemTime = 0;
|
public long releaseItemTime = 0;
|
||||||
/** Detection flag */
|
/** Detection flag */
|
||||||
public boolean isHackingRI = false;
|
public boolean isHackingRI = false;
|
||||||
/** Detection flag */
|
|
||||||
public boolean invalidItemUse = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reduce Improbable's data by the given amount, capped at a minimum of 0.
|
* Reduce Improbable's data by the given amount, capped at a minimum of 0.
|
||||||
@@ -88,9 +101,6 @@ public class CombinedData extends ACheckData implements IDataOnRemoveSubCheckDat
|
|||||||
case COMBINED_YAWRATE:
|
case COMBINED_YAWRATE:
|
||||||
yawFreq.clear(System.currentTimeMillis()); // TODO: Document there, which to use.
|
yawFreq.clear(System.currentTimeMillis()); // TODO: Document there, which to use.
|
||||||
break;
|
break;
|
||||||
case COMBINED_MUNCHHAUSEN:
|
|
||||||
munchHausenVL = 0;
|
|
||||||
break;
|
|
||||||
case COMBINED:
|
case COMBINED:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
|
|||||||
+127
-29
@@ -23,20 +23,23 @@ import org.bukkit.entity.Pose;
|
|||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.block.Action;
|
||||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||||
import org.bukkit.event.entity.EntityDamageEvent;
|
import org.bukkit.event.entity.EntityDamageEvent;
|
||||||
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
|
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
|
||||||
import org.bukkit.event.entity.EntityPoseChangeEvent;
|
import org.bukkit.event.entity.EntityPoseChangeEvent;
|
||||||
import org.bukkit.event.entity.EntityToggleGlideEvent;
|
import org.bukkit.event.entity.EntityToggleGlideEvent;
|
||||||
import org.bukkit.event.entity.PlayerDeathEvent;
|
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||||
import org.bukkit.event.player.PlayerFishEvent;
|
|
||||||
import org.bukkit.event.player.PlayerGameModeChangeEvent;
|
import org.bukkit.event.player.PlayerGameModeChangeEvent;
|
||||||
|
import org.bukkit.event.player.PlayerInteractEvent;
|
||||||
import org.bukkit.event.player.PlayerMoveEvent;
|
import org.bukkit.event.player.PlayerMoveEvent;
|
||||||
import org.bukkit.event.player.PlayerRespawnEvent;
|
import org.bukkit.event.player.PlayerRespawnEvent;
|
||||||
|
import org.bukkit.event.player.PlayerRiptideEvent;
|
||||||
import org.bukkit.event.player.PlayerToggleSneakEvent;
|
import org.bukkit.event.player.PlayerToggleSneakEvent;
|
||||||
import org.bukkit.event.player.PlayerToggleSprintEvent;
|
import org.bukkit.event.player.PlayerToggleSprintEvent;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.potion.PotionEffectType;
|
import org.bukkit.potion.PotionEffectType;
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
import fr.neatmonster.nocheatplus.NCPAPIProvider;
|
import fr.neatmonster.nocheatplus.NCPAPIProvider;
|
||||||
import fr.neatmonster.nocheatplus.checks.CheckListener;
|
import fr.neatmonster.nocheatplus.checks.CheckListener;
|
||||||
@@ -46,10 +49,12 @@ import fr.neatmonster.nocheatplus.checks.moving.MovingData;
|
|||||||
import fr.neatmonster.nocheatplus.checks.moving.model.PlayerMoveData;
|
import fr.neatmonster.nocheatplus.checks.moving.model.PlayerMoveData;
|
||||||
import fr.neatmonster.nocheatplus.checks.moving.model.PlayerMoveInfo;
|
import fr.neatmonster.nocheatplus.checks.moving.model.PlayerMoveInfo;
|
||||||
import fr.neatmonster.nocheatplus.checks.moving.velocity.VelocityFlags;
|
import fr.neatmonster.nocheatplus.checks.moving.velocity.VelocityFlags;
|
||||||
|
import fr.neatmonster.nocheatplus.compat.AlmostBoolean;
|
||||||
import fr.neatmonster.nocheatplus.compat.Bridge1_13;
|
import fr.neatmonster.nocheatplus.compat.Bridge1_13;
|
||||||
import fr.neatmonster.nocheatplus.compat.Bridge1_9;
|
import fr.neatmonster.nocheatplus.compat.Bridge1_9;
|
||||||
import fr.neatmonster.nocheatplus.compat.BridgeMisc;
|
import fr.neatmonster.nocheatplus.compat.BridgeMisc;
|
||||||
import fr.neatmonster.nocheatplus.compat.versions.ClientVersion;
|
import fr.neatmonster.nocheatplus.compat.versions.ClientVersion;
|
||||||
|
import fr.neatmonster.nocheatplus.compat.versions.ServerVersion;
|
||||||
import fr.neatmonster.nocheatplus.components.NoCheatPlusAPI;
|
import fr.neatmonster.nocheatplus.components.NoCheatPlusAPI;
|
||||||
import fr.neatmonster.nocheatplus.components.data.ICheckData;
|
import fr.neatmonster.nocheatplus.components.data.ICheckData;
|
||||||
import fr.neatmonster.nocheatplus.components.data.IData;
|
import fr.neatmonster.nocheatplus.components.data.IData;
|
||||||
@@ -73,12 +78,11 @@ import fr.neatmonster.nocheatplus.worlds.WorldFactoryArgument;
|
|||||||
* @author asofold
|
* @author asofold
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("UnstableApiUsage")
|
||||||
public class CombinedListener extends CheckListener implements JoinLeaveListener {
|
public class CombinedListener extends CheckListener implements JoinLeaveListener {
|
||||||
|
|
||||||
protected final Improbable improbable = addCheck(new Improbable());
|
protected final Improbable improbable = addCheck(new Improbable());
|
||||||
|
|
||||||
protected final MunchHausen munchHausen = addCheck(new MunchHausen());
|
|
||||||
|
|
||||||
private final Counters counters = NCPAPIProvider.getNoCheatPlusAPI().getGenericInstance(Counters.class);
|
private final Counters counters = NCPAPIProvider.getNoCheatPlusAPI().getGenericInstance(Counters.class);
|
||||||
|
|
||||||
private final int idFakeInvulnerable = counters.registerKey("fakeinvulnerable");
|
private final int idFakeInvulnerable = counters.registerKey("fakeinvulnerable");
|
||||||
@@ -115,7 +119,7 @@ public class CombinedListener extends CheckListener implements JoinLeaveListener
|
|||||||
// If -for whatever reason- the event is not available, handle toggle gliding with PMEs
|
// If -for whatever reason- the event is not available, handle toggle gliding with PMEs
|
||||||
queuedComponents.add(new Listener() {
|
queuedComponents.add(new Listener() {
|
||||||
// We can ignore the event here, if cancelled, since the player will not be able to move anyway
|
// We can ignore the event here, if cancelled, since the player will not be able to move anyway
|
||||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||||
public void onEventlessToggleGlide(final PlayerMoveEvent event) {
|
public void onEventlessToggleGlide(final PlayerMoveEvent event) {
|
||||||
final PlayerMoveData lastMove = DataManager.getPlayerData(event.getPlayer()).getGenericInstance(MovingData.class).playerMoves.getFirstPastMove();
|
final PlayerMoveData lastMove = DataManager.getPlayerData(event.getPlayer()).getGenericInstance(MovingData.class).playerMoves.getFirstPastMove();
|
||||||
final PlayerMoveData thisMove = DataManager.getPlayerData(event.getPlayer()).getGenericInstance(MovingData.class).playerMoves.getCurrentMove();
|
final PlayerMoveData thisMove = DataManager.getPlayerData(event.getPlayer()).getGenericInstance(MovingData.class).playerMoves.getCurrentMove();
|
||||||
@@ -130,12 +134,20 @@ public class CombinedListener extends CheckListener implements JoinLeaveListener
|
|||||||
}
|
}
|
||||||
if (BridgeMisc.hasEntityChangePoseEvent()) {
|
if (BridgeMisc.hasEntityChangePoseEvent()) {
|
||||||
queuedComponents.add(new Listener() {
|
queuedComponents.add(new Listener() {
|
||||||
@EventHandler(priority = EventPriority.LOWEST)
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
public void onChangingPose(final EntityPoseChangeEvent event) {
|
public void onChangingPose(final EntityPoseChangeEvent event) {
|
||||||
handlePoseChangeEvent(event.getEntity(), event.getPose());
|
handlePoseChangeEvent(event.getEntity(), event.getPose());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (Bridge1_13.hasPlayerRiptideEvent()) {
|
||||||
|
queuedComponents.add(new Listener() {
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
|
public void onTridentRelease(final PlayerRiptideEvent event) {
|
||||||
|
onReleasingTrident(event.getPlayer(), event.getVelocity().clone());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Register Data, Listener and Config.
|
// Register Data, Listener and Config.
|
||||||
api.register(api.newRegistrationContext()
|
api.register(api.newRegistrationContext()
|
||||||
@@ -162,6 +174,18 @@ public class CombinedListener extends CheckListener implements JoinLeaveListener
|
|||||||
.context() //
|
.context() //
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether the player has released a trident with riptide on, which will propel the player in air or water.
|
||||||
|
*
|
||||||
|
* @param player
|
||||||
|
* @param vel Riptide velocity.
|
||||||
|
*/
|
||||||
|
public void onReleasingTrident(Player player, Vector vel) {
|
||||||
|
final IPlayerData pData = DataManager.getPlayerData(player);
|
||||||
|
final MovingData data = pData.getGenericInstance(MovingData.class);
|
||||||
|
data.setTridentReleaseEvent(AlmostBoolean.MAYBE);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Judge if the player can effectively start to glide.
|
* Judge if the player can effectively start to glide.
|
||||||
@@ -256,7 +280,7 @@ public class CombinedListener extends CheckListener implements JoinLeaveListener
|
|||||||
* (see: {@code LocalPlayer.java -> aiStep() -> isMovingSlowly() -> isVisuallyCrawling()}).</li>
|
* (see: {@code LocalPlayer.java -> aiStep() -> isMovingSlowly() -> isVisuallyCrawling()}).</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>
|
* <p>
|
||||||
* Therefore, Bukkit's methods/event can no longer be relied upon to determine if a player should have reduced movement speed, as
|
* Therefore, Bukkit's methods/event can no longer be relied upon to determine if a player should move slower, as
|
||||||
* they are associated with shift key presses and not player poses; and we need to know this for the horizontal speed prediction
|
* they are associated with shift key presses and not player poses; and we need to know this for the horizontal speed prediction
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
@@ -280,16 +304,77 @@ public class CombinedListener extends CheckListener implements JoinLeaveListener
|
|||||||
final MovingConfig cc = pData.getGenericInstance(MovingConfig.class);
|
final MovingConfig cc = pData.getGenericInstance(MovingConfig.class);
|
||||||
if (newPose.equals(Pose.SWIMMING) && !BlockProperties.isInWater(player, player.getLocation(), cc.yOnGround)) {
|
if (newPose.equals(Pose.SWIMMING) && !BlockProperties.isInWater(player, player.getLocation(), cc.yOnGround)) {
|
||||||
// isVisuallyCrawling()...
|
// isVisuallyCrawling()...
|
||||||
pData.setIsInCrouchingPoseState(true);
|
pData.setCrouching(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (newPose.equals(Pose.SNEAKING)) {
|
if (newPose.equals(Pose.SNEAKING)) {
|
||||||
// Sneaking...
|
// Sneaking...
|
||||||
pData.setIsInCrouchingPoseState(true);
|
pData.setCrouching(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Entered another pose...
|
// Entered another pose...
|
||||||
pData.setIsInCrouchingPoseState(false);
|
pData.setCrouching(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final boolean isLowerthan1_7 = ServerVersion.isLowerThan("1.7");
|
||||||
|
private final boolean isLowerthan1_8 = ServerVersion.isLowerThan("1.8");
|
||||||
|
private final boolean isLowerthan1_9 = ServerVersion.isLowerThan("1.9");
|
||||||
|
private final boolean isLowerthan1_10 = ServerVersion.isLowerThan("1.10");
|
||||||
|
private final boolean isLowerthan1_11_1 = ServerVersion.isLowerThan("1.11.1");
|
||||||
|
private final boolean isB1_11_2_1_12_1 = ServerVersion.isMinecraftVersionBetween("1.11.1", true, "1.12.2", false);
|
||||||
|
private final boolean isLowerthan1_13 = ServerVersion.isLowerThan("1.13");
|
||||||
|
private final boolean isLowerthan1_14 = ServerVersion.isLowerThan("1.14");
|
||||||
|
private final boolean isLowerthan1_15 = ServerVersion.isLowerThan("1.15");
|
||||||
|
private final boolean isLowerthan1_18 = ServerVersion.isLowerThan("1.18");
|
||||||
|
private final boolean is1_18 = ServerVersion.isMinecraftVersionBetween("1.18", true, "1.18.2", false);
|
||||||
|
private final boolean isLowerthan1_20 = ServerVersion.isLowerThan("1.20");
|
||||||
|
private final boolean isB1_20_1_20_5 = ServerVersion.isMinecraftVersionBetween("1.20", true, "1.20.6", false);
|
||||||
|
private final boolean isLowerthan1_21_2 = ServerVersion.isLowerThan("1.21.2");
|
||||||
|
|
||||||
|
private ClientVersion configurePlayerVersion(final IPlayerData pData, final boolean debug) {
|
||||||
|
if (isLowerthan1_7) {
|
||||||
|
return ClientVersion.LOWER_THAN_KNOWN_VERSIONS;
|
||||||
|
}
|
||||||
|
if (isLowerthan1_8) {
|
||||||
|
return ClientVersion.V_1_7_10;
|
||||||
|
}
|
||||||
|
if (isLowerthan1_9) {
|
||||||
|
return ClientVersion.V_1_8;
|
||||||
|
}
|
||||||
|
if (isLowerthan1_10) {
|
||||||
|
return ClientVersion.V_1_9;
|
||||||
|
}
|
||||||
|
if (isLowerthan1_11_1) {
|
||||||
|
return ClientVersion.V_1_10;
|
||||||
|
}
|
||||||
|
if (isB1_11_2_1_12_1) {
|
||||||
|
return ClientVersion.V_1_11_1;
|
||||||
|
}
|
||||||
|
if (isLowerthan1_13) {
|
||||||
|
return ClientVersion.V_1_12_2;
|
||||||
|
}
|
||||||
|
if (isLowerthan1_14) {
|
||||||
|
return ClientVersion.V_1_13;
|
||||||
|
}
|
||||||
|
if (isLowerthan1_15) {
|
||||||
|
return ClientVersion.V_1_14;
|
||||||
|
}
|
||||||
|
if (isLowerthan1_18) {
|
||||||
|
return ClientVersion.V_1_17;
|
||||||
|
}
|
||||||
|
if (is1_18) {
|
||||||
|
return ClientVersion.V_1_18;
|
||||||
|
}
|
||||||
|
if (isLowerthan1_20) {
|
||||||
|
return ClientVersion.V_1_18_2;
|
||||||
|
}
|
||||||
|
if (isB1_20_1_20_5) {
|
||||||
|
return ClientVersion.V_1_20;
|
||||||
|
}
|
||||||
|
if (isLowerthan1_21_2) {
|
||||||
|
return ClientVersion.V_1_20_6;
|
||||||
|
}
|
||||||
|
return ClientVersion.HIGHER_THAN_KNOWN_VERSIONS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -298,6 +383,8 @@ public class CombinedListener extends CheckListener implements JoinLeaveListener
|
|||||||
final CombinedData data = pData.getGenericInstance(CombinedData.class);
|
final CombinedData data = pData.getGenericInstance(CombinedData.class);
|
||||||
final CombinedConfig cc = pData.getGenericInstance(CombinedConfig.class);
|
final CombinedConfig cc = pData.getGenericInstance(CombinedConfig.class);
|
||||||
final boolean debug = pData.isDebugActive(checkType);
|
final boolean debug = pData.isDebugActive(checkType);
|
||||||
|
// NCP will first set the ID version. If: protocolsupport/viaversion, cncp are all present, then CNCP will override the version set by protocolsupport/viaversion.
|
||||||
|
pData.setClientVersionID(configurePlayerVersion(pData, debug).getProtocolVersion());
|
||||||
if (cc.invulnerableCheck
|
if (cc.invulnerableCheck
|
||||||
&& (cc.invulnerableTriggerAlways || cc.invulnerableTriggerFallDistance && player.getFallDistance() > 0)) {
|
&& (cc.invulnerableTriggerAlways || cc.invulnerableTriggerFallDistance && player.getFallDistance() > 0)) {
|
||||||
// TODO: maybe make a heuristic for small fall distances with ground under feet (prevents future abuse with jumping) ?
|
// TODO: maybe make a heuristic for small fall distances with ground under feet (prevents future abuse with jumping) ?
|
||||||
@@ -317,15 +404,31 @@ public class CombinedListener extends CheckListener implements JoinLeaveListener
|
|||||||
if (BridgeMisc.hasEntityChangePoseEvent()) {
|
if (BridgeMisc.hasEntityChangePoseEvent()) {
|
||||||
if (player.getPose().equals(Pose.SWIMMING) && !BlockProperties.isInWater(player, player.getLocation(), mCC.yOnGround)) {
|
if (player.getPose().equals(Pose.SWIMMING) && !BlockProperties.isInWater(player, player.getLocation(), mCC.yOnGround)) {
|
||||||
// isVisuallyCrawling()...
|
// isVisuallyCrawling()...
|
||||||
pData.setIsInCrouchingPoseState(true);
|
pData.setCrouching(true);
|
||||||
}
|
}
|
||||||
else if (player.getPose().equals(Pose.SNEAKING)) {
|
else if (player.getPose().equals(Pose.SNEAKING)) {
|
||||||
// Sneaking...
|
// Sneaking...
|
||||||
pData.setIsInCrouchingPoseState(true);
|
pData.setCrouching(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
|
public void onLeftClickingBlocksOrAir(final PlayerInteractEvent event) {
|
||||||
|
final Player player = event.getPlayer();
|
||||||
|
if (event.getAction() != Action.LEFT_CLICK_AIR && event.getAction() != Action.LEFT_CLICK_BLOCK) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (BridgeMisc.mayLungeForward(player)) {
|
||||||
|
final IPlayerData pData = DataManager.getPlayerData(player);
|
||||||
|
final PlayerMoveData thisMove = pData.getGenericInstance(MovingData.class).playerMoves.getCurrentMove();
|
||||||
|
thisMove.lungingForward = true;
|
||||||
|
if (pData.isDebugActive(CheckType.MOVING)) {
|
||||||
|
debug(player, "Set lunging forward flag in this move.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.MONITOR)
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
public void onPlayerRespawn(final PlayerRespawnEvent event) {
|
public void onPlayerRespawn(final PlayerRespawnEvent event) {
|
||||||
final Player player = event.getPlayer();
|
final Player player = event.getPlayer();
|
||||||
@@ -344,7 +447,7 @@ public class CombinedListener extends CheckListener implements JoinLeaveListener
|
|||||||
final CombinedData data = pData.getGenericInstance(CombinedData.class);
|
final CombinedData data = pData.getGenericInstance(CombinedData.class);
|
||||||
data.resetImprobableData();
|
data.resetImprobableData();
|
||||||
pData.setSprintingState(false);
|
pData.setSprintingState(false);
|
||||||
pData.setIsInCrouchingPoseState(false);
|
pData.setCrouching(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.MONITOR)
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
@@ -377,23 +480,22 @@ public class CombinedListener extends CheckListener implements JoinLeaveListener
|
|||||||
// TODO: Actually, do legacy players (i.e.: 1.9 player) fire EntityPoseChangeEvent on newer servers?
|
// TODO: Actually, do legacy players (i.e.: 1.9 player) fire EntityPoseChangeEvent on newer servers?
|
||||||
if (!event.isSneaking()) {
|
if (!event.isSneaking()) {
|
||||||
// Player was sneaking and they now toggled it off.
|
// Player was sneaking and they now toggled it off.
|
||||||
pData.setIsInCrouchingPoseState(false);
|
pData.setCrouching(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (Bridge1_13.isSwimming(event.getPlayer()) || Bridge1_9.isGliding(event.getPlayer()) || Bridge1_13.isRiptiding(event.getPlayer())) {
|
if (Bridge1_13.isSwimming(event.getPlayer()) || Bridge1_9.isGliding(event.getPlayer()) || Bridge1_13.isRiptiding(event.getPlayer())) {
|
||||||
// Bukkit's ambiguous "isSneaking()" method would return true for all these cases, but like we've said above, sneaking is determined by player poses, not shift key presses. Just ignore.
|
// Bukkit's ambiguous "isShift()" method would return true for all these cases, but like we've said above, sneaking is determined by player poses, not shift key presses. Just ignore.
|
||||||
pData.setIsInCrouchingPoseState(false);
|
pData.setCrouching(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Legacy clients can only enter the CROUCHING pose if they press the shift key, so in this case shifting does equal sneaking.
|
// Legacy clients can only enter the CROUCHING pose if they press the shift key, so in this case shifting does equal sneaking.
|
||||||
pData.setIsInCrouchingPoseState(true);
|
pData.setCrouching(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** NOTE: Cancelling does nothing. It won't stop players from sprinting. So, don't ignore cancelled events. */
|
/** NOTE: Cancelling does nothing. It won't stop players from sprinting. So, don't ignore cancelled events. */
|
||||||
@EventHandler(priority = EventPriority.MONITOR)
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
public void onToggleSprint(final PlayerToggleSprintEvent event) {
|
public void onToggleSprint(final PlayerToggleSprintEvent event) {
|
||||||
final IPlayerData pData = DataManager.getPlayerData(event.getPlayer());
|
final IPlayerData pData = DataManager.getPlayerData(event.getPlayer());
|
||||||
final PlayerMoveData lastMove = pData.getGenericInstance(MovingData.class).playerMoves.getFirstPastMove();
|
|
||||||
if (!event.isSprinting()) {
|
if (!event.isSprinting()) {
|
||||||
// Player was sprinting and they now toggled it off.
|
// Player was sprinting and they now toggled it off.
|
||||||
pData.setSprintingState(false);
|
pData.setSprintingState(false);
|
||||||
@@ -471,8 +573,14 @@ public class CombinedListener extends CheckListener implements JoinLeaveListener
|
|||||||
aux.returnPlayerMoveInfo(moveInfo);
|
aux.returnPlayerMoveInfo(moveInfo);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
final PlayerMoveData thisMove = data.playerMoves.getCurrentMove();
|
||||||
|
if (BridgeMisc.mayLungeForward(player)) {
|
||||||
|
thisMove.lungingForward = true;
|
||||||
|
if (pData.isDebugActive(CheckType.MOVING)) {
|
||||||
|
debug(player, "Set lunging forward flag in this move.");
|
||||||
|
}
|
||||||
|
}
|
||||||
if (pData.isSprinting() || !BlockProperties.isAir(stack) && stack.getEnchantmentLevel(Enchantment.KNOCKBACK) > 0) {
|
if (pData.isSprinting() || !BlockProperties.isAir(stack) && stack.getEnchantmentLevel(Enchantment.KNOCKBACK) > 0) {
|
||||||
final PlayerMoveData thisMove = data.playerMoves.getCurrentMove();
|
|
||||||
thisMove.hasAttackSlowDown = true;
|
thisMove.hasAttackSlowDown = true;
|
||||||
if (pData.isDebugActive(CheckType.MOVING)) {
|
if (pData.isDebugActive(CheckType.MOVING)) {
|
||||||
debug(player, "Set attack slow down flag in this move.");
|
debug(player, "Set attack slow down flag in this move.");
|
||||||
@@ -513,14 +621,4 @@ public class CombinedListener extends CheckListener implements JoinLeaveListener
|
|||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
counters.addPrimaryThread(idFakeInvulnerable, 1);
|
counters.addPrimaryThread(idFakeInvulnerable, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** We listen for player fishing for the VERY critical and super important munch hausen check */
|
|
||||||
@EventHandler(priority = EventPriority.LOWEST)
|
|
||||||
public void onFishing(final PlayerFishEvent event) {
|
|
||||||
// Check also in case of cancelled events.
|
|
||||||
final Player player = event.getPlayer();
|
|
||||||
if (munchHausen.isEnabled(player) && munchHausen.checkFish(player, event.getCaught(), event.getState())) {
|
|
||||||
event.setCancelled(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -26,62 +26,74 @@ import fr.neatmonster.nocheatplus.players.IPlayerData;
|
|||||||
import fr.neatmonster.nocheatplus.utilities.TickTask;
|
import fr.neatmonster.nocheatplus.utilities.TickTask;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This check combines different other checks frequency and occurrences into one count.
|
* Meta-check that aggregates suspicion weights from multiple checks over time,
|
||||||
* (Intended for static access by other checks.)
|
* and evaluates if the overall behavior is statistically unlikely
|
||||||
|
* ("improbable") for a legitimate player.
|
||||||
*
|
*
|
||||||
* @author asofold
|
* Intended for static access by other checks, which can feed weights into this
|
||||||
|
* aggregator.
|
||||||
*
|
*
|
||||||
|
* Buckets are used internally to group suspicion scores into time slices.
|
||||||
*/
|
*/
|
||||||
public class Improbable extends Check implements IDisableListener {
|
public class Improbable extends Check implements IDisableListener {
|
||||||
|
|
||||||
private static Improbable instance = null;
|
private static Improbable instance = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return if to cancel.
|
* Run the Improbable check for a given player with a new weight.
|
||||||
* @param player
|
*
|
||||||
* @param weights
|
* @param player The player being checked.
|
||||||
* @param now
|
* @param weight The suspicion weight to add (severity of the detected behavior).
|
||||||
* @return
|
* @param now Current system time in ms.
|
||||||
|
* @param tags Optional tags for additional context.
|
||||||
|
* @param pData
|
||||||
|
* @return true if the action should be cancelled, false otherwise.
|
||||||
*/
|
*/
|
||||||
public static final boolean check(final Player player, final float weight, final long now, final String tags, final IPlayerData pData) {
|
public static final boolean check(final Player player, final float weight, final long now, final String tags, final IPlayerData pData) {
|
||||||
return instance.checkImprobable(player, weight, now, tags, pData);
|
return instance.checkImprobable(player, weight, now, tags, pData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update time references only, no feeding and no violation.
|
* Advance the internal time window without adding new suspicion.
|
||||||
* @param player
|
* Useful for keeping buckets aligned to the current time without
|
||||||
* @param now
|
* incrementing scores or triggering violations.
|
||||||
* @param pData
|
*
|
||||||
|
* @param now Current system time in ms.
|
||||||
|
* @param pData Player data container.
|
||||||
*/
|
*/
|
||||||
public static final void update(final long now, final IPlayerData pData) {
|
public static final void update(final long now, final IPlayerData pData) {
|
||||||
pData.getGenericInstance(CombinedData.class).improbableCount.update(now);
|
pData.getGenericInstance(CombinedData.class).improbableCount.update(now);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update time references only, no feeding and no violation (convenience method).
|
* Advance the internal time window for a given player without
|
||||||
* @param player
|
* adding new suspicion.
|
||||||
* @param now
|
*
|
||||||
|
* @param player The player.
|
||||||
|
* @param now Current system time in ms.
|
||||||
*/
|
*/
|
||||||
public static final void update(final Player player, final long now) {
|
public static final void update(final Player player, final long now) {
|
||||||
update(now, DataManager.getPlayerData(player));
|
update(now, DataManager.getPlayerData(player));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Feed the check but no violations processing (convenience method).
|
* Add suspicion weight to the player's current bucket without a violation.
|
||||||
* @param player
|
*
|
||||||
* @param weight
|
* @param player The player.
|
||||||
* @param now
|
* @param weight The suspicion weight to add.
|
||||||
* @param pData
|
* @param now Current system time in ms.
|
||||||
|
* @param pData
|
||||||
*/
|
*/
|
||||||
public static final void feed(final Player player, final float weight, final long now, final IPlayerData pData) {
|
public static final void feed(final Player player, final float weight, final long now, final IPlayerData pData) {
|
||||||
pData.getGenericInstance(CombinedData.class).improbableCount.add(now, weight);
|
pData.getGenericInstance(CombinedData.class).improbableCount.add(now, weight);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Feed the check but no violations processing (convenience method).
|
* Add suspicion weight to the player's current bucket without a violation.
|
||||||
* @param player
|
*
|
||||||
* @param weight
|
* @param player The player.
|
||||||
* @param now
|
* @param weight The suspicion weight to add.
|
||||||
|
* @param now Current system time in ms.
|
||||||
*/
|
*/
|
||||||
public static void feed(final Player player, final float weight, long now) {
|
public static void feed(final Player player, final float weight, long now) {
|
||||||
feed(player, weight, now, DataManager.getPlayerData(player));
|
feed(player, weight, now, DataManager.getPlayerData(player));
|
||||||
@@ -97,14 +109,15 @@ public class Improbable extends Check implements IDisableListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks a player for improbable behaviour.
|
* Perform the full Improbable check for a player by adding a new weight
|
||||||
* @param player
|
* and evaluating both short-term and long-term suspicion levels.
|
||||||
* @param weight
|
*
|
||||||
* @param now
|
* @param player The player being checked.
|
||||||
* @param tags
|
* @param weight The suspicion weight to add.
|
||||||
|
* @param now Current system time in ms.
|
||||||
|
* @param tags Optional tags with context about the source of suspicion.
|
||||||
* @param pData
|
* @param pData
|
||||||
* @return true if to cancel
|
* @return true if the suspicious action should be cancelled, false otherwise.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
private boolean checkImprobable(final Player player, final float weight, final long now, final String tags, final IPlayerData pData) {
|
private boolean checkImprobable(final Player player, final float weight, final long now, final String tags, final IPlayerData pData) {
|
||||||
if (!pData.isCheckActive(type, player)) {
|
if (!pData.isCheckActive(type, player)) {
|
||||||
@@ -113,12 +126,12 @@ public class Improbable extends Check implements IDisableListener {
|
|||||||
final CombinedData data = pData.getGenericInstance(CombinedData.class);
|
final CombinedData data = pData.getGenericInstance(CombinedData.class);
|
||||||
final CombinedConfig cc = pData.getGenericInstance(CombinedConfig.class);
|
final CombinedConfig cc = pData.getGenericInstance(CombinedConfig.class);
|
||||||
data.improbableCount.add(now, weight);
|
data.improbableCount.add(now, weight);
|
||||||
/** Score of the first 3 seconds (a bucket covers 3 secs)*/
|
// Score of the first bucket (covers ~3 seconds).
|
||||||
final float shortTerm = data.improbableCount.bucketScore(0);
|
final float shortTerm = data.improbableCount.bucketScore(0);
|
||||||
double violation = 0.0;
|
double violation = 0.0;
|
||||||
boolean violated = false;
|
boolean violated = false;
|
||||||
if (shortTerm * 0.8f > cc.improbableLevel / 20.0) {
|
if (shortTerm * 0.8f > cc.improbableLevel / 20.0) {
|
||||||
/** Lag factor for the first window lag (3 seconds) */
|
// Lag adjustment factor for the short-term window (~3 seconds).
|
||||||
final float lagFactor = pData.getCurrentWorldData().shouldAdjustToLag(type) ? TickTask.getLag(data.improbableCount.bucketDuration(), true) : 1f;
|
final float lagFactor = pData.getCurrentWorldData().shouldAdjustToLag(type) ? TickTask.getLag(data.improbableCount.bucketDuration(), true) : 1f;
|
||||||
// Re-check with lag adaptation.
|
// Re-check with lag adaptation.
|
||||||
if (shortTerm / lagFactor > cc.improbableLevel / 20.0) {
|
if (shortTerm / lagFactor > cc.improbableLevel / 20.0) {
|
||||||
@@ -126,10 +139,10 @@ public class Improbable extends Check implements IDisableListener {
|
|||||||
violated = true;
|
violated = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/** Sum score of all buckets, no weight */
|
// Total score across all buckets (~1 minute span).
|
||||||
final double fullTerm = data.improbableCount.score(1.0f);
|
final double fullTerm = data.improbableCount.score(1.0f);
|
||||||
if (fullTerm > cc.improbableLevel) {
|
if (fullTerm > cc.improbableLevel) {
|
||||||
/** Full window lag factor, 1 minute */
|
// Lag adjustment factor for the full window (~1 minute).
|
||||||
final float lagFactor = pData.getCurrentWorldData().shouldAdjustToLag(type) ? TickTask.getLag(data.improbableCount.bucketDuration() * data.improbableCount.numberOfBuckets(), true) : 1f;
|
final float lagFactor = pData.getCurrentWorldData().shouldAdjustToLag(type) ? TickTask.getLag(data.improbableCount.bucketDuration() * data.improbableCount.numberOfBuckets(), true) : 1f;
|
||||||
// Re-check with lag adaptation.
|
// Re-check with lag adaptation.
|
||||||
if (fullTerm / lagFactor > cc.improbableLevel) {
|
if (fullTerm / lagFactor > cc.improbableLevel) {
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
/*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package fr.neatmonster.nocheatplus.checks.combined;
|
|
||||||
|
|
||||||
import org.bukkit.entity.Entity;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.event.player.PlayerFishEvent.State;
|
|
||||||
|
|
||||||
import fr.neatmonster.nocheatplus.checks.Check;
|
|
||||||
import fr.neatmonster.nocheatplus.checks.CheckType;
|
|
||||||
import fr.neatmonster.nocheatplus.players.DataManager;
|
|
||||||
import fr.neatmonster.nocheatplus.players.IPlayerData;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Very, very important check.
|
|
||||||
* TODO: Demote this shit into a simple boolean config option...
|
|
||||||
* @author asofold
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class MunchHausen extends Check {
|
|
||||||
public MunchHausen(){
|
|
||||||
super(CheckType.COMBINED_MUNCHHAUSEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean checkFish(final Player player, final Entity caught, final State state) {
|
|
||||||
if (caught == null || !(caught instanceof Player)) return false;
|
|
||||||
final Player caughtPlayer = (Player) caught;
|
|
||||||
final IPlayerData pData = DataManager.getPlayerData(player);
|
|
||||||
final CombinedData data = pData.getGenericInstance(CombinedData.class);
|
|
||||||
if (player.equals(caughtPlayer)){
|
|
||||||
data.munchHausenVL += 1.0;
|
|
||||||
if (executeActions(player, data.munchHausenVL, 1.0,
|
|
||||||
pData.getGenericInstance(CombinedConfig.class).munchHausenActions).willCancel()){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else data.munchHausenVL *= 0.96;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -108,7 +108,7 @@ public class Critical extends Check {
|
|||||||
// 0: A full jump from ground requires more than 6 phases/events.
|
// 0: A full jump from ground requires more than 6 phases/events.
|
||||||
|| mData.sfJumpPhase > 0 && mData.sfJumpPhase <= mData.liftOffEnvelope.getMaxJumpPhase(mData.jumpAmplifier)
|
|| mData.sfJumpPhase > 0 && mData.sfJumpPhase <= mData.liftOffEnvelope.getMaxJumpPhase(mData.jumpAmplifier)
|
||||||
&& !moveInfo.from.seekCollisionAbove(0.2)
|
&& !moveInfo.from.seekCollisionAbove(0.2)
|
||||||
&& (lastMove.verVelUsed == null || !lastMove.verVelUsed.get(0).hasFlag(VelocityFlags.ORIGIN_BLOCK_BOUNCE))
|
&& (lastMove.verVelUsed.isEmpty() || !lastMove.verVelUsed.get(0).hasFlag(VelocityFlags.ORIGIN_BLOCK_BOUNCE))
|
||||||
// 0: Always invalidate critical hits if we judge the player to be on ground (given enough fall distance)
|
// 0: Always invalidate critical hits if we judge the player to be on ground (given enough fall distance)
|
||||||
|| Math.abs(ncpFallDistance - mcFallDistance) > 1e-5 && (moveInfo.from.isOnGround() || lastMove.touchedGroundWorkaround)
|
|| Math.abs(ncpFallDistance - mcFallDistance) > 1e-5 && (moveInfo.from.isOnGround() || lastMove.touchedGroundWorkaround)
|
||||||
// (Let SurvivalFly catch low-jumps).
|
// (Let SurvivalFly catch low-jumps).
|
||||||
|
|||||||
@@ -46,12 +46,15 @@ import fr.neatmonster.nocheatplus.checks.moving.location.tracking.LocationTrace;
|
|||||||
import fr.neatmonster.nocheatplus.checks.moving.location.tracking.LocationTrace.ITraceEntry;
|
import fr.neatmonster.nocheatplus.checks.moving.location.tracking.LocationTrace.ITraceEntry;
|
||||||
import fr.neatmonster.nocheatplus.checks.moving.player.UnusedVelocity;
|
import fr.neatmonster.nocheatplus.checks.moving.player.UnusedVelocity;
|
||||||
import fr.neatmonster.nocheatplus.checks.moving.velocity.VelocityFlags;
|
import fr.neatmonster.nocheatplus.checks.moving.velocity.VelocityFlags;
|
||||||
import fr.neatmonster.nocheatplus.compat.*;
|
import fr.neatmonster.nocheatplus.compat.Bridge1_9;
|
||||||
|
import fr.neatmonster.nocheatplus.compat.BridgeMisc;
|
||||||
|
import fr.neatmonster.nocheatplus.compat.IBridgeCrossPlugin;
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.BridgeEnchant;
|
import fr.neatmonster.nocheatplus.compat.bukkit.BridgeEnchant;
|
||||||
import fr.neatmonster.nocheatplus.compat.bukkit.BridgeHealth;
|
import fr.neatmonster.nocheatplus.compat.bukkit.BridgeHealth;
|
||||||
import fr.neatmonster.nocheatplus.components.NoCheatPlusAPI;
|
import fr.neatmonster.nocheatplus.components.NoCheatPlusAPI;
|
||||||
import fr.neatmonster.nocheatplus.components.data.ICheckData;
|
import fr.neatmonster.nocheatplus.components.data.ICheckData;
|
||||||
import fr.neatmonster.nocheatplus.components.data.IData;
|
import fr.neatmonster.nocheatplus.components.data.IData;
|
||||||
|
import fr.neatmonster.nocheatplus.components.registry.activation.Activation;
|
||||||
import fr.neatmonster.nocheatplus.components.registry.event.IGenericInstanceHandle;
|
import fr.neatmonster.nocheatplus.components.registry.event.IGenericInstanceHandle;
|
||||||
import fr.neatmonster.nocheatplus.components.registry.factory.IFactoryOne;
|
import fr.neatmonster.nocheatplus.components.registry.factory.IFactoryOne;
|
||||||
import fr.neatmonster.nocheatplus.components.registry.feature.JoinLeaveListener;
|
import fr.neatmonster.nocheatplus.components.registry.feature.JoinLeaveListener;
|
||||||
@@ -62,8 +65,8 @@ import fr.neatmonster.nocheatplus.players.DataManager;
|
|||||||
import fr.neatmonster.nocheatplus.players.IPlayerData;
|
import fr.neatmonster.nocheatplus.players.IPlayerData;
|
||||||
import fr.neatmonster.nocheatplus.players.PlayerFactoryArgument;
|
import fr.neatmonster.nocheatplus.players.PlayerFactoryArgument;
|
||||||
import fr.neatmonster.nocheatplus.stats.Counters;
|
import fr.neatmonster.nocheatplus.stats.Counters;
|
||||||
import fr.neatmonster.nocheatplus.utilities.entity.InventoryUtil;
|
|
||||||
import fr.neatmonster.nocheatplus.utilities.TickTask;
|
import fr.neatmonster.nocheatplus.utilities.TickTask;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.entity.InventoryUtil;
|
||||||
import fr.neatmonster.nocheatplus.utilities.location.LocUtil;
|
import fr.neatmonster.nocheatplus.utilities.location.LocUtil;
|
||||||
import fr.neatmonster.nocheatplus.utilities.map.BlockProperties;
|
import fr.neatmonster.nocheatplus.utilities.map.BlockProperties;
|
||||||
import fr.neatmonster.nocheatplus.utilities.math.MathUtil;
|
import fr.neatmonster.nocheatplus.utilities.math.MathUtil;
|
||||||
@@ -121,6 +124,8 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
|
|||||||
|
|
||||||
/* Debug */
|
/* Debug */
|
||||||
private final int idCancelDead = counters.registerKey("cancel.dead");
|
private final int idCancelDead = counters.registerKey("cancel.dead");
|
||||||
|
|
||||||
|
private final boolean protocolLibPresent = new Activation().pluginExist("ProtocolLib").isAvailable();
|
||||||
|
|
||||||
// Assume it to stay the same all time.
|
// Assume it to stay the same all time.
|
||||||
private final IGenericInstanceHandle<IBridgeCrossPlugin> crossPlugin = NCPAPIProvider.getNoCheatPlusAPI().getGenericInstanceHandle(IBridgeCrossPlugin.class);
|
private final IGenericInstanceHandle<IBridgeCrossPlugin> crossPlugin = NCPAPIProvider.getNoCheatPlusAPI().getGenericInstanceHandle(IBridgeCrossPlugin.class);
|
||||||
@@ -260,10 +265,10 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
|
|||||||
// Can't attack with an inventory open.
|
// Can't attack with an inventory open.
|
||||||
if (cc.enforceClosedInventory && !damaged.isDead()) {
|
if (cc.enforceClosedInventory && !damaged.isDead()) {
|
||||||
if (InventoryUtil.hasInventoryOpen(player)) {
|
if (InventoryUtil.hasInventoryOpen(player)) {
|
||||||
|
// Do not call Open.checkClose here to prevent a disabled open check from deactivating this feature.
|
||||||
player.closeInventory();
|
player.closeInventory();
|
||||||
// If closed, do reset InventoryData's stored data.
|
|
||||||
pData.getGenericInstance(InventoryData.class).inventoryOpenTime = 0;
|
pData.getGenericInstance(InventoryData.class).inventoryOpenTime = 0;
|
||||||
Improbable.feed(player, 0.5f, System.currentTimeMillis());
|
Improbable.feed(player, 1.5f, System.currentTimeMillis());
|
||||||
// (No cancel here! Less invasive for PvP)
|
// (No cancel here! Less invasive for PvP)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -272,7 +277,7 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
|
|||||||
if (cc.enforceItemRelease && !damaged.isDead()) {
|
if (cc.enforceItemRelease && !damaged.isDead()) {
|
||||||
if (BridgeMisc.isUsingItem(player)) {
|
if (BridgeMisc.isUsingItem(player)) {
|
||||||
pData.requestItemUseResync();
|
pData.requestItemUseResync();
|
||||||
Improbable.feed(player, 0.5f, System.currentTimeMillis());
|
Improbable.feed(player, 1.5f, System.currentTimeMillis());
|
||||||
// (No cancel here! Less invasive for PvP)
|
// (No cancel here! Less invasive for PvP)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -661,19 +666,21 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
|
|||||||
// Plugin compatibility thing.
|
// Plugin compatibility thing.
|
||||||
damagedData.lastNoDamageTicks = ndt;
|
damagedData.lastNoDamageTicks = ndt;
|
||||||
}
|
}
|
||||||
// Knock-back calculation (1.8: events only fire if they would count by ndt).
|
if (!protocolLibPresent) {
|
||||||
switch (event.getCause()) {
|
// Knock-back calculation (1.8: events only fire if they would count by ndt).
|
||||||
case ENTITY_ATTACK:
|
switch (event.getCause()) {
|
||||||
if (event instanceof EntityDamageByEntityEvent) {
|
case ENTITY_ATTACK:
|
||||||
final Entity entity = ((EntityDamageByEntityEvent) event).getDamager();
|
if (event instanceof EntityDamageByEntityEvent) {
|
||||||
if ((entity instanceof Player) && !damagedPlayer.isInsideVehicle()
|
final Entity entity = ((EntityDamageByEntityEvent) event).getDamager();
|
||||||
&& damagedPData.getGenericInstance(FightConfig.class).knockBackVelocityPvP) {
|
if ((entity instanceof Player) && !damagedPlayer.isInsideVehicle()
|
||||||
// TODO: Use the velocity event that is sent anyway and replace x/z if 0 (queue max. values).
|
&& damagedPData.getGenericInstance(FightConfig.class).knockBackVelocityPvP) {
|
||||||
applyKnockBack((Player) entity, damagedPlayer, damagedData, damagedPData);
|
// TODO: Use the velocity event that is sent anyway and replace x/z if 0 (queue max. values).
|
||||||
|
applyKnockBack((Player) entity, damagedPlayer, damagedData, damagedPData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
default:
|
||||||
default:
|
break;
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import fr.neatmonster.nocheatplus.checks.CheckType;
|
|||||||
import fr.neatmonster.nocheatplus.checks.ViolationData;
|
import fr.neatmonster.nocheatplus.checks.ViolationData;
|
||||||
import fr.neatmonster.nocheatplus.compat.MCAccess;
|
import fr.neatmonster.nocheatplus.compat.MCAccess;
|
||||||
import fr.neatmonster.nocheatplus.compat.blocks.changetracker.BlockChangeTracker.Direction;
|
import fr.neatmonster.nocheatplus.compat.blocks.changetracker.BlockChangeTracker.Direction;
|
||||||
|
import fr.neatmonster.nocheatplus.players.DataManager;
|
||||||
import fr.neatmonster.nocheatplus.utilities.collision.Axis;
|
import fr.neatmonster.nocheatplus.utilities.collision.Axis;
|
||||||
import fr.neatmonster.nocheatplus.utilities.collision.Axis.RichAxisData;
|
import fr.neatmonster.nocheatplus.utilities.collision.Axis.RichAxisData;
|
||||||
import fr.neatmonster.nocheatplus.utilities.collision.AxisAlignedBBUtils;
|
import fr.neatmonster.nocheatplus.utilities.collision.AxisAlignedBBUtils;
|
||||||
@@ -55,16 +56,25 @@ public class Visible extends Check {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform a visibility check to determine whether the player can see the target entity.<br>
|
* Perform a visibility check to determine whether the player can see the target entity.
|
||||||
* The check uses a ray tracing mechanism combined with neighbor block analysis to ascertain visibility, in order to workaround issues with packet inversion and de-synchronization.
|
* This check is look-agnostic: it only uses world geometry and the attacker's eye position; it does not consider yaw/pitch in any way.<br>
|
||||||
*
|
* The reason for this is to workaround issues with packet-inversion and de-synchronization.<p>
|
||||||
|
* The check performs a direct straight ray trace from the target entity (or its block) to the
|
||||||
|
* attacker's eye. If that ray is unobstructed, the hit is allowed.<br>
|
||||||
|
* If the direct ray is obstructed, the code attempts to find an alternative path by walking
|
||||||
|
* through neighbouring block coordinates. The search has been implemented in an opportunistic way: in each iteration, it picks a single neighbour that appears passable, updates the
|
||||||
|
* ray to originate from that neighbour and proceeds from there, thus, the neighbour walk is NOT exhaustive. There is no backtracking or branch
|
||||||
|
* enumeration. In fact, a visited set prevents re-checking the same block and {@link fr.neatmonster.nocheatplus.utilities.collision.tracing.ray.RayTracing.getMaxSteps()}
|
||||||
|
* bounds the search further. Because of these constraints, the algorithm may fail to discover some
|
||||||
|
* valid alternative paths (false positives), but the limit keeps the check performant.
|
||||||
|
*
|
||||||
* @param player The attacking player.
|
* @param player The attacking player.
|
||||||
* @param loc The location of the player's eye position.
|
* @param loc The location of the player's eye position.
|
||||||
* @param damaged The entity being attacked.
|
* @param damaged The entity being attacked.
|
||||||
* @param damagedIsFake Indicates whether the damaged entity is a fake entity.
|
* @param damagedIsFake Indicates whether the damaged entity is a fake entity (NPC).
|
||||||
* @param dLoc The location of the damaged entity.
|
* @param dLoc The location of the damaged entity.
|
||||||
* @param data Fight data.
|
* @param data
|
||||||
* @param cc Fight config.
|
* @param cc
|
||||||
* @return True if the hit is considered invalid due to visibility obstructions, false otherwise.
|
* @return True if the hit is considered invalid due to visibility obstructions, false otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean check(final Player player, final Location loc,
|
public boolean check(final Player player, final Location loc,
|
||||||
@@ -102,6 +112,7 @@ public class Visible extends Check {
|
|||||||
|
|
||||||
final BlockCache blockCache = this.wrapBlockCache.getBlockCache();
|
final BlockCache blockCache = this.wrapBlockCache.getBlockCache();
|
||||||
blockCache.setAccess(loc.getWorld());
|
blockCache.setAccess(loc.getWorld());
|
||||||
|
blockCache.setPlayerData(DataManager.getPlayerData(player));
|
||||||
rayTracing.setBlockCache(blockCache);
|
rayTracing.setBlockCache(blockCache);
|
||||||
// Perform a first ray trace check between the location of the damaged player and attacker's eye position.
|
// Perform a first ray trace check between the location of the damaged player and attacker's eye position.
|
||||||
rayTracing.set(dLoc.getX(), dLoc.getY(), dLoc.getZ(), eyeX, eyeY, eyeZ);
|
rayTracing.set(dLoc.getX(), dLoc.getY(), dLoc.getZ(), eyeX, eyeY, eyeZ);
|
||||||
|
|||||||
Loaded 100 of 221 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user