Compare commits
354
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4b30d940a | ||
|
|
c42b0f5b56 | ||
|
|
47ea342731 | ||
|
|
2f6ffc5d81 | ||
|
|
e9337f88e4 | ||
|
|
fb7295b396 | ||
|
|
2441ab68de | ||
|
|
5784943d8e | ||
|
|
d0b94bbda5 | ||
|
|
6f18622643 | ||
|
|
6b863655f1 | ||
|
|
631be06ec2 | ||
|
|
1c4361f2c6 | ||
|
|
2b5016442b | ||
|
|
e389e4b5e1 | ||
|
|
7d9cdb91b0 | ||
|
|
83e06bce77 | ||
|
|
42fd3e1cc7 | ||
|
|
26a771e4da | ||
|
|
fad1eb5736 | ||
|
|
1dc4361938 | ||
|
|
3d5491de4f | ||
|
|
75ff92c326 | ||
|
|
c530dcfa17 | ||
|
|
d720b8864f | ||
|
|
3e3dbf0f30 | ||
|
|
0bd154974b | ||
|
|
3f57026960 | ||
|
|
be9841fb9f | ||
|
|
d8b529e837 | ||
|
|
d01805ad13 | ||
|
|
f638772c5f | ||
|
|
b84b21a68f | ||
|
|
da230781e3 | ||
|
|
c91ef45062 | ||
|
|
9a87329618 | ||
|
|
24ee315c0b | ||
|
|
a4decb57f8 | ||
|
|
81feb5be3f | ||
|
|
2253fd0ec3 | ||
|
|
db4ad8e16d | ||
|
|
11571a2b8a | ||
|
|
c615c98d09 | ||
|
|
922b134467 | ||
|
|
8714e0e5a9 | ||
|
|
24570aa658 | ||
|
|
743a2d9cdb | ||
|
|
4b28aa7604 | ||
|
|
50425ada16 | ||
|
|
6d4f3650cc | ||
|
|
a5ced59a35 | ||
|
|
e9c6fac17a | ||
|
|
c68f7bd5f8 | ||
|
|
7015a9efa2 | ||
|
|
06bd96fcae | ||
|
|
db7bbef9b5 | ||
|
|
fa0d147e04 | ||
|
|
39e7cbe9ca | ||
|
|
84a1641968 | ||
|
|
26acd7b3a5 | ||
|
|
f51e5e4eae | ||
|
|
636eaf3d5d | ||
|
|
5b9fdd00c8 | ||
|
|
bc42035866 | ||
|
|
917eb371c8 | ||
|
|
ef45df9ea3 | ||
|
|
9f09fcd103 | ||
|
|
e1baecd32e | ||
|
|
44d66e0e5b | ||
|
|
0368f930b8 | ||
|
|
a83a0c8579 | ||
|
|
92b1ad28c2 | ||
|
|
5302f15b9d | ||
|
|
6a3eec1960 | ||
|
|
11425840ad | ||
|
|
8515df8224 | ||
|
|
fe5d6e34f2 | ||
|
|
e3e797e19a | ||
|
|
2293092048 | ||
|
|
147081ccc5 | ||
|
|
b924f65f91 | ||
|
|
259dd72f87 | ||
|
|
ab726bda78 | ||
|
|
163bbd7b0e | ||
|
|
968e166bde | ||
|
|
efe729c4d1 | ||
|
|
d40d19d6e3 | ||
|
|
b476b143f4 | ||
|
|
bb54697d9c | ||
|
|
24102c15e6 | ||
|
|
5100ef06c8 | ||
|
|
9b892eab85 | ||
|
|
eab5ca7f6d | ||
|
|
fd055c163e | ||
|
|
ea78d2ff40 | ||
|
|
6f074513f2 | ||
|
|
0719c5bdc6 | ||
|
|
36a394d142 | ||
|
|
0e08dbc6f2 | ||
|
|
003aa67a41 | ||
|
|
be45a43524 | ||
|
|
0d700848f8 | ||
|
|
1ff402036d | ||
|
|
9d36dbd2b8 | ||
|
|
c53c4378d8 | ||
|
|
eacf176f88 | ||
|
|
13afb067bb | ||
|
|
7af2f4b2e2 | ||
|
|
a1d8bfa6a1 | ||
|
|
47b3fe9ca2 | ||
|
|
0ce52ea107 | ||
|
|
d98fe31412 | ||
|
|
d8f338aada | ||
|
|
f5965c48b3 | ||
|
|
0df7077ddb | ||
|
|
222a9341bf | ||
|
|
35f6f7870a | ||
|
|
15b6e0ba27 | ||
|
|
968754efe0 | ||
|
|
a03c9e0194 | ||
|
|
734e7cbae8 | ||
|
|
52fe6691ac | ||
|
|
acd8d1192c | ||
|
|
8fc309cb76 | ||
|
|
45f16ec10f | ||
|
|
5ff8cb21c8 | ||
|
|
f8997b8739 | ||
|
|
ed21464c3a | ||
|
|
7954575216 | ||
|
|
1a11d3e733 | ||
|
|
487aa45a27 | ||
|
|
b7f9aeab06 | ||
|
|
6aaee382a6 | ||
|
|
8c84c506bf | ||
|
|
0622f92cf1 | ||
|
|
a8f81c141a | ||
|
|
efffceb9f2 | ||
|
|
75d1fc32c8 | ||
|
|
e087e93e2a | ||
|
|
e83c9967b4 | ||
|
|
2b1896020d | ||
|
|
61b17907ee | ||
|
|
339fb42f37 | ||
|
|
7985275697 | ||
|
|
661b60d07c | ||
|
|
d4f647e18b | ||
|
|
1761d853de | ||
|
|
42a6543f9b | ||
|
|
9356c6c323 | ||
|
|
a7a92f7e10 | ||
|
|
e149ca6be8 | ||
|
|
d44493743d | ||
|
|
7eb030de7d | ||
|
|
8fd4a4a87d | ||
|
|
8f71a427fd | ||
|
|
8b9bc356f2 | ||
|
|
89b6c8306e | ||
|
|
32a09aa36b | ||
|
|
3ea716c580 | ||
|
|
328d347845 | ||
|
|
d394c3d67c | ||
|
|
17b6b8a4e7 | ||
|
|
980226fab9 | ||
|
|
0f726a0f34 | ||
|
|
91ee057f4a | ||
|
|
c26afbc0af | ||
|
|
be56e28009 | ||
|
|
0697c425ca | ||
|
|
ca0ca471fb | ||
|
|
7b2aac3874 | ||
|
|
fbe8376ca0 | ||
|
|
22bc18859b | ||
|
|
25f9608d58 | ||
|
|
428f213e15 | ||
|
|
593d48a6e6 | ||
|
|
3dcf3c931a | ||
|
|
8cc45ddba3 | ||
|
|
6f23122657 | ||
|
|
c0b0759ed0 | ||
|
|
00252f3b97 | ||
|
|
531c49011b | ||
|
|
dea22454f7 | ||
|
|
12bda63785 | ||
|
|
c65e17cf0c | ||
|
|
753ca0e6ad | ||
|
|
8b85e1711d | ||
|
|
12b0dfd19d | ||
|
|
8c05d41423 | ||
|
|
c5ab0f5440 | ||
|
|
d92f5ef375 | ||
|
|
c313d6203a | ||
|
|
070f92aea2 | ||
|
|
4e3c5894b5 | ||
|
|
8cfb9f0f17 | ||
|
|
4e135e3f92 | ||
|
|
938522974b | ||
|
|
6994975387 | ||
|
|
6d3a172466 | ||
|
|
3aaa185617 | ||
|
|
980cd92333 | ||
|
|
89da2942c7 | ||
|
|
8ea905e053 | ||
|
|
885e959cdb | ||
|
|
02904a3dce | ||
|
|
e8d4bb2376 | ||
|
|
7695167578 | ||
|
|
94e5f63a65 | ||
|
|
9fa0f773d5 | ||
|
|
88111e6f7e | ||
|
|
9a5c16fe6a | ||
|
|
4efaf8d497 | ||
|
|
07881da451 | ||
|
|
bd6c2524e7 | ||
|
|
b096e02b9b | ||
|
|
ad6b24345b | ||
|
|
9694b756b5 | ||
|
|
6b5786be9d | ||
|
|
1f49ab2551 | ||
|
|
b18a5bec8e | ||
|
|
d741459e2f | ||
|
|
7007722aca | ||
|
|
05e7b3c1e5 | ||
|
|
13554546eb | ||
|
|
e2f3722b52 | ||
|
|
1b75734046 | ||
|
|
61d9609328 | ||
|
|
4cff51c89f | ||
|
|
ace5178e99 | ||
|
|
4a9d8627d3 | ||
|
|
fc89b932dd | ||
|
|
0852ac8326 | ||
|
|
87194d7f1b | ||
|
|
31fa689a98 | ||
|
|
5d366e742c | ||
|
|
7cc3c8f5ef | ||
|
|
7215dfcf60 | ||
|
|
5c709c1c54 | ||
|
|
1e5ad65c9f | ||
|
|
f6aeffb264 | ||
|
|
4593f3077c | ||
|
|
b92c8726de | ||
|
|
c560539a16 | ||
|
|
9cba7a95f7 | ||
|
|
d6d86ef04d | ||
|
|
ed643f3481 | ||
|
|
d8c2f77a0b | ||
|
|
6cfda647f7 | ||
|
|
fc58fcc721 | ||
|
|
5f0707956a | ||
|
|
528712e931 | ||
|
|
8fe609452b | ||
|
|
3afd2c4ac9 | ||
|
|
5793d7b678 | ||
|
|
569a8b4c4d | ||
|
|
9cab8f7469 | ||
|
|
1ac0b2efbe | ||
|
|
b78a36f1c2 | ||
|
|
e23d191cb8 | ||
|
|
8854b00b46 | ||
|
|
4be835de07 | ||
|
|
b8f59fa99c | ||
|
|
2236efea4f | ||
|
|
344ba4138d | ||
|
|
ba0fa7a869 | ||
|
|
21668a63fd | ||
|
|
2f6f9bcbc5 | ||
|
|
86d19de452 | ||
|
|
d4986449f1 | ||
|
|
3fc366bf1a | ||
|
|
9d192b215e | ||
|
|
b4ea4517e9 | ||
|
|
07ad12a16f | ||
|
|
f7529a18df | ||
|
|
c66522cb47 | ||
|
|
94a31f77ff | ||
|
|
c0630f9f6d | ||
|
|
3a145cb615 | ||
|
|
837516ea86 | ||
|
|
66228f59be | ||
|
|
800c79a898 | ||
|
|
2177b12bcf | ||
|
|
050c22ce27 | ||
|
|
ded614f30f | ||
|
|
88e439ebfd | ||
|
|
a027355c7d | ||
|
|
8a351ec928 | ||
|
|
b2fa4974ef | ||
|
|
9c2a034b43 | ||
|
|
45ba746300 | ||
|
|
483f317f83 | ||
|
|
dee3268bce | ||
|
|
2a88d3eef4 | ||
|
|
2898fed15a | ||
|
|
d2721077c9 | ||
|
|
dd7bb81b46 | ||
|
|
4515d21023 | ||
|
|
3315e3c148 | ||
|
|
2b552680a7 | ||
|
|
f78c80f04d | ||
|
|
bc239a7994 | ||
|
|
a508ec2279 | ||
|
|
cccecbacbc | ||
|
|
177a8b0527 | ||
|
|
d869ff959f | ||
|
|
772ba562e6 | ||
|
|
1786360d7c | ||
|
|
328596f01f | ||
|
|
6a13c9f822 | ||
|
|
3c78a63719 | ||
|
|
8b645b75df | ||
|
|
ae3ea30c92 | ||
|
|
01bd2c0c11 | ||
|
|
e91721299a | ||
|
|
4e08ec9fca | ||
|
|
b599008040 | ||
|
|
831f68b00c | ||
|
|
bccda6cb9e | ||
|
|
d0e77d2959 | ||
|
|
441ff72e1e | ||
|
|
aff131394e | ||
|
|
7a6f758853 | ||
|
|
eef8f538bc | ||
|
|
42d79ea01f | ||
|
|
df2c92555f | ||
|
|
380f6b5581 | ||
|
|
fb6450fb72 | ||
|
|
bb01c25605 | ||
|
|
d812e7dd8b | ||
|
|
60f3a765f3 | ||
|
|
31d6bd3664 | ||
|
|
4c0018b04c | ||
|
|
4b0b3df9d2 | ||
|
|
f699be1a65 | ||
|
|
b6496b31f0 | ||
|
|
1e601c3de9 | ||
|
|
00b39fec38 | ||
|
|
9224ab8a76 | ||
|
|
5307aebbf5 | ||
|
|
e7d8d613aa | ||
|
|
f469b08505 | ||
|
|
60b2a07659 | ||
|
|
8d6d448073 | ||
|
|
798156fedc | ||
|
|
e18510fc6b | ||
|
|
0fe96bf32d | ||
|
|
6d1f491b80 | ||
|
|
07ac8b99f0 | ||
|
|
622c35e935 | ||
|
|
c6adad5992 | ||
|
|
211722a8a7 | ||
|
|
a0082a4ce7 | ||
|
|
a4eb540d23 | ||
|
|
2d427d6c47 | ||
|
|
0d00168aaf |
No files matched your search
+46
-10
@@ -26,16 +26,15 @@ spotless {
|
||||
}
|
||||
|
||||
group = "ac.grim.grimac"
|
||||
version = "2.3.68"
|
||||
version = "2.3.69"
|
||||
description = "Libre simulation anticheat designed for 1.21 with 1.8-1.21 support, powered by PacketEvents 2.0."
|
||||
|
||||
java {
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
|
||||
}
|
||||
|
||||
// Set to false for debug builds
|
||||
// You cannot live reload classes if the jar relocates dependencies
|
||||
var relocate = true;
|
||||
// Checks Project properties -> environment variable -> defaults true
|
||||
val relocate: Boolean = project.findProperty("relocate")?.toString()?.toBoolean()
|
||||
?: System.getenv("RELOCATE_JAR")?.toBoolean()
|
||||
?: true
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
@@ -57,10 +56,10 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("com.github.retrooper:packetevents-spigot:2.5.1-SNAPSHOT")
|
||||
implementation("com.github.retrooper:packetevents-spigot:2.6.1-SNAPSHOT")
|
||||
implementation("co.aikar:acf-paper:0.5.1-SNAPSHOT")
|
||||
implementation("club.minnced:discord-webhooks:0.8.0") // Newer versions include kotlin-stdlib, which leads to incompatibility with plugins that use Kotlin
|
||||
implementation("it.unimi.dsi:fastutil:8.5.13")
|
||||
implementation("it.unimi.dsi:fastutil:8.5.15")
|
||||
implementation("github.scarsz:configuralize:1.4.0")
|
||||
|
||||
//implementation("com.github.grimanticheat:grimapi:1193c4fa41")
|
||||
@@ -68,9 +67,9 @@ dependencies {
|
||||
implementation("com.github.grimanticheat:grimapi:fc5634e444")
|
||||
|
||||
implementation("org.jetbrains:annotations:24.1.0")
|
||||
compileOnly("org.geysermc.floodgate:api:2.0-SNAPSHOT")
|
||||
compileOnly("org.geysermc.floodgate:api:2.2.3-SNAPSHOT")
|
||||
compileOnly("org.spigotmc:spigot-api:1.18.2-R0.1-SNAPSHOT")
|
||||
compileOnly("com.viaversion:viaversion-api:5.0.4-SNAPSHOT")
|
||||
compileOnly("com.viaversion:viaversion-api:5.1.1-SNAPSHOT")
|
||||
//
|
||||
compileOnly("io.netty:netty-all:4.1.85.Final")
|
||||
}
|
||||
@@ -156,6 +155,43 @@ publishing.publications.create<MavenPublication>("maven") {
|
||||
artifact(tasks["shadowJar"])
|
||||
}
|
||||
|
||||
java {
|
||||
sourceSets {
|
||||
create("java18") {
|
||||
java {
|
||||
srcDirs("src/main/java18")
|
||||
}
|
||||
compileClasspath += main.get().output
|
||||
runtimeClasspath += main.get().output
|
||||
}
|
||||
}
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(21))
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile> {
|
||||
if (name == "compileJava18Java") {
|
||||
options.compilerArgs.addAll(listOf("--add-modules", "jdk.incubator.vector", "-Xlint:unchecked"))
|
||||
sourceCompatibility = "18"
|
||||
targetCompatibility = "18"
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<Jar> {
|
||||
manifest {
|
||||
attributes("Multi-Release" to "true")
|
||||
}
|
||||
duplicatesStrategy = DuplicatesStrategy.INCLUDE // Important for multi-release JARs
|
||||
from(sourceSets.main.get().output)
|
||||
from(sourceSets.getByName("java18").output) {
|
||||
into("META-INF/versions/18")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.shadowJar {
|
||||
minimize()
|
||||
archiveFileName.set("${project.name}-${project.version}.jar")
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
package ac.grim.grimac.utils.vector;
|
||||
|
||||
import jdk.incubator.vector.*;
|
||||
|
||||
public final class SIMDVector3D implements Vector3D {
|
||||
|
||||
private static final VectorSpecies<Double> SPECIES = DoubleVector.SPECIES_PREFERRED;
|
||||
private static final VectorMask<Double> LENGTH_3_ARRAY_MASK = SPECIES.indexInRange(0, 3);
|
||||
private static final VectorShuffle<Double> CROSS_PRODUCT_SHUFFLE1 = VectorShuffle.fromArray(SPECIES, new int[]{1, 2, 0, 3}, 0);
|
||||
private static final VectorShuffle<Double> CROSS_PRODUCT_SHUFFLE2 = VectorShuffle.fromArray(SPECIES, new int[]{2, 0, 1, 3}, 0);
|
||||
private static final DoubleVector FOURTH_COMPONENT_ZERO = DoubleVector.zero(SPECIES).withLane(3, 1.0);
|
||||
|
||||
private DoubleVector vector;
|
||||
|
||||
public SIMDVector3D(double x, double y, double z) {
|
||||
this.vector = DoubleVector.fromArray(SPECIES, new double[]{x, y, z, 0}, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getX() {
|
||||
return this.vector.lane(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getY() {
|
||||
return this.vector.lane(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getZ() {
|
||||
return this.vector.lane(2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3D setX(double x) {
|
||||
this.vector = this.vector.withLane(0, x);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3D setY(double y) {
|
||||
this.vector = this.vector.withLane(1, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3D setZ(double z) {
|
||||
this.vector = this.vector.withLane(2, z);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double length() {
|
||||
return Math.sqrt(lengthSquared());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double lengthSquared() {
|
||||
return this.vector.mul(this.vector).reduceLanes(VectorOperators.ADD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3D multiply(double m) {
|
||||
this.vector = this.vector.mul(m);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3D normalize() {
|
||||
// DoubleVector squared = this.vector.mul(this.vector);
|
||||
// double sum = squared.reduceLanes(VectorOperators.ADD);
|
||||
// double length = Math.sqrt(sum);
|
||||
// this.vector = this.vector.div(length);
|
||||
// return this;
|
||||
double length = length();
|
||||
this.vector = this.vector.div(length).blend(FOURTH_COMPONENT_ZERO, LENGTH_3_ARRAY_MASK.not());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3D crossProduct(Vector3D other) {
|
||||
SIMDVector3D o = (SIMDVector3D) other;
|
||||
|
||||
DoubleVector tmp0 = this.vector.rearrange(CROSS_PRODUCT_SHUFFLE1);
|
||||
DoubleVector tmp1 = o.vector.rearrange(CROSS_PRODUCT_SHUFFLE2);
|
||||
|
||||
DoubleVector tmp2 = tmp0.mul(o.vector);
|
||||
DoubleVector tmp3 = tmp0.mul(tmp1);
|
||||
|
||||
DoubleVector tmp4 = tmp2.rearrange(CROSS_PRODUCT_SHUFFLE1);
|
||||
|
||||
this.vector = tmp3.sub(tmp4).blend(FOURTH_COMPONENT_ZERO, LENGTH_3_ARRAY_MASK.not());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3D add(Vector3D o) {
|
||||
this.vector = this.vector.add(((SIMDVector3D) o).vector);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3D subtract(Vector3D o) {
|
||||
this.vector = this.vector.sub(((SIMDVector3D) o).vector);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3D multiply(Vector3D o) {
|
||||
this.vector = this.vector.mul(((SIMDVector3D) o).vector);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double distance(Vector3D o) {
|
||||
return Math.sqrt(distanceSquared(o));
|
||||
}
|
||||
|
||||
@Override
|
||||
public double distanceSquared(Vector3D o) {
|
||||
DoubleVector diff = this.vector.sub(((SIMDVector3D) o).vector);
|
||||
return diff.mul(diff).reduceLanes(VectorOperators.ADD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double dot(Vector3D o) {
|
||||
return this.vector.mul(((SIMDVector3D) o).vector).reduceLanes(VectorOperators.ADD);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Vector3D clone() {
|
||||
// I think this makes a shallow clone if I do it like this?
|
||||
// try {
|
||||
// return (Vector3D) super.clone();
|
||||
// } catch (CloneNotSupportedException e) {
|
||||
// throw new Error(e);
|
||||
// }
|
||||
try {
|
||||
SIMDVector3D cloned = (SIMDVector3D) super.clone();
|
||||
// Create a new DoubleVector with the same values
|
||||
cloned.vector = DoubleVector.fromArray(SPECIES,
|
||||
new double[]{this.getX(), this.getY(), this.getZ(), 0}, 0);
|
||||
return cloned;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package ac.grim.grimac.utils.vector;
|
||||
|
||||
import jdk.incubator.vector.VectorMask;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
import jdk.incubator.vector.DoubleVector;
|
||||
|
||||
public class VectorOperationsJava18 implements VectorOperations {
|
||||
|
||||
@Override
|
||||
public Vector3D newVector(double x, double y, double z) {
|
||||
return new SIMDVector3D(x, y, z);
|
||||
}
|
||||
}
|
||||
@@ -59,15 +59,15 @@ public class AimProcessor extends Check implements RotationCheck {
|
||||
|
||||
if (this.xRotMode.size() > SIGNIFICANT_SAMPLES_THRESHOLD) {
|
||||
Pair<Double, Integer> modeX = this.xRotMode.getMode();
|
||||
if (modeX.second() > SIGNIFICANT_SAMPLES_THRESHOLD) {
|
||||
this.modeX = modeX.first();
|
||||
if (modeX.getSecond() > SIGNIFICANT_SAMPLES_THRESHOLD) {
|
||||
this.modeX = modeX.getFirst();
|
||||
this.sensitivityX = convertToSensitivity(this.modeX);
|
||||
}
|
||||
}
|
||||
if (this.yRotMode.size() > SIGNIFICANT_SAMPLES_THRESHOLD) {
|
||||
Pair<Double, Integer> modeY = this.yRotMode.getMode();
|
||||
if (modeY.second() > SIGNIFICANT_SAMPLES_THRESHOLD) {
|
||||
this.modeY = modeY.first();
|
||||
if (modeY.getSecond() > SIGNIFICANT_SAMPLES_THRESHOLD) {
|
||||
this.modeY = modeY.getFirst();
|
||||
this.sensitivityY = convertToSensitivity(this.modeY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public class BadPacketsO extends Check implements PacketCheck {
|
||||
boolean hasID = false;
|
||||
|
||||
for (Pair<Long, Long> iterator : keepaliveMap) {
|
||||
if (iterator.first() == id) {
|
||||
if (iterator.getFirst() == id) {
|
||||
hasID = true;
|
||||
break;
|
||||
}
|
||||
@@ -55,7 +55,7 @@ public class BadPacketsO extends Check implements PacketCheck {
|
||||
do {
|
||||
data = keepaliveMap.poll();
|
||||
if (data == null) break;
|
||||
} while (data.first() != id);
|
||||
} while (data.getFirst() != id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,13 @@ import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.data.HitData;
|
||||
import ac.grim.grimac.utils.data.Pair;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
|
||||
import ac.grim.grimac.utils.nmsutil.BlockRayTrace;
|
||||
import ac.grim.grimac.utils.data.packetentity.dragon.PacketEntityEnderDragonPart;
|
||||
import ac.grim.grimac.utils.nmsutil.ReachUtils;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
|
||||
@@ -31,14 +35,24 @@ import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
|
||||
// You may not copy the check unless you are licensed under GPL
|
||||
@CheckData(name = "Reach", configName = "Reach", setback = 10)
|
||||
@@ -46,11 +60,15 @@ public class Reach extends Check implements PacketCheck {
|
||||
// Only one flag per reach attack, per entity, per tick.
|
||||
// We store position because lastX isn't reliable on teleports.
|
||||
private final Int2ObjectMap<Vector3d> playerAttackQueue = new Int2ObjectOpenHashMap<>();
|
||||
// Used to prevent falses in the wall hit check
|
||||
private final Set<Vector3i> blocksChangedThisTick = new HashSet<>();
|
||||
|
||||
private static final List<EntityType> blacklisted = Arrays.asList(
|
||||
EntityTypes.BOAT,
|
||||
EntityTypes.CHEST_BOAT,
|
||||
EntityTypes.SHULKER);
|
||||
|
||||
private boolean ignoreNonPlayerTargets;
|
||||
private boolean cancelImpossibleHits;
|
||||
private double threshold;
|
||||
private double cancelBuffer; // For the next 4 hits after using reach, we aggressively cancel reach
|
||||
@@ -84,6 +102,10 @@ public class Reach extends Check implements PacketCheck {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ignoreNonPlayerTargets && !entity.getType().equals(EntityTypes.PLAYER)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Dead entities cause false flags (https://github.com/GrimAnticheat/Grim/issues/546)
|
||||
if (entity.isDead) return;
|
||||
|
||||
@@ -108,10 +130,11 @@ public class Reach extends Check implements PacketCheck {
|
||||
}
|
||||
|
||||
// If the player set their look, or we know they have a new tick
|
||||
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) ||
|
||||
final boolean isFlying = WrapperPlayClientPlayerFlying.isFlying(event.getPacketType());
|
||||
if (isFlying ||
|
||||
event.getPacketType() == PacketType.Play.Client.PONG ||
|
||||
event.getPacketType() == PacketType.Play.Client.WINDOW_CONFIRMATION) {
|
||||
tickBetterReachCheckWithAngle();
|
||||
tickBetterReachCheckWithAngle(isFlying);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +166,7 @@ public class Reach extends Check implements PacketCheck {
|
||||
}
|
||||
}
|
||||
|
||||
private void tickBetterReachCheckWithAngle() {
|
||||
private void tickBetterReachCheckWithAngle(boolean isFlying) {
|
||||
for (Int2ObjectMap.Entry<Vector3d> attack : playerAttackQueue.int2ObjectEntrySet()) {
|
||||
PacketEntity reachEntity = player.compensatedEntities.entityMap.get(attack.getIntKey());
|
||||
if (reachEntity != null) {
|
||||
@@ -158,6 +181,9 @@ public class Reach extends Check implements PacketCheck {
|
||||
}
|
||||
}
|
||||
playerAttackQueue.clear();
|
||||
// We can't use transactions for this because of this problem:
|
||||
// transaction -> block changed applied -> 2nd transaction -> list cleared -> attack packet -> flying -> reach block hit checked, falses
|
||||
if (isFlying) blocksChangedThisTick.clear();
|
||||
}
|
||||
|
||||
private String checkReach(PacketEntity reachEntity, Vector3d from, boolean isPrediction) {
|
||||
@@ -185,7 +211,7 @@ public class Reach extends Check implements PacketCheck {
|
||||
double minDistance = Double.MAX_VALUE;
|
||||
|
||||
// https://bugs.mojang.com/browse/MC-67665
|
||||
List<Vector> possibleLookDirs = new ArrayList<>(Collections.singletonList(ReachUtils.getLook(player, player.xRot, player.yRot)));
|
||||
List<Vector3D> possibleLookDirs = new ArrayList<>(Collections.singletonList(ReachUtils.getLook(player, player.xRot, player.yRot)));
|
||||
|
||||
// If we are a tick behind, we don't know their next look so don't bother doing this
|
||||
if (!isPrediction) {
|
||||
@@ -205,12 +231,12 @@ public class Reach extends Check implements PacketCheck {
|
||||
// +3 would be 3 + 3 = 6, which is the pre-1.20.5 behaviour, preventing "Missed Hitbox"
|
||||
final double distance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_ENTITY_INTERACTION_RANGE) + 3;
|
||||
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
|
||||
for (Vector lookVec : possibleLookDirs) {
|
||||
for (Vector3D lookVec : possibleLookDirs) {
|
||||
for (double eye : possibleEyeHeights) {
|
||||
Vector eyePos = new Vector(from.getX(), from.getY() + eye, from.getZ());
|
||||
Vector endReachPos = eyePos.clone().add(new Vector(lookVec.getX() * distance, lookVec.getY() * distance, lookVec.getZ() * distance));
|
||||
ac.grim.grimac.utils.vector.Vector3D eyePos = newVector3D(from.getX(), from.getY() + eye, from.getZ());
|
||||
ac.grim.grimac.utils.vector.Vector3D endReachPos = eyePos.clone().add(newVector3D(lookVec.getX() * distance, lookVec.getY() * distance, lookVec.getZ() * distance));
|
||||
|
||||
Vector intercept = ReachUtils.calculateIntercept(targetBox, eyePos, endReachPos).first();
|
||||
Vector3D intercept = ReachUtils.calculateIntercept(targetBox, eyePos, endReachPos).getFirst();
|
||||
|
||||
if (ReachUtils.isVecInside(targetBox, eyePos)) {
|
||||
minDistance = 0;
|
||||
@@ -223,9 +249,25 @@ public class Reach extends Check implements PacketCheck {
|
||||
}
|
||||
}
|
||||
|
||||
HitData foundHitData = null;
|
||||
// If the entity is within range of the player (we'll flag anyway if not, so no point checking blocks in this case)
|
||||
// Ignore when could be hitting through a moving shulker, piston blocks. They are just too glitchy/uncertain to check.
|
||||
if (minDistance <= 3 && !player.compensatedWorld.isNearHardEntity(player.boundingBox.copy().expand(4))) {
|
||||
final @Nullable Pair<Double, HitData> targetBlock = getTargetBlock(player, possibleLookDirs, from, minDistance);
|
||||
// And if the target block is closer to the player than the entity box, they should hit the block instead
|
||||
// So, this hit is invalid.
|
||||
if (targetBlock != null && targetBlock.getFirst() < (minDistance * minDistance)) { // targetBlock is squared
|
||||
minDistance = Double.MIN_VALUE;
|
||||
foundHitData = targetBlock.getSecond();
|
||||
}
|
||||
}
|
||||
|
||||
// if the entity is not exempt and the entity is alive
|
||||
if ((!blacklisted.contains(reachEntity.getType()) && reachEntity.isLivingEntity()) || reachEntity.getType() == EntityTypes.END_CRYSTAL) {
|
||||
if (minDistance == Double.MAX_VALUE) {
|
||||
if (minDistance == Double.MIN_VALUE && foundHitData != null) {
|
||||
cancelBuffer = 1;
|
||||
return "Hit block block=" + foundHitData.getState().getType().getName();
|
||||
} else if (minDistance == Double.MAX_VALUE) {
|
||||
cancelBuffer = 1;
|
||||
return "Missed hitbox";
|
||||
} else if (minDistance > player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_ENTITY_INTERACTION_RANGE)) {
|
||||
@@ -239,8 +281,48 @@ public class Reach extends Check implements PacketCheck {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void handleBlockChange(Vector3i vector3i, WrappedBlockState state) {
|
||||
if (blocksChangedThisTick.size() >= 40) return; // Don't let players freeze movement packets to grow this
|
||||
// Only do this for nearby blocks
|
||||
if (newVector3D(vector3i.x, vector3i.y, vector3i.z).distanceSquared(newVector3D(player.x, player.y, player.z)) > 6) return;
|
||||
// Only do this if the state really had any world impact
|
||||
if (state.equals(player.compensatedWorld.getWrappedBlockStateAt(vector3i))) return;
|
||||
blocksChangedThisTick.add(vector3i);
|
||||
}
|
||||
|
||||
// Returns a pair so we can check the block type in the flag
|
||||
@Nullable
|
||||
private Pair<Double, HitData> getTargetBlock(GrimPlayer player, List<Vector3D> possibleLookDirs, Vector3d from, double minDistance) {
|
||||
// Check every possible look direction and every possible eye height
|
||||
// IF *NONE* of them allow the player to hit the entity, this is an invalid hit
|
||||
HitData bestHitData = null;
|
||||
double min = Double.MAX_VALUE;
|
||||
for (Vector3D lookVec : possibleLookDirs) {
|
||||
for (double eye : player.getPossibleEyeHeights()) {
|
||||
Vector3D eyes = newVector3D(from.getX(), from.getY() + eye, from.getZ());
|
||||
final double reach = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_BLOCK_INTERACTION_RANGE);
|
||||
final HitData hitResult = BlockRayTrace.getNearestReachHitResult(player, eyes, lookVec, minDistance, reach);
|
||||
if (hitResult == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final double distance = eyes.distanceSquared(hitResult.getBlockHitLocation());
|
||||
// Block changes are uncertain, can't check this tick
|
||||
if (distance < (minDistance * minDistance) && blocksChangedThisTick.contains(hitResult.getPosition())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
bestHitData = hitResult;
|
||||
min = Math.min(min, distance);
|
||||
}
|
||||
}
|
||||
|
||||
return bestHitData == null ? null : Pair.of(min, bestHitData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReload(ConfigManager config) {
|
||||
this.ignoreNonPlayerTargets = config.getBooleanElse("Reach.ignore-non-player-targets", false);
|
||||
this.cancelImpossibleHits = config.getBooleanElse("Reach.block-impossible-hits", true);
|
||||
this.threshold = config.getDoubleElse("Reach.threshold", 0.0005);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package ac.grim.grimac.checks.impl.inventory;
|
||||
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.InventoryCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity.InteractAction;
|
||||
|
||||
@CheckData(name = "InventoryA", setback = 3, description = "Attacked an entity while inventory is open")
|
||||
public class InventoryA extends InventoryCheck {
|
||||
public InventoryA(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
super.onPacketReceive(event);
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
|
||||
WrapperPlayClientInteractEntity wrapper = new WrapperPlayClientInteractEntity(event);
|
||||
|
||||
if (wrapper.getAction() != InteractAction.ATTACK) return;
|
||||
|
||||
// Is not possible to attack while the inventory is open.
|
||||
if (player.hasInventoryOpen) {
|
||||
if (flagAndAlert()) {
|
||||
// Cancel the packet
|
||||
if (shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
closeInventory();
|
||||
}
|
||||
} else {
|
||||
reward();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package ac.grim.grimac.checks.impl.inventory;
|
||||
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.InventoryCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
|
||||
|
||||
@CheckData(name = "InventoryB", setback = 3, description = "Started digging blocks while inventory is open")
|
||||
public class InventoryB extends InventoryCheck {
|
||||
public InventoryB(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
public void handle(PacketReceiveEvent event, WrapperPlayClientPlayerDigging wrapper) {
|
||||
if (wrapper.getAction() != DiggingAction.START_DIGGING) return;
|
||||
|
||||
// Is not possible to start digging a block while the inventory is open.
|
||||
if (player.hasInventoryOpen) {
|
||||
if (flagAndAlert()) {
|
||||
// Cancel the packet
|
||||
if (shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
closeInventory();
|
||||
}
|
||||
} else {
|
||||
reward();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package ac.grim.grimac.checks.impl.inventory;
|
||||
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.InventoryCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
|
||||
|
||||
@CheckData(name = "InventoryC", setback = 3, description = "Placed a block while inventory is open")
|
||||
public class InventoryC extends InventoryCheck {
|
||||
|
||||
public InventoryC(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
public void onBlockPlace(final BlockPlace place) {
|
||||
// It is not possible to place a block while the inventory is open
|
||||
if (player.hasInventoryOpen) {
|
||||
if (flagAndAlert()) {
|
||||
if (shouldModifyPackets()) {
|
||||
place.resync();
|
||||
}
|
||||
closeInventory();
|
||||
}
|
||||
} else {
|
||||
reward();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package ac.grim.grimac.checks.impl.inventory;
|
||||
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.InventoryCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import ac.grim.grimac.utils.data.VectorData;
|
||||
import ac.grim.grimac.utils.data.VectorData.MoveVectorData;
|
||||
import ac.grim.grimac.utils.data.VehicleData;
|
||||
|
||||
import java.util.StringJoiner;
|
||||
|
||||
@CheckData(name = "InventoryD", setback = 1, decay = 0.25)
|
||||
public class InventoryD extends InventoryCheck {
|
||||
private int horseJumpVerbose;
|
||||
|
||||
public InventoryD(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(final PredictionComplete predictionComplete) {
|
||||
if (!predictionComplete.isChecked() ||
|
||||
predictionComplete.getData().isTeleport() ||
|
||||
player.getSetbackTeleportUtil().blockOffsets ||
|
||||
player.packetStateData.lastPacketWasTeleport ||
|
||||
player.packetStateData.isSlowedByUsingItem() ||
|
||||
System.currentTimeMillis() - player.lastBlockPlaceUseItem < 50L) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.hasInventoryOpen) {
|
||||
boolean inVehicle = player.compensatedEntities.getSelf().inVehicle();
|
||||
boolean isJumping, isMoving;
|
||||
|
||||
if (inVehicle) {
|
||||
VehicleData vehicle = player.vehicleData;
|
||||
|
||||
// Will flag once if player open anything with pressed space bar
|
||||
isJumping = vehicle.nextHorseJump > 0 && horseJumpVerbose++ >= 1;
|
||||
isMoving = vehicle.nextVehicleForward != 0 || vehicle.nextVehicleHorizontal != 0;
|
||||
} else {
|
||||
MoveVectorData move = findMovement(player.predictedVelocity);
|
||||
|
||||
isJumping = player.predictedVelocity.isJump();
|
||||
isMoving = move != null && (move.x != 0 || move.z != 0);
|
||||
}
|
||||
|
||||
if (!isMoving && !isJumping) {
|
||||
reward();
|
||||
return;
|
||||
}
|
||||
|
||||
if (flag()) {
|
||||
closeInventory();
|
||||
|
||||
StringJoiner joiner = new StringJoiner(" ");
|
||||
|
||||
if (isMoving) joiner.add("moving");
|
||||
if (isJumping) joiner.add("jumping");
|
||||
if (inVehicle) joiner.add("inVehicle");
|
||||
|
||||
alert(joiner.toString());
|
||||
}
|
||||
} else {
|
||||
horseJumpVerbose = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private MoveVectorData findMovement(VectorData vectorData) {
|
||||
if (vectorData instanceof MoveVectorData) {
|
||||
return (MoveVectorData) vectorData;
|
||||
}
|
||||
|
||||
while (vectorData != null) {
|
||||
vectorData = vectorData.lastVector;
|
||||
if (vectorData instanceof MoveVectorData) {
|
||||
return (MoveVectorData) vectorData;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package ac.grim.grimac.checks.impl.inventory;
|
||||
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.InventoryCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.event.PacketSendEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
|
||||
@CheckData(name = "InventoryE", setback = 3, description = "Sent a held item change packet while inventory is open")
|
||||
public class InventoryE extends InventoryCheck {
|
||||
private long lastTransaction = Long.MAX_VALUE; // Impossible transaction ID
|
||||
|
||||
public InventoryE(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
super.onPacketReceive(event);
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.HELD_ITEM_CHANGE) {
|
||||
// It is not possible to change hotbar slots with held item change while the inventory is open
|
||||
// A container click packet would be sent instead
|
||||
if (player.hasInventoryOpen) {
|
||||
if (this.lastTransaction < player.lastTransactionReceived.get()
|
||||
&& flagAndAlert()) {
|
||||
// Cancel the packet
|
||||
if (shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
player.getInventory().needResend = true;
|
||||
}
|
||||
closeInventory();
|
||||
}
|
||||
} else {
|
||||
reward();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSend(PacketSendEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Server.HELD_ITEM_CHANGE) {
|
||||
this.lastTransaction = player.lastTransactionSent.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package ac.grim.grimac.checks.impl.inventory;
|
||||
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.InventoryCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.inventory.InventoryDesyncStatus;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
|
||||
@CheckData(name = "InventoryF", setback = 3, description = "Sent a click window packet without a open inventory", experimental = true)
|
||||
public class InventoryF extends InventoryCheck {
|
||||
|
||||
public InventoryF(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
// Exempt on 1.9+ server version due to the Via hack done in PacketPlayerWindow, the exemption can be deleted
|
||||
// once we are ahead of ViaVersion
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9)
|
||||
|| player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) return;
|
||||
|
||||
super.onPacketReceive(event);
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW) {
|
||||
if (!player.hasInventoryOpen && player.inventoryDesyncStatus == InventoryDesyncStatus.NOT_DESYNCED) {
|
||||
if (flagAndAlert()) {
|
||||
// Cancel the packet
|
||||
if (shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
closeInventory();
|
||||
}
|
||||
} else {
|
||||
reward();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package ac.grim.grimac.checks.impl.inventory;
|
||||
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.InventoryCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
|
||||
|
||||
@CheckData(name = "InventoryG", setback = 3, description = "Sent a entity action packet while inventory is open", experimental = true)
|
||||
public class InventoryG extends InventoryCheck {
|
||||
|
||||
public InventoryG(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (player.packetStateData.lastPacketWasTeleport) return;
|
||||
super.onPacketReceive(event);
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.ENTITY_ACTION) {
|
||||
WrapperPlayClientEntityAction wrapper = new WrapperPlayClientEntityAction(event);
|
||||
WrapperPlayClientEntityAction.Action action = wrapper.getAction();
|
||||
|
||||
if (action == WrapperPlayClientEntityAction.Action.STOP_SNEAKING
|
||||
|| action == WrapperPlayClientEntityAction.Action.STOP_SPRINTING) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.hasInventoryOpen) {
|
||||
if (flagAndAlert()) {
|
||||
closeInventory();
|
||||
}
|
||||
} else {
|
||||
reward();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.misc;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
|
||||
|
||||
@CheckData(name = "TransactionOrder", experimental = false)
|
||||
public class TransactionOrder extends Check implements PacketCheck {
|
||||
|
||||
public TransactionOrder(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClickWindow;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClickWindow.WindowClickType;
|
||||
|
||||
@CheckData(name = "PacketOrderA", experimental = true)
|
||||
public class PacketOrderA extends Check implements PostPredictionCheck {
|
||||
public PacketOrderA(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private int invalid;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW) {
|
||||
final WindowClickType clickType = new WrapperPlayClientClickWindow(event).getWindowClickType();
|
||||
|
||||
if ((clickType == WindowClickType.PICKUP || clickType == WindowClickType.PICKUP_ALL) && player.packetOrderProcessor.isQuickMoveClicking()
|
||||
|| clickType == WindowClickType.QUICK_MOVE && player.packetOrderProcessor.isPickUpClicking()) {
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
|
||||
if (flagAndAlert() && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
invalid++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
// we don't need to check pre-1.9 players here (no tick skipping)
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
|
||||
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (; invalid >= 1; invalid--) {
|
||||
flagAndAlert();
|
||||
}
|
||||
}
|
||||
|
||||
invalid = 0;
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
package ac.grim.grimac.checks.impl.badpackets;
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
@@ -11,15 +11,15 @@ import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
|
||||
@CheckData(name = "BadPacketsH")
|
||||
public class BadPacketsH extends Check implements PacketCheck {
|
||||
@CheckData(name = "PacketOrderB")
|
||||
public class PacketOrderB extends Check implements PacketCheck {
|
||||
|
||||
// 1.9 packet order: INTERACT -> ANIMATION
|
||||
// 1.8 packet order: ANIMATION -> INTERACT
|
||||
// I personally think 1.8 made much more sense. You swing and THEN you hit!
|
||||
private boolean sentAnimation = player.getClientVersion().isNewerThan(ClientVersion.V_1_8);
|
||||
|
||||
public BadPacketsH(final GrimPlayer player) {
|
||||
public PacketOrderB(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
+40
-10
@@ -1,4 +1,4 @@
|
||||
package ac.grim.grimac.checks.impl.badpackets;
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
@@ -9,25 +9,33 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.InteractionHand;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
|
||||
|
||||
@CheckData(name = "BadPacketsM", experimental = true)
|
||||
public class BadPacketsM extends Check implements PacketCheck {
|
||||
public BadPacketsM(final GrimPlayer player) {
|
||||
@CheckData(name = "PacketOrderC", experimental = true)
|
||||
public class PacketOrderC extends Check implements PacketCheck {
|
||||
public PacketOrderC(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
// 1.7 players do not send INTERACT_AT, so we cannot check them
|
||||
private final boolean exempt = player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_7_10);
|
||||
private boolean sentInteractAt = false;
|
||||
private int requiredEntity;
|
||||
private InteractionHand requiredHand;
|
||||
private boolean requiredSneaking;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY && !exempt) {
|
||||
if (exempt) {
|
||||
return;
|
||||
}
|
||||
|
||||
final WrapperPlayClientInteractEntity wrapper = new WrapperPlayClientInteractEntity(event);
|
||||
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
|
||||
final WrapperPlayClientInteractEntity packet = new WrapperPlayClientInteractEntity(event);
|
||||
|
||||
final PacketEntity entity = player.compensatedEntities.entityMap.get(wrapper.getEntityId());
|
||||
final PacketEntity entity = player.compensatedEntities.entityMap.get(packet.getEntityId());
|
||||
|
||||
// For armor stands, vanilla clients send:
|
||||
// - when renaming the armor stand or in spectator mode: INTERACT_AT + INTERACT
|
||||
@@ -35,27 +43,49 @@ public class BadPacketsM extends Check implements PacketCheck {
|
||||
// Just exempt armor stands to be safe
|
||||
if (entity != null && entity.getType() == EntityTypes.ARMOR_STAND) return;
|
||||
|
||||
switch (wrapper.getAction()) {
|
||||
final boolean sneaking = packet.isSneaking().orElse(false);
|
||||
|
||||
switch (packet.getAction()) {
|
||||
// INTERACT_AT then INTERACT
|
||||
case INTERACT:
|
||||
if (!sentInteractAt) {
|
||||
if (flagAndAlert("Missed Interact-At") && shouldModifyPackets()) {
|
||||
if (flagAndAlert("Skipped Interact-At") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else if (packet.getEntityId() != requiredEntity || packet.getHand() != requiredHand || sneaking != requiredSneaking) {
|
||||
String verbose = "requiredEntity=" + requiredEntity + ", entity=" + packet.getEntityId()
|
||||
+ ", requiredHand=" + requiredHand + ", hand=" + packet.getHand()
|
||||
+ ", requiredSneaking=" + requiredSneaking + ", sneaking=" + sneaking;
|
||||
if (flagAndAlert(verbose) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
|
||||
sentInteractAt = false;
|
||||
break;
|
||||
case INTERACT_AT:
|
||||
if (sentInteractAt) {
|
||||
if (flagAndAlert("Missed Interact") && shouldModifyPackets()) {
|
||||
if (flagAndAlert("Skipped Interact") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
|
||||
requiredHand = packet.getHand();
|
||||
requiredEntity = packet.getEntityId();
|
||||
requiredSneaking = sneaking;
|
||||
sentInteractAt = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType())) {
|
||||
if (sentInteractAt) {
|
||||
sentInteractAt = false;
|
||||
flagAndAlert("Skipped Interact (Tick)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
|
||||
@CheckData(name = "PacketOrderD")
|
||||
public class PacketOrderD extends Check implements PacketCheck {
|
||||
public PacketOrderD(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
|
||||
@CheckData(name = "PacketOrderE", experimental = true)
|
||||
public class PacketOrderE extends Check implements PostPredictionCheck {
|
||||
public PacketOrderE(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private int invalid;
|
||||
private boolean setback;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.HELD_ITEM_CHANGE) {
|
||||
if (player.packetOrderProcessor.isAttacking()
|
||||
|| player.packetOrderProcessor.isRightClicking()
|
||||
|| player.packetOrderProcessor.isOpeningInventory()
|
||||
|| player.packetOrderProcessor.isReleasing()
|
||||
|| player.packetOrderProcessor.isSneaking()
|
||||
|| player.packetOrderProcessor.isSprinting()
|
||||
|| player.packetOrderProcessor.isLeavingBed()
|
||||
|| player.packetOrderProcessor.isStartingToGlide()
|
||||
|| player.packetOrderProcessor.isJumpingWithMount()
|
||||
) {
|
||||
if (player.getClientVersion().isNewerThan(ClientVersion.V_1_8) || flagAndAlert()) {
|
||||
invalid++;
|
||||
|
||||
if (player.packetOrderProcessor.isUsing()) {
|
||||
setback = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
|
||||
if (setback) {
|
||||
setback = false;
|
||||
setbackIfAboveSetbackVL();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (; invalid >= 1; invalid--) {
|
||||
if (flagAndAlert() && setback) {
|
||||
setback = false;
|
||||
setbackIfAboveSetbackVL();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
invalid = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
|
||||
|
||||
@CheckData(name = "PacketOrderF", experimental = true)
|
||||
public class PacketOrderF extends Check implements PostPredictionCheck {
|
||||
public PacketOrderF(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private int invalid;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY
|
||||
|| event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT
|
||||
|| event.getPacketType() == PacketType.Play.Client.USE_ITEM
|
||||
|| event.getPacketType() == PacketType.Play.Client.PICK_ITEM
|
||||
|| event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING
|
||||
|| (event.getPacketType() == PacketType.Play.Client.CLIENT_STATUS
|
||||
&& new WrapperPlayClientClientStatus(event).getAction() == WrapperPlayClientClientStatus.Action.OPEN_INVENTORY_ACHIEVEMENT)
|
||||
) if (player.packetOrderProcessor.isSprinting() || player.packetOrderProcessor.isSneaking()) {
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
|
||||
if (flagAndAlert() && shouldModifyPackets()) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING
|
||||
&& new WrapperPlayClientPlayerDigging(event).getAction() == DiggingAction.RELEASE_USE_ITEM
|
||||
) return; // don't cause a noslow
|
||||
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
invalid++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
// we don't need to check pre-1.9 players here (no tick skipping)
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
|
||||
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (; invalid >= 1; invalid--) {
|
||||
flagAndAlert();
|
||||
}
|
||||
}
|
||||
|
||||
invalid = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
|
||||
|
||||
@CheckData(name = "PacketOrderG", experimental = true)
|
||||
public class PacketOrderG extends Check implements PostPredictionCheck {
|
||||
public PacketOrderG(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private int invalid;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING
|
||||
|| (event.getPacketType() == PacketType.Play.Client.CLIENT_STATUS
|
||||
&& new WrapperPlayClientClientStatus(event).getAction() == WrapperPlayClientClientStatus.Action.OPEN_INVENTORY_ACHIEVEMENT)
|
||||
) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING) {
|
||||
final DiggingAction action = new WrapperPlayClientPlayerDigging(event).getAction();
|
||||
if (action == DiggingAction.RELEASE_USE_ITEM
|
||||
|| action == DiggingAction.START_DIGGING
|
||||
|| action == DiggingAction.CANCELLED_DIGGING
|
||||
|| action == DiggingAction.FINISHED_DIGGING
|
||||
) return;
|
||||
}
|
||||
|
||||
if (player.packetOrderProcessor.isAttacking()
|
||||
|| player.packetOrderProcessor.isReleasing()
|
||||
|| player.packetOrderProcessor.isRightClicking()
|
||||
|| player.packetOrderProcessor.isPicking()
|
||||
|| player.packetOrderProcessor.isDigging()
|
||||
) {
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
|
||||
if (flagAndAlert() && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
invalid++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
// we don't need to check pre-1.9 players here (no tick skipping)
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
|
||||
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (; invalid >= 1; invalid--) {
|
||||
flagAndAlert();
|
||||
}
|
||||
}
|
||||
|
||||
invalid = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
|
||||
|
||||
@CheckData(name = "PacketOrderH", experimental = true)
|
||||
public class PacketOrderH extends Check implements PostPredictionCheck {
|
||||
public PacketOrderH(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private int invalid;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.ENTITY_ACTION) {
|
||||
switch (new WrapperPlayClientEntityAction(event).getAction()) {
|
||||
case START_SPRINTING:
|
||||
case STOP_SPRINTING:
|
||||
if (player.packetOrderProcessor.isSneaking()) {
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
|
||||
flagAndAlert();
|
||||
} else {
|
||||
invalid++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
// we don't need to check pre-1.9 players here (no tick skipping)
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
|
||||
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (; invalid >= 1; invalid--) {
|
||||
flagAndAlert();
|
||||
}
|
||||
}
|
||||
|
||||
invalid = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.api.config.ConfigManager;
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import ac.grim.grimac.utils.nmsutil.BlockBreakSpeed;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
|
||||
@CheckData(name = "PacketOrderI", experimental = true)
|
||||
public class PacketOrderI extends Check implements PostPredictionCheck {
|
||||
public PacketOrderI(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private boolean exemptPlacingWhileDigging;
|
||||
|
||||
private boolean setback;
|
||||
private boolean digging; // for placing
|
||||
private final ArrayDeque<String> flags = new ArrayDeque<>();
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
|
||||
if (new WrapperPlayClientInteractEntity(event).getAction() == WrapperPlayClientInteractEntity.InteractAction.ATTACK) {
|
||||
if (player.packetOrderProcessor.isRightClicking() || player.packetOrderProcessor.isPicking() || player.packetOrderProcessor.isReleasing() || player.packetOrderProcessor.isDigging()) {
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
|
||||
if (flagAndAlert("attack") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
flags.add("attack");
|
||||
}
|
||||
}
|
||||
} else if (player.packetOrderProcessor.isReleasing() || player.packetOrderProcessor.isDigging()) {
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
|
||||
if (flagAndAlert("interact") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
flags.add("interact");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT || event.getPacketType() == PacketType.Play.Client.USE_ITEM) {
|
||||
if (player.packetOrderProcessor.isReleasing() || digging) {
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
|
||||
if (flagAndAlert("place/use") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
flags.add("place");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING) {
|
||||
WrapperPlayClientPlayerDigging packet = new WrapperPlayClientPlayerDigging(event);
|
||||
|
||||
switch (packet.getAction()) {
|
||||
case RELEASE_USE_ITEM:
|
||||
if (player.packetOrderProcessor.isAttacking() || player.packetOrderProcessor.isRightClicking() || player.packetOrderProcessor.isPicking() || player.packetOrderProcessor.isDigging()) {
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
|
||||
if (flagAndAlert("release")) {
|
||||
setback = true;
|
||||
}
|
||||
} else {
|
||||
flags.add("release");
|
||||
setback = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case START_DIGGING:
|
||||
double damage = BlockBreakSpeed.getBlockDamage(player, packet.getBlockPosition());
|
||||
if (damage >= 1 || damage <= 0 && player.gamemode == GameMode.CREATIVE) {
|
||||
return;
|
||||
}
|
||||
case CANCELLED_DIGGING:
|
||||
case FINISHED_DIGGING:
|
||||
if (exemptPlacingWhileDigging || player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_7_10)) {
|
||||
return;
|
||||
}
|
||||
digging = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8) && !player.packetStateData.lastPacketWasTeleport) {
|
||||
digging = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
|
||||
if (setback) {
|
||||
setbackIfAboveSetbackVL();
|
||||
setback = false;
|
||||
}
|
||||
} else if (player.isTickingReliablyFor(3)) {
|
||||
for (String verbose : flags) {
|
||||
if (flagAndAlert(verbose) && setback) {
|
||||
setbackIfAboveSetbackVL();
|
||||
setback = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flags.clear();
|
||||
setback = digging = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReload(ConfigManager config) {
|
||||
exemptPlacingWhileDigging = config.getBooleanElse(getConfigName() + ".exempt-placing-while-digging", false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
|
||||
@CheckData(name = "PacketOrderJ", experimental = true)
|
||||
public class PacketOrderJ extends Check implements PostPredictionCheck {
|
||||
public PacketOrderJ(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private int invalid;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT || event.getPacketType() == PacketType.Play.Client.USE_ITEM) {
|
||||
if (player.packetOrderProcessor.isAttacking() && !player.packetOrderProcessor.isInteracting()) {
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
|
||||
if (flagAndAlert() && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
invalid++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
// we don't need to check pre-1.9 players here (no tick skipping)
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
|
||||
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (; invalid >= 1; invalid--) {
|
||||
flagAndAlert();
|
||||
}
|
||||
}
|
||||
|
||||
invalid = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
|
||||
@CheckData(name = "PacketOrderK", experimental = true)
|
||||
public class PacketOrderK extends Check implements PostPredictionCheck {
|
||||
public PacketOrderK(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private final ArrayDeque<String> flags = new ArrayDeque<>();
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.CLIENT_STATUS) {
|
||||
if (new WrapperPlayClientClientStatus(event).getAction() == WrapperPlayClientClientStatus.Action.OPEN_INVENTORY_ACHIEVEMENT) {
|
||||
if (player.packetOrderProcessor.isClickingInInventory() || player.packetOrderProcessor.isClosingInventory()) {
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
|
||||
flagAndAlert("open");
|
||||
} else {
|
||||
flags.add("open");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW || event.getPacketType() == PacketType.Play.Client.CLOSE_WINDOW) {
|
||||
if (player.packetOrderProcessor.isOpeningInventory()) {
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
|
||||
if (flagAndAlert(event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW ? "click" : "close") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
flags.add(event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW ? "click" : "close");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
// we don't need to check pre-1.9 players here (no tick skipping)
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
|
||||
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (String verbose : flags) {
|
||||
flagAndAlert(verbose);
|
||||
}
|
||||
}
|
||||
|
||||
flags.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
|
||||
@CheckData(name = "PacketOrderL", experimental = true)
|
||||
public class PacketOrderL extends Check implements PostPredictionCheck {
|
||||
public PacketOrderL(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private final ArrayDeque<String> flags = new ArrayDeque<>();
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.CLIENT_STATUS) {
|
||||
if (new WrapperPlayClientClientStatus(event).getAction() == WrapperPlayClientClientStatus.Action.OPEN_INVENTORY_ACHIEVEMENT) {
|
||||
if (player.packetOrderProcessor.isDropping()) {
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
|
||||
if (flagAndAlert("inventory") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
flags.add("inventory");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING) {
|
||||
if (new WrapperPlayClientPlayerDigging(event).getAction() == DiggingAction.SWAP_ITEM_WITH_OFFHAND) {
|
||||
if (player.packetOrderProcessor.isDropping()) {
|
||||
flags.add("swap");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
// we don't need to check pre-1.9 players here (no tick skipping)
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
|
||||
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (String verbose : flags) {
|
||||
flagAndAlert(verbose);
|
||||
}
|
||||
}
|
||||
|
||||
flags.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerBlockPlacement;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
|
||||
|
||||
@CheckData(name = "PacketOrderM", experimental = true)
|
||||
public class PacketOrderM extends Check implements PostPredictionCheck {
|
||||
public PacketOrderM(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private int invalid;
|
||||
private boolean usingWithoutInteract, interacting;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
|
||||
if (new WrapperPlayClientInteractEntity(event).getAction() != WrapperPlayClientInteractEntity.InteractAction.ATTACK) {
|
||||
interacting = true;
|
||||
if (usingWithoutInteract) {
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
|
||||
if (flagAndAlert() && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
invalid++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.USE_ITEM
|
||||
|| event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT
|
||||
&& new WrapperPlayClientPlayerBlockPlacement(event).getFace() == BlockFace.OTHER) {
|
||||
if (!interacting) {
|
||||
usingWithoutInteract = true;
|
||||
}
|
||||
|
||||
interacting = false;
|
||||
}
|
||||
|
||||
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8) && !player.packetStateData.lastPacketWasTeleport) {
|
||||
usingWithoutInteract = interacting = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
// we don't need to check pre-1.9 players here (no tick skipping)
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
|
||||
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (; invalid >= 1; invalid--) {
|
||||
flagAndAlert();
|
||||
}
|
||||
}
|
||||
|
||||
invalid = 0;
|
||||
usingWithoutInteract = interacting = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.BlockPlaceCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerBlockPlacement;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
|
||||
|
||||
@CheckData(name = "PacketOrderN", experimental = true)
|
||||
public class PacketOrderN extends BlockPlaceCheck {
|
||||
public PacketOrderN(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private int invalid;
|
||||
private boolean usingWithoutPlacing, placing;
|
||||
|
||||
@Override
|
||||
public void onBlockPlace(BlockPlace place) {
|
||||
placing = true;
|
||||
if (usingWithoutPlacing) {
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
|
||||
if (flagAndAlert() && shouldModifyPackets() && shouldCancel()) {
|
||||
place.resync();
|
||||
}
|
||||
} else {
|
||||
invalid++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.USE_ITEM
|
||||
|| event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT
|
||||
&& new WrapperPlayClientPlayerBlockPlacement(event).getFace() == BlockFace.OTHER) {
|
||||
if (!placing) {
|
||||
usingWithoutPlacing = true;
|
||||
}
|
||||
|
||||
placing = false;
|
||||
}
|
||||
|
||||
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8) && !player.packetStateData.lastPacketWasTeleport) {
|
||||
usingWithoutPlacing = placing = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
// we don't need to check pre-1.9 players here (no tick skipping)
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
|
||||
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (; invalid >= 1; invalid--) {
|
||||
flagAndAlert();
|
||||
}
|
||||
}
|
||||
|
||||
invalid = 0;
|
||||
usingWithoutPlacing = placing = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.BlockPlaceCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.InteractionHand;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
|
||||
|
||||
@CheckData(name = "PacketOrderO", experimental = true)
|
||||
public class PacketOrderO extends BlockPlaceCheck {
|
||||
public PacketOrderO(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private boolean sentMainhand;
|
||||
|
||||
@Override
|
||||
public void onBlockPlace(BlockPlace place) {
|
||||
if (!isSupported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (place.getHand() == InteractionHand.OFF_HAND) {
|
||||
if (!sentMainhand) {
|
||||
if (flagAndAlert("Skipped Mainhand") && shouldModifyPackets() && shouldCancel()) {
|
||||
place.resync();
|
||||
}
|
||||
}
|
||||
|
||||
sentMainhand = false;
|
||||
} else {
|
||||
if (sentMainhand) {
|
||||
if (flagAndAlert("Skipped Offhand") && shouldModifyPackets() && shouldCancel()) {
|
||||
place.resync();
|
||||
}
|
||||
}
|
||||
|
||||
sentMainhand = !place.isBlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && !player.packetStateData.lastPacketWasTeleport && !player.packetStateData.lastPacketWasOnePointSeventeenDuplicate) {
|
||||
if (sentMainhand) {
|
||||
sentMainhand = false;
|
||||
flagAndAlert("Skipped Offhand (Tick)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSupported() {
|
||||
return player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13) && PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.InteractionHand;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity.InteractAction;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
|
||||
|
||||
@CheckData(name = "PacketOrderP", experimental = true)
|
||||
public class PacketOrderP extends Check implements PacketCheck {
|
||||
public PacketOrderP(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private boolean sentMainhand;
|
||||
private int requiredEntity;
|
||||
private boolean requiredSneaking;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
|
||||
final WrapperPlayClientInteractEntity packet = new WrapperPlayClientInteractEntity(event);
|
||||
InteractAction action = packet.getAction();
|
||||
if (action != InteractAction.ATTACK) {
|
||||
final boolean sneaking = packet.isSneaking().orElse(false);
|
||||
final int entity = packet.getEntityId();
|
||||
|
||||
if (packet.getHand() == InteractionHand.OFF_HAND) {
|
||||
if (action == InteractAction.INTERACT) {
|
||||
if (!sentMainhand) {
|
||||
if (flagAndAlert("Skipped Mainhand") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
sentMainhand = false;
|
||||
} else if (sneaking != requiredSneaking || entity != requiredEntity) {
|
||||
String verbose = "requiredEntity=" + requiredEntity + ", entity=" + entity
|
||||
+ ", requiredSneaking=" + requiredSneaking + ", sneaking=" + sneaking;
|
||||
if (flagAndAlert(verbose) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
requiredEntity = entity;
|
||||
requiredSneaking = sneaking;
|
||||
sentMainhand = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && !player.packetStateData.lastPacketWasTeleport && !player.packetStateData.lastPacketWasOnePointSeventeenDuplicate) {
|
||||
sentMainhand = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.*;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
||||
@Getter
|
||||
public final class PacketOrderProcessor extends Check implements PostPredictionCheck {
|
||||
public PacketOrderProcessor(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private boolean openingInventory; // only pre 1.9 clients on pre 1.9 servers
|
||||
private boolean swapping;
|
||||
private boolean dropping;
|
||||
private boolean interacting;
|
||||
private boolean attacking;
|
||||
private boolean releasing;
|
||||
private boolean digging;
|
||||
private boolean sprinting;
|
||||
private boolean sneaking;
|
||||
private boolean placing;
|
||||
private boolean using;
|
||||
private boolean picking;
|
||||
private boolean clickingInInventory;
|
||||
private boolean closingInventory;
|
||||
private boolean quickMoveClicking;
|
||||
private boolean pickUpClicking;
|
||||
private boolean leavingBed;
|
||||
private boolean startingToGlide;
|
||||
private boolean jumpingWithMount;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
final PacketTypeCommon packetType = event.getPacketType();
|
||||
|
||||
if (packetType == PacketType.Play.Client.CLIENT_STATUS) {
|
||||
if (new WrapperPlayClientClientStatus(event).getAction() == WrapperPlayClientClientStatus.Action.OPEN_INVENTORY_ACHIEVEMENT) {
|
||||
openingInventory = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (packetType == PacketType.Play.Client.INTERACT_ENTITY) {
|
||||
if (new WrapperPlayClientInteractEntity(event).getAction() == WrapperPlayClientInteractEntity.InteractAction.ATTACK) {
|
||||
attacking = true;
|
||||
} else {
|
||||
interacting = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (packetType == PacketType.Play.Client.PLAYER_DIGGING) {
|
||||
switch (new WrapperPlayClientPlayerDigging(event).getAction()) {
|
||||
case SWAP_ITEM_WITH_OFFHAND:
|
||||
swapping = true;
|
||||
break;
|
||||
case DROP_ITEM:
|
||||
case DROP_ITEM_STACK:
|
||||
dropping = true;
|
||||
break;
|
||||
case RELEASE_USE_ITEM:
|
||||
releasing = true;
|
||||
break;
|
||||
case FINISHED_DIGGING:
|
||||
case CANCELLED_DIGGING:
|
||||
case START_DIGGING:
|
||||
digging = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (packetType == PacketType.Play.Client.ENTITY_ACTION) {
|
||||
switch (new WrapperPlayClientEntityAction(event).getAction()) {
|
||||
case START_SPRINTING:
|
||||
case STOP_SPRINTING:
|
||||
sprinting = true;
|
||||
break;
|
||||
case STOP_SNEAKING:
|
||||
case START_SNEAKING:
|
||||
sneaking = true;
|
||||
break;
|
||||
case LEAVE_BED:
|
||||
leavingBed = true;
|
||||
break;
|
||||
case START_FLYING_WITH_ELYTRA:
|
||||
startingToGlide = true;
|
||||
break;
|
||||
case OPEN_HORSE_INVENTORY:
|
||||
openingInventory = true;
|
||||
break;
|
||||
case START_JUMPING_WITH_HORSE:
|
||||
case STOP_JUMPING_WITH_HORSE:
|
||||
jumpingWithMount = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (packetType == PacketType.Play.Client.USE_ITEM) {
|
||||
using = true;
|
||||
}
|
||||
|
||||
if (packetType == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT) {
|
||||
if (new WrapperPlayClientPlayerBlockPlacement(event).getFace() == BlockFace.OTHER) {
|
||||
using = true;
|
||||
} else {
|
||||
placing = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (packetType == PacketType.Play.Client.PICK_ITEM) {
|
||||
picking = true;
|
||||
}
|
||||
|
||||
if (packetType == PacketType.Play.Client.CLICK_WINDOW) {
|
||||
clickingInInventory = true;
|
||||
|
||||
switch (new WrapperPlayClientClickWindow(event).getWindowClickType()) {
|
||||
case QUICK_MOVE:
|
||||
quickMoveClicking = true;
|
||||
break;
|
||||
case PICKUP:
|
||||
case PICKUP_ALL:
|
||||
pickUpClicking = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (packetType == PacketType.Play.Client.CLOSE_WINDOW) {
|
||||
closingInventory = true;
|
||||
}
|
||||
|
||||
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8) && !player.packetStateData.lastPacketWasTeleport) {
|
||||
onTick();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
|
||||
onTick();
|
||||
}
|
||||
}
|
||||
|
||||
private void onTick() {
|
||||
openingInventory = false;
|
||||
swapping = false;
|
||||
dropping = false;
|
||||
attacking = false;
|
||||
interacting = false;
|
||||
releasing = false;
|
||||
digging = false;
|
||||
placing = false;
|
||||
using = false;
|
||||
picking = false;
|
||||
sprinting = false;
|
||||
sneaking = false;
|
||||
clickingInInventory = false;
|
||||
closingInventory = false;
|
||||
quickMoveClicking = false;
|
||||
pickUpClicking = false;
|
||||
leavingBed = false;
|
||||
startingToGlide = false;
|
||||
jumpingWithMount = false;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public boolean isRightClicking() {
|
||||
return placing || using || interacting;
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -2,7 +2,6 @@ package ac.grim.grimac.checks.impl.post;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
@@ -25,7 +24,7 @@ import java.util.Locale;
|
||||
import static com.github.retrooper.packetevents.protocol.packettype.PacketType.Play.Client.*;
|
||||
|
||||
@CheckData(name = "Post")
|
||||
public class PostCheck extends Check implements PacketCheck, PostPredictionCheck {
|
||||
public class Post extends Check implements PostPredictionCheck {
|
||||
private final ArrayDeque<PacketTypeCommon> post = new ArrayDeque<>();
|
||||
// Due to 1.9+ missing the idle packet, we must queue flags
|
||||
// 1.8 clients will have the same logic for simplicity, although it's not needed
|
||||
@@ -33,7 +32,7 @@ public class PostCheck extends Check implements PacketCheck, PostPredictionCheck
|
||||
private boolean sentFlying = false;
|
||||
private int isExemptFromSwingingCheck = Integer.MIN_VALUE;
|
||||
|
||||
public PostCheck(GrimPlayer playerData) {
|
||||
public Post(GrimPlayer playerData) {
|
||||
super(playerData);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import ac.grim.grimac.utils.lists.EvictingQueue;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@@ -45,8 +45,8 @@ public class DebugHandler extends Check implements PostPredictionCheck {
|
||||
|
||||
ChatColor color = pickColor(offset, offset);
|
||||
|
||||
Vector predicted = player.predictedVelocity.vector;
|
||||
Vector actually = player.actualMovement;
|
||||
Vector3D predicted = player.predictedVelocity.vector;
|
||||
Vector3D actually = player.actualMovement;
|
||||
|
||||
ChatColor xColor = pickColor(Math.abs(predicted.getX() - actually.getX()), offset);
|
||||
ChatColor yColor = pickColor(Math.abs(predicted.getY() - actually.getY()), offset);
|
||||
|
||||
@@ -9,7 +9,9 @@ import ac.grim.grimac.utils.math.VectorUtils;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
|
||||
@CheckData(name = "FarPlace")
|
||||
public class FarPlace extends BlockPlaceCheck {
|
||||
@@ -27,8 +29,8 @@ public class FarPlace extends BlockPlaceCheck {
|
||||
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
|
||||
for (double d : possibleEyeHeights) {
|
||||
SimpleCollisionBox box = new SimpleCollisionBox(blockPos);
|
||||
Vector eyes = new Vector(player.x, player.y + d, player.z);
|
||||
Vector best = VectorUtils.cutBoxToVector(eyes, box);
|
||||
Vector3D eyes = newVector3D(player.x, player.y + d, player.z);
|
||||
Vector3D best = VectorUtils.cutBoxToVector(eyes, box);
|
||||
min = Math.min(min, eyes.distanceSquared(best));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
package ac.grim.grimac.checks.impl.scaffolding;
|
||||
|
||||
import ac.grim.grimac.api.config.ConfigManager;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.BlockPlaceCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.data.HitData;
|
||||
import ac.grim.grimac.utils.nmsutil.BlockRayTrace;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@CheckData(name = "LineOfSightPlace")
|
||||
public class LineOfSightPlace extends BlockPlaceCheck {
|
||||
|
||||
private double flagBuffer = 0; // If the player flags once, force them to play legit, or we will cancel the tick before.
|
||||
private boolean ignorePost = false;
|
||||
private boolean useBlockWhitelist;
|
||||
private HashSet<StateType> blockWhitelist;
|
||||
|
||||
// 15 is the maximum set of Collision boxes that will be used in the ray trace check
|
||||
// it corresponds to the size of the collision boxes from the modern Cauldron
|
||||
// Since this is used per-player we can avoid calling new[] by allocating it per-player in the check
|
||||
private final SimpleCollisionBox[] collisionBoxBuffer = new SimpleCollisionBox[15];
|
||||
|
||||
public final ConcurrentHashMap<Vector3i, WrappedBlockState> blocksChangedList = new ConcurrentHashMap<>();
|
||||
|
||||
public LineOfSightPlace(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockPlace(final BlockPlace place) {
|
||||
if (checkIfShouldSkip(place)) return;
|
||||
|
||||
// if (flagBuffer > 0 && !didRayTraceHit(place)) {
|
||||
// ignorePost = true;
|
||||
// // If the player hit and has flagged this check recently
|
||||
// if (flagAndAlert("pre-flying: " + player.compensatedWorld.getWrappedBlockStateAt(place.getPlacedAgainstBlockLocation()).getType()) && shouldModifyPackets() && shouldCancel()) {
|
||||
// place.resync(); // Deny the block placement.
|
||||
// }
|
||||
// }
|
||||
// Only flag (which will only happen in post) if we miss pre
|
||||
if (didRayTraceHit(place)) {
|
||||
ignorePost = true;
|
||||
} else if (flagBuffer > 0) {
|
||||
if (flagAndAlert("pre-flying: " + player.compensatedWorld.getWrappedBlockStateAt(place.getPlacedAgainstBlockLocation()).getType()) && shouldModifyPackets() && shouldCancel()) {
|
||||
place.resync(); // Deny the block placement.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use post flying because it has the correct rotation, and can't false easily.
|
||||
@Override
|
||||
public void onPostFlyingBlockPlace(BlockPlace place) {
|
||||
if (checkIfShouldSkip(place)) return;
|
||||
|
||||
// Don't flag twice
|
||||
if (ignorePost) {
|
||||
ignorePost = false;
|
||||
return;
|
||||
}
|
||||
blocksChangedList.clear();
|
||||
// Ray trace to try and hit the target block.
|
||||
boolean hit = didRayTraceHit(place);
|
||||
// This can false with rapidly moving yaw in 1.8+ clients
|
||||
if (!hit) {
|
||||
flagBuffer = 1;
|
||||
flagAndAlert("post-flying: " + player.compensatedWorld.getWrappedBlockStateAt(place.getPlacedAgainstBlockLocation()).getType());
|
||||
}
|
||||
else {
|
||||
flagBuffer = Math.max(0, flagBuffer - 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkIfShouldSkip(BlockPlace place) {
|
||||
StateType targetBlockStateType = player.compensatedWorld.getWrappedBlockStateAt(place.getPlacedAgainstBlockLocation()).getType();
|
||||
if (player.gamemode == GameMode.SPECTATOR) return true; // A waste to check creative mode players
|
||||
if (targetBlockStateType == StateTypes.REDSTONE_WIRE) return true; // Redstone too buggy
|
||||
if (player.compensatedWorld.isNearHardEntity(player.boundingBox.copy().expand(4))) return true; // Shulkers and Pistons are too buggy
|
||||
|
||||
if (useBlockWhitelist) {
|
||||
return !isBlockTypeWhitelisted(targetBlockStateType);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void handleBlockChange(Vector3i vector3i, WrappedBlockState state) {
|
||||
if (blocksChangedList.size() >= 60) return; // Don't let players freeze movement packets to grow this
|
||||
// Only do this for nearby blocks
|
||||
if (new Vector3d(vector3i.x, vector3i.y, vector3i.z).distanceSquared(new Vector3d(player.x, player.y, player.z)) > 36) return;
|
||||
// Only do this if the state really had any world impact
|
||||
// this line always just returns the current world state in compensatedWorld
|
||||
// if (state.equals(player.compensatedWorld.getWrappedBlockStateAt(vector3i))) return;
|
||||
blocksChangedList.put(vector3i, state);
|
||||
}
|
||||
|
||||
private boolean didRayTraceHit(BlockPlace place) {
|
||||
double[] possibleEyeHeights = player.getPossibleEyeHeights();
|
||||
|
||||
// Start checking if player is in the block
|
||||
double minEyeHeight = Double.MAX_VALUE;
|
||||
double maxEyeHeight = Double.MIN_VALUE;
|
||||
for (double height : possibleEyeHeights) {
|
||||
minEyeHeight = Math.min(minEyeHeight, height);
|
||||
maxEyeHeight = Math.max(maxEyeHeight, height);
|
||||
}
|
||||
|
||||
double movementThreshold = player.getMovementThreshold();
|
||||
|
||||
SimpleCollisionBox eyePositions = new SimpleCollisionBox(player.x, player.y + minEyeHeight, player.z, player.x, player.y + maxEyeHeight, player.z);
|
||||
eyePositions.expand(movementThreshold);
|
||||
|
||||
int[] interactBlockVec = new int[]{place.blockPosition.x, place.blockPosition.y, place.blockPosition.z};
|
||||
BlockFace expectedBlockFace = place.getDirection();
|
||||
|
||||
// If the player is inside a block, then they can ray trace through the block and hit the other side of the block
|
||||
// This may potentially be exploitable as a minor bypass
|
||||
if (eyePositions.isIntersected(new SimpleCollisionBox(interactBlockVec[0], interactBlockVec[1], interactBlockVec[2]))) {
|
||||
return true;
|
||||
}
|
||||
// End checking if the player is in the block
|
||||
double[][] possibleLookDirs;
|
||||
// 1.9+ players could be a tick behind because we don't get skipped ticks
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
|
||||
possibleLookDirs = new double[][]{
|
||||
{player.xRot, player.yRot},
|
||||
{player.lastXRot, player.lastYRot},
|
||||
{player.lastXRot, player.yRot}
|
||||
};
|
||||
} else if (player.getClientVersion().isOlderThan(ClientVersion.V_1_8)) {
|
||||
// 1.7 players do not have any of these issues! They are always on the latest look vector
|
||||
possibleLookDirs = new double[][]{{player.xRot, player.yRot}};
|
||||
} else {
|
||||
possibleLookDirs = new double[][]{
|
||||
{player.xRot, player.yRot},
|
||||
{player.lastXRot, player.yRot}
|
||||
};
|
||||
}
|
||||
|
||||
// We do not need to add 0.03/0.0002 to maxDistance to ensure our raytrace hits blocks
|
||||
// Since we expand the hitboxes of the expectedTargetBlock by 0.03/0.002 already later
|
||||
double maxDistance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_BLOCK_INTERACTION_RANGE);
|
||||
|
||||
// Define possible offsets
|
||||
// TODO, vectorize this with SIMD or AVX for performance
|
||||
double[][] offsets = {
|
||||
{0, 0, 0},
|
||||
{movementThreshold, 0, 0},
|
||||
{-movementThreshold, 0, 0},
|
||||
{0, movementThreshold, 0},
|
||||
{0, -movementThreshold, 0},
|
||||
{0, 0, movementThreshold},
|
||||
{0, 0, -movementThreshold}
|
||||
};
|
||||
|
||||
double[] eyePosition = new double[3];
|
||||
double[] eyeLookDir = new double[3];
|
||||
|
||||
for (double eyeHeight : possibleEyeHeights) {
|
||||
for (double[] lookDir : possibleLookDirs) {
|
||||
for (double[] offset : offsets) {
|
||||
eyePosition[0] = player.x + offset[0];
|
||||
eyePosition[1] = player.y + eyeHeight + offset[1];
|
||||
eyePosition[2] = player.z + offset[2];
|
||||
|
||||
calculateDirection(eyeLookDir, lookDir[0], lookDir[1]);
|
||||
|
||||
if (didRayTraceHitTargetBlock(eyePosition, eyeLookDir, maxDistance, interactBlockVec, expectedBlockFace)) {
|
||||
return true; // If any possible face matches the client-side placement, assume it's legitimate
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false; // No matching face found
|
||||
}
|
||||
|
||||
// Helper method to calculate direction (replace the Ray class method)
|
||||
private void calculateDirection(double[] result, double xRot, double yRot) {
|
||||
float rotX = (float) Math.toRadians(xRot);
|
||||
float rotY = (float) Math.toRadians(yRot);
|
||||
result[1] = -player.trigHandler.sin(rotY);
|
||||
double xz = player.trigHandler.cos(rotY);
|
||||
result[0] = -xz * player.trigHandler.sin(rotX);
|
||||
result[2] = xz * player.trigHandler.cos(rotX);
|
||||
}
|
||||
|
||||
private boolean didRayTraceHitTargetBlock(double[] eyePos, double[] eyeDir, double maxDistance, int[] targetBlockVec, BlockFace expectedBlockFace) {
|
||||
HitData hitData = BlockRayTrace.getNearestReachHitResult(player, eyePos, eyeDir, maxDistance, maxDistance, targetBlockVec, expectedBlockFace, collisionBoxBuffer, null, false);
|
||||
if (hitData == null || !hitData.success)
|
||||
hitData = BlockRayTrace.getNearestReachHitResult(player, eyePos, eyeDir, maxDistance, maxDistance, targetBlockVec,
|
||||
expectedBlockFace, collisionBoxBuffer, player.checkManager.getBlockPlaceCheck(LineOfSightPlace.class).blocksChangedList, false);
|
||||
|
||||
// we check for hitdata != null because of being in expanded hitbox, or there was no result, do we still need this?
|
||||
return hitData != null && hitData.success;
|
||||
}
|
||||
|
||||
private boolean isBlockTypeWhitelisted(StateType type) {
|
||||
return blockWhitelist.contains(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReload(ConfigManager config) {
|
||||
useBlockWhitelist = config.getBooleanElse("LineOfSightPlace.use-block-whitelist", false);
|
||||
blockWhitelist = new HashSet<>();
|
||||
List<String> blocks = config.getStringList("LineOfSightPlace.block-whitelist");
|
||||
for (String block : blocks) {
|
||||
blockWhitelist.add(StateTypes.getByName(block));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,21 +48,27 @@ public class PositionPlace extends BlockPlaceCheck {
|
||||
|
||||
// So now we have the player's possible eye positions
|
||||
// So then look at the face that the player has clicked
|
||||
boolean flag = switch (place.getDirection()) {
|
||||
case NORTH -> // Z- face
|
||||
eyePositions.minZ > combined.minZ;
|
||||
case SOUTH -> // Z+ face
|
||||
eyePositions.maxZ < combined.maxZ;
|
||||
case EAST -> // X+ face
|
||||
eyePositions.maxX < combined.maxX;
|
||||
case WEST -> // X- face
|
||||
eyePositions.minX > combined.minX;
|
||||
case UP -> // Y+ face
|
||||
eyePositions.maxY < combined.maxY;
|
||||
case DOWN -> // Y- face
|
||||
eyePositions.minY > combined.minY;
|
||||
default -> false;
|
||||
};
|
||||
boolean flag = false;
|
||||
switch (place.getDirection()) {
|
||||
case NORTH: // Z- face
|
||||
flag = eyePositions.minZ > combined.minZ;
|
||||
break;
|
||||
case SOUTH: // Z+ face
|
||||
flag = eyePositions.maxZ < combined.maxZ;
|
||||
break;
|
||||
case EAST: // X+ face
|
||||
flag = eyePositions.maxX < combined.maxX;
|
||||
break;
|
||||
case WEST: // X- face
|
||||
flag = eyePositions.minX > combined.minX;
|
||||
break;
|
||||
case UP: // Y+ face
|
||||
flag = eyePositions.maxY < combined.maxY;
|
||||
break;
|
||||
case DOWN: // Y- face
|
||||
flag = eyePositions.minY > combined.minY;
|
||||
break;
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
if (flagAndAlert() && shouldModifyPackets() && shouldCancel()) {
|
||||
|
||||
@@ -1,122 +1 @@
|
||||
package ac.grim.grimac.checks.impl.scaffolding;
|
||||
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.BlockPlaceCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.data.Pair;
|
||||
import ac.grim.grimac.utils.nmsutil.Ray;
|
||||
import ac.grim.grimac.utils.nmsutil.ReachUtils;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3f;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@CheckData(name = "RotationPlace")
|
||||
public class RotationPlace extends BlockPlaceCheck {
|
||||
double flagBuffer = 0; // If the player flags once, force them to play legit, or we will cancel the tick before.
|
||||
boolean ignorePost = false;
|
||||
|
||||
public RotationPlace(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockPlace(final BlockPlace place) {
|
||||
if (place.getMaterial() == StateTypes.SCAFFOLDING) return;
|
||||
if (player.gamemode == GameMode.SPECTATOR) return; // you don't send flying packets when spectating entities
|
||||
if (flagBuffer > 0 && !didRayTraceHit(place)) {
|
||||
ignorePost = true;
|
||||
// If the player hit and has flagged this check recently
|
||||
if (flagAndAlert("pre-flying") && shouldModifyPackets() && shouldCancel()) {
|
||||
place.resync(); // Deny the block placement.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use post flying because it has the correct rotation, and can't false easily.
|
||||
@Override
|
||||
public void onPostFlyingBlockPlace(BlockPlace place) {
|
||||
if (place.getMaterial() == StateTypes.SCAFFOLDING) return;
|
||||
if (player.gamemode == GameMode.SPECTATOR) return; // you don't send flying packets when spectating entities
|
||||
|
||||
// Don't flag twice
|
||||
if (ignorePost) {
|
||||
ignorePost = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Ray trace to try and hit the target block.
|
||||
boolean hit = didRayTraceHit(place);
|
||||
// This can false with rapidly moving yaw in 1.8+ clients
|
||||
if (!hit) {
|
||||
flagBuffer = 1;
|
||||
flagAndAlert("post-flying");
|
||||
} else {
|
||||
flagBuffer = Math.max(0, flagBuffer - 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean didRayTraceHit(BlockPlace place) {
|
||||
SimpleCollisionBox box = new SimpleCollisionBox(place.getPlacedAgainstBlockLocation());
|
||||
|
||||
List<Vector3f> possibleLookDirs = new ArrayList<>(Arrays.asList(
|
||||
new Vector3f(player.xRot, player.yRot, 0),
|
||||
new Vector3f(player.lastXRot, player.yRot, 0)
|
||||
));
|
||||
|
||||
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
|
||||
|
||||
// Start checking if player is in the block
|
||||
double minEyeHeight = Double.MAX_VALUE;
|
||||
double maxEyeHeight = Double.MIN_VALUE;
|
||||
for (double height : possibleEyeHeights) {
|
||||
minEyeHeight = Math.min(minEyeHeight, height);
|
||||
maxEyeHeight = Math.max(maxEyeHeight, height);
|
||||
}
|
||||
|
||||
SimpleCollisionBox eyePositions = new SimpleCollisionBox(player.x, player.y + minEyeHeight, player.z, player.x, player.y + maxEyeHeight, player.z);
|
||||
eyePositions.expand(player.getMovementThreshold());
|
||||
|
||||
// If the player is inside a block, then they can ray trace through the block and hit the other side of the block
|
||||
if (eyePositions.isIntersected(box)) {
|
||||
return true;
|
||||
}
|
||||
// End checking if the player is in the block
|
||||
|
||||
// 1.9+ players could be a tick behind because we don't get skipped ticks
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
|
||||
possibleLookDirs.add(new Vector3f(player.lastXRot, player.lastYRot, 0));
|
||||
}
|
||||
|
||||
// 1.7 players do not have any of these issues! They are always on the latest look vector
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_8)) {
|
||||
possibleLookDirs = Collections.singletonList(new Vector3f(player.xRot, player.yRot, 0));
|
||||
}
|
||||
|
||||
final double distance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_BLOCK_INTERACTION_RANGE);
|
||||
for (double d : possibleEyeHeights) {
|
||||
for (Vector3f lookDir : possibleLookDirs) {
|
||||
// x, y, z are correct for the block placement even after post tick because of code elsewhere
|
||||
Vector3d starting = new Vector3d(player.x, player.y + d, player.z);
|
||||
// xRot and yRot are a tick behind
|
||||
Ray trace = new Ray(player, starting.getX(), starting.getY(), starting.getZ(), lookDir.getX(), lookDir.getY());
|
||||
Pair<Vector, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
|
||||
|
||||
if (intercept.first() != null) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -19,12 +19,14 @@ import com.github.retrooper.packetevents.util.Vector3f;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerExplosion;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
|
||||
@CheckData(name = "AntiExplosion", configName = "Explosion", setback = 10)
|
||||
public class ExplosionHandler extends Check implements PostPredictionCheck {
|
||||
Deque<VelocityData> firstBreadMap = new LinkedList<>();
|
||||
@@ -121,7 +123,7 @@ public class ExplosionHandler extends Check implements PostPredictionCheck {
|
||||
}
|
||||
|
||||
public void addPlayerExplosion(int breadOne, Vector3f explosion) {
|
||||
firstBreadMap.add(new VelocityData(-1, breadOne, player.getSetbackTeleportUtil().isSendingSetback, new Vector(explosion.getX(), explosion.getY(), explosion.getZ())));
|
||||
firstBreadMap.add(new VelocityData(-1, breadOne, player.getSetbackTeleportUtil().isSendingSetback, newVector3D(explosion.getX(), explosion.getY(), explosion.getZ())));
|
||||
}
|
||||
|
||||
public void setPointThree(boolean isPointThree) {
|
||||
|
||||
@@ -15,12 +15,14 @@ import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityVelocity;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
|
||||
// We are making a velocity sandwich between two pieces of transaction packets (bread)
|
||||
@CheckData(name = "AntiKB", alternativeName = "AntiKnockback", configName = "Knockback", setback = 10, decay = 0.025)
|
||||
public class KnockbackHandler extends Check implements PostPredictionCheck {
|
||||
@@ -70,12 +72,12 @@ public class KnockbackHandler extends Check implements PostPredictionCheck {
|
||||
|
||||
// Wrap velocity between two transactions
|
||||
player.sendTransaction();
|
||||
addPlayerKnockback(entityId, player.lastTransactionSent.get(), new Vector(playerVelocity.getX(), playerVelocity.getY(), playerVelocity.getZ()));
|
||||
addPlayerKnockback(entityId, player.lastTransactionSent.get(), newVector3D(playerVelocity.getX(), playerVelocity.getY(), playerVelocity.getZ()));
|
||||
event.getTasksAfterSend().add(player::sendTransaction);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull public Pair<VelocityData, Vector> getFutureKnockback() {
|
||||
@NotNull public Pair<VelocityData, Vector3D> getFutureKnockback() {
|
||||
// Chronologically in the future
|
||||
if (firstBreadMap.size() > 0) {
|
||||
VelocityData data = firstBreadMap.peek();
|
||||
@@ -97,7 +99,7 @@ public class KnockbackHandler extends Check implements PostPredictionCheck {
|
||||
return new Pair<>(null, null);
|
||||
}
|
||||
|
||||
private void addPlayerKnockback(int entityID, int breadOne, Vector knockback) {
|
||||
private void addPlayerKnockback(int entityID, int breadOne, Vector3D knockback) {
|
||||
firstBreadMap.add(new VelocityData(entityID, breadOne, player.getSetbackTeleportUtil().isSendingSetback, knockback));
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
|
||||
import ac.grim.grimac.utils.collisions.HitboxData;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.ComplexCollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
@@ -18,6 +19,7 @@ import java.util.List;
|
||||
public class BlockPlaceCheck extends Check implements RotationCheck, PostPredictionCheck {
|
||||
private static final List<StateType> weirdBoxes = new ArrayList<>();
|
||||
private static final List<StateType> buggyBoxes = new ArrayList<>();
|
||||
private final SimpleCollisionBox[] boxes = new SimpleCollisionBox[ComplexCollisionBox.DEFAULT_MAX_COLLISION_BOX_SIZE];
|
||||
|
||||
protected int cancelVL;
|
||||
|
||||
@@ -69,11 +71,11 @@ public class BlockPlaceCheck extends Check implements RotationCheck, PostPredict
|
||||
Vector3i clicked = place.getPlacedAgainstBlockLocation();
|
||||
CollisionBox placedOn = HitboxData.getBlockHitbox(player, place.getMaterial(), player.getClientVersion(), player.compensatedWorld.getWrappedBlockStateAt(clicked), clicked.getX(), clicked.getY(), clicked.getZ());
|
||||
|
||||
List<SimpleCollisionBox> boxes = new ArrayList<>();
|
||||
placedOn.downCast(boxes);
|
||||
int size = placedOn.downCast(boxes);
|
||||
|
||||
SimpleCollisionBox combined = new SimpleCollisionBox(clicked.getX(), clicked.getY(), clicked.getZ());
|
||||
for (SimpleCollisionBox box : boxes) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
SimpleCollisionBox box = boxes[i];
|
||||
double minX = Math.max(box.minX, combined.minX);
|
||||
double minY = Math.max(box.minY, combined.minY);
|
||||
double minZ = Math.max(box.minZ, combined.minZ);
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package ac.grim.grimac.checks.type;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientCloseWindow;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerCloseWindow;
|
||||
import org.jetbrains.annotations.MustBeInvokedByOverriders;
|
||||
|
||||
public class InventoryCheck extends BlockPlaceCheck implements PacketCheck {
|
||||
// Impossible transaction ID
|
||||
protected static final long NONE = Long.MAX_VALUE;
|
||||
|
||||
protected long closeTransaction = NONE;
|
||||
protected int closePacketsToSkip;
|
||||
|
||||
public InventoryCheck(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@MustBeInvokedByOverriders
|
||||
public void onPacketReceive(final PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW) {
|
||||
// Disallow any clicks if inventory is closing
|
||||
if (closeTransaction != NONE && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
player.getInventory().needResend = true;
|
||||
}
|
||||
} else if (event.getPacketType() == PacketType.Play.Client.CLOSE_WINDOW) {
|
||||
// Players with high ping can close inventory faster than send transaction back
|
||||
if (closeTransaction != NONE && closePacketsToSkip-- <= 0) {
|
||||
closeTransaction = NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void closeInventory() {
|
||||
if (closeTransaction != NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
int windowId = player.getInventory().openWindowID;
|
||||
|
||||
player.user.writePacket(new WrapperPlayServerCloseWindow(windowId));
|
||||
|
||||
// Force close inventory on server side
|
||||
closePacketsToSkip = 1; // Sending close packet to itself, so skip it
|
||||
PacketEvents.getAPI().getProtocolManager().receivePacket(
|
||||
player.user.getChannel(), new WrapperPlayClientCloseWindow(windowId)
|
||||
);
|
||||
|
||||
player.sendTransaction();
|
||||
|
||||
int transaction = player.lastTransactionSent.get();
|
||||
closeTransaction = transaction;
|
||||
player.latencyUtils.addRealTimeTask(transaction, () -> {
|
||||
if (closeTransaction == transaction) {
|
||||
closeTransaction = NONE;
|
||||
}
|
||||
});
|
||||
|
||||
player.user.flushPackets();
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,8 @@ public class GrimSpectate extends BaseCommand {
|
||||
@CommandPermission("grim.spectate")
|
||||
@CommandCompletion("@players")
|
||||
public void onSpectate(CommandSender sender, @Optional OnlinePlayer target) {
|
||||
if (!(sender instanceof Player player)) return;
|
||||
if (!(sender instanceof Player)) return;
|
||||
Player player = (Player) sender;
|
||||
|
||||
if (target != null && target.getPlayer().getUniqueId().equals(player.getUniqueId())) {
|
||||
String message = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("cannot-run-on-self", "%prefix% &cYou cannot use this command on yourself!");
|
||||
|
||||
@@ -17,7 +17,8 @@ public class GrimStopSpectating extends BaseCommand {
|
||||
@CommandCompletion("here")
|
||||
public void onStopSpectate(CommandSender sender, String[] args) {
|
||||
String string = args.length > 0 ? args[0] : null;
|
||||
if (!(sender instanceof Player player)) return;
|
||||
if (!(sender instanceof Player)) return;
|
||||
Player player = (Player) sender;
|
||||
if (GrimAPI.INSTANCE.getSpectateManager().isSpectating(player.getUniqueId())) {
|
||||
boolean teleportBack = string == null || !string.equalsIgnoreCase("here");
|
||||
GrimAPI.INSTANCE.getSpectateManager().disable(player, teleportBack);
|
||||
|
||||
@@ -3,18 +3,15 @@ package ac.grim.grimac.events.packets;
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.checks.impl.badpackets.BadPacketsX;
|
||||
import ac.grim.grimac.checks.impl.badpackets.BadPacketsZ;
|
||||
import ac.grim.grimac.checks.impl.inventory.InventoryB;
|
||||
import ac.grim.grimac.events.packets.patch.ResyncWorldUtil;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.*;
|
||||
import ac.grim.grimac.utils.blockplace.BlockPlaceResult;
|
||||
import ac.grim.grimac.utils.blockplace.ConsumesBlockPlace;
|
||||
import ac.grim.grimac.utils.collisions.HitboxData;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.data.*;
|
||||
import ac.grim.grimac.utils.inventory.Inventory;
|
||||
import ac.grim.grimac.utils.latency.CompensatedWorld;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.math.VectorUtils;
|
||||
import ac.grim.grimac.utils.nmsutil.*;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
@@ -24,7 +21,6 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.event.PacketSendEvent;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.ConnectionState;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.item.ItemStack;
|
||||
import com.github.retrooper.packetevents.protocol.item.type.ItemType;
|
||||
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
|
||||
@@ -47,11 +43,7 @@ import com.github.retrooper.packetevents.wrapper.play.client.*;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerAcknowledgeBlockChanges;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSetSlot;
|
||||
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class CheckManagerListener extends PacketListenerAbstract {
|
||||
@@ -60,79 +52,8 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
super(PacketListenerPriority.LOW);
|
||||
}
|
||||
|
||||
// Copied from MCP...
|
||||
// Returns null if there isn't anything.
|
||||
//
|
||||
// I do have to admit that I'm starting to like bifunctions/new java 8 things more than I originally did.
|
||||
// although I still don't understand Mojang's obsession with streams in some of the hottest methods... that kills performance
|
||||
public static HitData traverseBlocks(GrimPlayer player, Vector3d start, Vector3d end, BiFunction<WrappedBlockState, Vector3i, HitData> predicate) {
|
||||
// I guess go back by the collision epsilon?
|
||||
double endX = GrimMath.lerp(-1.0E-7D, end.x, start.x);
|
||||
double endY = GrimMath.lerp(-1.0E-7D, end.y, start.y);
|
||||
double endZ = GrimMath.lerp(-1.0E-7D, end.z, start.z);
|
||||
double startX = GrimMath.lerp(-1.0E-7D, start.x, end.x);
|
||||
double startY = GrimMath.lerp(-1.0E-7D, start.y, end.y);
|
||||
double startZ = GrimMath.lerp(-1.0E-7D, start.z, end.z);
|
||||
int floorStartX = GrimMath.floor(startX);
|
||||
int floorStartY = GrimMath.floor(startY);
|
||||
int floorStartZ = GrimMath.floor(startZ);
|
||||
|
||||
|
||||
if (start.equals(end)) return null;
|
||||
|
||||
WrappedBlockState state = player.compensatedWorld.getWrappedBlockStateAt(floorStartX, floorStartY, floorStartZ);
|
||||
HitData apply = predicate.apply(state, new Vector3i(floorStartX, floorStartY, floorStartZ));
|
||||
|
||||
if (apply != null) {
|
||||
return apply;
|
||||
}
|
||||
|
||||
double xDiff = endX - startX;
|
||||
double yDiff = endY - startY;
|
||||
double zDiff = endZ - startZ;
|
||||
double xSign = Math.signum(xDiff);
|
||||
double ySign = Math.signum(yDiff);
|
||||
double zSign = Math.signum(zDiff);
|
||||
|
||||
double posXInverse = xSign == 0 ? Double.MAX_VALUE : xSign / xDiff;
|
||||
double posYInverse = ySign == 0 ? Double.MAX_VALUE : ySign / yDiff;
|
||||
double posZInverse = zSign == 0 ? Double.MAX_VALUE : zSign / zDiff;
|
||||
|
||||
double d12 = posXInverse * (xSign > 0 ? 1.0D - GrimMath.frac(startX) : GrimMath.frac(startX));
|
||||
double d13 = posYInverse * (ySign > 0 ? 1.0D - GrimMath.frac(startY) : GrimMath.frac(startY));
|
||||
double d14 = posZInverse * (zSign > 0 ? 1.0D - GrimMath.frac(startZ) : GrimMath.frac(startZ));
|
||||
|
||||
// Can't figure out what this code does currently
|
||||
while (d12 <= 1.0D || d13 <= 1.0D || d14 <= 1.0D) {
|
||||
if (d12 < d13) {
|
||||
if (d12 < d14) {
|
||||
floorStartX += xSign;
|
||||
d12 += posXInverse;
|
||||
} else {
|
||||
floorStartZ += zSign;
|
||||
d14 += posZInverse;
|
||||
}
|
||||
} else if (d13 < d14) {
|
||||
floorStartY += ySign;
|
||||
d13 += posYInverse;
|
||||
} else {
|
||||
floorStartZ += zSign;
|
||||
d14 += posZInverse;
|
||||
}
|
||||
|
||||
state = player.compensatedWorld.getWrappedBlockStateAt(floorStartX, floorStartY, floorStartZ);
|
||||
apply = predicate.apply(state, new Vector3i(floorStartX, floorStartY, floorStartZ));
|
||||
|
||||
if (apply != null) {
|
||||
return apply;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void placeWaterLavaSnowBucket(GrimPlayer player, ItemStack held, StateType toPlace, InteractionHand hand) {
|
||||
HitData data = getNearestHitResult(player, StateTypes.AIR, false);
|
||||
HitData data = BlockRayTrace.getNearestHitResult(player, StateTypes.AIR, false);
|
||||
if (data != null) {
|
||||
BlockPlace blockPlace = new BlockPlace(player, hand, data.getPosition(), data.getClosestDirection().getFaceValue(), data.getClosestDirection(), held, data);
|
||||
|
||||
@@ -227,8 +148,9 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
|
||||
private static void handleBlockPlaceOrUseItem(PacketWrapper<?> packet, GrimPlayer player) {
|
||||
// Legacy "use item" packet
|
||||
if (packet instanceof WrapperPlayClientPlayerBlockPlacement place &&
|
||||
if (packet instanceof WrapperPlayClientPlayerBlockPlacement &&
|
||||
PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_9)) {
|
||||
WrapperPlayClientPlayerBlockPlacement place = (WrapperPlayClientPlayerBlockPlacement) packet;
|
||||
|
||||
if (player.gamemode == GameMode.SPECTATOR || player.gamemode == GameMode.ADVENTURE) return;
|
||||
|
||||
@@ -243,7 +165,9 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
}
|
||||
}
|
||||
|
||||
if (packet instanceof WrapperPlayClientUseItem place) {
|
||||
if (packet instanceof WrapperPlayClientUseItem) {
|
||||
WrapperPlayClientUseItem place = (WrapperPlayClientUseItem) packet;
|
||||
|
||||
if (player.gamemode == GameMode.SPECTATOR || player.gamemode == GameMode.ADVENTURE) return;
|
||||
|
||||
ItemStack placedWith = player.getInventory().getHeldItem();
|
||||
@@ -255,7 +179,9 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
}
|
||||
|
||||
// Check for interactable first (door, etc)
|
||||
if (packet instanceof WrapperPlayClientPlayerBlockPlacement place) {
|
||||
if (packet instanceof WrapperPlayClientPlayerBlockPlacement) {
|
||||
WrapperPlayClientPlayerBlockPlacement place = (WrapperPlayClientPlayerBlockPlacement) packet;
|
||||
|
||||
ItemStack placedWith = player.getInventory().getHeldItem();
|
||||
ItemStack offhand = player.getInventory().getOffHand();
|
||||
|
||||
@@ -264,7 +190,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
// The offhand is unable to interact with blocks like this... try to stop some desync points before they happen
|
||||
if ((!player.isSneaking || onlyAir) && place.getHand() == InteractionHand.MAIN_HAND) {
|
||||
Vector3i blockPosition = place.getBlockPosition();
|
||||
BlockPlace blockPlace = new BlockPlace(player, place.getHand(), blockPosition, place.getFaceId(), place.getFace(), placedWith, getNearestHitResult(player, null, true));
|
||||
BlockPlace blockPlace = new BlockPlace(player, place.getHand(), blockPosition, place.getFaceId(), place.getFace(), placedWith, BlockRayTrace.getNearestHitResult(player, null, true));
|
||||
|
||||
// Right-clicking a trapdoor/door/etc.
|
||||
StateType placedAgainst = blockPlace.getPlacedAgainstMaterial();
|
||||
@@ -291,7 +217,8 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
}
|
||||
}
|
||||
|
||||
if (packet instanceof WrapperPlayClientPlayerBlockPlacement place) {
|
||||
if (packet instanceof WrapperPlayClientPlayerBlockPlacement) {
|
||||
WrapperPlayClientPlayerBlockPlacement place = (WrapperPlayClientPlayerBlockPlacement) packet;
|
||||
Vector3i blockPosition = place.getBlockPosition();
|
||||
BlockFace face = place.getFace();
|
||||
|
||||
@@ -303,7 +230,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
placedWith = player.getInventory().getOffHand();
|
||||
}
|
||||
|
||||
BlockPlace blockPlace = new BlockPlace(player, place.getHand(), blockPosition, place.getFaceId(), face, placedWith, getNearestHitResult(player, null, true));
|
||||
BlockPlace blockPlace = new BlockPlace(player, place.getHand(), blockPosition, place.getFaceId(), face, placedWith, BlockRayTrace.getNearestHitResult(player, null, true));
|
||||
// At this point, it is too late to cancel, so we can only flag, and cancel subsequent block places more aggressively
|
||||
if (!player.compensatedEntities.getSelf().inVehicle()) {
|
||||
player.checkManager.onPostFlyingBlockPlace(blockPlace);
|
||||
@@ -460,6 +387,8 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
WrappedBlockState block = player.compensatedWorld.getWrappedBlockStateAt(digPosition);
|
||||
final StateType type = block.getType();
|
||||
|
||||
player.checkManager.getPacketCheck(InventoryB.class).handle(event, dig);
|
||||
|
||||
player.checkManager.getPacketCheck(BadPacketsX.class).handle(event, dig, type);
|
||||
player.checkManager.getPacketCheck(BadPacketsZ.class).handle(event, dig);
|
||||
|
||||
@@ -510,7 +439,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
player.placeUseItemPackets.add(new BlockPlaceSnapshot(packet, player.isSneaking));
|
||||
} else {
|
||||
// Anti-air place
|
||||
BlockPlace blockPlace = new BlockPlace(player, packet.getHand(), packet.getBlockPosition(), packet.getFaceId(), packet.getFace(), placedWith, getNearestHitResult(player, null, true));
|
||||
BlockPlace blockPlace = new BlockPlace(player, packet.getHand(), packet.getBlockPosition(), packet.getFaceId(), packet.getFace(), placedWith, BlockRayTrace.getNearestHitResult(player, null, true));
|
||||
blockPlace.setCursor(packet.getCursorPosition());
|
||||
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_11) && player.getClientVersion().isOlderThan(ClientVersion.V_1_11)) {
|
||||
@@ -584,7 +513,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
}
|
||||
|
||||
private static void placeBucket(GrimPlayer player, InteractionHand hand) {
|
||||
HitData data = getNearestHitResult(player, null, true);
|
||||
HitData data = BlockRayTrace.getNearestHitResult(player, null, true);
|
||||
|
||||
if (data != null) {
|
||||
BlockPlace blockPlace = new BlockPlace(player, hand, data.getPosition(), data.getClosestDirection().getFaceValue(), data.getClosestDirection(), ItemStack.EMPTY, data);
|
||||
@@ -750,7 +679,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
}
|
||||
|
||||
private static void placeLilypad(GrimPlayer player, InteractionHand hand) {
|
||||
HitData data = getNearestHitResult(player, null, true);
|
||||
HitData data = BlockRayTrace.getNearestHitResult(player, null, true);
|
||||
|
||||
if (data != null) {
|
||||
// A lilypad cannot replace a fluid
|
||||
@@ -780,56 +709,6 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
}
|
||||
}
|
||||
|
||||
private static HitData getNearestHitResult(GrimPlayer player, StateType heldItem, boolean sourcesHaveHitbox) {
|
||||
Vector3d startingPos = new Vector3d(player.x, player.y + player.getEyeHeight(), player.z);
|
||||
Vector startingVec = new Vector(startingPos.getX(), startingPos.getY(), startingPos.getZ());
|
||||
Ray trace = new Ray(player, startingPos.getX(), startingPos.getY(), startingPos.getZ(), player.xRot, player.yRot);
|
||||
final double distance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_BLOCK_INTERACTION_RANGE);
|
||||
Vector endVec = trace.getPointAtDistance(distance);
|
||||
Vector3d endPos = new Vector3d(endVec.getX(), endVec.getY(), endVec.getZ());
|
||||
|
||||
return traverseBlocks(player, startingPos, endPos, (block, vector3i) -> {
|
||||
CollisionBox data = HitboxData.getBlockHitbox(player, heldItem, player.getClientVersion(), block, vector3i.getX(), vector3i.getY(), vector3i.getZ());
|
||||
List<SimpleCollisionBox> boxes = new ArrayList<>();
|
||||
data.downCast(boxes);
|
||||
|
||||
double bestHitResult = Double.MAX_VALUE;
|
||||
Vector bestHitLoc = null;
|
||||
BlockFace bestFace = null;
|
||||
|
||||
for (SimpleCollisionBox box : boxes) {
|
||||
Pair<Vector, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
|
||||
if (intercept.first() == null) continue; // No intercept
|
||||
|
||||
Vector hitLoc = intercept.first();
|
||||
|
||||
if (hitLoc.distanceSquared(startingVec) < bestHitResult) {
|
||||
bestHitResult = hitLoc.distanceSquared(startingVec);
|
||||
bestHitLoc = hitLoc;
|
||||
bestFace = intercept.second();
|
||||
}
|
||||
}
|
||||
if (bestHitLoc != null) {
|
||||
return new HitData(vector3i, bestHitLoc, bestFace, block);
|
||||
}
|
||||
|
||||
if (sourcesHaveHitbox &&
|
||||
(player.compensatedWorld.isWaterSourceBlock(vector3i.getX(), vector3i.getY(), vector3i.getZ())
|
||||
|| player.compensatedWorld.getLavaFluidLevelAt(vector3i.getX(), vector3i.getY(), vector3i.getZ()) == (8 / 9f))) {
|
||||
double waterHeight = player.compensatedWorld.getFluidLevelAt(vector3i.getX(), vector3i.getY(), vector3i.getZ());
|
||||
SimpleCollisionBox box = new SimpleCollisionBox(vector3i.getX(), vector3i.getY(), vector3i.getZ(), vector3i.getX() + 1, vector3i.getY() + waterHeight, vector3i.getZ() + 1);
|
||||
|
||||
Pair<Vector, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
|
||||
|
||||
if (intercept.first() != null) {
|
||||
return new HitData(vector3i, intercept.first(), intercept.second(), block);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSend(PacketSendEvent event) {
|
||||
if (event.getConnectionState() != ConnectionState.PLAY) return;
|
||||
|
||||
@@ -227,8 +227,9 @@ public class PacketEntityReplication extends Check implements PacketCheck {
|
||||
|
||||
if (status.getStatus() == 31) {
|
||||
PacketEntity hook = player.compensatedEntities.getEntity(status.getEntityId());
|
||||
if (!(hook instanceof PacketEntityHook hookEntity)) return;
|
||||
if (!(hook instanceof PacketEntityHook)) return;
|
||||
|
||||
PacketEntityHook hookEntity = (PacketEntityHook) hook;
|
||||
if (hookEntity.attached == player.entityID) {
|
||||
player.sendTransaction();
|
||||
// We don't transaction sandwich this, it's too rare to be a real problem.
|
||||
@@ -453,7 +454,8 @@ public class PacketEntityReplication extends Check implements PacketCheck {
|
||||
player.latencyUtils.addRealTimeTask(lastTrans, () -> {
|
||||
PacketEntity entity = player.compensatedEntities.getEntity(entityId);
|
||||
if (entity == null) return;
|
||||
if (entity instanceof PacketEntityTrackXRot xRotEntity && yaw != null) {
|
||||
if (entity instanceof PacketEntityTrackXRot && yaw != null) {
|
||||
PacketEntityTrackXRot xRotEntity = (PacketEntityTrackXRot) entity;
|
||||
xRotEntity.packetYaw = yaw;
|
||||
xRotEntity.steps = entity.isBoat() ? 10 : 3;
|
||||
}
|
||||
|
||||
@@ -19,11 +19,13 @@ import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerJoinGame;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerRespawn;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateHealth;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
|
||||
/**
|
||||
* PlayerRespawnS2CPacket info (1.20.2+):
|
||||
* If the world is different (check via registry key), world is recreated (all entities etc destroyed).
|
||||
@@ -185,7 +187,7 @@ public class PacketPlayerRespawn extends PacketListenerAbstract {
|
||||
player.compensatedEntities.hasSprintingAttributeEnabled = false;
|
||||
}
|
||||
player.pose = Pose.STANDING;
|
||||
player.clientVelocity = new Vector();
|
||||
player.clientVelocity = newVector3D();
|
||||
player.gamemode = respawn.getGameMode();
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_17)) {
|
||||
player.compensatedWorld.setDimension(respawn.getDimensionType(), event.getUser());
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
package ac.grim.grimac.events.packets;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntitySelf;
|
||||
import ac.grim.grimac.utils.inventory.InventoryDesyncStatus;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
|
||||
import com.github.retrooper.packetevents.event.PacketListenerPriority;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.event.PacketSendEvent;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus.Action;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerOpenWindow;
|
||||
|
||||
public class PacketPlayerWindow extends PacketListenerAbstract {
|
||||
|
||||
public PacketPlayerWindow() {
|
||||
super(PacketListenerPriority.LOWEST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && !event.isCancelled()) {
|
||||
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
|
||||
if (player == null) return;
|
||||
|
||||
if (player.hasInventoryOpen && isNearNetherPortal(player)) {
|
||||
handleInventoryClose(player, InventoryDesyncStatus.NETHER_PORTAL);
|
||||
}
|
||||
}
|
||||
|
||||
// Client Status is sent in 1.7-1.8
|
||||
if (event.getPacketType() == PacketType.Play.Client.CLIENT_STATUS) {
|
||||
WrapperPlayClientClientStatus wrapper = new WrapperPlayClientClientStatus(event);
|
||||
|
||||
if (wrapper.getAction() == Action.OPEN_INVENTORY_ACHIEVEMENT) {
|
||||
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
|
||||
if (player == null) return;
|
||||
|
||||
handleInventoryOpen(player);
|
||||
}
|
||||
}
|
||||
|
||||
// We need to do this due to 1.9 not sending anymore the inventory action in the Client Status
|
||||
if (event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW) {
|
||||
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
|
||||
if (player == null) return;
|
||||
|
||||
// TODO: Remove this check after we finish the before ViaVersion injection
|
||||
// Explanation: On 1.7 and 1.8 we have OPEN_INVENTORY_ACHIEVEMENT on CLIENT_STATUS packet
|
||||
// but after Via translation this information gets lost
|
||||
// This is a workaround to atleast make our inventory checks work "decently" in 1.8 clients for 1.9+ servers
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9)
|
||||
&& player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
|
||||
handleInventoryOpen(player);
|
||||
}
|
||||
|
||||
if (player.getClientVersion().isNewerThan(ClientVersion.V_1_8)) {
|
||||
handleInventoryOpen(player);
|
||||
}
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.CLOSE_WINDOW) {
|
||||
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
|
||||
if (player == null) return;
|
||||
|
||||
handleInventoryClose(player, InventoryDesyncStatus.NOT_DESYNCED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSend(PacketSendEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Server.RESPAWN) {
|
||||
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
|
||||
if (player == null) return;
|
||||
|
||||
player.sendTransaction();
|
||||
|
||||
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(),
|
||||
() -> handleInventoryClose(player, InventoryDesyncStatus.NOT_DESYNCED));
|
||||
} else if (event.getPacketType() == PacketType.Play.Server.OPEN_WINDOW) {
|
||||
WrapperPlayServerOpenWindow wrapper = new WrapperPlayServerOpenWindow(event);
|
||||
|
||||
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
|
||||
if (player == null) return;
|
||||
|
||||
player.sendTransaction();
|
||||
|
||||
String legacyType = wrapper.getLegacyType();
|
||||
int modernType = wrapper.getType();
|
||||
InventoryDesyncStatus inventoryDesyncStatus = getContainerDesyncStatus(player, legacyType, modernType);
|
||||
|
||||
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(),
|
||||
() -> {
|
||||
if (inventoryDesyncStatus == InventoryDesyncStatus.NOT_DESYNCED) {
|
||||
handleInventoryOpen(player);
|
||||
} else {
|
||||
handleInventoryClose(player, inventoryDesyncStatus);
|
||||
}
|
||||
});
|
||||
} else if (event.getPacketType() == PacketType.Play.Server.OPEN_HORSE_WINDOW) {
|
||||
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
|
||||
if (player == null) return;
|
||||
|
||||
player.sendTransaction();
|
||||
|
||||
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(),
|
||||
() -> handleInventoryOpen(player));
|
||||
} else if (event.getPacketType() == PacketType.Play.Server.CLOSE_WINDOW) {
|
||||
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
|
||||
if (player == null) return;
|
||||
|
||||
player.sendTransaction();
|
||||
|
||||
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(),
|
||||
() -> handleInventoryClose(player, InventoryDesyncStatus.NOT_DESYNCED));
|
||||
}
|
||||
}
|
||||
|
||||
private void handleInventoryOpen(GrimPlayer player) {
|
||||
if (!player.hasInventoryOpen) {
|
||||
player.lastInventoryOpen = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
player.hasInventoryOpen = true;
|
||||
}
|
||||
|
||||
private void handleInventoryClose(GrimPlayer player, InventoryDesyncStatus desyncStatus) {
|
||||
player.hasInventoryOpen = false;
|
||||
player.inventoryDesyncStatus = desyncStatus;
|
||||
}
|
||||
|
||||
public InventoryDesyncStatus getContainerDesyncStatus(GrimPlayer player, String legacyType, int modernType) {
|
||||
// Closing beacon with the cross button cause desync in 1.7-1.8
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8) &&
|
||||
("minecraft:beacon".equals(legacyType) || modernType == 8)) {
|
||||
return player.inventoryDesyncStatus = InventoryDesyncStatus.BEACON;
|
||||
}
|
||||
|
||||
if (isNearNetherPortal(player)) {
|
||||
return player.inventoryDesyncStatus = InventoryDesyncStatus.NETHER_PORTAL;
|
||||
}
|
||||
|
||||
return player.inventoryDesyncStatus = InventoryDesyncStatus.NOT_DESYNCED;
|
||||
}
|
||||
|
||||
public boolean isNearNetherPortal(GrimPlayer player) {
|
||||
// Going inside nether portal with opened inventory cause desync, fixed in 1.12.2
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_12_1) &&
|
||||
player.pointThreeEstimator.isNearNetherPortal) {
|
||||
PacketEntitySelf playerEntity = player.compensatedEntities.getSelf();
|
||||
// Client ignore nether portal if player has passengers or riding an entity
|
||||
if (!playerEntity.inVehicle() && playerEntity.passengers.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ import java.util.BitSet;
|
||||
public class PacketWorldReaderEight extends BasePacketWorldReader {
|
||||
@Override
|
||||
public void handleMapChunkBulk(final GrimPlayer player, final PacketSendEvent event) {
|
||||
PacketWrapper<?> wrapper = new PacketWrapper<>(event);
|
||||
PacketWrapper wrapper = new PacketWrapper(event);
|
||||
ByteBuf buffer = (ByteBuf) wrapper.getBuffer();
|
||||
|
||||
boolean skylight = wrapper.readBoolean();
|
||||
@@ -47,7 +47,7 @@ public class PacketWorldReaderEight extends BasePacketWorldReader {
|
||||
|
||||
@Override
|
||||
public void handleMapChunk(final GrimPlayer player, final PacketSendEvent event) {
|
||||
PacketWrapper<?> wrapper = new PacketWrapper<>(event);
|
||||
PacketWrapper wrapper = new PacketWrapper(event);
|
||||
|
||||
final int chunkX = wrapper.readInt();
|
||||
final int chunkZ = wrapper.readInt();
|
||||
|
||||
@@ -12,12 +12,13 @@ import ac.grim.grimac.checks.impl.crash.*;
|
||||
import ac.grim.grimac.checks.impl.exploit.ExploitA;
|
||||
import ac.grim.grimac.checks.impl.exploit.ExploitB;
|
||||
import ac.grim.grimac.checks.impl.groundspoof.NoFallA;
|
||||
import ac.grim.grimac.checks.impl.inventory.*;
|
||||
import ac.grim.grimac.checks.impl.misc.ClientBrand;
|
||||
import ac.grim.grimac.checks.impl.misc.FastBreak;
|
||||
import ac.grim.grimac.checks.impl.misc.GhostBlockMitigation;
|
||||
import ac.grim.grimac.checks.impl.misc.TransactionOrder;
|
||||
import ac.grim.grimac.checks.impl.movement.*;
|
||||
import ac.grim.grimac.checks.impl.post.PostCheck;
|
||||
import ac.grim.grimac.checks.impl.packetorder.*;
|
||||
import ac.grim.grimac.checks.impl.post.Post;
|
||||
import ac.grim.grimac.checks.impl.prediction.DebugHandler;
|
||||
import ac.grim.grimac.checks.impl.prediction.NoFallB;
|
||||
import ac.grim.grimac.checks.impl.prediction.OffsetHandler;
|
||||
@@ -85,12 +86,10 @@ public class CheckManager {
|
||||
.put(BadPacketsE.class, new BadPacketsE(player))
|
||||
.put(BadPacketsF.class, new BadPacketsF(player))
|
||||
.put(BadPacketsG.class, new BadPacketsG(player))
|
||||
.put(BadPacketsH.class, new BadPacketsH(player))
|
||||
.put(BadPacketsI.class, new BadPacketsI(player))
|
||||
.put(BadPacketsJ.class, new BadPacketsJ(player))
|
||||
.put(BadPacketsK.class, new BadPacketsK(player))
|
||||
.put(BadPacketsL.class, new BadPacketsL(player))
|
||||
.put(BadPacketsM.class, new BadPacketsM(player))
|
||||
.put(BadPacketsN.class, new BadPacketsN(player))
|
||||
.put(BadPacketsP.class, new BadPacketsP(player))
|
||||
.put(BadPacketsQ.class, new BadPacketsQ(player))
|
||||
@@ -103,8 +102,16 @@ public class CheckManager {
|
||||
.put(BadPacketsX.class, new BadPacketsX(player))
|
||||
.put(BadPacketsY.class, new BadPacketsY(player))
|
||||
.put(BadPacketsZ.class, new BadPacketsZ(player))
|
||||
.put(InventoryA.class, new InventoryA(player))
|
||||
.put(InventoryB.class, new InventoryB(player))
|
||||
.put(InventoryE.class, new InventoryE(player))
|
||||
.put(InventoryF.class, new InventoryF(player))
|
||||
.put(InventoryG.class, new InventoryG(player))
|
||||
.put(FastBreak.class, new FastBreak(player))
|
||||
.put(TransactionOrder.class, new TransactionOrder(player))
|
||||
.put(PacketOrderB.class, new PacketOrderB(player))
|
||||
.put(PacketOrderC.class, new PacketOrderC(player))
|
||||
.put(PacketOrderD.class, new PacketOrderD(player))
|
||||
.put(PacketOrderP.class, new PacketOrderP(player))
|
||||
.put(NoSlowB.class, new NoSlowB(player))
|
||||
.put(SetbackBlocker.class, new SetbackBlocker(player)) // Must be last class otherwise we can't check while blocking packets
|
||||
.build();
|
||||
@@ -127,8 +134,20 @@ public class CheckManager {
|
||||
.put(ExplosionHandler.class, new ExplosionHandler(player))
|
||||
.put(KnockbackHandler.class, new KnockbackHandler(player))
|
||||
.put(GhostBlockDetector.class, new GhostBlockDetector(player))
|
||||
.put(InventoryD.class, new InventoryD(player))
|
||||
.put(Phase.class, new Phase(player))
|
||||
.put(PostCheck.class, new PostCheck(player))
|
||||
.put(PacketOrderProcessor.class, player.packetOrderProcessor)
|
||||
.put(Post.class, new Post(player))
|
||||
.put(PacketOrderA.class, new PacketOrderA(player))
|
||||
.put(PacketOrderE.class, new PacketOrderE(player))
|
||||
.put(PacketOrderF.class, new PacketOrderF(player))
|
||||
.put(PacketOrderG.class, new PacketOrderG(player))
|
||||
.put(PacketOrderH.class, new PacketOrderH(player))
|
||||
.put(PacketOrderI.class, new PacketOrderI(player))
|
||||
.put(PacketOrderJ.class, new PacketOrderJ(player))
|
||||
.put(PacketOrderK.class, new PacketOrderK(player))
|
||||
.put(PacketOrderL.class, new PacketOrderL(player))
|
||||
.put(PacketOrderM.class, new PacketOrderM(player))
|
||||
.put(NoFallB.class, new NoFallB(player))
|
||||
.put(OffsetHandler.class, new OffsetHandler(player))
|
||||
.put(SuperDebug.class, new SuperDebug(player))
|
||||
@@ -147,6 +166,7 @@ public class CheckManager {
|
||||
.build();
|
||||
|
||||
blockPlaceCheck = new ImmutableClassToInstanceMap.Builder<BlockPlaceCheck>()
|
||||
.put(InventoryC.class, new InventoryC(player))
|
||||
.put(InvalidPlaceA.class, new InvalidPlaceA(player))
|
||||
.put(InvalidPlaceB.class, new InvalidPlaceB(player))
|
||||
.put(AirLiquidPlace.class, new AirLiquidPlace(player))
|
||||
@@ -154,8 +174,10 @@ public class CheckManager {
|
||||
.put(FarPlace.class, new FarPlace(player))
|
||||
.put(FabricatedPlace.class, new FabricatedPlace(player))
|
||||
.put(PositionPlace.class, new PositionPlace(player))
|
||||
.put(RotationPlace.class, new RotationPlace(player))
|
||||
.put(PacketOrderN.class, new PacketOrderN(player))
|
||||
.put(PacketOrderO.class, new PacketOrderO(player))
|
||||
.put(DuplicateRotPlace.class, new DuplicateRotPlace(player))
|
||||
.put(LineOfSightPlace.class, new LineOfSightPlace(player))
|
||||
.put(GhostBlockMitigation.class, new GhostBlockMitigation(player))
|
||||
.build();
|
||||
|
||||
@@ -286,11 +308,17 @@ public class CheckManager {
|
||||
return (T) packetChecks.get(check);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends BlockPlaceCheck> T getBlockPlaceCheck(Class<T> check) {
|
||||
return (T) blockPlaceCheck.get(check);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends PacketCheck> T getPrePredictionCheck(Class<T> check) {
|
||||
return (T) prePredictionChecks.get(check);
|
||||
}
|
||||
|
||||
|
||||
private PacketEntityReplication packetEntityReplication = null;
|
||||
|
||||
public PacketEntityReplication getEntityReplication() {
|
||||
|
||||
@@ -19,6 +19,7 @@ import ac.grim.grimac.utils.math.VectorUtils;
|
||||
import ac.grim.grimac.utils.nmsutil.Collisions;
|
||||
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
|
||||
import ac.grim.grimac.utils.nmsutil.ReachUtils;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
@@ -31,13 +32,15 @@ import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
|
||||
public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
// Sync to netty
|
||||
public final ConcurrentLinkedQueue<TeleportData> pendingTeleports = new ConcurrentLinkedQueue<>();
|
||||
@@ -65,7 +68,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
@Override
|
||||
public void onPredictionComplete(final PredictionComplete predictionComplete) {
|
||||
// Grab friction now when we know player on ground and other variables
|
||||
Vector afterTickFriction = player.clientVelocity.clone();
|
||||
Vector3D afterTickFriction = player.clientVelocity.clone();
|
||||
|
||||
// We must first check if the player has accepted their setback
|
||||
// If the setback isn't complete, then this position is illegitimate
|
||||
@@ -115,7 +118,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void simulateFriction(Vector vector) {
|
||||
private void simulateFriction(Vector3D vector) {
|
||||
// We must always do this before simulating positions, as this is the last actual (safe) movement
|
||||
// We must not do this for knockback or explosions, as they are at the start of the tick
|
||||
if (player.wasTouchingWater) {
|
||||
@@ -123,9 +126,9 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
} else if (player.wasTouchingLava) {
|
||||
vector.multiply(0.5D);
|
||||
if (player.hasGravity)
|
||||
vector.add(new Vector(0.0D, -player.gravity / 4.0D, 0.0D));
|
||||
vector.add(newVector3D(0.0D, -player.gravity / 4.0D, 0.0D));
|
||||
} else if (player.isGliding) {
|
||||
PredictionEngineElytra.getElytraMovement(player, vector, ReachUtils.getLook(player, player.xRot, player.yRot)).multiply(player.stuckSpeedMultiplier).multiply(new Vector(0.99F, 0.98F, 0.99F));
|
||||
PredictionEngineElytra.getElytraMovement(player, vector, ReachUtils.getLook(player, player.xRot, player.yRot)).multiply(player.stuckSpeedMultiplier).multiply(newVector3D(0.99F, 0.98F, 0.99F));
|
||||
vector.setY(vector.getY() - 0.05); // Make the player fall a bit
|
||||
} else { // Gliding doesn't have friction, we handle it differently
|
||||
PredictionEngineNormal.staticVectorEndOfTick(player, vector); // Lava and normal movement
|
||||
@@ -147,18 +150,18 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
lastWorldResync = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
Vector clientVel = lastKnownGoodPosition.vector.clone();
|
||||
Vector3D clientVel = lastKnownGoodPosition.vector.clone();
|
||||
|
||||
Pair<VelocityData, Vector> futureKb = player.checkManager.getKnockbackHandler().getFutureKnockback();
|
||||
Pair<VelocityData, Vector3D> futureKb = player.checkManager.getKnockbackHandler().getFutureKnockback();
|
||||
VelocityData futureExplosion = player.checkManager.getExplosionHandler().getFutureExplosion();
|
||||
|
||||
// Velocity sets
|
||||
if (futureKb.first() != null) {
|
||||
clientVel = futureKb.second();
|
||||
if (futureKb.getFirst() != null) {
|
||||
clientVel = futureKb.getSecond();
|
||||
}
|
||||
|
||||
// Explosion adds
|
||||
if (futureExplosion != null && (futureKb.first() == null || futureKb.first().transaction < futureExplosion.transaction)) {
|
||||
if (futureExplosion != null && (futureKb.getFirst() == null || futureKb.getFirst().transaction < futureExplosion.transaction)) {
|
||||
clientVel.add(futureExplosion.vector);
|
||||
}
|
||||
|
||||
@@ -169,7 +172,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
|
||||
// Mini prediction engine - simulate collisions
|
||||
if (simulateNextTickPosition) {
|
||||
Vector collide = Collisions.collide(player, clientVel.getX(), clientVel.getY(), clientVel.getZ());
|
||||
Vector3D collide = Collisions.collide(player, clientVel.getX(), clientVel.getY(), clientVel.getZ());
|
||||
|
||||
position = position.withX(position.getX() + collide.getX());
|
||||
// 1.8 players need the collision epsilon to not phase into blocks when being setback
|
||||
@@ -328,16 +331,16 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
while (true) {
|
||||
Pair<Integer, Vector3d> teleportPos = player.vehicleData.vehicleTeleports.peek();
|
||||
if (teleportPos == null) break;
|
||||
if (lastTransaction < teleportPos.first()) {
|
||||
if (lastTransaction < teleportPos.getFirst()) {
|
||||
break;
|
||||
}
|
||||
|
||||
Vector3d position = teleportPos.second();
|
||||
Vector3d position = teleportPos.getSecond();
|
||||
if (position.getX() == x && position.getY() == y && position.getZ() == z) {
|
||||
player.vehicleData.vehicleTeleports.poll();
|
||||
|
||||
return true;
|
||||
} else if (lastTransaction > teleportPos.first() + 1) {
|
||||
} else if (lastTransaction > teleportPos.getFirst() + 1) {
|
||||
player.vehicleData.vehicleTeleports.poll();
|
||||
|
||||
// Vehicles have terrible netcode so just ignore it if the teleport wasn't from us setting the player back
|
||||
@@ -412,7 +415,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
data = new TeleportData(safePosition, flags, transaction, teleportId);
|
||||
requiredSetBack = new SetBackData(data, player.xRot, player.yRot, null, false, plugin);
|
||||
|
||||
this.lastKnownGoodPosition = new SetbackPosWithVector(safePosition, new Vector());
|
||||
this.lastKnownGoodPosition = new SetbackPosWithVector(safePosition, newVector3D());
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
@@ -420,6 +423,6 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
@Setter
|
||||
public static class SetbackPosWithVector {
|
||||
private Vector3d pos;
|
||||
private Vector vector;
|
||||
private Vector3D vector;
|
||||
}
|
||||
}
|
||||
@@ -71,8 +71,14 @@ public class SpectateManager implements Initable {
|
||||
spectatingPlayers.remove(uuid);
|
||||
}
|
||||
|
||||
private record PreviousState(org.bukkit.GameMode gameMode, Location location) {
|
||||
private static class PreviousState {
|
||||
public PreviousState(org.bukkit.GameMode gameMode, Location location) {
|
||||
this.gameMode = gameMode;
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
private final org.bukkit.GameMode gameMode;
|
||||
private final Location location;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import ac.grim.grimac.manager.tick.Tickable;
|
||||
import ac.grim.grimac.manager.tick.impl.ClientVersionSetter;
|
||||
import ac.grim.grimac.manager.tick.impl.ResetTick;
|
||||
import ac.grim.grimac.manager.tick.impl.TickInventory;
|
||||
import ac.grim.grimac.manager.tick.impl.UpdateChangedBlocksList;
|
||||
import com.google.common.collect.ClassToInstanceMap;
|
||||
import com.google.common.collect.ImmutableClassToInstanceMap;
|
||||
|
||||
@@ -22,6 +23,7 @@ public class TickManager {
|
||||
asyncTick = new ImmutableClassToInstanceMap.Builder<Tickable>()
|
||||
.put(ClientVersionSetter.class, new ClientVersionSetter()) // Async because permission lookups might take a while, depending on the plugin
|
||||
.put(TickInventory.class, new TickInventory()) // Async because I've never gotten an exception from this. It's probably safe.
|
||||
.put(UpdateChangedBlocksList.class, new UpdateChangedBlocksList())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ public class ConfigManagerFileImpl implements ConfigManager, BasicReloadable {
|
||||
GrimAPI.INSTANCE.getPlugin().getDataFolder().mkdirs();
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
upgrade();
|
||||
config.addSource(GrimAC.class, "config", getConfigFile("config.yml"));
|
||||
config.addSource(GrimAC.class, "messages", getConfigFile("messages.yml"));
|
||||
config.addSource(GrimAC.class, "discord", getConfigFile("discord.yml"));
|
||||
@@ -81,7 +82,7 @@ public class ConfigManagerFileImpl implements ConfigManager, BasicReloadable {
|
||||
|
||||
configVersion = Integer.parseInt(configStringVersion);
|
||||
// TODO: Do we have to hardcode this?
|
||||
configString = configString.replaceAll("config-version: " + configStringVersion, "config-version: 9");
|
||||
configString = configString.replaceAll("config-version: " + configStringVersion, "config-version: 10");
|
||||
Files.write(config.toPath(), configString.getBytes());
|
||||
|
||||
upgradeModernConfig(config, configString, configVersion);
|
||||
@@ -123,6 +124,9 @@ public class ConfigManagerFileImpl implements ConfigManager, BasicReloadable {
|
||||
if (configVersion < 9) {
|
||||
newOffsetHandlingAntiKB(config, configString);
|
||||
}
|
||||
if (configVersion < 10) {
|
||||
addInventoryPunishments();
|
||||
}
|
||||
}
|
||||
|
||||
private void removeLegacyTwoPointOne(File config) throws IOException {
|
||||
@@ -173,6 +177,28 @@ public class ConfigManagerFileImpl implements ConfigManager, BasicReloadable {
|
||||
}
|
||||
}
|
||||
|
||||
private void addInventoryPunishments() {
|
||||
File config = new File(GrimAPI.INSTANCE.getPlugin().getDataFolder(), "punishments.yml");
|
||||
String configString;
|
||||
if (config.exists()) {
|
||||
try {
|
||||
configString = new String(Files.readAllBytes(config.toPath()));
|
||||
|
||||
// If it works, it isn't stupid. Only replace it if it exactly matches the default config.
|
||||
String reachSection = " Reach:";
|
||||
String inventorySection = " Inventory:\n remove-violations-after: 300\n checks:\n - \"Inventory\"\n commands:\n - \"10:10 [alert]\"\n - \"20:20 [webhook]\"\n - \"20:20 [proxy]\"\n";
|
||||
|
||||
int index = configString.indexOf(reachSection);
|
||||
if (index != -1) {
|
||||
configString = configString.substring(0, index) + inventorySection + configString.substring(index);
|
||||
}
|
||||
|
||||
Files.write(config.toPath(), configString.getBytes());
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void fixBadPacketsAndAdjustPingConfig(File config, String configString) {
|
||||
try {
|
||||
configString = configString.replaceAll("max-ping: \\d+", "max-transaction-time: 60");
|
||||
|
||||
@@ -8,7 +8,7 @@ public class BStats implements Initable {
|
||||
public void start() {
|
||||
int pluginId = 12820; // <-- Replace with the id of your plugin!
|
||||
try {
|
||||
new io.github.retrooper.packetevents.bstats.Metrics(GrimAPI.INSTANCE.getPlugin(), pluginId);
|
||||
new io.github.retrooper.packetevents.bstats.bukkit.Metrics(GrimAPI.INSTANCE.getPlugin(), pluginId);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ public class PacketManager implements Initable {
|
||||
PacketEvents.getAPI().getEventManager().registerListener(new PacketConfigurationListener());
|
||||
PacketEvents.getAPI().getEventManager().registerListener(new PacketPlayerJoinQuit());
|
||||
PacketEvents.getAPI().getEventManager().registerListener(new PacketPingListener());
|
||||
PacketEvents.getAPI().getEventManager().registerListener(new PacketPlayerWindow());
|
||||
PacketEvents.getAPI().getEventManager().registerListener(new PacketPlayerDigging());
|
||||
PacketEvents.getAPI().getEventManager().registerListener(new PacketPlayerAttack());
|
||||
PacketEvents.getAPI().getEventManager().registerListener(new PacketEntityAction());
|
||||
|
||||
@@ -10,26 +10,29 @@ import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import ac.grim.grimac.utils.data.VectorData;
|
||||
import ac.grim.grimac.utils.lists.EvictingQueue;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
|
||||
public final class SuperDebug extends Check implements PostPredictionCheck {
|
||||
private static final StringBuilder[] flags = new StringBuilder[256]; // 17 MB of logs in memory
|
||||
|
||||
Object2IntMap<StringBuilder> continuedDebug = new Object2IntOpenHashMap<>();
|
||||
|
||||
List<VectorData> predicted = new EvictingQueue<>(60);
|
||||
List<Vector> actually = new EvictingQueue<>(60);
|
||||
List<Vector3D> actually = new EvictingQueue<>(60);
|
||||
List<Location> locations = new EvictingQueue<>(60);
|
||||
List<Vector> startTickClientVel = new EvictingQueue<>(60);
|
||||
List<Vector> baseTickAddition = new EvictingQueue<>(60);
|
||||
List<Vector> baseTickWater = new EvictingQueue<>(60);
|
||||
List<Vector3D> startTickClientVel = new EvictingQueue<>(60);
|
||||
List<Vector3D> baseTickAddition = new EvictingQueue<>(60);
|
||||
List<Vector3D> baseTickWater = new EvictingQueue<>(60);
|
||||
|
||||
public SuperDebug(GrimPlayer player) {
|
||||
super(player);
|
||||
@@ -80,11 +83,11 @@ public final class SuperDebug extends Check implements PostPredictionCheck {
|
||||
|
||||
for (int i = 0; i < predicted.size(); i++) {
|
||||
VectorData predict = predicted.get(i);
|
||||
Vector actual = actually.get(i);
|
||||
Vector3D actual = actually.get(i);
|
||||
Location loc = locations.get(i);
|
||||
Vector startTickVel = startTickClientVel.get(i);
|
||||
Vector addition = baseTickAddition.get(i);
|
||||
Vector water = baseTickWater.get(i);
|
||||
Vector3D startTickVel = startTickClientVel.get(i);
|
||||
Vector3D addition = baseTickAddition.get(i);
|
||||
Vector3D water = baseTickWater.get(i);
|
||||
appendDebug(sb, predict, actual, loc, startTickVel, addition, water);
|
||||
}
|
||||
|
||||
@@ -189,7 +192,7 @@ public final class SuperDebug extends Check implements PostPredictionCheck {
|
||||
continuedDebug.put(sb, 40);
|
||||
}
|
||||
|
||||
private void appendDebug(StringBuilder sb, VectorData predict, Vector actual, Location location, Vector startTick, Vector addition, Vector water) {
|
||||
private void appendDebug(StringBuilder sb, VectorData predict, Vector3D actual, Location location, Vector3D startTick, Vector3D addition, Vector3D water) {
|
||||
if (predict.isZeroPointZeroThree()) {
|
||||
sb.append("Movement threshold/tick skipping\n");
|
||||
}
|
||||
@@ -225,11 +228,11 @@ public final class SuperDebug extends Check implements PostPredictionCheck {
|
||||
// Apply 0.003/0.005 to make numbers more accurate
|
||||
Set<VectorData> set = new HashSet<>(Collections.singletonList(new VectorData(startTick.clone(), VectorData.VectorType.BestVelPicked)));
|
||||
new PredictionEngine().applyMovementThreshold(player, set);
|
||||
Vector trueStartVel = ((VectorData) set.toArray()[0]).vector;
|
||||
Vector3D trueStartVel = ((VectorData) set.toArray()[0]).vector;
|
||||
|
||||
Vector clientMovement = getPlayerMathMovement(player, actual.clone().subtract(trueStartVel), location.xRot);
|
||||
Vector simulatedMovement = getPlayerMathMovement(player, predict.vector.clone().subtract(trueStartVel), location.xRot);
|
||||
Vector offset = actual.clone().subtract(predict.vector);
|
||||
Vector3D clientMovement = getPlayerMathMovement(player, actual.clone().subtract(trueStartVel), location.xRot);
|
||||
Vector3D simulatedMovement = getPlayerMathMovement(player, predict.vector.clone().subtract(trueStartVel), location.xRot);
|
||||
Vector3D offset = actual.clone().subtract(predict.vector);
|
||||
trueStartVel.add(addition);
|
||||
trueStartVel.add(water);
|
||||
|
||||
@@ -268,14 +271,14 @@ public final class SuperDebug extends Check implements PostPredictionCheck {
|
||||
sb.append("\n\n");
|
||||
}
|
||||
|
||||
private Vector getPlayerMathMovement(GrimPlayer player, Vector wantedMovement, float f2) {
|
||||
private Vector3D getPlayerMathMovement(GrimPlayer player, Vector3D wantedMovement, float f2) {
|
||||
float f3 = player.trigHandler.sin(f2 * 0.017453292f);
|
||||
float f4 = player.trigHandler.cos(f2 * 0.017453292f);
|
||||
|
||||
float bestTheoreticalX = (float) (f3 * wantedMovement.getZ() + f4 * wantedMovement.getX()) / (f3 * f3 + f4 * f4);
|
||||
float bestTheoreticalZ = (float) (-f3 * wantedMovement.getX() + f4 * wantedMovement.getZ()) / (f3 * f3 + f4 * f4);
|
||||
|
||||
return new Vector(bestTheoreticalX, 0, bestTheoreticalZ);
|
||||
return newVector3D(bestTheoreticalX, 0, bestTheoreticalZ);
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
|
||||
@@ -42,7 +42,7 @@ public class TickEndEvent implements Initable {
|
||||
// but whatever. At least plugins can't break it, I guess.
|
||||
//
|
||||
// Pledge injects into another list, so we should be safe injecting into this one
|
||||
List<?> wrapper = Collections.synchronizedList(new HookedListWrapper<>(endOfTickObject) {
|
||||
List<?> wrapper = Collections.synchronizedList(new HookedListWrapper<Object>(endOfTickObject) {
|
||||
@Override
|
||||
public void onIterator() {
|
||||
hasTicked = true;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package ac.grim.grimac.manager.tick.impl;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.manager.tick.Tickable;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
|
||||
|
||||
public class UpdateChangedBlocksList implements Tickable {
|
||||
@Override
|
||||
public void tick() {
|
||||
for (GrimPlayer player : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,8 @@ import ac.grim.grimac.api.config.ConfigManager;
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.impl.aim.processor.AimProcessor;
|
||||
import ac.grim.grimac.checks.impl.misc.ClientBrand;
|
||||
import ac.grim.grimac.checks.impl.misc.TransactionOrder;
|
||||
import ac.grim.grimac.checks.impl.packetorder.PacketOrderD;
|
||||
import ac.grim.grimac.checks.impl.packetorder.PacketOrderProcessor;
|
||||
import ac.grim.grimac.events.packets.CheckManagerListener;
|
||||
import ac.grim.grimac.manager.*;
|
||||
import ac.grim.grimac.predictionengine.MovementCheckRunner;
|
||||
@@ -21,6 +22,7 @@ import ac.grim.grimac.utils.data.packetentity.PacketEntitySelf;
|
||||
import ac.grim.grimac.utils.data.tags.SyncedTags;
|
||||
import ac.grim.grimac.utils.enums.FluidTag;
|
||||
import ac.grim.grimac.utils.enums.Pose;
|
||||
import ac.grim.grimac.utils.inventory.InventoryDesyncStatus;
|
||||
import ac.grim.grimac.utils.latency.*;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.math.TrigHandler;
|
||||
@@ -54,7 +56,7 @@ import net.kyori.adventure.text.TranslatableComponent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
@@ -64,6 +66,10 @@ import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static ac.grim.grimac.utils.data.VectorData.VectorType;
|
||||
import static ac.grim.grimac.utils.data.VectorData.VectorType.Normal;
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
|
||||
// Everything in this class should be sync'd to the anticheat thread.
|
||||
// Put variables sync'd to the netty thread in PacketStateData
|
||||
// Variables that need lag compensation should have their own class
|
||||
@@ -90,8 +96,9 @@ public class GrimPlayer implements GrimUser {
|
||||
public MovementCheckRunner movementCheckRunner;
|
||||
public SyncedTags tagManager;
|
||||
// End manager like classes
|
||||
public Vector clientVelocity = new Vector();
|
||||
public ac.grim.grimac.utils.vector.Vector3D clientVelocity = newVector3D();
|
||||
PacketTracker packetTracker;
|
||||
public final PacketOrderProcessor packetOrderProcessor = new PacketOrderProcessor(this);
|
||||
private long transactionPing = 0;
|
||||
public long lastTransSent = 0;
|
||||
public long lastTransReceived = 0;
|
||||
@@ -103,9 +110,9 @@ public class GrimPlayer implements GrimUser {
|
||||
public boolean hasGravity = true;
|
||||
public final long joinTime = System.currentTimeMillis();
|
||||
public boolean playerEntityHasGravity = true;
|
||||
public VectorData predictedVelocity = new VectorData(new Vector(), VectorData.VectorType.Normal);
|
||||
public Vector actualMovement = new Vector();
|
||||
public Vector stuckSpeedMultiplier = new Vector(1, 1, 1);
|
||||
public VectorData predictedVelocity = new VectorData(newVector3D(), Normal);
|
||||
public Vector3D actualMovement = newVector3D();
|
||||
public Vector3D stuckSpeedMultiplier = newVector3D(1, 1, 1);
|
||||
public UncertaintyHandler uncertaintyHandler;
|
||||
public double gravity;
|
||||
public float friction;
|
||||
@@ -184,9 +191,9 @@ public class GrimPlayer implements GrimUser {
|
||||
public TrigHandler trigHandler;
|
||||
public PacketStateData packetStateData;
|
||||
// Keep track of basetick stuff
|
||||
public Vector baseTickAddition = new Vector();
|
||||
public Vector baseTickWaterPushing = new Vector();
|
||||
public Vector startTickClientVel = new Vector();
|
||||
public Vector3D baseTickAddition = newVector3D();
|
||||
public Vector3D baseTickWaterPushing = newVector3D();
|
||||
public Vector3D startTickClientVel = newVector3D();
|
||||
// For syncing the player's full swing in 1.9+
|
||||
public int movementPackets = 0;
|
||||
public VelocityData firstBreadKB = null;
|
||||
@@ -213,6 +220,9 @@ public class GrimPlayer implements GrimUser {
|
||||
}
|
||||
|
||||
public int totalFlyingPacketsSent;
|
||||
public boolean hasInventoryOpen;
|
||||
public long lastInventoryOpen;
|
||||
public InventoryDesyncStatus inventoryDesyncStatus;
|
||||
public Queue<BlockPlaceSnapshot> placeUseItemPackets = new LinkedBlockingQueue<>();
|
||||
// This variable is for support with test servers that want to be able to disable grim
|
||||
// Grim disabler 2022 still working!
|
||||
@@ -326,7 +336,7 @@ public class GrimPlayer implements GrimUser {
|
||||
boolean hasID = false;
|
||||
int skipped = 0;
|
||||
for (Pair<Short, Long> iterator : transactionsSent) {
|
||||
if (iterator.first() == id) {
|
||||
if (iterator.getFirst() == id) {
|
||||
hasID = true;
|
||||
break;
|
||||
}
|
||||
@@ -338,7 +348,7 @@ public class GrimPlayer implements GrimUser {
|
||||
if (packetTracker != null) packetTracker.setIntervalPackets(packetTracker.getIntervalPackets() - 1);
|
||||
|
||||
if (skipped > 0 && System.currentTimeMillis() - joinTime > 5000)
|
||||
checkManager.getPacketCheck(TransactionOrder.class).flagAndAlert("skipped: " + skipped);
|
||||
checkManager.getPacketCheck(PacketOrderD.class).flagAndAlert("skipped: " + skipped);
|
||||
|
||||
do {
|
||||
data = transactionsSent.poll();
|
||||
@@ -347,9 +357,9 @@ public class GrimPlayer implements GrimUser {
|
||||
|
||||
lastTransactionReceived.incrementAndGet();
|
||||
lastTransReceived = System.currentTimeMillis();
|
||||
transactionPing = (System.nanoTime() - data.second());
|
||||
playerClockAtLeast = data.second();
|
||||
} while (data.first() != id);
|
||||
transactionPing = (System.nanoTime() - data.getSecond());
|
||||
playerClockAtLeast = data.getSecond();
|
||||
} while (data.getFirst() != id);
|
||||
|
||||
// A transaction means a new tick, so apply any block places
|
||||
CheckManagerListener.handleQueuedPlaces(this, false, 0, 0, System.currentTimeMillis());
|
||||
@@ -357,18 +367,18 @@ public class GrimPlayer implements GrimUser {
|
||||
}
|
||||
|
||||
// Were we the ones who sent the packet?
|
||||
return data != null && data.first() == id;
|
||||
return data != null && data.getFirst() == id;
|
||||
}
|
||||
|
||||
public void baseTickAddWaterPushing(Vector vector) {
|
||||
public void baseTickAddWaterPushing(ac.grim.grimac.utils.vector.Vector3D vector) {
|
||||
baseTickWaterPushing.add(vector);
|
||||
}
|
||||
|
||||
public void baseTickAddVector(Vector vector) {
|
||||
public void baseTickAddVector(Vector3D vector) {
|
||||
clientVelocity.add(vector);
|
||||
}
|
||||
|
||||
public void trackBaseTickAddition(Vector vector) {
|
||||
public void trackBaseTickAddition(Vector3D vector) {
|
||||
baseTickAddition.add(vector);
|
||||
}
|
||||
|
||||
@@ -443,7 +453,8 @@ public class GrimPlayer implements GrimUser {
|
||||
|
||||
public void disconnect(Component reason) {
|
||||
String textReason;
|
||||
if (reason instanceof TranslatableComponent translatableComponent) {
|
||||
if (reason instanceof TranslatableComponent) {
|
||||
TranslatableComponent translatableComponent = (TranslatableComponent) reason;
|
||||
textReason = translatableComponent.key();
|
||||
} else {
|
||||
textReason = LegacyComponentSerializer.legacySection().serialize(reason);
|
||||
|
||||
@@ -38,7 +38,11 @@ import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import static ac.grim.grimac.utils.data.VectorData.VectorType;
|
||||
import static ac.grim.grimac.utils.data.VectorData.VectorType.Dead;
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
|
||||
public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
// Averaged over 500 predictions (Defaults set slightly above my 3600x results)
|
||||
@@ -138,10 +142,10 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
double negX = Math.min(-0.05, GrimMath.clamp(player.actualMovement.getX(), -16, 16) - 0.05);
|
||||
double negZ = Math.min(-0.05, GrimMath.clamp(player.actualMovement.getZ(), -16, 16) - 0.05);
|
||||
|
||||
Vector NE = Collisions.maybeBackOffFromEdge(new Vector(posX, 0, negZ), player, true);
|
||||
Vector NW = Collisions.maybeBackOffFromEdge(new Vector(negX, 0, negZ), player, true);
|
||||
Vector SE = Collisions.maybeBackOffFromEdge(new Vector(posX, 0, posZ), player, true);
|
||||
Vector SW = Collisions.maybeBackOffFromEdge(new Vector(negX, 0, posZ), player, true);
|
||||
Vector3D NE = Collisions.maybeBackOffFromEdge(newVector3D(posX, 0, negZ), player, true);
|
||||
Vector3D NW = Collisions.maybeBackOffFromEdge(newVector3D(negX, 0, negZ), player, true);
|
||||
Vector3D SE = Collisions.maybeBackOffFromEdge(newVector3D(posX, 0, posZ), player, true);
|
||||
Vector3D SW = Collisions.maybeBackOffFromEdge(newVector3D(negX, 0, posZ), player, true);
|
||||
|
||||
boolean isEast = NE.getX() != posX || SE.getX() != posX;
|
||||
boolean isWest = NW.getX() != negX || SW.getX() != negX;
|
||||
@@ -184,7 +188,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
player.vehicleData.wasVehicleSwitch = false;
|
||||
|
||||
if (riding != null) {
|
||||
Vector pos = new Vector(player.x, player.y, player.z);
|
||||
Vector3D pos = newVector3D(player.x, player.y, player.z);
|
||||
SimpleCollisionBox interTruePositions = riding.getPossibleCollisionBoxes();
|
||||
|
||||
// We shrink the expanded bounding box to what the packet positions can be, for a smaller box
|
||||
@@ -194,7 +198,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
interTruePositions.expand(-width, 0, -width);
|
||||
interTruePositions.expandMax(0, -height, 0);
|
||||
|
||||
Vector cutTo = VectorUtils.cutBoxToVector(pos, interTruePositions);
|
||||
Vector3D cutTo = VectorUtils.cutBoxToVector(pos, interTruePositions);
|
||||
|
||||
// Now we need to simulate a tick starting at the most optimal position
|
||||
// The start position is never sent, so we assume the most optimal start position
|
||||
@@ -212,14 +216,14 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
} else {
|
||||
// Server always teleports the player when they eject anyways,
|
||||
// so just let the player control where they eject within reason, they get set back anyways
|
||||
if (new Vector(player.lastX, player.lastY, player.lastZ).distance(new Vector(player.x, player.y, player.z)) > 3) {
|
||||
if (newVector3D(player.lastX, player.lastY, player.lastZ).distance(newVector3D(player.x, player.y, player.z)) > 3) {
|
||||
player.getSetbackTeleportUtil().executeForceResync(); // Too far! (I think this value is sane)
|
||||
}
|
||||
|
||||
handleTeleport(update);
|
||||
|
||||
if (player.isClimbing) {
|
||||
Vector ladder = player.clientVelocity.clone().setY(0.2);
|
||||
Vector3D ladder = player.clientVelocity.clone().setY(0.2);
|
||||
PredictionEngineNormal.staticVectorEndOfTick(player, ladder);
|
||||
player.lastWasClimbing = ladder.getY();
|
||||
}
|
||||
@@ -258,7 +262,8 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
// When in control of the entity, the player sets the entity position to their current position
|
||||
riding.setPositionRaw(GetBoundingBox.getPacketEntityBoundingBox(player, player.x, player.y, player.z, riding));
|
||||
|
||||
if (riding instanceof PacketEntityTrackXRot boat) {
|
||||
if (riding instanceof PacketEntityTrackXRot) {
|
||||
PacketEntityTrackXRot boat = (PacketEntityTrackXRot) riding;
|
||||
boat.packetYaw = player.xRot;
|
||||
boat.interpYaw = player.xRot;
|
||||
boat.steps = 0;
|
||||
@@ -299,7 +304,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
player.clientControlledVerticalCollision = Math.abs(player.y % (1 / 64D)) < 0.00001;
|
||||
|
||||
// This isn't the final velocity of the player in the tick, only the one applied to the player
|
||||
player.actualMovement = new Vector(player.x - player.lastX, player.y - player.lastY, player.z - player.lastZ);
|
||||
player.actualMovement = newVector3D(player.x - player.lastX, player.y - player.lastY, player.z - player.lastZ);
|
||||
|
||||
if (player.isSprinting != player.lastSprinting) {
|
||||
player.compensatedEntities.hasSprintingAttributeEnabled = player.isSprinting;
|
||||
@@ -338,7 +343,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
boolean clientClaimsRiptide = player.packetStateData.tryingToRiptide;
|
||||
if (player.packetStateData.tryingToRiptide) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
boolean isInWater = player.compensatedWorld.isRaining || Collisions.hasMaterial(player, player.boundingBox.copy().expand(0.1f), (block) -> Materials.isWater(CompensatedWorld.blockVersion, block.first()));
|
||||
boolean isInWater = player.compensatedWorld.isRaining || Collisions.hasMaterial(player, player.boundingBox.copy().expand(0.1f), (block) -> Materials.isWater(CompensatedWorld.blockVersion, block.getFirst()));
|
||||
|
||||
if (currentTime - player.packetStateData.lastRiptide < 450 || !isInWater) {
|
||||
player.packetStateData.tryingToRiptide = false;
|
||||
@@ -349,7 +354,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
|
||||
SimpleCollisionBox steppingOnBB = GetBoundingBox.getCollisionBoxForPlayer(player, player.x, player.y, player.z).expand(0.03).offset(0, -1, 0);
|
||||
Collisions.hasMaterial(player, steppingOnBB, (pair) -> {
|
||||
WrappedBlockState data = pair.first();
|
||||
WrappedBlockState data = pair.getFirst();
|
||||
if (data.getType() == StateTypes.SLIME_BLOCK && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_8)) {
|
||||
player.uncertaintyHandler.isSteppingOnSlime = true;
|
||||
player.uncertaintyHandler.isSteppingOnBouncyBlock = true;
|
||||
@@ -395,8 +400,8 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
|
||||
player.uncertaintyHandler.isNearGlitchyBlock = player.getClientVersion().isOlderThan(ClientVersion.V_1_9)
|
||||
&& Collisions.hasMaterial(player, expandedBB.copy().expand(0.2),
|
||||
checkData -> BlockTags.ANVIL.contains(checkData.first().getType())
|
||||
|| checkData.first().getType() == StateTypes.CHEST || checkData.first().getType() == StateTypes.TRAPPED_CHEST);
|
||||
checkData -> BlockTags.ANVIL.contains(checkData.getFirst().getType())
|
||||
|| checkData.getFirst().getType() == StateTypes.CHEST || checkData.getFirst().getType() == StateTypes.TRAPPED_CHEST);
|
||||
|
||||
player.uncertaintyHandler.isOrWasNearGlitchyBlock = isGlitchy || player.uncertaintyHandler.isNearGlitchyBlock;
|
||||
player.uncertaintyHandler.checkForHardCollision();
|
||||
@@ -426,8 +431,8 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
// Exempt if the player is dead or is riding a dead entity
|
||||
if (player.compensatedEntities.getSelf().isDead || (riding != null && riding.isDead)) {
|
||||
// Dead players can't cheat, if you find a way how they could, open an issue
|
||||
player.predictedVelocity = new VectorData(new Vector(), VectorData.VectorType.Dead);
|
||||
player.clientVelocity = new Vector();
|
||||
player.predictedVelocity = new VectorData(newVector3D(), Dead);
|
||||
player.clientVelocity = newVector3D();
|
||||
} else if (player.disableGrim || (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_8) && player.gamemode == GameMode.SPECTATOR) || player.isFlying) {
|
||||
// We could technically check spectator but what's the point...
|
||||
// Added complexity to analyze a gamemode used mainly by moderators
|
||||
@@ -450,7 +455,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
// Riptiding while on the ground moves the hitbox upwards before any movement code runs
|
||||
// It's a pain to support and this is my best attempt
|
||||
if (player.lastOnGround && player.packetStateData.tryingToRiptide && !player.compensatedEntities.getSelf().inVehicle()) {
|
||||
Vector pushingMovement = Collisions.collide(player, 0, 1.1999999F, 0);
|
||||
Vector3D pushingMovement = Collisions.collide(player, 0, 1.1999999F, 0);
|
||||
player.verticalCollision = pushingMovement.getY() != 1.1999999F;
|
||||
double currentY = player.clientVelocity.getY();
|
||||
|
||||
@@ -462,7 +467,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
player.lastY += pushingMovement.getY();
|
||||
new PlayerBaseTick(player).updatePlayerPose();
|
||||
player.boundingBox = GetBoundingBox.getPlayerBoundingBox(player, player.lastX, player.lastY, player.lastZ);
|
||||
player.actualMovement = new Vector(player.x - player.lastX, player.y - player.lastY, player.z - player.lastZ);
|
||||
player.actualMovement = newVector3D(player.x - player.lastX, player.y - player.lastY, player.z - player.lastZ);
|
||||
|
||||
player.couldSkipTick = true;
|
||||
|
||||
@@ -514,7 +519,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
//
|
||||
// Checking for oldClientVel being too high fixes BleachHack vertical scaffold
|
||||
if (player.getSetbackTeleportUtil().getRequiredSetBack() != null && player.getSetbackTeleportUtil().getRequiredSetBack().getTicksComplete() == 1) {
|
||||
Vector setbackVel = player.getSetbackTeleportUtil().getRequiredSetBack().getVelocity();
|
||||
Vector3D setbackVel = player.getSetbackTeleportUtil().getRequiredSetBack().getVelocity();
|
||||
// A player must have velocity going INTO the ground to be able to jump
|
||||
// Otherwise they could ignore upwards velocity that isn't useful into more useful upwards velocity (towering)
|
||||
// So if they are supposed to be going upwards, or are supposed to be off the ground, resync
|
||||
@@ -543,7 +548,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
// Patch sprint jumping with elytra exploit
|
||||
if (player.bukkitPlayer != null && player.isGliding && player.predictedVelocity.isJump() && player.isSprinting && !allowSprintJumpingWithElytra) {
|
||||
SetbackTeleportUtil.SetbackPosWithVector lastKnownGoodPosition = player.getSetbackTeleportUtil().lastKnownGoodPosition;
|
||||
lastKnownGoodPosition.setVector(lastKnownGoodPosition.getVector().multiply(new Vector(0.6 * 0.91, 1, 0.6 * 0.91)));
|
||||
lastKnownGoodPosition.setVector(lastKnownGoodPosition.getVector().multiply(newVector3D(0.6 * 0.91, 1, 0.6 * 0.91)));
|
||||
player.getSetbackTeleportUtil().executeNonSimulatingSetback();
|
||||
}
|
||||
|
||||
@@ -620,7 +625,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
* @param pushingMovement The collision result when trying to move the player upwards by 1.2f
|
||||
* @return Whether it is more likely that this player was on the ground the tick they riptided
|
||||
*/
|
||||
private boolean likelyGroundRiptide(Vector pushingMovement) {
|
||||
private boolean likelyGroundRiptide(Vector3D pushingMovement) {
|
||||
// Y velocity gets reset if the player collides vertically
|
||||
double riptideYResult = Riptide.getRiptideVelocity(player).getY();
|
||||
|
||||
|
||||
@@ -9,16 +9,19 @@ import ac.grim.grimac.utils.enums.Pose;
|
||||
import ac.grim.grimac.utils.latency.CompensatedEntities;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.nmsutil.*;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateAttributes;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
|
||||
public class PlayerBaseTick {
|
||||
GrimPlayer player;
|
||||
|
||||
@@ -40,11 +43,11 @@ public class PlayerBaseTick {
|
||||
|
||||
public void doBaseTick() {
|
||||
// Keep track of basetick stuff
|
||||
player.baseTickAddition = new Vector();
|
||||
player.baseTickWaterPushing = new Vector();
|
||||
player.baseTickAddition = newVector3D();
|
||||
player.baseTickWaterPushing = newVector3D();
|
||||
|
||||
if (player.isFlying && player.isSneaking && !player.compensatedEntities.getSelf().inVehicle()) {
|
||||
Vector flyingShift = new Vector(0, player.flySpeed * -3, 0);
|
||||
Vector3D flyingShift = newVector3D(0, player.flySpeed * -3, 0);
|
||||
player.baseTickAddVector(flyingShift);
|
||||
player.trackBaseTickAddition(flyingShift);
|
||||
}
|
||||
@@ -60,7 +63,7 @@ public class PlayerBaseTick {
|
||||
// You cannot crouch while flying, only shift - could be specific to 1.14?
|
||||
// pre-1.13 clients don't have this code
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13) && player.wasTouchingWater && player.isSneaking && !player.isFlying && !player.compensatedEntities.getSelf().inVehicle()) {
|
||||
Vector waterPushVector = new Vector(0, -0.04f, 0);
|
||||
Vector3D waterPushVector = newVector3D(0, -0.04f, 0);
|
||||
player.baseTickAddVector(waterPushVector);
|
||||
player.trackBaseTickAddition(waterPushVector);
|
||||
}
|
||||
@@ -151,7 +154,7 @@ public class PlayerBaseTick {
|
||||
|
||||
// Might be null after respawn?
|
||||
final Optional<WrapperPlayServerUpdateAttributes.Property> property = playerSpeed.property();
|
||||
if (property.isEmpty()) return;
|
||||
if (!property.isPresent()) return;
|
||||
|
||||
// The client first desync's this attribute
|
||||
property.get().getModifiers().removeIf(modifier -> modifier.getUUID().equals(CompensatedEntities.SNOW_MODIFIER_UUID) || modifier.getName().getKey().equals("powder_snow"));
|
||||
@@ -360,12 +363,22 @@ public class PlayerBaseTick {
|
||||
double d7 = direction2 == BlockFace.WEST || direction2 == BlockFace.EAST ? relativeXMovement : relativeZMovement;
|
||||
d6 = direction2 == BlockFace.EAST || direction2 == BlockFace.SOUTH ? 1.0 - d7 : d7;
|
||||
// d7 and d6 flip the movement direction based on desired movement direction
|
||||
boolean doesSuffocate = switch (direction2) {
|
||||
case EAST -> this.suffocatesAt(blockX + 1, blockZ);
|
||||
case WEST -> this.suffocatesAt(blockX - 1, blockZ);
|
||||
case NORTH -> this.suffocatesAt(blockX, blockZ - 1);
|
||||
default -> this.suffocatesAt(blockX, blockZ + 1);
|
||||
};
|
||||
boolean doesSuffocate;
|
||||
switch (direction2) {
|
||||
case EAST:
|
||||
doesSuffocate = this.suffocatesAt(blockX + 1, blockZ);
|
||||
break;
|
||||
case WEST:
|
||||
doesSuffocate = this.suffocatesAt(blockX - 1, blockZ);
|
||||
break;
|
||||
case NORTH:
|
||||
doesSuffocate = this.suffocatesAt(blockX, blockZ - 1);
|
||||
break;
|
||||
default:
|
||||
case SOUTH:
|
||||
doesSuffocate = this.suffocatesAt(blockX, blockZ + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
if (d6 >= lowestValue || doesSuffocate) continue;
|
||||
lowestValue = d6;
|
||||
@@ -413,7 +426,7 @@ public class PlayerBaseTick {
|
||||
}
|
||||
|
||||
boolean hasPushed = false;
|
||||
Vector vec3 = new Vector();
|
||||
Vector3D vec3 = newVector3D();
|
||||
|
||||
for (int x = floorX; x < ceilX; ++x) {
|
||||
for (int y = floorY; y < ceilY; ++y) {
|
||||
@@ -463,7 +476,7 @@ public class PlayerBaseTick {
|
||||
}
|
||||
double d2 = 0.0;
|
||||
boolean hasTouched = false;
|
||||
Vector vec3 = new Vector();
|
||||
Vector3D vec3 = newVector3D();
|
||||
int n7 = 0;
|
||||
|
||||
for (int x = floorX; x < ceilX; ++x) {
|
||||
@@ -488,7 +501,7 @@ public class PlayerBaseTick {
|
||||
d2 = Math.max(fluidHeightToWorld - aABB.minY, d2);
|
||||
|
||||
if (!player.isFlying) {
|
||||
Vector vec32 = FluidTypeFlowing.getFlow(player, x, y, z);
|
||||
Vector3D vec32 = FluidTypeFlowing.getFlow(player, x, y, z);
|
||||
if (d2 < 0.4) {
|
||||
vec32 = vec32.multiply(d2);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package ac.grim.grimac.predictionengine;
|
||||
|
||||
import ac.grim.grimac.checks.impl.combat.Reach;
|
||||
import ac.grim.grimac.checks.impl.scaffolding.LineOfSightPlace;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.predictionengine.predictions.PredictionEngine;
|
||||
import ac.grim.grimac.utils.collisions.CollisionData;
|
||||
@@ -16,13 +18,16 @@ import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState
|
||||
import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import java.util.OptionalInt;
|
||||
import java.util.Set;
|
||||
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
|
||||
/**
|
||||
* A lot of care was put into handling all the stupid stuff occurring between events
|
||||
* <p>
|
||||
@@ -104,6 +109,7 @@ public class PointThreeEstimator {
|
||||
private boolean isNearHorizontalFlowingLiquid = false; // We can't calculate the direction, only a toggle
|
||||
private boolean isNearVerticalFlowingLiquid = false; // We can't calculate exact values, once again a toggle
|
||||
private boolean isNearBubbleColumn = false; // We can't calculate exact values once again
|
||||
public boolean isNearNetherPortal = false; // We can't calculate exact values once again
|
||||
|
||||
private int maxPositiveLevitation = Integer.MIN_VALUE; // Positive potion effects [0, 128]
|
||||
private int minNegativeLevitation = Integer.MAX_VALUE; // Negative potion effects [-127, -1]r
|
||||
@@ -140,7 +146,7 @@ public class PointThreeEstimator {
|
||||
isNearBubbleColumn = true;
|
||||
}
|
||||
|
||||
Vector fluidVector = FluidTypeFlowing.getFlow(player, x, y, z);
|
||||
Vector3D fluidVector = FluidTypeFlowing.getFlow(player, x, y, z);
|
||||
if (fluidVector.getX() != 0 || fluidVector.getZ() != 0) {
|
||||
isNearHorizontalFlowingLiquid = true;
|
||||
}
|
||||
@@ -151,6 +157,10 @@ public class PointThreeEstimator {
|
||||
isNearFluid = true;
|
||||
}
|
||||
|
||||
player.checkManager.getPacketCheck(Reach.class).handleBlockChange(new Vector3i(x, y, z), state);
|
||||
player.checkManager.getBlockPlaceCheck(LineOfSightPlace.class).handleBlockChange(new Vector3i(x, y, z), state);
|
||||
|
||||
|
||||
if (pointThreeBox.isIntersected(new SimpleCollisionBox(x, y, z))) {
|
||||
// https://github.com/MWHunter/Grim/issues/613
|
||||
int controllingEntityId = player.compensatedEntities.getSelf().inVehicle() ? player.getRidingVehicleId() : player.entityID;
|
||||
@@ -245,27 +255,32 @@ public class PointThreeEstimator {
|
||||
isNearClimbable = false;
|
||||
isNearBubbleColumn = false;
|
||||
isNearFluid = false;
|
||||
isNearNetherPortal = false;
|
||||
|
||||
// Check for flowing water
|
||||
Collisions.hasMaterial(player, pointThreeBox, (pair) -> {
|
||||
final WrappedBlockState state = pair.first();
|
||||
final WrappedBlockState state = pair.getFirst();
|
||||
final StateType stateType = state.getType();
|
||||
if (player.tagManager.block(SyncedTags.CLIMBABLE).contains(stateType) || (stateType == StateTypes.POWDER_SNOW && !player.compensatedEntities.getSelf().inVehicle() && player.getInventory().getBoots().getType() == ItemTypes.LEATHER_BOOTS)) {
|
||||
isNearClimbable = true;
|
||||
}
|
||||
|
||||
if (BlockTags.TRAPDOORS.contains(stateType)) {
|
||||
isNearClimbable = isNearClimbable || Collisions.trapdoorUsableAsLadder(player, pair.second().getX(), pair.second().getY(), pair.second().getZ(), state);
|
||||
isNearClimbable = isNearClimbable || Collisions.trapdoorUsableAsLadder(player, pair.getSecond().getX(), pair.getSecond().getY(), pair.getSecond().getZ(), state);
|
||||
}
|
||||
|
||||
if (stateType == StateTypes.BUBBLE_COLUMN) {
|
||||
isNearBubbleColumn = true;
|
||||
}
|
||||
|
||||
if (Materials.isWater(player.getClientVersion(), pair.first()) || pair.first().getType() == StateTypes.LAVA) {
|
||||
if (Materials.isWater(player.getClientVersion(), pair.getFirst()) || pair.getFirst().getType() == StateTypes.LAVA) {
|
||||
isNearFluid = true;
|
||||
}
|
||||
|
||||
if (state.getType() == StateTypes.NETHER_PORTAL) {
|
||||
isNearNetherPortal = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
@@ -303,7 +318,7 @@ public class PointThreeEstimator {
|
||||
player.boundingBox = player.boundingBox.copy().expand(0.03, 0, 0.03).offset(0, 0.03, 0);
|
||||
// 0.16 magic value -> 0.03 plus gravity, plus some additional lenience
|
||||
double searchDistance = -0.2 + Math.min(0, y);
|
||||
Vector collisionResult = Collisions.collide(player, 0, searchDistance, 0);
|
||||
Vector3D collisionResult = Collisions.collide(player, 0, searchDistance, 0);
|
||||
player.boundingBox = playerBox;
|
||||
return collisionResult.getY() != searchDistance;
|
||||
}
|
||||
@@ -337,9 +352,9 @@ public class PointThreeEstimator {
|
||||
// Takes 0.01 millis, on average, to compute... this should be improved eventually
|
||||
for (VectorData data : init) {
|
||||
// Try to get the vector as close to zero as possible to give the best chance at 0.03...
|
||||
Vector toZeroVec = new PredictionEngine().handleStartingVelocityUncertainty(player, data, new Vector());
|
||||
Vector3D toZeroVec = new PredictionEngine().handleStartingVelocityUncertainty(player, data, newVector3D());
|
||||
// Collide to handle mostly gravity, but other scenarios similar to this.
|
||||
Vector collisionResult = Collisions.collide(player, toZeroVec.getX(), toZeroVec.getY(), toZeroVec.getZ(), Integer.MIN_VALUE, null);
|
||||
Vector3D collisionResult = Collisions.collide(player, toZeroVec.getX(), toZeroVec.getY(), toZeroVec.getZ(), Integer.MIN_VALUE, null);
|
||||
|
||||
// If this tick is the tick after y velocity was by 0, a stepping movement is POSSIBLE to have been hidden
|
||||
// A bit hacky... is there a better way? I'm unsure...
|
||||
|
||||
@@ -13,11 +13,12 @@ import ac.grim.grimac.utils.nmsutil.ReachUtils;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
|
||||
public class UncertaintyHandler {
|
||||
private final GrimPlayer player;
|
||||
// Handles uncertainty when a piston could have pushed a player in a direction
|
||||
@@ -148,15 +149,15 @@ public class UncertaintyHandler {
|
||||
entityBox.maxY -= height;
|
||||
entityBox.expand(-width / 2, 0, -width / 2);
|
||||
|
||||
Vector maxLocation = new Vector(entityBox.maxX, entityBox.maxY, entityBox.maxZ);
|
||||
Vector minLocation = new Vector(entityBox.minX, entityBox.minY, entityBox.minZ);
|
||||
Vector3D maxLocation = newVector3D(entityBox.maxX, entityBox.maxY, entityBox.maxZ);
|
||||
Vector3D minLocation = newVector3D(entityBox.minX, entityBox.minY, entityBox.minZ);
|
||||
|
||||
Vector diff = minLocation.subtract(new Vector(player.lastX, player.lastY + 0.8 * 1.8, player.lastZ)).multiply(0.1);
|
||||
Vector3D diff = minLocation.subtract(newVector3D(player.lastX, player.lastY + 0.8 * 1.8, player.lastZ)).multiply(0.1);
|
||||
fishingRodPullBox.minX = Math.min(0, diff.getX());
|
||||
fishingRodPullBox.minY = Math.min(0, diff.getY());
|
||||
fishingRodPullBox.minZ = Math.min(0, diff.getZ());
|
||||
|
||||
diff = maxLocation.subtract(new Vector(player.lastX, player.lastY + 0.8 * 1.8, player.lastZ)).multiply(0.1);
|
||||
diff = maxLocation.subtract(newVector3D(player.lastX, player.lastY + 0.8 * 1.8, player.lastZ)).multiply(0.1);
|
||||
fishingRodPullBox.maxX = Math.max(0, diff.getX());
|
||||
fishingRodPullBox.maxY = Math.max(0, diff.getY());
|
||||
fishingRodPullBox.maxZ = Math.max(0, diff.getZ());
|
||||
@@ -171,8 +172,8 @@ public class UncertaintyHandler {
|
||||
|
||||
fireworksBox = new SimpleCollisionBox();
|
||||
|
||||
Vector currentLook = ReachUtils.getLook(player, player.xRot, player.yRot);
|
||||
Vector lastLook = ReachUtils.getLook(player, player.lastXRot, player.lastYRot);
|
||||
Vector3D currentLook = ReachUtils.getLook(player, player.xRot, player.yRot);
|
||||
ac.grim.grimac.utils.vector.Vector3D lastLook = ReachUtils.getLook(player, player.lastXRot, player.lastYRot);
|
||||
|
||||
double antiTickSkipping = player.isPointThree() ? 0 : 0.05; // With 0.03, let that handle tick skipping
|
||||
|
||||
@@ -304,7 +305,8 @@ public class UncertaintyHandler {
|
||||
// This is the end of that section.
|
||||
|
||||
// I can't figure out how the client exactly tracks boost time
|
||||
if (player.compensatedEntities.getSelf().getRiding() instanceof PacketEntityRideable vehicle) {
|
||||
if (player.compensatedEntities.getSelf().getRiding() instanceof PacketEntityRideable) {
|
||||
PacketEntityRideable vehicle = (PacketEntityRideable) player.compensatedEntities.getSelf().getRiding();
|
||||
if (vehicle.currentBoostTime < vehicle.boostTimeMax + 20)
|
||||
offset -= 0.01;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,9 @@ import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import io.github.retrooper.packetevents.util.viaversion.ViaVersionUtil;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
|
||||
public class MovementTicker {
|
||||
public final GrimPlayer player;
|
||||
@@ -77,9 +79,9 @@ public class MovementTicker {
|
||||
player.uncertaintyHandler.collidingEntities.add(possibleCollidingEntities);
|
||||
}
|
||||
|
||||
public void move(Vector inputVel, Vector collide) {
|
||||
public void move(Vector3D inputVel, Vector3D collide) {
|
||||
if (player.stuckSpeedMultiplier.getX() < 0.99) {
|
||||
player.clientVelocity = new Vector();
|
||||
player.clientVelocity = newVector3D();
|
||||
}
|
||||
|
||||
if (inputVel.getX() != collide.getX()) {
|
||||
@@ -172,21 +174,21 @@ public class MovementTicker {
|
||||
|
||||
// The game disregards movements smaller than 1e-7 (such as in boats)
|
||||
if (collide.lengthSquared() < 1e-7) {
|
||||
collide = new Vector();
|
||||
collide = newVector3D();
|
||||
}
|
||||
|
||||
// This is where vanilla moves the bounding box and sets it
|
||||
player.predictedVelocity = new VectorData(collide.clone(), player.predictedVelocity.lastVector, player.predictedVelocity.vectorType);
|
||||
|
||||
float f = BlockProperties.getBlockSpeedFactor(player, player.mainSupportingBlockData, new Vector3d(player.x, player.y, player.z));
|
||||
player.clientVelocity.multiply(new Vector(f, 1, f));
|
||||
player.clientVelocity.multiply(newVector3D(f, 1, f));
|
||||
|
||||
// Reset stuck speed so it can update
|
||||
if (player.stuckSpeedMultiplier.getX() < 0.99) {
|
||||
player.uncertaintyHandler.lastStuckSpeedMultiplier.reset();
|
||||
}
|
||||
|
||||
player.stuckSpeedMultiplier = new Vector(1, 1, 1);
|
||||
player.stuckSpeedMultiplier = newVector3D(1, 1, 1);
|
||||
|
||||
// 1.15 and older clients use the handleInsideBlocks method for lava
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_16))
|
||||
@@ -201,7 +203,7 @@ public class MovementTicker {
|
||||
|
||||
// Flying players are not affected by cobwebs/sweet berry bushes
|
||||
if (player.isFlying) {
|
||||
player.stuckSpeedMultiplier = new Vector(1, 1, 1);
|
||||
player.stuckSpeedMultiplier = newVector3D(1, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,14 +390,14 @@ public class MovementTicker {
|
||||
|
||||
// Lava movement changed in 1.16
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_16) && player.slightlyTouchingLava) {
|
||||
player.clientVelocity = player.clientVelocity.multiply(new Vector(0.5D, 0.800000011920929D, 0.5D));
|
||||
player.clientVelocity = player.clientVelocity.multiply(newVector3D(0.5D, 0.800000011920929D, 0.5D));
|
||||
player.clientVelocity = FluidFallingAdjustedMovement.getFluidFallingAdjustedMovement(player, playerGravity, isFalling, player.clientVelocity);
|
||||
} else {
|
||||
player.clientVelocity.multiply(0.5D);
|
||||
}
|
||||
|
||||
if (player.hasGravity)
|
||||
player.clientVelocity.add(new Vector(0.0D, -playerGravity / 4.0D, 0.0D));
|
||||
player.clientVelocity.add(newVector3D(0.0D, -playerGravity / 4.0D, 0.0D));
|
||||
|
||||
} else if (player.isGliding) {
|
||||
player.friction = 0.99F; // Not vanilla, just useful for other grim stuff
|
||||
|
||||
@@ -5,7 +5,9 @@ import ac.grim.grimac.utils.data.packetentity.PacketEntityHorse;
|
||||
import ac.grim.grimac.utils.nmsutil.Collisions;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
|
||||
public class MovementTickerHorse extends MovementTickerLivingVehicle {
|
||||
|
||||
@@ -26,7 +28,7 @@ public class MovementTickerHorse extends MovementTickerLivingVehicle {
|
||||
forwardsInput *= 0.25F;
|
||||
}
|
||||
|
||||
this.movementInput = new Vector(horizInput, 0, forwardsInput);
|
||||
this.movementInput = newVector3D(horizInput, 0, forwardsInput);
|
||||
if (movementInput.lengthSquared() > 1) movementInput.normalize();
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -7,10 +7,13 @@ import ac.grim.grimac.predictionengine.predictions.rideable.PredictionEngineRide
|
||||
import ac.grim.grimac.predictionengine.predictions.rideable.PredictionEngineRideableWaterLegacy;
|
||||
import ac.grim.grimac.utils.nmsutil.BlockProperties;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
|
||||
public class MovementTickerLivingVehicle extends MovementTicker {
|
||||
Vector movementInput = new Vector();
|
||||
Vector3D movementInput = newVector3D();
|
||||
|
||||
public MovementTickerLivingVehicle(GrimPlayer player) {
|
||||
super(player);
|
||||
|
||||
@@ -3,12 +3,14 @@ package ac.grim.grimac.predictionengine.movementtick;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntityRideable;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
|
||||
public class MovementTickerPig extends MovementTickerRideable {
|
||||
public MovementTickerPig(GrimPlayer player) {
|
||||
super(player);
|
||||
movementInput = new Vector(0, 0, 1);
|
||||
movementInput = newVector3D(0, 0, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+5
-3
@@ -11,15 +11,17 @@ import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.resources.ResourceLocation;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateAttributes;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
|
||||
public class MovementTickerStrider extends MovementTickerRideable {
|
||||
|
||||
public MovementTickerStrider(GrimPlayer player) {
|
||||
super(player);
|
||||
movementInput = new Vector(0, 0, 1);
|
||||
movementInput = newVector3D(0, 0, 1);
|
||||
}
|
||||
|
||||
public static void floatStrider(GrimPlayer player) {
|
||||
@@ -27,7 +29,7 @@ public class MovementTickerStrider extends MovementTickerRideable {
|
||||
if (isAbove(player) && player.compensatedWorld.getLavaFluidLevelAt((int) Math.floor(player.x), (int) Math.floor(player.y + 1), (int) Math.floor(player.z)) == 0) {
|
||||
player.onGround = true;
|
||||
} else {
|
||||
player.clientVelocity.multiply(0.5).add(new Vector(0, 0.05, 0));
|
||||
player.clientVelocity.multiply(0.5).add(newVector3D(0, 0.05, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,20 +11,31 @@ import ac.grim.grimac.utils.nmsutil.Collisions;
|
||||
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
|
||||
import ac.grim.grimac.utils.nmsutil.JumpPower;
|
||||
import ac.grim.grimac.utils.nmsutil.Riptide;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static ac.grim.grimac.utils.data.VectorData.VectorType;
|
||||
import static ac.grim.grimac.utils.data.VectorData.VectorType.ZeroPointZeroThree;
|
||||
import static ac.grim.grimac.utils.nmsutil.Collisions.isEmpty;
|
||||
import static ac.grim.grimac.utils.nmsutil.GetBoundingBox.getBoundingBoxFromPosAndSize;
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
import static com.github.retrooper.packetevents.protocol.attribute.Attributes.GENERIC_GRAVITY;
|
||||
import static com.github.retrooper.packetevents.protocol.player.ClientVersion.V_1_13;
|
||||
import static java.lang.Math.*;
|
||||
import static java.util.Collections.max;
|
||||
|
||||
public class PredictionEngine {
|
||||
|
||||
public static Vector clampMovementToHardBorder(GrimPlayer player, Vector outputVel) {
|
||||
public static Vector3D clampMovementToHardBorder(GrimPlayer player, Vector3D outputVel) {
|
||||
// TODO: Reimplement
|
||||
return outputVel;
|
||||
}
|
||||
|
||||
public static Vector transformInputsToVector(GrimPlayer player, Vector theoreticalInput) {
|
||||
public static Vector3D transformInputsToVector(GrimPlayer player, Vector3D theoreticalInput) {
|
||||
float bestPossibleX;
|
||||
float bestPossibleZ;
|
||||
|
||||
@@ -42,15 +53,15 @@ public class PredictionEngine {
|
||||
bestPossibleZ *= 0.2F;
|
||||
}
|
||||
|
||||
Vector inputVector = new Vector(bestPossibleX, 0, bestPossibleZ);
|
||||
Vector3D inputVector = newVector3D(bestPossibleX, 0, bestPossibleZ);
|
||||
inputVector.multiply(0.98F);
|
||||
|
||||
// Simulate float rounding imprecision
|
||||
inputVector = new Vector((float) inputVector.getX(), (float) inputVector.getY(), (float) inputVector.getZ());
|
||||
inputVector = newVector3D((float) inputVector.getX(), (float) inputVector.getY(), (float) inputVector.getZ());
|
||||
|
||||
if (inputVector.lengthSquared() > 1) {
|
||||
double d0 = Math.sqrt(inputVector.getX() * inputVector.getX() + inputVector.getY() * inputVector.getY() + inputVector.getZ() * inputVector.getZ());
|
||||
inputVector = new Vector(inputVector.getX() / d0, inputVector.getY() / d0, inputVector.getZ() / d0);
|
||||
double d0 = sqrt(inputVector.getX() * inputVector.getX() + inputVector.getY() * inputVector.getY() + inputVector.getZ() * inputVector.getZ());
|
||||
inputVector = newVector3D(inputVector.getX() / d0, inputVector.getY() / d0, inputVector.getZ() / d0);
|
||||
}
|
||||
|
||||
return inputVector;
|
||||
@@ -62,7 +73,7 @@ public class PredictionEngine {
|
||||
if (player.uncertaintyHandler.influencedByBouncyBlock()) {
|
||||
for (VectorData data : init) {
|
||||
// Try to get the vector as close to zero as possible to give the best chance at 0.03...
|
||||
Vector toZeroVec = new PredictionEngine().handleStartingVelocityUncertainty(player, data, new Vector(0, -1000000000, 0)); // Downwards without overflow risk
|
||||
Vector3D toZeroVec = new PredictionEngine().handleStartingVelocityUncertainty(player, data, newVector3D(0, -1000000000, 0)); // Downwards without overflow risk
|
||||
|
||||
player.uncertaintyHandler.nextTickSlimeBlockUncertainty = Math.max(Math.abs(toZeroVec.getY()), player.uncertaintyHandler.nextTickSlimeBlockUncertainty);
|
||||
}
|
||||
@@ -105,8 +116,8 @@ public class PredictionEngine {
|
||||
double bestInput = Double.MAX_VALUE;
|
||||
|
||||
VectorData bestCollisionVel = null;
|
||||
Vector beforeCollisionMovement = null;
|
||||
Vector originalClientVel = player.clientVelocity.clone();
|
||||
Vector3D beforeCollisionMovement = null;
|
||||
Vector3D originalClientVel = player.clientVelocity.clone();
|
||||
|
||||
SimpleCollisionBox originalBB = player.boundingBox;
|
||||
// 0.03 doesn't exist with vehicles, thank god
|
||||
@@ -116,9 +127,9 @@ public class PredictionEngine {
|
||||
player.skippedTickInActualMovement = false;
|
||||
|
||||
for (VectorData clientVelAfterInput : possibleVelocities) {
|
||||
Vector primaryPushMovement = handleStartingVelocityUncertainty(player, clientVelAfterInput, player.actualMovement);
|
||||
Vector3D primaryPushMovement = handleStartingVelocityUncertainty(player, clientVelAfterInput, player.actualMovement);
|
||||
|
||||
Vector bestTheoreticalCollisionResult = VectorUtils.cutBoxToVector(player.actualMovement, new SimpleCollisionBox(0, Math.min(0, primaryPushMovement.getY()), 0, primaryPushMovement.getX(), Math.max(0.6, primaryPushMovement.getY()), primaryPushMovement.getZ()).sort());
|
||||
Vector3D bestTheoreticalCollisionResult = VectorUtils.cutBoxToVector(player.actualMovement, new SimpleCollisionBox(0, Math.min(0, primaryPushMovement.getY()), 0, primaryPushMovement.getX(), Math.max(0.6, primaryPushMovement.getY()), primaryPushMovement.getZ()).sort());
|
||||
// Check if this vector could ever possible beat the last vector in terms of accuracy
|
||||
// This is quite a good optimization :)
|
||||
if (bestTheoreticalCollisionResult.distanceSquared(player.actualMovement) > bestInput && !clientVelAfterInput.isKnockback() && !clientVelAfterInput.isExplosion()) {
|
||||
@@ -132,9 +143,9 @@ public class PredictionEngine {
|
||||
}
|
||||
|
||||
// Returns pair of primary push movement, and then outputvel
|
||||
Pair<Vector, Vector> output = doSeekingWallCollisions(player, primaryPushMovement, originalClientVel, clientVelAfterInput);
|
||||
primaryPushMovement = output.first();
|
||||
Vector outputVel = clampMovementToHardBorder(player, output.second());
|
||||
Pair<Vector3D, Vector3D> output = doSeekingWallCollisions(player, primaryPushMovement, originalClientVel, clientVelAfterInput);
|
||||
primaryPushMovement = output.getFirst();
|
||||
Vector3D outputVel = clampMovementToHardBorder(player, output.getSecond());
|
||||
|
||||
double resultAccuracy = outputVel.distanceSquared(player.actualMovement);
|
||||
|
||||
@@ -208,7 +219,7 @@ public class PredictionEngine {
|
||||
}
|
||||
}
|
||||
|
||||
private Pair<Vector, Vector> doSeekingWallCollisions(GrimPlayer player, Vector primaryPushMovement, Vector originalClientVel, VectorData clientVelAfterInput) {
|
||||
private Pair<Vector3D, Vector3D> doSeekingWallCollisions(GrimPlayer player, Vector3D primaryPushMovement, Vector3D originalClientVel, VectorData clientVelAfterInput) {
|
||||
boolean vehicleKB = player.compensatedEntities.getSelf().inVehicle() && clientVelAfterInput.isKnockback() && clientVelAfterInput.vector.getY() == 0;
|
||||
// Extra collision epsilon required for vehicles to be accurate
|
||||
double xAdditional = Math.signum(primaryPushMovement.getX()) * SimpleCollisionBox.COLLISION_EPSILON;
|
||||
@@ -221,9 +232,9 @@ public class PredictionEngine {
|
||||
double testX = primaryPushMovement.getX() + xAdditional;
|
||||
double testY = primaryPushMovement.getY() + yAdditional;
|
||||
double testZ = primaryPushMovement.getZ() + zAdditional;
|
||||
primaryPushMovement = new Vector(testX, testY, testZ);
|
||||
primaryPushMovement = newVector3D(testX, testY, testZ);
|
||||
|
||||
Vector outputVel = Collisions.collide(player, primaryPushMovement.getX(), primaryPushMovement.getY(), primaryPushMovement.getZ(), originalClientVel.getY(), clientVelAfterInput);
|
||||
Vector3D outputVel = Collisions.collide(player, primaryPushMovement.getX(), primaryPushMovement.getY(), primaryPushMovement.getZ(), originalClientVel.getY(), clientVelAfterInput);
|
||||
|
||||
if (testX == outputVel.getX()) { // the player didn't have X collision, don't ruin offset by collision epsilon
|
||||
primaryPushMovement.setX(primaryPushMovement.getX() - xAdditional);
|
||||
@@ -249,20 +260,20 @@ public class PredictionEngine {
|
||||
|
||||
// For now just let the player control their Y velocity within 0.03. Gravity should stop exploits.
|
||||
// 0.03 - 0.784 < -0.03 = can't skip next tick
|
||||
Vector pointThreeVector = new Vector();
|
||||
Vector3D pointThreeVector = newVector3D();
|
||||
|
||||
// Stop a bypass (and fix falses) by carrying over the player's current velocity IF they couldn't have modified it
|
||||
if (!player.pointThreeEstimator.controlsVerticalMovement()) {
|
||||
pointThreeVector.setY(player.clientVelocity.getY());
|
||||
} else { // Carry over the current Y velocity to try and help with gravity issues
|
||||
pointThreePossibilities.add(new VectorData(new Vector(0, player.clientVelocity.getY(), 0), VectorData.VectorType.ZeroPointZeroThree));
|
||||
pointThreePossibilities.add(new VectorData(newVector3D(0, player.clientVelocity.getY(), 0), ZeroPointZeroThree));
|
||||
}
|
||||
|
||||
pointThreePossibilities.add(new VectorData(pointThreeVector, VectorData.VectorType.ZeroPointZeroThree));
|
||||
|
||||
// Swim hop
|
||||
if (player.pointThreeEstimator.isNearFluid && !Collisions.isEmpty(player, player.boundingBox.copy().expand(0.4, 0, 0.4)) && !player.onGround) { // onGround can still be used here, else generic 0.03
|
||||
pointThreePossibilities.add(new VectorData(new Vector(0, 0.3, 0), VectorData.VectorType.ZeroPointZeroThree));
|
||||
pointThreePossibilities.add(new VectorData(newVector3D(0, 0.3, 0), ZeroPointZeroThree));
|
||||
}
|
||||
|
||||
// Swimming vertically can add more Y velocity than normal
|
||||
@@ -279,7 +290,7 @@ public class PredictionEngine {
|
||||
player.clientVelocity.getX(), 0, player.clientVelocity.getZ()).expand(0.5, -SimpleCollisionBox.COLLISION_EPSILON, 0.5)))) {
|
||||
|
||||
// Calculate the Y velocity after friction
|
||||
Vector hackyClimbVector = new Vector(0, 0.2, 0);
|
||||
Vector3D hackyClimbVector = newVector3D(0, 0.2, 0);
|
||||
PredictionEngineNormal.staticVectorEndOfTick(player, hackyClimbVector);
|
||||
|
||||
pointThreePossibilities.add(new VectorData(hackyClimbVector, VectorData.VectorType.ZeroPointZeroThree));
|
||||
@@ -290,8 +301,8 @@ public class PredictionEngine {
|
||||
addExplosionToPossibilities(player, pointThreePossibilities);
|
||||
|
||||
if (player.packetStateData.tryingToRiptide) {
|
||||
Vector riptideAddition = Riptide.getRiptideVelocity(player);
|
||||
pointThreePossibilities.add(new VectorData(player.clientVelocity.clone().add(riptideAddition), new VectorData(new Vector(), VectorData.VectorType.ZeroPointZeroThree), VectorData.VectorType.Trident));
|
||||
Vector3D riptideAddition = Riptide.getRiptideVelocity(player);
|
||||
pointThreePossibilities.add(new VectorData(player.clientVelocity.clone().add(riptideAddition), new VectorData(newVector3D(), ZeroPointZeroThree), VectorData.VectorType.Trident));
|
||||
}
|
||||
|
||||
possibleVelocities.addAll(applyInputsToVelocityPossibilities(player, pointThreePossibilities, speed));
|
||||
@@ -312,7 +323,7 @@ public class PredictionEngine {
|
||||
// Water pushing movement is affected by initial velocity due to 0.003 eating pushing in the past
|
||||
if (vectorData.isKnockback() && player.baseTickWaterPushing.lengthSquared() != 0) {
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13)) {
|
||||
Vector vec3 = player.baseTickWaterPushing.clone();
|
||||
Vector3D vec3 = player.baseTickWaterPushing.clone();
|
||||
if (Math.abs(vectorData.vector.getX()) < 0.003 && Math.abs(vectorData.vector.getZ()) < 0.003 && player.baseTickWaterPushing.length() < 0.0045000000000000005D) {
|
||||
vec3 = vec3.normalize().multiply(0.0045000000000000005);
|
||||
}
|
||||
@@ -332,7 +343,7 @@ public class PredictionEngine {
|
||||
addExplosionToPossibilities(player, velocities);
|
||||
|
||||
if (player.packetStateData.tryingToRiptide) {
|
||||
Vector riptideAddition = Riptide.getRiptideVelocity(player);
|
||||
Vector3D riptideAddition = Riptide.getRiptideVelocity(player);
|
||||
velocities.add(new VectorData(player.clientVelocity.clone().add(riptideAddition), VectorData.VectorType.Trident));
|
||||
}
|
||||
|
||||
@@ -361,7 +372,7 @@ public class PredictionEngine {
|
||||
private void addAttackSlowToPossibilities(GrimPlayer player, Set<VectorData> velocities) {
|
||||
for (int x = 1; x <= Math.min(player.maxPlayerAttackSlow, 5); x++) {
|
||||
for (VectorData data : new HashSet<>(velocities)) {
|
||||
velocities.add(data.returnNewModified(data.vector.clone().multiply(new Vector(0.6, 1, 0.6)), VectorData.VectorType.AttackSlow));
|
||||
velocities.add(data.returnNewModified(data.vector.clone().multiply(newVector3D(0.6, 1, 0.6)), VectorData.VectorType.AttackSlow));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -411,52 +422,63 @@ public class PredictionEngine {
|
||||
// Order priority (to avoid false positives and false flagging future predictions):
|
||||
// Knockback and explosions
|
||||
// 0.03 ticks
|
||||
// Movement without input
|
||||
// Normal movement
|
||||
// First bread knockback and explosions
|
||||
// Flagging groundspoof
|
||||
// Flagging flip items
|
||||
if (a.isExplosion())
|
||||
aScore -= 5;
|
||||
aScore -= 10;
|
||||
|
||||
if (a.isKnockback())
|
||||
aScore -= 5;
|
||||
aScore -= 10;
|
||||
|
||||
if (b.isExplosion())
|
||||
bScore -= 5;
|
||||
bScore -= 10;
|
||||
|
||||
if (b.isKnockback())
|
||||
bScore -= 5;
|
||||
bScore -= 10;
|
||||
|
||||
if (a.isFirstBreadExplosion())
|
||||
aScore += 1;
|
||||
aScore += 2;
|
||||
|
||||
if (b.isFirstBreadExplosion())
|
||||
bScore += 1;
|
||||
bScore += 2;
|
||||
|
||||
if (a.isFirstBreadKb())
|
||||
aScore += 1;
|
||||
aScore += 2;
|
||||
|
||||
if (b.isFirstBreadKb())
|
||||
bScore += 1;
|
||||
bScore += 2;
|
||||
|
||||
if (a.isFlipItem())
|
||||
aScore += 3;
|
||||
aScore += 6;
|
||||
|
||||
if (b.isFlipItem())
|
||||
bScore += 3;
|
||||
bScore += 6;
|
||||
|
||||
if (a.isZeroPointZeroThree())
|
||||
aScore -= 1;
|
||||
aScore -= 2;
|
||||
|
||||
if (b.isZeroPointZeroThree())
|
||||
bScore -= 2;
|
||||
|
||||
if (a.isWithInput() || a.isJump())
|
||||
aScore += 1;
|
||||
else
|
||||
aScore -= 1;
|
||||
|
||||
if (b.isWithInput() || b.isJump())
|
||||
bScore += 1;
|
||||
else
|
||||
bScore -= 1;
|
||||
|
||||
// If the player is on the ground but the vector leads the player off the ground
|
||||
if ((player.compensatedEntities.getSelf().inVehicle() ? player.clientControlledVerticalCollision : player.onGround) && a.vector.getY() >= 0)
|
||||
aScore += 2;
|
||||
aScore += 4;
|
||||
|
||||
if ((player.compensatedEntities.getSelf().inVehicle() ? player.clientControlledVerticalCollision : player.onGround) && b.vector.getY() >= 0)
|
||||
bScore += 2;
|
||||
bScore += 4;
|
||||
|
||||
if (aScore != bScore)
|
||||
return Integer.compare(aScore, bScore);
|
||||
@@ -464,15 +486,15 @@ public class PredictionEngine {
|
||||
return Double.compare(a.vector.distanceSquared(player.actualMovement), b.vector.distanceSquared(player.actualMovement));
|
||||
}
|
||||
|
||||
public Vector handleStartingVelocityUncertainty(GrimPlayer player, VectorData vector, Vector targetVec) {
|
||||
double avgColliding = Collections.max(player.uncertaintyHandler.collidingEntities);
|
||||
public Vector3D handleStartingVelocityUncertainty(GrimPlayer player, VectorData vector, Vector3D targetVec) {
|
||||
double avgColliding = max(player.uncertaintyHandler.collidingEntities);
|
||||
|
||||
double additionHorizontal = player.uncertaintyHandler.getOffsetHorizontal(vector);
|
||||
double additionVertical = player.uncertaintyHandler.getVerticalOffset(vector);
|
||||
|
||||
double pistonX = Collections.max(player.uncertaintyHandler.pistonX);
|
||||
double pistonY = Collections.max(player.uncertaintyHandler.pistonY);
|
||||
double pistonZ = Collections.max(player.uncertaintyHandler.pistonZ);
|
||||
double pistonX = max(player.uncertaintyHandler.pistonX);
|
||||
double pistonY = max(player.uncertaintyHandler.pistonY);
|
||||
double pistonZ = max(player.uncertaintyHandler.pistonZ);
|
||||
|
||||
additionHorizontal += player.uncertaintyHandler.lastHorizontalOffset;
|
||||
additionVertical += player.uncertaintyHandler.lastVerticalOffset;
|
||||
@@ -515,13 +537,13 @@ public class PredictionEngine {
|
||||
// 0.075 seems safe?
|
||||
//
|
||||
// Be somewhat careful as there is an antikb (for horizontal) that relies on this lenience
|
||||
Vector uncertainty = new Vector(avgColliding * 0.08, additionVertical, avgColliding * 0.08);
|
||||
Vector3D uncertainty = newVector3D(avgColliding * 0.08, additionVertical, avgColliding * 0.08);
|
||||
|
||||
Vector min = new Vector(player.uncertaintyHandler.xNegativeUncertainty - additionHorizontal, -bonusY + player.uncertaintyHandler.yNegativeUncertainty, player.uncertaintyHandler.zNegativeUncertainty - additionHorizontal);
|
||||
Vector max = new Vector(player.uncertaintyHandler.xPositiveUncertainty + additionHorizontal, bonusY + player.uncertaintyHandler.yPositiveUncertainty, player.uncertaintyHandler.zPositiveUncertainty + additionHorizontal);
|
||||
Vector3D min = newVector3D(player.uncertaintyHandler.xNegativeUncertainty - additionHorizontal, -bonusY + player.uncertaintyHandler.yNegativeUncertainty, player.uncertaintyHandler.zNegativeUncertainty - additionHorizontal);
|
||||
Vector3D max = newVector3D(player.uncertaintyHandler.xPositiveUncertainty + additionHorizontal, bonusY + player.uncertaintyHandler.yPositiveUncertainty, player.uncertaintyHandler.zPositiveUncertainty + additionHorizontal);
|
||||
|
||||
Vector minVector = vector.vector.clone().add(min.subtract(uncertainty));
|
||||
Vector maxVector = vector.vector.clone().add(max.add(uncertainty));
|
||||
Vector3D minVector = vector.vector.clone().add(min.subtract(uncertainty));
|
||||
Vector3D maxVector = vector.vector.clone().add(max.add(uncertainty));
|
||||
|
||||
// Handle the player landing within 0.03 movement, which resets Y velocity
|
||||
if (player.uncertaintyHandler.onGroundUncertain && vector.vector.getY() < 0) {
|
||||
@@ -548,7 +570,7 @@ public class PredictionEngine {
|
||||
// We can't simulate the player's Y velocity, unknown number of ticks with a gravity change
|
||||
// Feel free to simulate all 104857600000000000000000000 possibilities!
|
||||
if (!player.pointThreeEstimator.canPredictNextVerticalMovement()) {
|
||||
minVector.setY(minVector.getY() - player.compensatedEntities.getSelf().getAttributeValue(Attributes.GENERIC_GRAVITY));
|
||||
minVector.setY(minVector.getY() - player.compensatedEntities.getSelf().getAttributeValue(GENERIC_GRAVITY));
|
||||
}
|
||||
|
||||
// Hidden slime block bounces by missing idle tick and 0.03
|
||||
@@ -588,9 +610,9 @@ public class PredictionEngine {
|
||||
box.maxZ += sneaking.getSneakingPotentialHiddenVelocity().maxZ;
|
||||
|
||||
if (player.uncertaintyHandler.fireworksBox != null) {
|
||||
double minXdiff = Math.min(0, player.uncertaintyHandler.fireworksBox.minX - originalVec.vector.getX());
|
||||
double minYdiff = Math.min(0, player.uncertaintyHandler.fireworksBox.minY - originalVec.vector.getY());
|
||||
double minZdiff = Math.min(0, player.uncertaintyHandler.fireworksBox.minZ - originalVec.vector.getZ());
|
||||
double minXdiff = min(0, player.uncertaintyHandler.fireworksBox.minX - originalVec.vector.getX());
|
||||
double minYdiff = min(0, player.uncertaintyHandler.fireworksBox.minY - originalVec.vector.getY());
|
||||
double minZdiff = min(0, player.uncertaintyHandler.fireworksBox.minZ - originalVec.vector.getZ());
|
||||
double maxXdiff = Math.max(0, player.uncertaintyHandler.fireworksBox.maxX - originalVec.vector.getX());
|
||||
double maxYdiff = Math.max(0, player.uncertaintyHandler.fireworksBox.maxY - originalVec.vector.getY());
|
||||
double maxZdiff = Math.max(0, player.uncertaintyHandler.fireworksBox.maxZ - originalVec.vector.getZ());
|
||||
@@ -646,7 +668,7 @@ public class PredictionEngine {
|
||||
//
|
||||
// Or the player is switching in and out of controlling a vehicle, in which friction messes it up
|
||||
//
|
||||
if (player.uncertaintyHandler.lastVehicleSwitch.hasOccurredSince(0) || player.uncertaintyHandler.lastHardCollidingLerpingEntity.hasOccurredSince(3) || (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13) && vector.vector.getY() > 0 && vector.isZeroPointZeroThree() && !Collisions.isEmpty(player, GetBoundingBox.getBoundingBoxFromPosAndSize(player, player.lastX, vector.vector.getY() + player.lastY + 0.6, player.lastZ, 0.6f, 1.26f)))) {
|
||||
if (player.uncertaintyHandler.lastVehicleSwitch.hasOccurredSince(0) || player.uncertaintyHandler.lastHardCollidingLerpingEntity.hasOccurredSince(3) || (player.getClientVersion().isNewerThanOrEquals(V_1_13) && vector.vector.getY() > 0 && vector.isZeroPointZeroThree() && !isEmpty(player, getBoundingBoxFromPosAndSize(player, player.lastX, vector.vector.getY() + player.lastY + 0.6, player.lastZ, 0.6f, 1.26f)))) {
|
||||
box.expandToAbsoluteCoordinates(0, 0, 0);
|
||||
}
|
||||
|
||||
@@ -658,9 +680,9 @@ public class PredictionEngine {
|
||||
if (player.wasTouchingWater) trueFriction = 0.96;
|
||||
|
||||
double maxY = Math.max(box.maxY, box.maxY + ((box.maxY - player.gravity) * 0.91));
|
||||
double minY = Math.min(box.minY, box.minY + ((box.minY - player.gravity) * 0.91));
|
||||
double minX = Math.min(box.minX, box.minX + (-player.speed * trueFriction));
|
||||
double minZ = Math.min(box.minZ, box.minZ + (-player.speed * trueFriction));
|
||||
double minY = min(box.minY, box.minY + ((box.minY - player.gravity) * 0.91));
|
||||
double minX = min(box.minX, box.minX + (-player.speed * trueFriction));
|
||||
double minZ = min(box.minZ, box.minZ + (-player.speed * trueFriction));
|
||||
double maxX = Math.max(box.maxX, box.maxX + (player.speed * trueFriction));
|
||||
double maxZ = Math.max(box.maxZ, box.maxZ + (player.speed * trueFriction));
|
||||
|
||||
@@ -676,15 +698,15 @@ public class PredictionEngine {
|
||||
maxVector = box.max();
|
||||
|
||||
if (pistonX != 0) {
|
||||
minVector.setX(Math.min(minVector.getX() - pistonX, pistonX));
|
||||
minVector.setX(min(minVector.getX() - pistonX, pistonX));
|
||||
maxVector.setX(Math.max(maxVector.getX() + pistonX, pistonX));
|
||||
}
|
||||
if (pistonY != 0) {
|
||||
minVector.setY(Math.min(minVector.getY() - pistonY, pistonY));
|
||||
minVector.setY(min(minVector.getY() - pistonY, pistonY));
|
||||
maxVector.setY(Math.max(maxVector.getY() + pistonY, pistonY));
|
||||
}
|
||||
if (pistonZ != 0) {
|
||||
minVector.setZ(Math.min(minVector.getZ() - pistonZ, pistonZ));
|
||||
minVector.setZ(min(minVector.getZ() - pistonZ, pistonZ));
|
||||
maxVector.setZ(Math.max(maxVector.getZ() + pistonZ, pistonZ));
|
||||
}
|
||||
return VectorUtils.cutBoxToVector(targetVec, minVector, maxVector);
|
||||
@@ -714,7 +736,7 @@ public class PredictionEngine {
|
||||
if (loopSlowed == 1 && !possibleLastTickOutput.isZeroPointZeroThree()) continue;
|
||||
for (int x = -1; x <= 1; x++) {
|
||||
for (int z = zMin; z <= 1; z++) {
|
||||
VectorData result = new VectorData(possibleLastTickOutput.vector.clone().add(getMovementResultFromInput(player, transformInputsToVector(player, new Vector(x, 0, z)), speed, player.xRot)), possibleLastTickOutput, VectorData.VectorType.InputResult);
|
||||
VectorData result = new VectorData.MoveVectorData(possibleLastTickOutput.vector.clone().add(getMovementResultFromInput(player, transformInputsToVector(player, newVector3D(x, 0, z)), speed, player.xRot)), possibleLastTickOutput, VectorData.VectorType.InputResult, x, z);
|
||||
result = result.returnNewModified(result.vector.clone().multiply(player.stuckSpeedMultiplier), VectorData.VectorType.StuckMultiplier);
|
||||
result = result.returnNewModified(handleOnClimbable(result.vector.clone(), player), VectorData.VectorType.Climbable);
|
||||
// Signal that we need to flip sneaking bounding box
|
||||
@@ -781,21 +803,21 @@ public class PredictionEngine {
|
||||
|
||||
// This is just the vanilla equation, which accepts invalid inputs greater than 1
|
||||
// We need it because of collision support when a player is using speed
|
||||
public Vector getMovementResultFromInput(GrimPlayer player, Vector inputVector, float f, float f2) {
|
||||
public Vector3D getMovementResultFromInput(GrimPlayer player, Vector3D inputVector, float f, float f2) {
|
||||
float f3 = player.trigHandler.sin(f2 * 0.017453292f);
|
||||
float f4 = player.trigHandler.cos(f2 * 0.017453292f);
|
||||
|
||||
double xResult = inputVector.getX() * f4 - inputVector.getZ() * f3;
|
||||
double zResult = inputVector.getZ() * f4 + inputVector.getX() * f3;
|
||||
|
||||
return new Vector(xResult * f, 0, zResult * f);
|
||||
return newVector3D(xResult * f, 0, zResult * f);
|
||||
}
|
||||
|
||||
public Vector handleOnClimbable(Vector vector, GrimPlayer player) {
|
||||
public Vector3D handleOnClimbable(Vector3D vector, GrimPlayer player) {
|
||||
return vector;
|
||||
}
|
||||
|
||||
public void doJump(GrimPlayer player, Vector vector) {
|
||||
public void doJump(GrimPlayer player, Vector3D vector) {
|
||||
if (!player.lastOnGround || player.onGround)
|
||||
return;
|
||||
|
||||
|
||||
+21
-14
@@ -3,28 +3,35 @@ package ac.grim.grimac.predictionengine.predictions;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.data.VectorData;
|
||||
import ac.grim.grimac.utils.nmsutil.ReachUtils;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
import static com.github.retrooper.packetevents.protocol.attribute.Attributes.GENERIC_GRAVITY;
|
||||
import static com.github.retrooper.packetevents.protocol.player.ClientVersion.V_1_18_2;
|
||||
import static com.github.retrooper.packetevents.protocol.player.ClientVersion.V_1_20_5;
|
||||
import static java.lang.Math.*;
|
||||
|
||||
public class PredictionEngineElytra extends PredictionEngine {
|
||||
// Inputs have no effect on movement
|
||||
@Override
|
||||
public List<VectorData> applyInputsToVelocityPossibilities(GrimPlayer player, Set<VectorData> possibleVectors, float speed) {
|
||||
List<VectorData> results = new ArrayList<>();
|
||||
Vector currentLook = ReachUtils.getLook(player, player.xRot, player.yRot);
|
||||
Vector3D currentLook = ReachUtils.getLook(player, player.xRot, player.yRot);
|
||||
|
||||
for (VectorData data : possibleVectors) {
|
||||
Vector elytraResult = getElytraMovement(player, data.vector.clone(), currentLook).multiply(player.stuckSpeedMultiplier).multiply(new Vector(0.99F, 0.98F, 0.99F));
|
||||
Vector3D elytraResult = getElytraMovement(player, data.vector.clone(), currentLook).multiply(player.stuckSpeedMultiplier).multiply(newVector3D(0.99F, 0.98F, 0.99F));
|
||||
results.add(data.returnNewModified(elytraResult, VectorData.VectorType.InputResult));
|
||||
|
||||
// We must bruteforce Optifine ShitMath
|
||||
player.trigHandler.toggleShitMath();
|
||||
elytraResult = getElytraMovement(player, data.vector.clone(), ReachUtils.getLook(player, player.xRot, player.yRot)).multiply(player.stuckSpeedMultiplier).multiply(new Vector(0.99F, 0.98F, 0.99F));
|
||||
elytraResult = getElytraMovement(player, data.vector.clone(), ReachUtils.getLook(player, player.xRot, player.yRot)).multiply(player.stuckSpeedMultiplier).multiply(newVector3D(0.99F, 0.98F, 0.99F));
|
||||
player.trigHandler.toggleShitMath();
|
||||
results.add(data.returnNewModified(elytraResult, VectorData.VectorType.InputResult));
|
||||
}
|
||||
@@ -32,42 +39,42 @@ public class PredictionEngineElytra extends PredictionEngine {
|
||||
return results;
|
||||
}
|
||||
|
||||
public static Vector getElytraMovement(GrimPlayer player, Vector vector, Vector lookVector) {
|
||||
public static Vector3D getElytraMovement(GrimPlayer player, Vector3D vector, Vector3D lookVector) {
|
||||
float yRotRadians = player.yRot * 0.017453292F;
|
||||
double horizontalSqrt = Math.sqrt(lookVector.getX() * lookVector.getX() + lookVector.getZ() * lookVector.getZ());
|
||||
double horizontalSqrt = sqrt(lookVector.getX() * lookVector.getX() + lookVector.getZ() * lookVector.getZ());
|
||||
double horizontalLength = vector.clone().setY(0).length();
|
||||
double length = lookVector.length();
|
||||
|
||||
// Mojang changed from using their math to using regular java math in 1.18.2 elytra movement
|
||||
double vertCosRotation = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_18_2) ? Math.cos(yRotRadians) : player.trigHandler.cos(yRotRadians);
|
||||
vertCosRotation = (float) (vertCosRotation * vertCosRotation * Math.min(1.0D, length / 0.4D));
|
||||
double vertCosRotation = player.getClientVersion().isNewerThanOrEquals(V_1_18_2) ? cos(yRotRadians) : player.trigHandler.cos(yRotRadians);
|
||||
vertCosRotation = (float) (vertCosRotation * vertCosRotation * min(1.0D, length / 0.4D));
|
||||
|
||||
// So we actually use the player's actual movement to get the gravity/slow falling status
|
||||
// However, this is wrong with elytra movement because players can control vertical movement after gravity is calculated
|
||||
// Yeah, slow falling needs a refactor in grim.
|
||||
double recalculatedGravity = player.compensatedEntities.getSelf().getAttributeValue(Attributes.GENERIC_GRAVITY);
|
||||
double recalculatedGravity = player.compensatedEntities.getSelf().getAttributeValue(GENERIC_GRAVITY);
|
||||
if (player.clientVelocity.getY() <= 0 && player.compensatedEntities.getSlowFallingAmplifier().isPresent()) {
|
||||
recalculatedGravity = player.getClientVersion().isOlderThan(ClientVersion.V_1_20_5) ? 0.01 : Math.min(recalculatedGravity, 0.01);
|
||||
recalculatedGravity = player.getClientVersion().isOlderThan(V_1_20_5) ? 0.01 : min(recalculatedGravity, 0.01);
|
||||
}
|
||||
|
||||
vector.add(new Vector(0.0D, recalculatedGravity * (-1.0D + vertCosRotation * 0.75D), 0.0D));
|
||||
vector.add(newVector3D(0.0D, recalculatedGravity * (-1.0D + vertCosRotation * 0.75D), 0.0D));
|
||||
double d5;
|
||||
|
||||
// Handle slowing the player down when falling
|
||||
if (vector.getY() < 0.0D && horizontalSqrt > 0.0D) {
|
||||
d5 = vector.getY() * -0.1D * vertCosRotation;
|
||||
vector.add(new Vector(lookVector.getX() * d5 / horizontalSqrt, d5, lookVector.getZ() * d5 / horizontalSqrt));
|
||||
vector.add(newVector3D(lookVector.getX() * d5 / horizontalSqrt, d5, lookVector.getZ() * d5 / horizontalSqrt));
|
||||
}
|
||||
|
||||
// Handle accelerating the player when they are looking down
|
||||
if (yRotRadians < 0.0F && horizontalSqrt > 0.0D) {
|
||||
d5 = horizontalLength * (double) (-player.trigHandler.sin(yRotRadians)) * 0.04D;
|
||||
vector.add(new Vector(-lookVector.getX() * d5 / horizontalSqrt, d5 * 3.2D, -lookVector.getZ() * d5 / horizontalSqrt));
|
||||
vector.add(newVector3D(-lookVector.getX() * d5 / horizontalSqrt, d5 * 3.2D, -lookVector.getZ() * d5 / horizontalSqrt));
|
||||
}
|
||||
|
||||
// Handle accelerating the player sideways
|
||||
if (horizontalSqrt > 0) {
|
||||
vector.add(new Vector((lookVector.getX() / horizontalSqrt * horizontalLength - vector.getX()) * 0.1D, 0.0D, (lookVector.getZ() / horizontalSqrt * horizontalLength - vector.getZ()) * 0.1D));
|
||||
vector.add(newVector3D((lookVector.getX() / horizontalSqrt * horizontalLength - vector.getX()) * 0.1D, 0.0D, (lookVector.getZ() / horizontalSqrt * horizontalLength - vector.getZ()) * 0.1D));
|
||||
}
|
||||
|
||||
return vector;
|
||||
|
||||
@@ -3,11 +3,13 @@ package ac.grim.grimac.predictionengine.predictions;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.data.VectorData;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
|
||||
public class PredictionEngineLava extends PredictionEngine {
|
||||
@Override
|
||||
public void addJumpsToPossibilities(GrimPlayer player, Set<VectorData> existingVelocities) {
|
||||
@@ -16,11 +18,11 @@ public class PredictionEngineLava extends PredictionEngine {
|
||||
double extraVelFromVertTickSkipUpwards = GrimMath.clamp(player.actualMovement.getY(), vector.vector.clone().getY(), vector.vector.clone().getY() + 0.05f);
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().setY(extraVelFromVertTickSkipUpwards), vector, VectorData.VectorType.Jump));
|
||||
} else {
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().add(new Vector(0, 0.04f, 0)), vector, VectorData.VectorType.Jump));
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().add(newVector3D(0, 0.04f, 0)), vector, VectorData.VectorType.Jump));
|
||||
}
|
||||
|
||||
if (player.slightlyTouchingLava && player.lastOnGround && !player.onGround) {
|
||||
Vector withJump = vector.vector.clone();
|
||||
Vector3D withJump = vector.vector.clone();
|
||||
super.doJump(player, withJump);
|
||||
existingVelocities.add(new VectorData(withJump, vector, VectorData.VectorType.Jump));
|
||||
}
|
||||
|
||||
+18
-12
@@ -6,20 +6,26 @@ import ac.grim.grimac.utils.data.VectorData;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.nmsutil.Collisions;
|
||||
import ac.grim.grimac.utils.nmsutil.JumpPower;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
import com.github.retrooper.packetevents.protocol.item.ItemStack;
|
||||
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.potion.PotionTypes;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.Set;
|
||||
|
||||
import static ac.grim.grimac.utils.math.GrimMath.clamp;
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
import static com.github.retrooper.packetevents.protocol.world.states.type.StateTypes.SCAFFOLDING;
|
||||
import static java.lang.Math.max;
|
||||
|
||||
public class PredictionEngineNormal extends PredictionEngine {
|
||||
|
||||
public static void staticVectorEndOfTick(GrimPlayer player, Vector vector) {
|
||||
public static void staticVectorEndOfTick(GrimPlayer player, Vector3D vector) {
|
||||
double adjustedY = vector.getY();
|
||||
final OptionalInt levitation = player.compensatedEntities.getPotionLevelForPlayer(PotionTypes.LEVITATION);
|
||||
if (levitation.isPresent()) {
|
||||
@@ -38,7 +44,7 @@ public class PredictionEngineNormal extends PredictionEngine {
|
||||
@Override
|
||||
public void addJumpsToPossibilities(GrimPlayer player, Set<VectorData> existingVelocities) {
|
||||
for (VectorData vector : new HashSet<>(existingVelocities)) {
|
||||
Vector jump = vector.vector.clone();
|
||||
Vector3D jump = vector.vector.clone();
|
||||
|
||||
if (!player.isFlying) {
|
||||
// Negative jump boost does not allow the player to leave the ground
|
||||
@@ -47,14 +53,14 @@ public class PredictionEngineNormal extends PredictionEngine {
|
||||
// And 0.03 didn't affect onGround status
|
||||
// The player cannot jump
|
||||
final OptionalInt jumpBoost = player.compensatedEntities.getPotionLevelForPlayer(PotionTypes.JUMP_BOOST);
|
||||
if (((jumpBoost.isEmpty() || jumpBoost.getAsInt() >= 0) && player.onGround) || !player.lastOnGround)
|
||||
if (((!jumpBoost.isPresent() || jumpBoost.getAsInt() >= 0) && player.onGround) || !player.lastOnGround)
|
||||
return;
|
||||
|
||||
JumpPower.jumpFromGround(player, jump);
|
||||
} else {
|
||||
jump.add(new Vector(0, player.flySpeed * 3, 0));
|
||||
jump.add(newVector3D(0, player.flySpeed * 3, 0));
|
||||
if (!player.wasFlying) {
|
||||
Vector edgeCaseJump = jump.clone();
|
||||
Vector3D edgeCaseJump = jump.clone();
|
||||
JumpPower.jumpFromGround(player, edgeCaseJump);
|
||||
existingVelocities.add(vector.returnNewModified(edgeCaseJump, VectorData.VectorType.Jump));
|
||||
}
|
||||
@@ -82,7 +88,7 @@ public class PredictionEngineNormal extends PredictionEngine {
|
||||
if (player.lastWasClimbing == 0 && (player.pointThreeEstimator.isNearClimbable() || player.isClimbing) && (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14)
|
||||
|| !Collisions.isEmpty(player, player.boundingBox.copy().expand(
|
||||
player.clientVelocity.getX(), 0, player.clientVelocity.getZ()).expand(0.5, -SimpleCollisionBox.COLLISION_EPSILON, 0.5))) || walkingOnPowderSnow) {
|
||||
Vector ladderVelocity = player.clientVelocity.clone().setY(0.2);
|
||||
Vector3D ladderVelocity = player.clientVelocity.clone().setY(0.2);
|
||||
staticVectorEndOfTick(player, ladderVelocity);
|
||||
player.lastWasClimbing = ladderVelocity.getY();
|
||||
}
|
||||
@@ -93,17 +99,17 @@ public class PredictionEngineNormal extends PredictionEngine {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector handleOnClimbable(Vector vector, GrimPlayer player) {
|
||||
public Vector3D handleOnClimbable(Vector3D vector, GrimPlayer player) {
|
||||
if (player.isClimbing) {
|
||||
// Reset fall distance when climbing
|
||||
player.fallDistance = 0;
|
||||
|
||||
vector.setX(GrimMath.clamp(vector.getX(), -0.15F, 0.15F));
|
||||
vector.setZ(GrimMath.clamp(vector.getZ(), -0.15F, 0.15F));
|
||||
vector.setY(Math.max(vector.getY(), -0.15F));
|
||||
vector.setX(clamp(vector.getX(), -0.15F, 0.15F));
|
||||
vector.setZ(clamp(vector.getZ(), -0.15F, 0.15F));
|
||||
vector.setY(max(vector.getY(), -0.15F));
|
||||
|
||||
// Yes, this uses shifting not crouching
|
||||
if (vector.getY() < 0.0 && !(player.compensatedWorld.getStateTypeAt(player.lastX, player.lastY, player.lastZ) == StateTypes.SCAFFOLDING) && player.isSneaking && !player.isFlying) {
|
||||
if (vector.getY() < 0.0 && !(player.compensatedWorld.getStateTypeAt(player.lastX, player.lastY, player.lastZ) == SCAFFOLDING) && player.isSneaking && !player.isFlying) {
|
||||
vector.setY(0.0);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-7
@@ -9,11 +9,15 @@ import ac.grim.grimac.utils.nmsutil.Collisions;
|
||||
import ac.grim.grimac.utils.nmsutil.FluidFallingAdjustedMovement;
|
||||
import ac.grim.grimac.utils.nmsutil.ReachUtils;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static ac.grim.grimac.utils.data.VectorData.VectorType;
|
||||
import static ac.grim.grimac.utils.data.VectorData.VectorType.SwimmingSpace;
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
|
||||
public class PredictionEngineWater extends PredictionEngine {
|
||||
boolean isFalling;
|
||||
double playerGravity;
|
||||
@@ -21,9 +25,9 @@ public class PredictionEngineWater extends PredictionEngine {
|
||||
float swimmingFriction;
|
||||
double lastY;
|
||||
|
||||
public static void staticVectorEndOfTick(GrimPlayer player, Vector vector, float swimmingFriction, double playerGravity, boolean isFalling) {
|
||||
vector.multiply(new Vector(swimmingFriction, 0.8F, swimmingFriction));
|
||||
Vector fluidVector = FluidFallingAdjustedMovement.getFluidFallingAdjustedMovement(player, playerGravity, isFalling, vector);
|
||||
public static void staticVectorEndOfTick(GrimPlayer player, Vector3D vector, float swimmingFriction, double playerGravity, boolean isFalling) {
|
||||
vector.multiply(newVector3D(swimmingFriction, 0.8F, swimmingFriction));
|
||||
Vector3D fluidVector = FluidFallingAdjustedMovement.getFluidFallingAdjustedMovement(player, playerGravity, isFalling, vector);
|
||||
vector.setX(fluidVector.getX());
|
||||
vector.setY(fluidVector.getY());
|
||||
vector.setZ(fluidVector.getZ());
|
||||
@@ -47,7 +51,7 @@ public class PredictionEngineWater extends PredictionEngine {
|
||||
double scalar = lookYAmount < -0.2 ? 0.085 : 0.06;
|
||||
|
||||
// The player can always press jump and activate this
|
||||
swimmingVelocities.add(vector.returnNewModified(new Vector(vector.vector.getX(), vector.vector.getY() + ((lookYAmount - vector.vector.getY()) * scalar), vector.vector.getZ()), VectorData.VectorType.SwimmingSpace));
|
||||
swimmingVelocities.add(vector.returnNewModified(newVector3D(vector.vector.getX(), vector.vector.getY() + ((lookYAmount - vector.vector.getY()) * scalar), vector.vector.getZ()), SwimmingSpace));
|
||||
|
||||
// This scenario will occur if the player does not press jump and the other conditions are met
|
||||
// Theoretically we should check this BEFORE allowing no look, but there isn't a cheat that takes advantage of this yet
|
||||
@@ -80,11 +84,11 @@ public class PredictionEngineWater extends PredictionEngine {
|
||||
double extraVelFromVertTickSkipUpwards = GrimMath.clamp(player.actualMovement.getY(), vector.vector.clone().getY(), vector.vector.clone().getY() + 0.05f);
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().setY(extraVelFromVertTickSkipUpwards), vector, VectorData.VectorType.Jump));
|
||||
} else {
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().add(new Vector(0, 0.04f, 0)), vector, VectorData.VectorType.Jump));
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().add(newVector3D(0, 0.04f, 0)), vector, VectorData.VectorType.Jump));
|
||||
}
|
||||
|
||||
if (player.slightlyTouchingWater && player.lastOnGround && !player.onGround) {
|
||||
Vector withJump = vector.vector.clone();
|
||||
Vector3D withJump = vector.vector.clone();
|
||||
super.doJump(player, withJump);
|
||||
existingVelocities.add(new VectorData(withJump, vector, VectorData.VectorType.Jump));
|
||||
}
|
||||
|
||||
+12
-9
@@ -2,11 +2,15 @@ package ac.grim.grimac.predictionengine.predictions;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.data.VectorData;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
import static java.lang.Math.sqrt;
|
||||
|
||||
public class PredictionEngineWaterLegacy extends PredictionEngine {
|
||||
double playerGravity;
|
||||
float swimmingSpeed;
|
||||
@@ -23,11 +27,11 @@ public class PredictionEngineWaterLegacy extends PredictionEngine {
|
||||
|
||||
// This is just the vanilla equation for legacy water movement
|
||||
@Override
|
||||
public Vector getMovementResultFromInput(GrimPlayer player, Vector inputVector, float f, float f2) {
|
||||
public Vector3D getMovementResultFromInput(GrimPlayer player, Vector3D inputVector, float f, float f2) {
|
||||
float lengthSquared = (float) inputVector.lengthSquared();
|
||||
|
||||
if (lengthSquared >= 1.0E-4F) {
|
||||
lengthSquared = (float) Math.sqrt(lengthSquared);
|
||||
lengthSquared = (float) sqrt(lengthSquared);
|
||||
|
||||
if (lengthSquared < 1.0F) {
|
||||
lengthSquared = 1.0F;
|
||||
@@ -38,21 +42,20 @@ public class PredictionEngineWaterLegacy extends PredictionEngine {
|
||||
float sinResult = player.trigHandler.sin(player.xRot * 0.017453292F);
|
||||
float cosResult = player.trigHandler.cos(player.xRot * 0.017453292F);
|
||||
|
||||
return new Vector(inputVector.getX() * cosResult - inputVector.getZ() * sinResult,
|
||||
inputVector.getY(), inputVector.getZ() * cosResult + inputVector.getX() * sinResult);
|
||||
return newVector3D(inputVector.getX() * cosResult - inputVector.getZ() * sinResult, inputVector.getY(), inputVector.getZ() * cosResult + inputVector.getX() * sinResult);
|
||||
}
|
||||
|
||||
return new Vector();
|
||||
return newVector3D();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addJumpsToPossibilities(GrimPlayer player, Set<VectorData> existingVelocities) {
|
||||
for (VectorData vector : new HashSet<>(existingVelocities)) {
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().add(new Vector(0, 0.04f, 0)), vector, VectorData.VectorType.Jump));
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().add(newVector3D(0, 0.04f, 0)), vector, VectorData.VectorType.Jump));
|
||||
|
||||
if (player.skippedTickInActualMovement) {
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().add(new Vector(0, 0.02f, 0)), vector, VectorData.VectorType.Jump));
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().add(newVector3D(0, 0.02f, 0)), vector, VectorData.VectorType.Jump));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,7 +65,7 @@ public class PredictionEngineWaterLegacy extends PredictionEngine {
|
||||
super.endOfTick(player, playerGravity);
|
||||
|
||||
for (VectorData vector : player.getPossibleVelocitiesMinusKnockback()) {
|
||||
vector.vector.multiply(new Vector(swimmingFriction, 0.8F, swimmingFriction));
|
||||
vector.vector.multiply(newVector3D(swimmingFriction, 0.8F, swimmingFriction));
|
||||
|
||||
// Gravity
|
||||
vector.vector.setY(vector.vector.getY() - 0.02D);
|
||||
|
||||
+9
-6
@@ -13,12 +13,15 @@ import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
import static java.lang.Math.PI;
|
||||
|
||||
public class BoatPredictionEngine extends PredictionEngine {
|
||||
public BoatPredictionEngine(GrimPlayer player) {
|
||||
player.uncertaintyHandler.collidingEntities.add(0); // We don't do collisions like living entities
|
||||
@@ -147,7 +150,7 @@ public class BoatPredictionEngine extends PredictionEngine {
|
||||
// so if a player tries to move in both directions, a packet will
|
||||
// show that the player is staying, but the boat will move anyway
|
||||
if (player.vehicleData.vehicleForward == 0) {
|
||||
Vector vector = data.vector.clone();
|
||||
Vector3D vector = data.vector.clone();
|
||||
controlBoat(player, vector, true);
|
||||
vector.multiply(player.stuckSpeedMultiplier);
|
||||
vectors.add(data.returnNewModified(vector, VectorData.VectorType.InputResult));
|
||||
@@ -184,7 +187,7 @@ public class BoatPredictionEngine extends PredictionEngine {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void floatBoat(GrimPlayer player, Vector vector) {
|
||||
private void floatBoat(GrimPlayer player, Vector3D vector) {
|
||||
double d1 = player.hasGravity ? -0.04f : 0;
|
||||
double d2 = 0.0D;
|
||||
float invFriction = 0.05F;
|
||||
@@ -194,7 +197,7 @@ public class BoatPredictionEngine extends PredictionEngine {
|
||||
|
||||
player.lastY = getWaterLevelAbove(player) - 0.5625F + 0.101D;
|
||||
player.boundingBox = GetBoundingBox.getCollisionBoxForPlayer(player, player.lastX, player.lastY, player.lastZ);
|
||||
player.actualMovement = new Vector(player.x - player.lastX, player.y - player.lastY, player.z - player.lastZ);
|
||||
player.actualMovement = newVector3D(player.x - player.lastX, player.y - player.lastY, player.z - player.lastZ);
|
||||
vector.setY(0);
|
||||
|
||||
player.vehicleData.lastYd = 0.0D;
|
||||
@@ -260,7 +263,7 @@ public class BoatPredictionEngine extends PredictionEngine {
|
||||
return (float) (l + 1);
|
||||
}
|
||||
|
||||
private void controlBoat(GrimPlayer player, Vector vector, boolean intermediate) {
|
||||
private void controlBoat(GrimPlayer player, Vector3D vector, boolean intermediate) {
|
||||
float f = 0.0F;
|
||||
if (player.vehicleData.vehicleHorizontal != 0 && (!intermediate && player.vehicleData.vehicleForward == 0)) {
|
||||
f += 0.005F;
|
||||
@@ -275,6 +278,6 @@ public class BoatPredictionEngine extends PredictionEngine {
|
||||
f -= 0.005F;
|
||||
}
|
||||
|
||||
vector.add(new Vector(player.trigHandler.sin(-player.xRot * ((float) Math.PI / 180F)) * f, 0, (double) (player.trigHandler.cos(player.xRot * ((float) Math.PI / 180F)) * f)));
|
||||
vector.add(newVector3D(player.trigHandler.sin(-player.xRot * ((float) PI / 180F)) * f, 0, (double) (player.trigHandler.cos(player.xRot * ((float) PI / 180F)) * f)));
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -3,16 +3,16 @@ package ac.grim.grimac.predictionengine.predictions.rideable;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.predictionengine.predictions.PredictionEngineLava;
|
||||
import ac.grim.grimac.utils.data.VectorData;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class PredictionEngineRideableLava extends PredictionEngineLava {
|
||||
|
||||
Vector movementVector;
|
||||
Vector3D movementVector;
|
||||
|
||||
public PredictionEngineRideableLava(Vector movementVector) {
|
||||
public PredictionEngineRideableLava(Vector3D movementVector) {
|
||||
this.movementVector = movementVector;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -3,16 +3,16 @@ package ac.grim.grimac.predictionengine.predictions.rideable;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.predictionengine.predictions.PredictionEngineNormal;
|
||||
import ac.grim.grimac.utils.data.VectorData;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class PredictionEngineRideableNormal extends PredictionEngineNormal {
|
||||
|
||||
Vector movementVector;
|
||||
Vector3D movementVector;
|
||||
|
||||
public PredictionEngineRideableNormal(Vector movementVector) {
|
||||
public PredictionEngineRideableNormal(Vector3D movementVector) {
|
||||
this.movementVector = movementVector;
|
||||
}
|
||||
|
||||
|
||||
+8
-4
@@ -8,16 +8,20 @@ import ac.grim.grimac.utils.data.packetentity.PacketEntityHorse;
|
||||
import ac.grim.grimac.utils.nmsutil.JumpPower;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.potion.PotionTypes;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.Set;
|
||||
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
|
||||
public class PredictionEngineRideableUtils {
|
||||
public static Set<VectorData> handleJumps(GrimPlayer player, Set<VectorData> possibleVectors) {
|
||||
if (!(player.compensatedEntities.getSelf().getRiding() instanceof PacketEntityHorse horse)) return possibleVectors;
|
||||
if (!(player.compensatedEntities.getSelf().getRiding() instanceof PacketEntityHorse)) return possibleVectors;
|
||||
|
||||
PacketEntityHorse horse = (PacketEntityHorse) player.compensatedEntities.getSelf().getRiding();
|
||||
|
||||
// Setup player inputs
|
||||
float f = player.vehicleData.vehicleHorizontal * 0.5F;
|
||||
@@ -55,7 +59,7 @@ public class PredictionEngineRideableUtils {
|
||||
for (VectorData vectorData : possibleVectors) {
|
||||
vectorData.vector.setY(d1);
|
||||
if (f1 > 0.0F) {
|
||||
vectorData.vector.add(new Vector(-0.4F * f2 * player.vehicleData.horseJump, 0.0D, 0.4F * f3 * player.vehicleData.horseJump));
|
||||
vectorData.vector.add(newVector3D(-0.4F * f2 * player.vehicleData.horseJump, 0.0D, 0.4F * f3 * player.vehicleData.horseJump));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +75,7 @@ public class PredictionEngineRideableUtils {
|
||||
return possibleVectors;
|
||||
}
|
||||
|
||||
public static List<VectorData> applyInputsToVelocityPossibilities(Vector movementVector, GrimPlayer player, Set<VectorData> possibleVectors, float speed) {
|
||||
public static List<VectorData> applyInputsToVelocityPossibilities(Vector3D movementVector, GrimPlayer player, Set<VectorData> possibleVectors, float speed) {
|
||||
List<VectorData> returnVectors = new ArrayList<>();
|
||||
|
||||
for (VectorData possibleLastTickOutput : possibleVectors) {
|
||||
|
||||
+3
-3
@@ -3,16 +3,16 @@ package ac.grim.grimac.predictionengine.predictions.rideable;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.predictionengine.predictions.PredictionEngineWater;
|
||||
import ac.grim.grimac.utils.data.VectorData;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class PredictionEngineRideableWater extends PredictionEngineWater {
|
||||
|
||||
Vector movementVector;
|
||||
Vector3D movementVector;
|
||||
|
||||
public PredictionEngineRideableWater(Vector movementVector) {
|
||||
public PredictionEngineRideableWater(Vector3D movementVector) {
|
||||
this.movementVector = movementVector;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -3,15 +3,15 @@ package ac.grim.grimac.predictionengine.predictions.rideable;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.predictionengine.predictions.PredictionEngineWaterLegacy;
|
||||
import ac.grim.grimac.utils.data.VectorData;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class PredictionEngineRideableWaterLegacy extends PredictionEngineWaterLegacy {
|
||||
Vector movementVector;
|
||||
Vector3D movementVector;
|
||||
|
||||
public PredictionEngineRideableWaterLegacy(Vector movementVector) {
|
||||
public PredictionEngineRideableWaterLegacy(Vector3D movementVector) {
|
||||
this.movementVector = movementVector;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import ac.grim.grimac.utils.collisions.AxisUtil;
|
||||
import ac.grim.grimac.utils.collisions.CollisionData;
|
||||
import ac.grim.grimac.utils.collisions.blocks.DoorHandler;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.ComplexCollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.data.HitData;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
|
||||
@@ -16,6 +17,7 @@ import ac.grim.grimac.utils.nmsutil.BoundingBoxSize;
|
||||
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
|
||||
import ac.grim.grimac.utils.nmsutil.Materials;
|
||||
import ac.grim.grimac.utils.nmsutil.ReachUtils;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
@@ -34,19 +36,23 @@ import com.github.retrooper.packetevents.util.Vector3f;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static ac.grim.grimac.utils.nmsutil.ReachUtils.calculateIntercept;
|
||||
import static ac.grim.grimac.utils.nmsutil.ReachUtils.getLook;
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
import static com.github.retrooper.packetevents.protocol.attribute.Attributes.PLAYER_BLOCK_INTERACTION_RANGE;
|
||||
|
||||
public class BlockPlace {
|
||||
protected static final BlockFace[] UPDATE_SHAPE_ORDER = new BlockFace[]{BlockFace.WEST, BlockFace.EAST, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.DOWN, BlockFace.UP};
|
||||
private static final BlockFace[] BY_2D = new BlockFace[]{BlockFace.SOUTH, BlockFace.WEST, BlockFace.NORTH, BlockFace.EAST};
|
||||
static final BlockFace[] BY_3D = new BlockFace[]{BlockFace.DOWN, BlockFace.UP, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.WEST, BlockFace.EAST};
|
||||
@Setter
|
||||
Vector3i blockPosition;
|
||||
public Vector3i blockPosition;
|
||||
@Getter
|
||||
InteractionHand hand;
|
||||
@Getter
|
||||
@@ -72,6 +78,9 @@ public class BlockPlace {
|
||||
|
||||
@Getter private final boolean block;
|
||||
|
||||
// Allocated once instead of in functions to reduce new[] calls that need to be made. Since per-instance BlockPlace is always dealt with on the same thread we can use 1 buffer array
|
||||
private final SimpleCollisionBox[] collisions = new SimpleCollisionBox[ComplexCollisionBox.DEFAULT_MAX_COLLISION_BOX_SIZE];
|
||||
|
||||
public BlockPlace(GrimPlayer player, InteractionHand hand, Vector3i blockPosition, int faceId, BlockFace face, ItemStack itemStack, HitData hitData) {
|
||||
this.player = player;
|
||||
this.hand = hand;
|
||||
@@ -200,12 +209,12 @@ public class BlockPlace {
|
||||
if (BlockTags.LEAVES.contains(data.getType())) return false;
|
||||
if (BlockTags.FENCE_GATES.contains(data.getType())) return false;
|
||||
|
||||
List<SimpleCollisionBox> collisions = new ArrayList<>();
|
||||
box.downCast(collisions);
|
||||
int size = box.downCast(collisions);
|
||||
|
||||
AxisSelect axis = AxisUtil.getAxis(facing.getOppositeFace());
|
||||
|
||||
for (SimpleCollisionBox simpleBox : collisions) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
SimpleCollisionBox simpleBox = collisions[i];
|
||||
simpleBox = axis.modify(simpleBox);
|
||||
if (simpleBox.minX <= 7 / 16d && simpleBox.maxX >= 7 / 16d
|
||||
&& simpleBox.minY <= 0 && simpleBox.maxY >= 10 / 16d
|
||||
@@ -225,12 +234,12 @@ public class BlockPlace {
|
||||
if (isFullFace(facing)) return true;
|
||||
if (BlockTags.LEAVES.contains(data.getType())) return false;
|
||||
|
||||
List<SimpleCollisionBox> collisions = new ArrayList<>();
|
||||
box.downCast(collisions);
|
||||
int size = box.downCast(collisions);
|
||||
|
||||
AxisSelect axis = AxisUtil.getAxis(facing.getOppositeFace());
|
||||
|
||||
for (SimpleCollisionBox simpleBox : collisions) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
SimpleCollisionBox simpleBox = collisions[i];
|
||||
simpleBox = axis.modify(simpleBox);
|
||||
if (simpleBox.minX <= 2 / 16d && simpleBox.maxX >= 14 / 16d
|
||||
&& simpleBox.minY <= 0 && simpleBox.maxY >= 1
|
||||
@@ -289,10 +298,10 @@ public class BlockPlace {
|
||||
}
|
||||
}
|
||||
|
||||
List<SimpleCollisionBox> collisions = new ArrayList<>();
|
||||
box.downCast(collisions);
|
||||
int size = box.downCast(collisions);
|
||||
|
||||
for (SimpleCollisionBox simpleBox : collisions) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
SimpleCollisionBox simpleBox = collisions[i];
|
||||
if (axis.modify(simpleBox).isFullBlockNoCache()) return true;
|
||||
}
|
||||
|
||||
@@ -318,12 +327,12 @@ public class BlockPlace {
|
||||
if (isFullFace(facing)) return true;
|
||||
if (BlockTags.LEAVES.contains(data.getType())) return false;
|
||||
|
||||
List<SimpleCollisionBox> collisions = new ArrayList<>();
|
||||
box.downCast(collisions);
|
||||
int size = box.downCast(collisions);
|
||||
|
||||
AxisSelect axis = AxisUtil.getAxis(facing.getOppositeFace());
|
||||
|
||||
for (SimpleCollisionBox simpleBox : collisions) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
SimpleCollisionBox simpleBox = collisions[i];
|
||||
simpleBox = axis.modify(simpleBox);
|
||||
// If all sides to the box have width, there is collision.
|
||||
switch (facing) {
|
||||
@@ -537,14 +546,21 @@ public class BlockPlace {
|
||||
}
|
||||
|
||||
public Vector3i getNormalBlockFace() {
|
||||
return switch (face) {
|
||||
case DOWN -> new Vector3i(0, -1, 0);
|
||||
case SOUTH -> new Vector3i(0, 0, 1);
|
||||
case NORTH -> new Vector3i(0, 0, -1);
|
||||
case WEST -> new Vector3i(-1, 0, 0);
|
||||
case EAST -> new Vector3i(1, 0, 0);
|
||||
default -> new Vector3i(0, 1, 0);
|
||||
};
|
||||
switch (face) {
|
||||
default:
|
||||
case UP:
|
||||
return new Vector3i(0, 1, 0);
|
||||
case DOWN:
|
||||
return new Vector3i(0, -1, 0);
|
||||
case SOUTH:
|
||||
return new Vector3i(0, 0, 1);
|
||||
case NORTH:
|
||||
return new Vector3i(0, 0, -1);
|
||||
case WEST:
|
||||
return new Vector3i(-1, 0, 0);
|
||||
case EAST:
|
||||
return new Vector3i(1, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void set(StateType material) {
|
||||
@@ -658,18 +674,18 @@ public class BlockPlace {
|
||||
// You also have the desync caused by eye height as apparently tracking the player's ticks wasn't important to you
|
||||
// No mojang, you really do need to track client ticks to get their accurate eye height.
|
||||
// another damn desync added... maybe next decade it will get fixed and double the amount of issues.
|
||||
public Vector getClickedLocation() {
|
||||
public Vector3D getClickedLocation() {
|
||||
SimpleCollisionBox box = new SimpleCollisionBox(getPlacedAgainstBlockLocation());
|
||||
Vector look = ReachUtils.getLook(player, player.xRot, player.yRot);
|
||||
Vector3D look = getLook(player, player.xRot, player.yRot);
|
||||
|
||||
final double distance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_BLOCK_INTERACTION_RANGE) + 3;
|
||||
Vector eyePos = new Vector(player.x, player.y + player.getEyeHeight(), player.z);
|
||||
Vector endReachPos = eyePos.clone().add(new Vector(look.getX() * distance, look.getY() * distance, look.getZ() * distance));
|
||||
Vector intercept = ReachUtils.calculateIntercept(box, eyePos, endReachPos).first();
|
||||
final double distance = player.compensatedEntities.getSelf().getAttributeValue(PLAYER_BLOCK_INTERACTION_RANGE) + 3;
|
||||
Vector3D eyePos = newVector3D(player.x, player.y + player.getEyeHeight(), player.z);
|
||||
Vector3D endReachPos = eyePos.clone().add(newVector3D(look.getX() * distance, look.getY() * distance, look.getZ() * distance));
|
||||
Vector3D intercept = calculateIntercept(box, eyePos, endReachPos).getFirst();
|
||||
|
||||
// Bring this back to relative to the block
|
||||
// The player didn't even click the block... (we should force resync BEFORE we get here!)
|
||||
if (intercept == null) return new Vector();
|
||||
if (intercept == null) return newVector3D();
|
||||
|
||||
intercept.setX(intercept.getX() - box.minX);
|
||||
intercept.setY(intercept.getY() - box.minY);
|
||||
|
||||
@@ -21,11 +21,13 @@ import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateValue;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
|
||||
|
||||
public enum BlockPlaceResult {
|
||||
|
||||
// If the block only has directional data
|
||||
@@ -80,7 +82,7 @@ public enum BlockPlaceResult {
|
||||
}, ItemTypes.SNOW),
|
||||
|
||||
SLAB((player, place) -> {
|
||||
Vector clickedPos = place.getClickedLocation();
|
||||
Vector3D clickedPos = place.getClickedLocation();
|
||||
WrappedBlockState slabData = place.getMaterial().createBlockState(CompensatedWorld.blockVersion);
|
||||
WrappedBlockState existing = place.getExistingBlockData();
|
||||
|
||||
@@ -909,7 +911,7 @@ public enum BlockPlaceResult {
|
||||
WrappedBlockState ccwState = place.getDirectionalState(ccw);
|
||||
CollisionBox ccwBox = CollisionData.getData(ccwState.getType()).getMovementCollisionBox(player, player.getClientVersion(), ccwState);
|
||||
|
||||
Vector aboveCCWPos = place.getClickedLocation().add(new Vector(ccw.getModX(), ccw.getModY(), ccw.getModZ())).add(new Vector(0, 1, 0));
|
||||
Vector3D aboveCCWPos = place.getClickedLocation().add(newVector3D(ccw.getModX(), ccw.getModY(), ccw.getModZ())).add(newVector3D(0, 1, 0));
|
||||
WrappedBlockState aboveCCWState = player.compensatedWorld.getWrappedBlockStateAt(aboveCCWPos);
|
||||
CollisionBox aboveCCWBox = CollisionData.getData(aboveCCWState.getType()).getMovementCollisionBox(player, player.getClientVersion(), aboveCCWState);
|
||||
|
||||
@@ -917,7 +919,7 @@ public enum BlockPlaceResult {
|
||||
WrappedBlockState cwState = place.getDirectionalState(cw);
|
||||
CollisionBox cwBox = CollisionData.getData(cwState.getType()).getMovementCollisionBox(player, player.getClientVersion(), cwState);
|
||||
|
||||
Vector aboveCWPos = place.getClickedLocation().add(new Vector(cw.getModX(), cw.getModY(), cw.getModZ())).add(new Vector(0, 1, 0));
|
||||
Vector3D aboveCWPos = place.getClickedLocation().add(newVector3D(cw.getModX(), cw.getModY(), cw.getModZ())).add(newVector3D(0, 1, 0));
|
||||
WrappedBlockState aboveCWState = player.compensatedWorld.getWrappedBlockStateAt(aboveCWPos);
|
||||
CollisionBox aboveCWBox = CollisionData.getData(aboveCWState.getType()).getMovementCollisionBox(player, player.getClientVersion(), aboveCWState);
|
||||
|
||||
@@ -934,7 +936,7 @@ public enum BlockPlaceResult {
|
||||
if ((!isCWLower || isCCWLower) && i >= 0) {
|
||||
int j = playerFacing.getModX();
|
||||
int k = playerFacing.getModZ();
|
||||
Vector vec3 = place.getClickedLocation();
|
||||
Vector3D vec3 = place.getClickedLocation();
|
||||
double d0 = vec3.getX();
|
||||
double d1 = vec3.getY();
|
||||
hinge = (j >= 0 || d1 >= 0.5D) && (j <= 0 || d1 <= 0.5D) && (k >= 0 || d0 <= 0.5D) && (k <= 0 || d0 >= 0.5D) ? Hinge.LEFT : Hinge.RIGHT;
|
||||
|
||||
@@ -107,11 +107,17 @@ public class ConsumesBlockPlace {
|
||||
Attachment attachment = bell.getAttachment();
|
||||
BlockFace dir2 = BlockFace.valueOf(direction.name());
|
||||
|
||||
return switch (attachment) {
|
||||
case FLOOR -> AxisUtil.isSameAxis(dir, dir2);
|
||||
case SINGLE_WALL, DOUBLE_WALL -> !AxisUtil.isSameAxis(dir, dir2);
|
||||
case CEILING -> true;
|
||||
};
|
||||
switch (attachment) {
|
||||
case FLOOR:
|
||||
return AxisUtil.isSameAxis(dir, dir2);
|
||||
case SINGLE_WALL:
|
||||
case DOUBLE_WALL:
|
||||
return !AxisUtil.isSameAxis(dir, dir2);
|
||||
case CEILING:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package ac.grim.grimac.utils.blockstate.helper;
|
||||
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import org.bukkit.util.Vector;
|
||||
import ac.grim.grimac.utils.vector.Vector3D;
|
||||
|
||||
public class BlockFaceHelper {
|
||||
public static boolean isFaceVertical(BlockFace face) {
|
||||
@@ -18,45 +19,67 @@ public class BlockFaceHelper {
|
||||
*/
|
||||
@Deprecated
|
||||
public static BlockFace fromBukkitFace(org.bukkit.block.BlockFace face) {
|
||||
return switch (face) {
|
||||
case NORTH -> BlockFace.NORTH;
|
||||
case SOUTH -> BlockFace.SOUTH;
|
||||
case WEST -> BlockFace.WEST;
|
||||
case EAST -> BlockFace.EAST;
|
||||
case UP -> BlockFace.UP;
|
||||
case DOWN -> BlockFace.DOWN;
|
||||
default -> BlockFace.OTHER;
|
||||
};
|
||||
switch (face) {
|
||||
case NORTH:
|
||||
return BlockFace.NORTH;
|
||||
case SOUTH:
|
||||
return BlockFace.SOUTH;
|
||||
case WEST:
|
||||
return BlockFace.WEST;
|
||||
case EAST:
|
||||
return BlockFace.EAST;
|
||||
case UP:
|
||||
return BlockFace.UP;
|
||||
case DOWN:
|
||||
return BlockFace.DOWN;
|
||||
default:
|
||||
return BlockFace.OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
public static BlockFace getClockWise(BlockFace face) {
|
||||
return switch (face) {
|
||||
case NORTH -> BlockFace.EAST;
|
||||
case SOUTH -> BlockFace.WEST;
|
||||
case WEST -> BlockFace.NORTH;
|
||||
default -> BlockFace.SOUTH;
|
||||
};
|
||||
switch (face) {
|
||||
case NORTH:
|
||||
return BlockFace.EAST;
|
||||
case SOUTH:
|
||||
return BlockFace.WEST;
|
||||
case WEST:
|
||||
return BlockFace.NORTH;
|
||||
case EAST:
|
||||
default:
|
||||
return BlockFace.SOUTH;
|
||||
}
|
||||
}
|
||||
|
||||
public static BlockFace getPEClockWise(BlockFace face) {
|
||||
return switch (face) {
|
||||
case NORTH -> BlockFace.EAST;
|
||||
case SOUTH -> BlockFace.WEST;
|
||||
case WEST -> BlockFace.NORTH;
|
||||
default -> BlockFace.SOUTH;
|
||||
};
|
||||
switch (face) {
|
||||
case NORTH:
|
||||
return BlockFace.EAST;
|
||||
case SOUTH:
|
||||
return BlockFace.WEST;
|
||||
case WEST:
|
||||
return BlockFace.NORTH;
|
||||
case EAST:
|
||||
default:
|
||||
return BlockFace.SOUTH;
|
||||
}
|
||||
}
|
||||
|
||||
public static BlockFace getCounterClockwise(BlockFace face) {
|
||||
return switch (face) {
|
||||
case NORTH -> BlockFace.WEST;
|
||||
case SOUTH -> BlockFace.EAST;
|
||||
case WEST -> BlockFace.SOUTH;
|
||||
default -> BlockFace.NORTH;
|
||||
};
|
||||
switch (face) {
|
||||
case NORTH:
|
||||
return BlockFace.WEST;
|
||||
case SOUTH:
|
||||
return BlockFace.EAST;
|
||||
case WEST:
|
||||
return BlockFace.SOUTH;
|
||||
case EAST:
|
||||
default:
|
||||
return BlockFace.NORTH;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector offset(Vector toOffset, BlockFace face) {
|
||||
public Vector3D offset(Vector3D toOffset, BlockFace face) {
|
||||
toOffset.setX(toOffset.getX() + face.getModX());
|
||||
toOffset.setY(toOffset.getY() + face.getModY());
|
||||
toOffset.setZ(toOffset.getZ() + face.getModZ());
|
||||
|
||||
@@ -54,22 +54,36 @@ public enum AxisUtil {
|
||||
}
|
||||
|
||||
public static AxisSelect getAxis(BlockFace face) {
|
||||
return switch (face) {
|
||||
case EAST -> EAST.select;
|
||||
case WEST -> WEST.select;
|
||||
case NORTH -> NORTH.select;
|
||||
case SOUTH -> SOUTH.select;
|
||||
case UP -> UP.select;
|
||||
default -> DOWN.select;
|
||||
};
|
||||
switch (face) {
|
||||
case EAST:
|
||||
return EAST.select;
|
||||
case WEST:
|
||||
return WEST.select;
|
||||
case NORTH:
|
||||
return NORTH.select;
|
||||
case SOUTH:
|
||||
return SOUTH.select;
|
||||
case UP:
|
||||
return UP.select;
|
||||
case DOWN:
|
||||
default:
|
||||
return DOWN.select;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isSameAxis(BlockFace one, BlockFace two) {
|
||||
return switch (one) {
|
||||
case WEST, EAST -> two == BlockFace.WEST || two == BlockFace.EAST;
|
||||
case NORTH, SOUTH -> two == BlockFace.NORTH || two == BlockFace.SOUTH;
|
||||
case UP, DOWN -> two == BlockFace.UP || two == BlockFace.DOWN;
|
||||
default -> false;
|
||||
};
|
||||
switch (one) {
|
||||
case WEST:
|
||||
case EAST:
|
||||
return two == BlockFace.WEST || two == BlockFace.EAST;
|
||||
case NORTH:
|
||||
case SOUTH:
|
||||
return two == BlockFace.NORTH || two == BlockFace.SOUTH;
|
||||
case UP:
|
||||
case DOWN:
|
||||
return two == BlockFace.UP || two == BlockFace.DOWN;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ import java.util.stream.Stream;
|
||||
// This is actually mean to be put into PacketEvents, but I don't like proprietary plugins stealing my code...
|
||||
public enum CollisionData {
|
||||
VINE((player, version, block, x, y, z) -> {
|
||||
ComplexCollisionBox boxes = new ComplexCollisionBox();
|
||||
ComplexCollisionBox boxes = new ComplexCollisionBox(5);
|
||||
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13) && block.isUp())
|
||||
boxes.add(new HexCollisionBox(0.0D, 15.0D, 0.0D, 16.0D, 16.0D, 16.0D));
|
||||
@@ -73,11 +73,19 @@ public enum CollisionData {
|
||||
|
||||
BREWING_STAND((player, version, block, x, y, z) -> {
|
||||
int base = 0;
|
||||
// maxIndex is 3 instead of 2 for legacy clients because for 1.8 players there is a very rare bug
|
||||
// That we handle later in the code requiring us to add a box https://bugs.mojang.com/browse/MC-85109 For 1.8 PLAYERS
|
||||
// 1.8 Brewing Stand hitbox is a fullblock until it is hit sometimes, can be caused be restarting client and joining server
|
||||
int maxIndex = 3;
|
||||
|
||||
if (version.isNewerThanOrEquals(ClientVersion.V_1_13))
|
||||
// Yes I know we only need maxIndex = 3 for 1.8 specifically
|
||||
// No I'm not adding a special clause for which would require another if check, I'll take compute > memory any day
|
||||
if (version.isNewerThanOrEquals(ClientVersion.V_1_13)) {
|
||||
maxIndex = 2;
|
||||
base = 1;
|
||||
}
|
||||
|
||||
return new ComplexCollisionBox(
|
||||
return new ComplexCollisionBox(maxIndex,
|
||||
new HexCollisionBox(base, 0, base, 16 - base, 2, 16 - base),
|
||||
new SimpleCollisionBox(0.4375, 0.0, 0.4375, 0.5625, 0.875, 0.5625, false));
|
||||
|
||||
@@ -100,7 +108,7 @@ public enum CollisionData {
|
||||
if (version.isOlderThanOrEquals(ClientVersion.V_1_12_2))
|
||||
height = 0.3125;
|
||||
|
||||
return new ComplexCollisionBox(
|
||||
return new ComplexCollisionBox(5,
|
||||
new SimpleCollisionBox(0, 0, 0, 1, height, 1, false),
|
||||
new SimpleCollisionBox(0, height, 0, 0.125, 1, 1, false),
|
||||
new SimpleCollisionBox(1 - 0.125, height, 0, 1, 1, 1, false),
|
||||
@@ -119,7 +127,7 @@ public enum CollisionData {
|
||||
// The base is 0.75×0.75, and its floor is 0.25b high.
|
||||
// The top is 1×0.625, and its ceiling is 0.375b low.
|
||||
if (version.isNewerThanOrEquals(ClientVersion.V_1_13)) {
|
||||
ComplexCollisionBox complexAnvil = new ComplexCollisionBox();
|
||||
ComplexCollisionBox complexAnvil = new ComplexCollisionBox(4);
|
||||
// Base of the anvil
|
||||
complexAnvil.add(new HexCollisionBox(2, 0, 2, 14, 4, 14));
|
||||
if (face == BlockFace.NORTH || face == BlockFace.SOUTH) {
|
||||
@@ -166,29 +174,37 @@ public enum CollisionData {
|
||||
|
||||
// Overwrite previous SKULL enum for legacy, where head and wall skull isn't separate
|
||||
WALL_SKULL((player, version, data, x, y, z) -> {
|
||||
return switch (data.getFacing()) {
|
||||
case SOUTH -> new SimpleCollisionBox(0.25F, 0.25F, 0.0F, 0.75F, 0.75F, 0.5F, false);
|
||||
case WEST -> new SimpleCollisionBox(0.5F, 0.25F, 0.25F, 1.0F, 0.75F, 0.75F, false);
|
||||
case EAST -> new SimpleCollisionBox(0.0F, 0.25F, 0.25F, 0.5F, 0.75F, 0.75F, false);
|
||||
default -> new SimpleCollisionBox(0.25F, 0.25F, 0.5F, 0.75F, 0.75F, 1.0F, false);
|
||||
};
|
||||
switch (data.getFacing()) {
|
||||
case SOUTH:
|
||||
return new SimpleCollisionBox(0.25F, 0.25F, 0.0F, 0.75F, 0.75F, 0.5F, false);
|
||||
case WEST:
|
||||
return new SimpleCollisionBox(0.5F, 0.25F, 0.25F, 1.0F, 0.75F, 0.75F, false);
|
||||
case EAST:
|
||||
return new SimpleCollisionBox(0.0F, 0.25F, 0.25F, 0.5F, 0.75F, 0.75F, false);
|
||||
default:
|
||||
return new SimpleCollisionBox(0.25F, 0.25F, 0.5F, 0.75F, 0.75F, 1.0F, false);
|
||||
}
|
||||
}, StateTypes.CREEPER_WALL_HEAD, StateTypes.DRAGON_WALL_HEAD, StateTypes.PLAYER_WALL_HEAD, StateTypes.ZOMBIE_WALL_HEAD,
|
||||
StateTypes.SKELETON_WALL_SKULL, StateTypes.WITHER_SKELETON_WALL_SKULL),
|
||||
|
||||
PIGLIN_WALL_HEAD((player, version, data, x, y, z) -> {
|
||||
return switch (data.getFacing()) {
|
||||
case SOUTH -> new HexCollisionBox(3.0D, 4.0D, 0.0D, 13.0D, 12.0D, 8.0D);
|
||||
case EAST -> new HexCollisionBox(0.0D, 4.0D, 3.0D, 8.0D, 12.0D, 13.0D);
|
||||
case WEST -> new HexCollisionBox(8.0D, 4.0D, 3.0D, 16.0D, 12.0D, 13.0D);
|
||||
default -> new HexCollisionBox(3.0D, 4.0D, 8.0D, 13.0D, 12.0D, 16.0D);
|
||||
};
|
||||
switch (data.getFacing()) {
|
||||
case SOUTH:
|
||||
return new HexCollisionBox(3.0D, 4.0D, 0.0D, 13.0D, 12.0D, 8.0D);
|
||||
case EAST:
|
||||
return new HexCollisionBox(0.0D, 4.0D, 3.0D, 8.0D, 12.0D, 13.0D);
|
||||
case WEST:
|
||||
return new HexCollisionBox(8.0D, 4.0D, 3.0D, 16.0D, 12.0D, 13.0D);
|
||||
default:
|
||||
return new HexCollisionBox(3.0D, 4.0D, 8.0D, 13.0D, 12.0D, 16.0D);
|
||||
}
|
||||
}, StateTypes.PIGLIN_WALL_HEAD),
|
||||
|
||||
DOOR(new DoorHandler(), BlockTags.DOORS.getStates().toArray(new StateType[0])),
|
||||
|
||||
HOPPER((player, version, data, x, y, z) -> {
|
||||
if (version.isNewerThanOrEquals(ClientVersion.V_1_13)) {
|
||||
ComplexCollisionBox hopperBox = new ComplexCollisionBox();
|
||||
ComplexCollisionBox hopperBox = new ComplexCollisionBox(7);
|
||||
|
||||
switch (data.getFacing()) {
|
||||
case DOWN:
|
||||
@@ -219,7 +235,7 @@ public enum CollisionData {
|
||||
} else {
|
||||
double height = 0.125 * 5;
|
||||
|
||||
return new ComplexCollisionBox(
|
||||
return new ComplexCollisionBox(5,
|
||||
new SimpleCollisionBox(0, 0, 0, 1, height, 1, false),
|
||||
new SimpleCollisionBox(0, height, 0, 0.125, 1, 1, false),
|
||||
new SimpleCollisionBox(1 - 0.125, height, 0, 1, 1, 1, false),
|
||||
@@ -237,9 +253,7 @@ public enum CollisionData {
|
||||
return new SimpleCollisionBox(eatenPosition, 0, 0.0625, 1 - 0.0625, height, 1 - 0.0625, false);
|
||||
}, StateTypes.CAKE),
|
||||
|
||||
COCOA_BEANS((player, version, data, x, y, z) -> {
|
||||
return getCocoa(version, data.getAge(), data.getFacing());
|
||||
}, StateTypes.COCOA),
|
||||
COCOA_BEANS((player, version, data, x, y, z) -> getCocoa(version, data.getAge(), data.getFacing()), StateTypes.COCOA),
|
||||
|
||||
STONE_CUTTER((player, version, data, x, y, z) -> {
|
||||
if (version.isOlderThanOrEquals(ClientVersion.V_1_13_2))
|
||||
@@ -290,7 +304,7 @@ public enum CollisionData {
|
||||
|
||||
}
|
||||
|
||||
ComplexCollisionBox complex = new ComplexCollisionBox(
|
||||
ComplexCollisionBox complex = new ComplexCollisionBox(3,
|
||||
new HexCollisionBox(5.0D, 6.0D, 5.0D, 11.0D, 13.0D, 11.0D),
|
||||
new HexCollisionBox(4.0D, 4.0D, 4.0D, 12.0D, 6.0D, 12.0D));
|
||||
|
||||
@@ -324,7 +338,8 @@ public enum CollisionData {
|
||||
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
|
||||
if (player.lastY > y + 1 - 1e-5 && !player.isSneaking) {
|
||||
return new ComplexCollisionBox(new HexCollisionBox(0.0D, 14.0D, 0.0D, 16.0D, 16.0D, 16.0D),
|
||||
return new ComplexCollisionBox(5,
|
||||
new HexCollisionBox(0.0D, 14.0D, 0.0D, 16.0D, 16.0D, 16.0D),
|
||||
new HexCollisionBox(0.0D, 0.0D, 0.0D, 2.0D, 16.0D, 2.0D),
|
||||
new HexCollisionBox(14.0D, 0.0D, 0.0D, 16.0D, 16.0D, 2.0D),
|
||||
new HexCollisionBox(0.0D, 0.0D, 14.0D, 2.0D, 16.0D, 16.0),
|
||||
@@ -341,12 +356,16 @@ public enum CollisionData {
|
||||
if (version.isOlderThanOrEquals(ClientVersion.V_1_8))
|
||||
width = 2;
|
||||
|
||||
return switch (data.getFacing()) {
|
||||
case NORTH -> new HexCollisionBox(0.0D, 0.0D, 16.0D - width, 16.0D, 16.0D, 16.0D);
|
||||
case SOUTH -> new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, width);
|
||||
case WEST -> new HexCollisionBox(16.0D - width, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D);
|
||||
default -> new HexCollisionBox(0.0D, 0.0D, 0.0D, width, 16.0D, 16.0D);
|
||||
};
|
||||
switch (data.getFacing()) {
|
||||
case NORTH:
|
||||
return new HexCollisionBox(0.0D, 0.0D, 16.0D - width, 16.0D, 16.0D, 16.0D);
|
||||
case SOUTH:
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, width);
|
||||
case WEST:
|
||||
return new HexCollisionBox(16.0D - width, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D);
|
||||
default:
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, width, 16.0D, 16.0D);
|
||||
}
|
||||
}, StateTypes.LADDER),
|
||||
|
||||
CAMPFIRE((player, version, data, x, y, z) -> {
|
||||
@@ -368,11 +387,13 @@ public enum CollisionData {
|
||||
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
|
||||
if (data.isHanging()) {
|
||||
return new ComplexCollisionBox(new HexCollisionBox(5.0D, 1.0D, 5.0D, 11.0D, 8.0D, 11.0D),
|
||||
return new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(5.0D, 1.0D, 5.0D, 11.0D, 8.0D, 11.0D),
|
||||
new HexCollisionBox(6.0D, 8.0D, 6.0D, 10.0D, 10.0D, 10.0D));
|
||||
}
|
||||
|
||||
return new ComplexCollisionBox(new HexCollisionBox(5.0D, 0.0D, 5.0D, 11.0D, 7.0D, 11.0D),
|
||||
return new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(5.0D, 0.0D, 5.0D, 11.0D, 7.0D, 11.0D),
|
||||
new HexCollisionBox(6.0D, 7.0D, 6.0D, 10.0D, 9.0D, 10.0D));
|
||||
|
||||
}, StateTypes.LANTERN, StateTypes.SOUL_LANTERN),
|
||||
@@ -382,7 +403,7 @@ public enum CollisionData {
|
||||
if (version.isOlderThanOrEquals(ClientVersion.V_1_13_2))
|
||||
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
|
||||
return new ComplexCollisionBox(
|
||||
return new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D), // base
|
||||
new HexCollisionBox(4.0D, 2.0D, 4.0D, 12.0D, 14.0D, 12.0D)); // post
|
||||
}, StateTypes.LECTERN),
|
||||
@@ -411,7 +432,7 @@ public enum CollisionData {
|
||||
}
|
||||
|
||||
if (version.isOlderThanOrEquals(ClientVersion.V_1_13_2)) {
|
||||
ComplexCollisionBox complexAnvil = new ComplexCollisionBox();
|
||||
ComplexCollisionBox complexAnvil = new ComplexCollisionBox(4);
|
||||
// Base of the anvil
|
||||
complexAnvil.add(new HexCollisionBox(2, 0, 2, 14, 4, 14));
|
||||
|
||||
@@ -431,13 +452,15 @@ public enum CollisionData {
|
||||
Face attachment = data.getFace();
|
||||
if (attachment == Face.FLOOR) {
|
||||
if (facing == BlockFace.NORTH || facing == BlockFace.SOUTH) {
|
||||
return new ComplexCollisionBox(new HexCollisionBox(2.0D, 0.0D, 6.0D, 4.0D, 7.0D, 10.0D),
|
||||
return new ComplexCollisionBox(5,
|
||||
new HexCollisionBox(2.0D, 0.0D, 6.0D, 4.0D, 7.0D, 10.0D),
|
||||
new HexCollisionBox(12.0D, 0.0D, 6.0D, 14.0D, 7.0D, 10.0D),
|
||||
new HexCollisionBox(2.0D, 7.0D, 5.0D, 4.0D, 13.0D, 11.0D),
|
||||
new HexCollisionBox(12.0D, 7.0D, 5.0D, 14.0D, 13.0D, 11.0D),
|
||||
new HexCollisionBox(4.0D, 4.0D, 2.0D, 12.0D, 16.0D, 14.0D));
|
||||
} else {
|
||||
return new ComplexCollisionBox(new HexCollisionBox(6.0D, 0.0D, 2.0D, 10.0D, 7.0D, 4.0D),
|
||||
return new ComplexCollisionBox(5,
|
||||
new HexCollisionBox(6.0D, 0.0D, 2.0D, 10.0D, 7.0D, 4.0D),
|
||||
new HexCollisionBox(6.0D, 0.0D, 12.0D, 10.0D, 7.0D, 14.0D),
|
||||
new HexCollisionBox(5.0D, 7.0D, 2.0D, 11.0D, 13.0D, 4.0D),
|
||||
new HexCollisionBox(5.0D, 7.0D, 12.0D, 11.0D, 13.0D, 14.0D),
|
||||
@@ -446,25 +469,29 @@ public enum CollisionData {
|
||||
} else if (attachment == Face.WALL) {
|
||||
switch (facing) {
|
||||
case NORTH:
|
||||
return new ComplexCollisionBox(new HexCollisionBox(2.0D, 6.0D, 7.0D, 4.0D, 10.0D, 16.0D),
|
||||
return new ComplexCollisionBox(5,
|
||||
new HexCollisionBox(2.0D, 6.0D, 7.0D, 4.0D, 10.0D, 16.0D),
|
||||
new HexCollisionBox(12.0D, 6.0D, 7.0D, 14.0D, 10.0D, 16.0D),
|
||||
new HexCollisionBox(2.0D, 5.0D, 3.0D, 4.0D, 11.0D, 9.0D),
|
||||
new HexCollisionBox(12.0D, 5.0D, 3.0D, 14.0D, 11.0D, 9.0D),
|
||||
new HexCollisionBox(4.0D, 2.0D, 0.0D, 12.0D, 14.0D, 12.0D));
|
||||
case WEST:
|
||||
return new ComplexCollisionBox(new HexCollisionBox(7.0D, 6.0D, 2.0D, 16.0D, 10.0D, 4.0D),
|
||||
return new ComplexCollisionBox(5,
|
||||
new HexCollisionBox(7.0D, 6.0D, 2.0D, 16.0D, 10.0D, 4.0D),
|
||||
new HexCollisionBox(7.0D, 6.0D, 12.0D, 16.0D, 10.0D, 14.0D),
|
||||
new HexCollisionBox(3.0D, 5.0D, 2.0D, 9.0D, 11.0D, 4.0D),
|
||||
new HexCollisionBox(3.0D, 5.0D, 12.0D, 9.0D, 11.0D, 14.0D),
|
||||
new HexCollisionBox(0.0D, 2.0D, 4.0D, 12.0D, 14.0D, 12.0D));
|
||||
case SOUTH:
|
||||
return new ComplexCollisionBox(new HexCollisionBox(2.0D, 6.0D, 0.0D, 4.0D, 10.0D, 7.0D),
|
||||
return new ComplexCollisionBox(5,
|
||||
new HexCollisionBox(2.0D, 6.0D, 0.0D, 4.0D, 10.0D, 7.0D),
|
||||
new HexCollisionBox(12.0D, 6.0D, 0.0D, 14.0D, 10.0D, 7.0D),
|
||||
new HexCollisionBox(2.0D, 5.0D, 7.0D, 4.0D, 11.0D, 13.0D),
|
||||
new HexCollisionBox(12.0D, 5.0D, 7.0D, 14.0D, 11.0D, 13.0D),
|
||||
new HexCollisionBox(4.0D, 2.0D, 4.0D, 12.0D, 14.0D, 16.0D));
|
||||
case EAST:
|
||||
return new ComplexCollisionBox(new HexCollisionBox(0.0D, 6.0D, 2.0D, 9.0D, 10.0D, 4.0D),
|
||||
return new ComplexCollisionBox(5,
|
||||
new HexCollisionBox(0.0D, 6.0D, 2.0D, 9.0D, 10.0D, 4.0D),
|
||||
new HexCollisionBox(0.0D, 6.0D, 12.0D, 9.0D, 10.0D, 14.0D),
|
||||
new HexCollisionBox(7.0D, 5.0D, 2.0D, 13.0D, 11.0D, 4.0D),
|
||||
new HexCollisionBox(7.0D, 5.0D, 12.0D, 13.0D, 11.0D, 14.0D),
|
||||
@@ -472,13 +499,15 @@ public enum CollisionData {
|
||||
}
|
||||
} else {
|
||||
if (facing == BlockFace.NORTH || facing == BlockFace.SOUTH) {
|
||||
return new ComplexCollisionBox(new HexCollisionBox(2.0D, 9.0D, 6.0D, 4.0D, 16.0D, 10.0D),
|
||||
return new ComplexCollisionBox(5,
|
||||
new HexCollisionBox(2.0D, 9.0D, 6.0D, 4.0D, 16.0D, 10.0D),
|
||||
new HexCollisionBox(12.0D, 9.0D, 6.0D, 14.0D, 16.0D, 10.0D),
|
||||
new HexCollisionBox(2.0D, 3.0D, 5.0D, 4.0D, 9.0D, 11.0D),
|
||||
new HexCollisionBox(12.0D, 3.0D, 5.0D, 14.0D, 9.0D, 11.0D),
|
||||
new HexCollisionBox(4.0D, 0.0D, 2.0D, 12.0D, 12.0D, 14.0D));
|
||||
} else {
|
||||
return new ComplexCollisionBox(new HexCollisionBox(6.0D, 9.0D, 2.0D, 10.0D, 16.0D, 4.0D),
|
||||
return new ComplexCollisionBox(5,
|
||||
new HexCollisionBox(6.0D, 9.0D, 2.0D, 10.0D, 16.0D, 4.0D),
|
||||
new HexCollisionBox(6.0D, 9.0D, 12.0D, 10.0D, 16.0D, 14.0D),
|
||||
new HexCollisionBox(5.0D, 3.0D, 2.0D, 11.0D, 9.0D, 4.0D),
|
||||
new HexCollisionBox(5.0D, 3.0D, 12.0D, 11.0D, 9.0D, 14.0D),
|
||||
@@ -513,12 +542,16 @@ public enum CollisionData {
|
||||
if (data.isOpen())
|
||||
return NoCollisionBox.INSTANCE;
|
||||
|
||||
return switch (data.getFacing()) {
|
||||
case NORTH, SOUTH -> new SimpleCollisionBox(0.0F, 0.0F, 0.375F, 1.0F, 1.5F, 0.625F, false);
|
||||
case WEST, EAST -> new SimpleCollisionBox(0.375F, 0.0F, 0.0F, 0.625F, 1.5F, 1.0F, false);
|
||||
default -> // This code is unreachable but the compiler does not know this
|
||||
NoCollisionBox.INSTANCE;
|
||||
};
|
||||
switch (data.getFacing()) {
|
||||
case NORTH:
|
||||
case SOUTH:
|
||||
return new SimpleCollisionBox(0.0F, 0.0F, 0.375F, 1.0F, 1.5F, 0.625F, false);
|
||||
case WEST:
|
||||
case EAST:
|
||||
return new SimpleCollisionBox(0.375F, 0.0F, 0.0F, 0.625F, 1.5F, 1.0F, false);
|
||||
default: // This code is unreachable but the compiler does not know this
|
||||
return NoCollisionBox.INSTANCE;
|
||||
}
|
||||
}, BlockTags.FENCE_GATES.getStates().toArray(new StateType[0])),
|
||||
|
||||
FENCE(new DynamicCollisionFence(), BlockTags.FENCES.getStates().toArray(new StateType[0])),
|
||||
@@ -551,7 +584,7 @@ public enum CollisionData {
|
||||
StateTypes.ENCHANTING_TABLE),
|
||||
|
||||
FRAME((player, version, data, x, y, z) -> {
|
||||
ComplexCollisionBox complexCollisionBox = new ComplexCollisionBox(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 13.0D, 16.0D));
|
||||
ComplexCollisionBox complexCollisionBox = new ComplexCollisionBox(2, new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 13.0D, 16.0D));
|
||||
|
||||
if (data.isEye()) {
|
||||
if (version.isNewerThanOrEquals(ClientVersion.V_1_13)) { // 1.13 players have a 0.5x0.5 eye
|
||||
@@ -622,7 +655,7 @@ public enum CollisionData {
|
||||
if (version.isOlderThan(ClientVersion.V_1_14))
|
||||
return new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.5625, 1.0F, false);
|
||||
|
||||
ComplexCollisionBox baseBox = new ComplexCollisionBox(new HexCollisionBox(0.0D, 3.0D, 0.0D, 16.0D, 9.0D, 16.0D));
|
||||
ComplexCollisionBox baseBox = new ComplexCollisionBox(3, new HexCollisionBox(0.0D, 3.0D, 0.0D, 16.0D, 9.0D, 16.0D));
|
||||
|
||||
switch (data.getFacing()) {
|
||||
case NORTH:
|
||||
@@ -656,13 +689,11 @@ public enum CollisionData {
|
||||
0.625, 0.625, 0.625, false),
|
||||
StateTypes.STRUCTURE_VOID),
|
||||
|
||||
END_ROD((player, version, data, x, y, z) -> {
|
||||
return getEndRod(version, data.getFacing());
|
||||
}, StateTypes.END_ROD, StateTypes.LIGHTNING_ROD),
|
||||
END_ROD((player, version, data, x, y, z) -> getEndRod(version, data.getFacing()), StateTypes.END_ROD, StateTypes.LIGHTNING_ROD),
|
||||
|
||||
CAULDRON((player, version, data, x, y, z) -> {
|
||||
if (version.isNewerThan(ClientVersion.getById(467))) { // changed in 19w13a, 1.14 Snapshot
|
||||
return new ComplexCollisionBox(
|
||||
return new ComplexCollisionBox(15,
|
||||
new SimpleCollisionBox(0.0, 0.0, 0.0, 0.125, 1.0, 0.25, false),
|
||||
new SimpleCollisionBox(0.0, 0.0, 0.75, 0.125, 1.0, 1.0, false),
|
||||
new SimpleCollisionBox(0.125, 0.0, 0.0, 0.25, 1.0, 0.125, false),
|
||||
@@ -684,7 +715,7 @@ public enum CollisionData {
|
||||
if (version.isOlderThan(ClientVersion.V_1_13))
|
||||
height = 0.3125;
|
||||
|
||||
return new ComplexCollisionBox(
|
||||
return new ComplexCollisionBox(5,
|
||||
new SimpleCollisionBox(0, 0, 0, 1, height, 1, false),
|
||||
new SimpleCollisionBox(0, height, 0, 0.125, 1, 1, false),
|
||||
new SimpleCollisionBox(1 - 0.125, height, 0, 1, 1, 1, false),
|
||||
@@ -704,9 +735,7 @@ public enum CollisionData {
|
||||
SOULSAND(new SimpleCollisionBox(0, 0, 0, 1, 0.875, 1, false),
|
||||
StateTypes.SOUL_SAND),
|
||||
|
||||
PICKLE((player, version, data, x, y, z) -> {
|
||||
return getPicklesBox(version, data.getPickles());
|
||||
}, StateTypes.SEA_PICKLE),
|
||||
PICKLE((player, version, data, x, y, z) -> getPicklesBox(version, data.getPickles()), StateTypes.SEA_PICKLE),
|
||||
|
||||
TURTLEEGG((player, version, data, x, y, z) -> {
|
||||
// ViaVersion replacement block (West facing cocoa beans)
|
||||
@@ -733,27 +762,34 @@ public enum CollisionData {
|
||||
BlockTags.FLOWER_POTS.getStates().toArray(new StateType[0])),
|
||||
|
||||
WALL_SIGN((player, version, data, x, y, z) -> {
|
||||
return switch (data.getFacing()) {
|
||||
case NORTH -> new HexCollisionBox(0.0D, 4.5D, 14.0D, 16.0D, 12.5D, 16.0D);
|
||||
case SOUTH -> new HexCollisionBox(0.0D, 4.5D, 0.0D, 16.0D, 12.5D, 2.0D);
|
||||
case WEST -> new HexCollisionBox(14.0D, 4.5D, 0.0D, 16.0D, 12.5D, 16.0D);
|
||||
case EAST -> new HexCollisionBox(0.0D, 4.5D, 0.0D, 2.0D, 12.5D, 16.0D);
|
||||
default -> NoCollisionBox.INSTANCE;
|
||||
};
|
||||
switch (data.getFacing()) {
|
||||
case NORTH:
|
||||
return new HexCollisionBox(0.0D, 4.5D, 14.0D, 16.0D, 12.5D, 16.0D);
|
||||
case SOUTH:
|
||||
return new HexCollisionBox(0.0D, 4.5D, 0.0D, 16.0D, 12.5D, 2.0D);
|
||||
case WEST:
|
||||
return new HexCollisionBox(14.0D, 4.5D, 0.0D, 16.0D, 12.5D, 16.0D);
|
||||
case EAST:
|
||||
return new HexCollisionBox(0.0D, 4.5D, 0.0D, 2.0D, 12.5D, 16.0D);
|
||||
default:
|
||||
return NoCollisionBox.INSTANCE;
|
||||
}
|
||||
}, BlockTags.WALL_SIGNS.getStates().toArray(new StateType[0])),
|
||||
|
||||
WALL_FAN((player, version, data, x, y, z) -> {
|
||||
return switch (data.getFacing()) {
|
||||
case NORTH -> new HexCollisionBox(0.0D, 4.0D, 5.0D, 16.0D, 12.0D, 16.0D);
|
||||
case SOUTH -> new HexCollisionBox(0.0D, 4.0D, 0.0D, 16.0D, 12.0D, 11.0D);
|
||||
case WEST -> new HexCollisionBox(5.0D, 4.0D, 0.0D, 16.0D, 12.0D, 16.0D);
|
||||
default -> new HexCollisionBox(0.0D, 4.0D, 0.0D, 11.0D, 12.0D, 16.0D);
|
||||
};
|
||||
switch (data.getFacing()) {
|
||||
case NORTH:
|
||||
return new HexCollisionBox(0.0D, 4.0D, 5.0D, 16.0D, 12.0D, 16.0D);
|
||||
case SOUTH:
|
||||
return new HexCollisionBox(0.0D, 4.0D, 0.0D, 16.0D, 12.0D, 11.0D);
|
||||
case WEST:
|
||||
return new HexCollisionBox(5.0D, 4.0D, 0.0D, 16.0D, 12.0D, 16.0D);
|
||||
default:
|
||||
return new HexCollisionBox(0.0D, 4.0D, 0.0D, 11.0D, 12.0D, 16.0D);
|
||||
}
|
||||
}, BlockTags.WALL_CORALS.getStates().toArray(new StateType[0])),
|
||||
|
||||
CORAL_PLANT((player, version, data, x, y, z) -> {
|
||||
return new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 15.0D, 14.0D);
|
||||
}, Stream.concat(
|
||||
CORAL_PLANT((player, version, data, x, y, z) -> new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 15.0D, 14.0D), Stream.concat(
|
||||
Arrays.stream(BlockTags.CORAL_PLANTS.getStates().toArray(new StateType[0])),
|
||||
Stream.of(StateTypes.DEAD_HORN_CORAL, StateTypes.DEAD_TUBE_CORAL, StateTypes.DEAD_BRAIN_CORAL,
|
||||
StateTypes.DEAD_BUBBLE_CORAL, StateTypes.DEAD_FIRE_CORAL)
|
||||
@@ -797,47 +833,64 @@ public enum CollisionData {
|
||||
}, StateTypes.TRIPWIRE),
|
||||
|
||||
TRIPWIRE_HOOK((player, version, data, x, y, z) -> {
|
||||
return switch (data.getFacing()) {
|
||||
case NORTH -> new HexCollisionBox(5.0D, 0.0D, 10.0D, 11.0D, 10.0D, 16.0D);
|
||||
case SOUTH -> new HexCollisionBox(5.0D, 0.0D, 0.0D, 11.0D, 10.0D, 6.0D);
|
||||
case WEST -> new HexCollisionBox(10.0D, 0.0D, 5.0D, 16.0D, 10.0D, 11.0D);
|
||||
default -> new HexCollisionBox(0.0D, 0.0D, 5.0D, 6.0D, 10.0D, 11.0D);
|
||||
};
|
||||
switch (data.getFacing()) {
|
||||
case NORTH:
|
||||
return new HexCollisionBox(5.0D, 0.0D, 10.0D, 11.0D, 10.0D, 16.0D);
|
||||
case SOUTH:
|
||||
return new HexCollisionBox(5.0D, 0.0D, 0.0D, 11.0D, 10.0D, 6.0D);
|
||||
case WEST:
|
||||
return new HexCollisionBox(10.0D, 0.0D, 5.0D, 16.0D, 10.0D, 11.0D);
|
||||
default:
|
||||
return new HexCollisionBox(0.0D, 0.0D, 5.0D, 6.0D, 10.0D, 11.0D);
|
||||
}
|
||||
}, StateTypes.TRIPWIRE_HOOK),
|
||||
|
||||
TORCH(new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 10.0D, 10.0D),
|
||||
StateTypes.TORCH, StateTypes.REDSTONE_TORCH),
|
||||
|
||||
WALL_TORCH((player, version, data, x, y, z) -> {
|
||||
return switch (data.getFacing()) {
|
||||
case NORTH -> new HexCollisionBox(5.5D, 3.0D, 11.0D, 10.5D, 13.0D, 16.0D);
|
||||
case SOUTH -> new HexCollisionBox(5.5D, 3.0D, 0.0D, 10.5D, 13.0D, 5.0D);
|
||||
case WEST -> new HexCollisionBox(11.0D, 3.0D, 5.5D, 16.0D, 13.0D, 10.5D);
|
||||
case EAST -> new HexCollisionBox(0.0D, 3.0D, 5.5D, 5.0D, 13.0D, 10.5D);
|
||||
switch (data.getFacing()) {
|
||||
case NORTH:
|
||||
return new HexCollisionBox(5.5D, 3.0D, 11.0D, 10.5D, 13.0D, 16.0D);
|
||||
case SOUTH:
|
||||
return new HexCollisionBox(5.5D, 3.0D, 0.0D, 10.5D, 13.0D, 5.0D);
|
||||
case WEST:
|
||||
return new HexCollisionBox(11.0D, 3.0D, 5.5D, 16.0D, 13.0D, 10.5D);
|
||||
case EAST:
|
||||
return new HexCollisionBox(0.0D, 3.0D, 5.5D, 5.0D, 13.0D, 10.5D);
|
||||
// 1.13 separates wall and normal torches, 1.12 does not
|
||||
default -> new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 10.0D, 10.0D);
|
||||
};
|
||||
default:
|
||||
return new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 10.0D, 10.0D);
|
||||
}
|
||||
}, StateTypes.WALL_TORCH, StateTypes.REDSTONE_WALL_TORCH),
|
||||
|
||||
// 1.17 blocks
|
||||
CANDLE((player, version, data, x, y, z) -> {
|
||||
if (version.isNewerThanOrEquals(ClientVersion.V_1_17)) {
|
||||
return switch (data.getCandles()) {
|
||||
case 1 -> new HexCollisionBox(7.0, 0.0, 7.0, 9.0, 6.0, 9.0);
|
||||
case 2 -> new HexCollisionBox(5.0, 0.0, 6.0, 11.0, 6.0, 9.0);
|
||||
case 3 -> new HexCollisionBox(5.0, 0.0, 6.0, 10.0, 6.0, 11.0);
|
||||
default -> new HexCollisionBox(5.0, 0.0, 5.0, 11.0, 6.0, 10.0);
|
||||
};
|
||||
switch (data.getCandles()) {
|
||||
case 1:
|
||||
return new HexCollisionBox(7.0, 0.0, 7.0, 9.0, 6.0, 9.0);
|
||||
case 2:
|
||||
return new HexCollisionBox(5.0, 0.0, 6.0, 11.0, 6.0, 9.0);
|
||||
case 3:
|
||||
return new HexCollisionBox(5.0, 0.0, 6.0, 10.0, 6.0, 11.0);
|
||||
default:
|
||||
return new HexCollisionBox(5.0, 0.0, 5.0, 11.0, 6.0, 10.0);
|
||||
}
|
||||
}
|
||||
|
||||
return getPicklesBox(version, data.getCandles());
|
||||
}, BlockTags.CANDLES.getStates().toArray(new StateType[0])),
|
||||
|
||||
CANDLE_CAKE((player, version, data, x, y, z) -> {
|
||||
ComplexCollisionBox cake = new ComplexCollisionBox(new HexCollisionBox(1.0, 0.0, 1.0, 15.0, 8.0, 15.0));
|
||||
if (version.isNewerThanOrEquals(ClientVersion.V_1_17))
|
||||
cake.add(new HexCollisionBox(7.0, 8.0, 7.0, 9.0, 14.0, 9.0));
|
||||
return cake;
|
||||
SimpleCollisionBox cake = new HexCollisionBox(1.0, 0.0, 1.0, 15.0, 8.0, 15.0);
|
||||
if (version.isOlderThan(ClientVersion.V_1_17)) {
|
||||
return cake;
|
||||
} else {
|
||||
return new ComplexCollisionBox(2,
|
||||
cake,
|
||||
new HexCollisionBox(7.0, 8.0, 7.0, 9.0, 14.0, 9.0));
|
||||
}
|
||||
}, BlockTags.CANDLE_CAKES.getStates().toArray(new StateType[0])),
|
||||
|
||||
SCULK_SENSOR(new HexCollisionBox(0.0, 0.0, 0.0, 16.0, 8.0, 16.0), StateTypes.SCULK_SENSOR, StateTypes.CALIBRATED_SCULK_SENSOR),
|
||||
@@ -913,26 +966,17 @@ public enum CollisionData {
|
||||
return new HexCollisionBox(6.0D, 0.0D, 0.0D, 10.0D, 16.0D, 16.0D);
|
||||
}, StateTypes.NETHER_PORTAL),
|
||||
|
||||
AZALEA((player, version, data, x, y, z) -> {
|
||||
return new ComplexCollisionBox(new HexCollisionBox(0.0, 8.0, 0.0, 16.0, 16.0, 16.0),
|
||||
new HexCollisionBox(6.0, 0.0, 6.0, 10.0, 8.0, 10.0));
|
||||
}, StateTypes.AZALEA, StateTypes.FLOWERING_AZALEA),
|
||||
AZALEA((player, version, data, x, y, z) -> new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(0.0, 8.0, 0.0, 16.0, 16.0, 16.0),
|
||||
new HexCollisionBox(6.0, 0.0, 6.0, 10.0, 8.0, 10.0)), StateTypes.AZALEA, StateTypes.FLOWERING_AZALEA),
|
||||
|
||||
AMETHYST_CLUSTER((player, version, data, x, y, z) -> {
|
||||
return getAmethystBox(version, data.getFacing(), 7, 3);
|
||||
}, StateTypes.AMETHYST_CLUSTER),
|
||||
AMETHYST_CLUSTER((player, version, data, x, y, z) -> getAmethystBox(version, data.getFacing(), 7, 3), StateTypes.AMETHYST_CLUSTER),
|
||||
|
||||
SMALL_AMETHYST_BUD((player, version, data, x, y, z) -> {
|
||||
return getAmethystBox(version, data.getFacing(), 3, 4);
|
||||
}, StateTypes.SMALL_AMETHYST_BUD),
|
||||
SMALL_AMETHYST_BUD((player, version, data, x, y, z) -> getAmethystBox(version, data.getFacing(), 3, 4), StateTypes.SMALL_AMETHYST_BUD),
|
||||
|
||||
MEDIUM_AMETHYST_BUD((player, version, data, x, y, z) -> {
|
||||
return getAmethystBox(version, data.getFacing(), 4, 3);
|
||||
}, StateTypes.MEDIUM_AMETHYST_BUD),
|
||||
MEDIUM_AMETHYST_BUD((player, version, data, x, y, z) -> getAmethystBox(version, data.getFacing(), 4, 3), StateTypes.MEDIUM_AMETHYST_BUD),
|
||||
|
||||
LARGE_AMETHYST_BUD((player, version, data, x, y, z) -> {
|
||||
return getAmethystBox(version, data.getFacing(), 5, 3);
|
||||
}, StateTypes.LARGE_AMETHYST_BUD),
|
||||
LARGE_AMETHYST_BUD((player, version, data, x, y, z) -> getAmethystBox(version, data.getFacing(), 5, 3), StateTypes.LARGE_AMETHYST_BUD),
|
||||
|
||||
MUD_BLOCK((player, version, data, x, y, z) -> {
|
||||
if (version.isNewerThanOrEquals(ClientVersion.V_1_19)) {
|
||||
@@ -945,13 +989,18 @@ public enum CollisionData {
|
||||
if (!data.isHanging()) {
|
||||
return new HexCollisionBox(7.0D, 0.0D, 7.0D, 9.0D, 16.0D, 9.0D);
|
||||
}
|
||||
return switch (data.getAge()) {
|
||||
case 0 -> new HexCollisionBox(7.0D, 13.0D, 7.0D, 9.0D, 16.0D, 9.0D);
|
||||
case 1 -> new HexCollisionBox(7.0D, 10.0D, 7.0D, 9.0D, 16.0D, 9.0D);
|
||||
case 2 -> new HexCollisionBox(7.0D, 7.0D, 7.0D, 9.0D, 16.0D, 9.0D);
|
||||
case 3 -> new HexCollisionBox(7.0D, 3.0D, 7.0D, 9.0D, 16.0D, 9.0D);
|
||||
default -> new HexCollisionBox(7.0D, 0.0D, 7.0D, 9.0D, 16.0D, 9.0D);
|
||||
};
|
||||
switch (data.getAge()) {
|
||||
case 0:
|
||||
return new HexCollisionBox(7.0D, 13.0D, 7.0D, 9.0D, 16.0D, 9.0D);
|
||||
case 1:
|
||||
return new HexCollisionBox(7.0D, 10.0D, 7.0D, 9.0D, 16.0D, 9.0D);
|
||||
case 2:
|
||||
return new HexCollisionBox(7.0D, 7.0D, 7.0D, 9.0D, 16.0D, 9.0D);
|
||||
case 3:
|
||||
return new HexCollisionBox(7.0D, 3.0D, 7.0D, 9.0D, 16.0D, 9.0D);
|
||||
default:
|
||||
return new HexCollisionBox(7.0D, 0.0D, 7.0D, 9.0D, 16.0D, 9.0D);
|
||||
}
|
||||
}, StateTypes.MANGROVE_PROPAGULE),
|
||||
|
||||
SCULK_SHRIKER((player, version, data, x, y, z) -> {
|
||||
@@ -986,11 +1035,16 @@ public enum CollisionData {
|
||||
}, StateTypes.PITCHER_CROP),
|
||||
|
||||
WALL_HANGING_SIGNS((player, version, data, x, y, z) -> {
|
||||
return switch (data.getFacing()) {
|
||||
case NORTH, SOUTH -> new HexCollisionBox(0.0, 14.0, 6.0, 16.0, 16.0, 10.0);
|
||||
case WEST, EAST -> new HexCollisionBox(6.0, 14.0, 0.0, 10.0, 16.0, 16.0);
|
||||
default -> NoCollisionBox.INSTANCE;
|
||||
};
|
||||
switch (data.getFacing()) {
|
||||
case NORTH:
|
||||
case SOUTH:
|
||||
return new HexCollisionBox(0.0, 14.0, 6.0, 16.0, 16.0, 10.0);
|
||||
case WEST:
|
||||
case EAST:
|
||||
return new HexCollisionBox(6.0, 14.0, 0.0, 10.0, 16.0, 16.0);
|
||||
default:
|
||||
return NoCollisionBox.INSTANCE;
|
||||
}
|
||||
}, BlockTags.WALL_HANGING_SIGNS.getStates().toArray(new StateType[0])),
|
||||
|
||||
DEFAULT(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.STONE);
|
||||
@@ -1028,14 +1082,20 @@ public enum CollisionData {
|
||||
if (version.isOlderThanOrEquals(ClientVersion.V_1_16_4))
|
||||
return NoCollisionBox.INSTANCE;
|
||||
|
||||
return switch (facing) {
|
||||
case DOWN -> new HexCollisionBox(param_1, 16 - param_0, param_1, 16 - param_1, 16.0, 16 - param_1);
|
||||
case NORTH -> new HexCollisionBox(param_1, param_1, 16 - param_0, 16 - param_1, 16 - param_1, 16.0);
|
||||
case SOUTH -> new HexCollisionBox(param_1, param_1, 0.0, 16 - param_1, 16 - param_1, param_0);
|
||||
case EAST -> new HexCollisionBox(0.0, param_1, param_1, param_0, 16 - param_1, 16 - param_1);
|
||||
case WEST -> new HexCollisionBox(16 - param_0, param_1, param_1, 16.0, 16 - param_1, 16 - param_1);
|
||||
default -> new HexCollisionBox(param_1, 0.0, param_1, 16 - param_1, param_0, 16 - param_1);
|
||||
};
|
||||
switch (facing) {
|
||||
case DOWN:
|
||||
return new HexCollisionBox(param_1, 16 - param_0, param_1, 16 - param_1, 16.0, 16 - param_1);
|
||||
case NORTH:
|
||||
return new HexCollisionBox(param_1, param_1, 16 - param_0, 16 - param_1, 16 - param_1, 16.0);
|
||||
case SOUTH:
|
||||
return new HexCollisionBox(param_1, param_1, 0.0, 16 - param_1, 16 - param_1, param_0);
|
||||
case EAST:
|
||||
return new HexCollisionBox(0.0, param_1, param_1, param_0, 16 - param_1, 16 - param_1);
|
||||
case WEST:
|
||||
return new HexCollisionBox(16 - param_0, param_1, param_1, 16.0, 16 - param_1, 16 - param_1);
|
||||
default:
|
||||
return new HexCollisionBox(param_1, 0.0, param_1, 16 - param_1, param_0, 16 - param_1);
|
||||
}
|
||||
}
|
||||
|
||||
private static CollisionBox getPicklesBox(ClientVersion version, int pickles) {
|
||||
@@ -1044,13 +1104,18 @@ public enum CollisionData {
|
||||
return getCocoa(version, pickles, BlockFace.WEST);
|
||||
}
|
||||
|
||||
return switch (pickles) {
|
||||
case 1 -> new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 6.0D, 10.0D);
|
||||
case 2 -> new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 6.0D, 13.0D);
|
||||
case 3 -> new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 6.0D, 14.0D);
|
||||
case 4 -> new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 7.0D, 14.0D);
|
||||
default -> NoCollisionBox.INSTANCE;
|
||||
};
|
||||
switch (pickles) {
|
||||
case 1:
|
||||
return new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 6.0D, 10.0D);
|
||||
case 2:
|
||||
return new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 6.0D, 13.0D);
|
||||
case 3:
|
||||
return new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 6.0D, 14.0D);
|
||||
case 4:
|
||||
return new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 7.0D, 14.0D);
|
||||
default:
|
||||
return NoCollisionBox.INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
public static CollisionBox getCocoa(ClientVersion version, int age, BlockFace direction) {
|
||||
@@ -1105,11 +1170,16 @@ public enum CollisionData {
|
||||
if (version.isOlderThan(ClientVersion.V_1_9))
|
||||
return NoCollisionBox.INSTANCE;
|
||||
|
||||
return switch (face) {
|
||||
case NORTH, SOUTH -> new HexCollisionBox(6.0D, 6.0D, 0.0D, 10.0D, 10.0D, 16.0D);
|
||||
case EAST, WEST -> new HexCollisionBox(0.0D, 6.0D, 6.0D, 16.0D, 10.0D, 10.0D);
|
||||
default -> new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 16.0D, 10.0);
|
||||
};
|
||||
switch (face) {
|
||||
case NORTH:
|
||||
case SOUTH:
|
||||
return new HexCollisionBox(6.0D, 6.0D, 0.0D, 10.0D, 10.0D, 16.0D);
|
||||
case EAST:
|
||||
case WEST:
|
||||
return new HexCollisionBox(0.0D, 6.0D, 6.0D, 16.0D, 10.0D, 10.0D);
|
||||
default:
|
||||
return new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 16.0D, 10.0);
|
||||
}
|
||||
}
|
||||
|
||||
// Would pre-computing all states be worth the memory cost? I doubt it
|
||||
|
||||
@@ -20,29 +20,32 @@ import java.util.*;
|
||||
public enum HitboxData {
|
||||
|
||||
RAILS((player, item, version, data, x, y, z) -> {
|
||||
return switch (data.getShape()) {
|
||||
case ASCENDING_NORTH, ASCENDING_SOUTH, ASCENDING_EAST, ASCENDING_WEST -> {
|
||||
switch (data.getShape()) {
|
||||
case ASCENDING_NORTH:
|
||||
case ASCENDING_SOUTH:
|
||||
case ASCENDING_EAST:
|
||||
case ASCENDING_WEST:
|
||||
if (version.isOlderThan(ClientVersion.V_1_8)) {
|
||||
StateType railType = data.getType();
|
||||
// Activator rails always appear as flat detector rails in 1.7.10 because of ViaVersion
|
||||
// Ascending power rails in 1.7 have flat rail hitbox https://bugs.mojang.com/browse/MC-9134
|
||||
if (railType == StateTypes.ACTIVATOR_RAIL || (railType == StateTypes.POWERED_RAIL && data.isPowered())) {
|
||||
yield new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F, false);
|
||||
return new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F, false);
|
||||
}
|
||||
yield new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.625F, 1.0F, false);
|
||||
return new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.625F, 1.0F, false);
|
||||
} else if (version.isOlderThan(ClientVersion.V_1_9)) {
|
||||
yield new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.625F, 1.0F, false);
|
||||
return new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.625F, 1.0F, false);
|
||||
} else if (version.isNewerThanOrEquals(ClientVersion.V_1_9) && version.isOlderThan(ClientVersion.V_1_10)) {
|
||||
// https://bugs.mojang.com/browse/MC-89552 sloped rails in 1.9 - it is slightly taller than a regular rail
|
||||
yield new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.1875F, 1.0F, false);
|
||||
return new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.1875F, 1.0F, false);
|
||||
} else if (version.isOlderThan(ClientVersion.V_1_11)) {
|
||||
// https://bugs.mojang.com/browse/MC-102638 All sloped rails are full blocks in 1.10
|
||||
yield new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
}
|
||||
yield new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D);
|
||||
}
|
||||
default -> new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D);
|
||||
};
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D);
|
||||
default:
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D);
|
||||
}
|
||||
}, BlockTags.RAILS.getStates().toArray(new StateType[0])),
|
||||
|
||||
END_PORTAL((player, item, version, data, x, y, z) -> {
|
||||
@@ -106,31 +109,35 @@ public enum HitboxData {
|
||||
}
|
||||
}
|
||||
|
||||
return switch (face) {
|
||||
case FLOOR -> {
|
||||
switch (face) {
|
||||
case FLOOR:
|
||||
// X-AXIS
|
||||
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
|
||||
yield new SimpleCollisionBox(0.25, 0.0, 0.3125, 0.75, 0.375, 0.6875, false);
|
||||
return new SimpleCollisionBox(0.25, 0.0, 0.3125, 0.75, 0.375, 0.6875, false);
|
||||
}
|
||||
// Z-AXIS
|
||||
yield new SimpleCollisionBox(0.3125, 0.0, 0.25, 0.6875, 0.375, 0.75, false);
|
||||
// Z-AXIS
|
||||
}
|
||||
case WALL -> switch (facing) {
|
||||
case EAST -> new SimpleCollisionBox(0.0, 0.25, 0.3125, 0.375, 0.75, 0.6875, false);
|
||||
case WEST -> new SimpleCollisionBox(0.625, 0.25, 0.3125, 1.0, 0.75, 0.6875, false);
|
||||
case SOUTH -> new SimpleCollisionBox(0.3125, 0.25, 0.0, 0.6875, 0.75, 0.375, false);
|
||||
default -> new SimpleCollisionBox(0.3125, 0.25, 0.625, 0.6875, 0.75, 1.0, false);
|
||||
};
|
||||
default -> {
|
||||
return new SimpleCollisionBox(0.3125, 0.0, 0.25, 0.6875, 0.375, 0.75, false);
|
||||
case WALL:
|
||||
switch (facing) {
|
||||
case EAST:
|
||||
return new SimpleCollisionBox(0.0, 0.25, 0.3125, 0.375, 0.75, 0.6875, false);
|
||||
case WEST:
|
||||
return new SimpleCollisionBox(0.625, 0.25, 0.3125, 1.0, 0.75, 0.6875, false);
|
||||
case SOUTH:
|
||||
return new SimpleCollisionBox(0.3125, 0.25, 0.0, 0.6875, 0.75, 0.375, false);
|
||||
case NORTH:
|
||||
default:
|
||||
return new SimpleCollisionBox(0.3125, 0.25, 0.625, 0.6875, 0.75, 1.0, false);
|
||||
}
|
||||
case CEILING:
|
||||
default:
|
||||
// X-AXIS
|
||||
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
|
||||
yield new SimpleCollisionBox(0.25, 0.625, 0.3125, 0.75, 1.0, 0.6875, false);
|
||||
return new SimpleCollisionBox(0.25, 0.625, 0.3125, 0.75, 1.0, 0.6875, false);
|
||||
}
|
||||
// Z-Axis
|
||||
yield new SimpleCollisionBox(0.3125, 0.625, 0.25, 0.6875, 1.0, 0.75, false);
|
||||
}
|
||||
};
|
||||
return new SimpleCollisionBox(0.3125, 0.625, 0.25, 0.6875, 1.0, 0.75, false);
|
||||
}
|
||||
}), StateTypes.LEVER),
|
||||
|
||||
BUTTON((player, item, version, data, x, y, z) -> {
|
||||
@@ -164,17 +171,27 @@ public enum HitboxData {
|
||||
|
||||
switch (face) {
|
||||
case WALL:
|
||||
return switch (facing) {
|
||||
case EAST ->
|
||||
powered ? new HexCollisionBox(0.0, 6.0, 5.0, 1.0, 10.0, 11.0) : new HexCollisionBox(0.0, 6.0, 5.0, 2.0, 10.0, 11.0);
|
||||
case WEST ->
|
||||
powered ? new HexCollisionBox(15.0, 6.0, 5.0, 16.0, 10.0, 11.0) : new HexCollisionBox(14.0, 6.0, 5.0, 16.0, 10.0, 11.0);
|
||||
case SOUTH ->
|
||||
powered ? new HexCollisionBox(5.0, 6.0, 0.0, 11.0, 10.0, 1.0) : new HexCollisionBox(5.0, 6.0, 0.0, 11.0, 10.0, 2.0);
|
||||
case NORTH, UP, DOWN ->
|
||||
powered ? new HexCollisionBox(5.0, 6.0, 15.0, 11.0, 10.0, 16.0) : new HexCollisionBox(5.0, 6.0, 14.0, 11.0, 10.0, 16.0);
|
||||
default -> NoCollisionBox.INSTANCE;
|
||||
};
|
||||
CollisionBox shape;
|
||||
switch (facing) {
|
||||
case EAST:
|
||||
shape = powered ? new HexCollisionBox(0.0, 6.0, 5.0, 1.0, 10.0, 11.0) : new HexCollisionBox(0.0, 6.0, 5.0, 2.0, 10.0, 11.0);
|
||||
break;
|
||||
case WEST:
|
||||
shape = powered ? new HexCollisionBox(15.0, 6.0, 5.0, 16.0, 10.0, 11.0) : new HexCollisionBox(14.0, 6.0, 5.0, 16.0, 10.0, 11.0);
|
||||
break;
|
||||
case SOUTH:
|
||||
shape = powered ? new HexCollisionBox(5.0, 6.0, 0.0, 11.0, 10.0, 1.0) : new HexCollisionBox(5.0, 6.0, 0.0, 11.0, 10.0, 2.0);
|
||||
break;
|
||||
case NORTH:
|
||||
case UP:
|
||||
case DOWN:
|
||||
shape = powered ? new HexCollisionBox(5.0, 6.0, 15.0, 11.0, 10.0, 16.0) : new HexCollisionBox(5.0, 6.0, 14.0, 11.0, 10.0, 16.0);
|
||||
break;
|
||||
default:
|
||||
shape = NoCollisionBox.INSTANCE;
|
||||
}
|
||||
|
||||
return shape;
|
||||
case CEILING:
|
||||
// ViaVersion shows lever
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_8)) {
|
||||
@@ -205,22 +222,32 @@ public enum HitboxData {
|
||||
WALL(new DynamicHitboxWall(), BlockTags.WALLS.getStates().toArray(new StateType[0])),
|
||||
|
||||
WALL_SIGN((player, item, version, data, x, y, z) -> {
|
||||
return switch (data.getFacing()) {
|
||||
case NORTH -> new HexCollisionBox(0.0, 4.5, 14.0, 16.0, 12.5, 16.0);
|
||||
case SOUTH -> new HexCollisionBox(0.0, 4.5, 0.0, 16.0, 12.5, 2.0);
|
||||
case EAST -> new HexCollisionBox(0.0, 4.5, 0.0, 2.0, 12.5, 16.0);
|
||||
case WEST -> new HexCollisionBox(14.0, 4.5, 0.0, 16.0, 12.5, 16.0);
|
||||
default -> NoCollisionBox.INSTANCE;
|
||||
};
|
||||
switch (data.getFacing()) {
|
||||
case NORTH:
|
||||
return new HexCollisionBox(0.0, 4.5, 14.0, 16.0, 12.5, 16.0);
|
||||
case SOUTH:
|
||||
return new HexCollisionBox(0.0, 4.5, 0.0, 16.0, 12.5, 2.0);
|
||||
case EAST:
|
||||
return new HexCollisionBox(0.0, 4.5, 0.0, 2.0, 12.5, 16.0);
|
||||
case WEST:
|
||||
return new HexCollisionBox(14.0, 4.5, 0.0, 16.0, 12.5, 16.0);
|
||||
default:
|
||||
return NoCollisionBox.INSTANCE;
|
||||
}
|
||||
}, BlockTags.WALL_SIGNS.getStates().toArray(new StateType[0])),
|
||||
|
||||
WALL_HANGING_SIGN((player, item, version, data, x, y, z) -> {
|
||||
return switch (data.getFacing()) {
|
||||
case NORTH, SOUTH -> new ComplexCollisionBox(new HexCollisionBox(0.0D, 14.0D, 6.0D, 16.0D, 16.0D, 10.0D),
|
||||
new HexCollisionBox(1.0D, 0.0D, 7.0D, 15.0D, 10.0D, 9.0D));
|
||||
default -> new ComplexCollisionBox(new HexCollisionBox(6.0D, 14.0D, 0.0D, 10.0D, 16.0D, 16.0D),
|
||||
new HexCollisionBox(7.0D, 0.0D, 1.0D, 9.0D, 10.0D, 15.0D));
|
||||
};
|
||||
switch (data.getFacing()) {
|
||||
case NORTH:
|
||||
case SOUTH:
|
||||
return new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(0.0D, 14.0D, 6.0D, 16.0D, 16.0D, 10.0D),
|
||||
new HexCollisionBox(1.0D, 0.0D, 7.0D, 15.0D, 10.0D, 9.0D));
|
||||
default:
|
||||
return new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(6.0D, 14.0D, 0.0D, 10.0D, 16.0D, 16.0D),
|
||||
new HexCollisionBox(7.0D, 0.0D, 1.0D, 9.0D, 10.0D, 15.0D));
|
||||
}
|
||||
}, BlockTags.WALL_HANGING_SIGNS.getStates().toArray(new StateType[0])),
|
||||
|
||||
STANDING_SIGN((player, item, version, data, x, y, z) ->
|
||||
@@ -245,13 +272,18 @@ public enum HitboxData {
|
||||
return WALL_SIGN.dynamic.fetch(player, item, version, data, x, y, z);
|
||||
}
|
||||
|
||||
return switch (data.getFacing()) {
|
||||
case NORTH -> new HexCollisionBox(0.0, 0.0, 14.0, 16.0, 12.5, 16.0);
|
||||
case EAST -> new HexCollisionBox(0.0, 0.0, 0.0, 2.0, 12.5, 16.0);
|
||||
case WEST -> new HexCollisionBox(14.0, 0.0, 0.0, 16.0, 12.5, 16.0);
|
||||
case SOUTH -> new HexCollisionBox(0.0, 0.0, 0.0, 16.0, 12.5, 2.0);
|
||||
default -> throw new IllegalStateException("Impossible Banner Facing State; Something very wrong is going on");
|
||||
};
|
||||
switch (data.getFacing()) {
|
||||
case NORTH:
|
||||
return new HexCollisionBox(0.0, 0.0, 14.0, 16.0, 12.5, 16.0);
|
||||
case EAST:
|
||||
return new HexCollisionBox(0.0, 0.0, 0.0, 2.0, 12.5, 16.0);
|
||||
case WEST:
|
||||
return new HexCollisionBox(14.0, 0.0, 0.0, 16.0, 12.5, 16.0);
|
||||
case SOUTH:
|
||||
return new HexCollisionBox(0.0, 0.0, 0.0, 16.0, 12.5, 2.0);
|
||||
default:
|
||||
throw new IllegalStateException("Impossible Banner Facing State; Something very wrong is going on");
|
||||
}
|
||||
}, StateTypes.WHITE_WALL_BANNER, StateTypes.ORANGE_WALL_BANNER, StateTypes.MAGENTA_WALL_BANNER,
|
||||
StateTypes.LIGHT_BLUE_WALL_BANNER, StateTypes.YELLOW_WALL_BANNER, StateTypes.LIME_WALL_BANNER,
|
||||
StateTypes.PINK_WALL_BANNER, StateTypes.GRAY_WALL_BANNER, StateTypes.LIGHT_GRAY_WALL_BANNER,
|
||||
@@ -263,7 +295,7 @@ public enum HitboxData {
|
||||
if (version.isOlderThan(ClientVersion.V_1_13)) {
|
||||
return new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F);
|
||||
} else {
|
||||
return new ComplexCollisionBox(
|
||||
return new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(1.0, 0.0, 1.0, 15.0, 2.0, 15.0),
|
||||
new SimpleCollisionBox(0.4375, 0.0, 0.4375, 0.5625, 0.875, 0.5625, false));
|
||||
}
|
||||
@@ -303,7 +335,8 @@ public enum HitboxData {
|
||||
}, StateTypes.SNOW),
|
||||
|
||||
LECTERN_BLOCK((player, item, version, data, x, y, z) -> {
|
||||
ComplexCollisionBox common = new ComplexCollisionBox(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D),
|
||||
ComplexCollisionBox common = new ComplexCollisionBox(5,
|
||||
new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D),
|
||||
new HexCollisionBox(4.0D, 2.0D, 4.0D, 12.0D, 14.0D, 12.0D));
|
||||
|
||||
if (data.getFacing() == BlockFace.WEST) {
|
||||
@@ -329,7 +362,7 @@ public enum HitboxData {
|
||||
|
||||
GLOW_LICHEN_SCULK_VEIN((player, item, version, data, x, y, z) -> {
|
||||
if (version.isNewerThan(ClientVersion.V_1_16_4)) {
|
||||
ComplexCollisionBox box = new ComplexCollisionBox();
|
||||
ComplexCollisionBox box = new ComplexCollisionBox(6);
|
||||
|
||||
if (data.isUp()) {
|
||||
box.add(new HexCollisionBox(0.0D, 15.0D, 0.0D, 16.0D, 16.0D, 16.0D));
|
||||
@@ -395,12 +428,17 @@ public enum HitboxData {
|
||||
if (version.isOlderThan(ClientVersion.V_1_13))
|
||||
return new HexCollisionBox(7.0D, 0.0D, 7.0D, 9.0D, 16.0D, 9.0D);
|
||||
|
||||
return switch (data.getFacing()) {
|
||||
case SOUTH -> new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 10.0D, 16.0D);
|
||||
case WEST -> new HexCollisionBox(0.0D, 0.0D, 6.0D, 10.0D, 10.0D, 10.0D);
|
||||
case NORTH -> new HexCollisionBox(6.0D, 0.0D, 0.0D, 10.0D, 10.0D, 10.0D);
|
||||
default -> new HexCollisionBox(6.0D, 0.0D, 6.0D, 16.0D, 10.0D, 10.0D);
|
||||
};
|
||||
switch (data.getFacing()) {
|
||||
case SOUTH:
|
||||
return new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 10.0D, 16.0D);
|
||||
case WEST:
|
||||
return new HexCollisionBox(0.0D, 0.0D, 6.0D, 10.0D, 10.0D, 10.0D);
|
||||
case NORTH:
|
||||
return new HexCollisionBox(6.0D, 0.0D, 0.0D, 10.0D, 10.0D, 10.0D);
|
||||
case EAST:
|
||||
default:
|
||||
return new HexCollisionBox(6.0D, 0.0D, 6.0D, 16.0D, 10.0D, 10.0D);
|
||||
}
|
||||
}, StateTypes.ATTACHED_MELON_STEM, StateTypes.ATTACHED_PUMPKIN_STEM),
|
||||
|
||||
PUMPKIN_STEM((player, item, version, data, x, y, z) -> {
|
||||
@@ -491,7 +529,7 @@ public enum HitboxData {
|
||||
}
|
||||
|
||||
// STABLE_SHAPE for the scaffolding
|
||||
ComplexCollisionBox box = new ComplexCollisionBox(
|
||||
ComplexCollisionBox box = new ComplexCollisionBox(9,
|
||||
new HexCollisionBox(0.0D, 14.0D, 0.0D, 16.0D, 16.0D, 16.0D),
|
||||
new HexCollisionBox(0.0D, 0.0D, 0.0D, 2.0D, 16.0D, 2.0D),
|
||||
new HexCollisionBox(14.0D, 0.0D, 0.0D, 16.0D, 16.0D, 2.0D),
|
||||
@@ -512,7 +550,7 @@ public enum HitboxData {
|
||||
if (version.isOlderThanOrEquals(ClientVersion.V_1_16_4))
|
||||
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
|
||||
ComplexCollisionBox box = new ComplexCollisionBox();
|
||||
ComplexCollisionBox box = new ComplexCollisionBox(2);
|
||||
|
||||
if (data.getFacing() == BlockFace.NORTH) { // Stem
|
||||
box.add(new HexCollisionBox(5.0D, 0.0D, 9.0D, 11.0D, 15.0D, 15.0D));
|
||||
@@ -635,11 +673,16 @@ public enum HitboxData {
|
||||
}
|
||||
|
||||
private static int getPropaguleMinHeight(int age) {
|
||||
return switch (age) {
|
||||
case 0, 1, 2 -> 13 - age * 3;
|
||||
case 3, 4 -> (4 - age) * 3;
|
||||
default -> throw new IllegalStateException("Impossible Propagule Height");
|
||||
};
|
||||
switch (age) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
return 13 - age * 3;
|
||||
case 3:
|
||||
case 4:
|
||||
return (4 - age) * 3;
|
||||
}
|
||||
throw new IllegalStateException("Impossible Propagule Height");
|
||||
}
|
||||
|
||||
private static CollisionBox getVineCollisionBox(ClientVersion version, boolean isWeeping, boolean isBlock) {
|
||||
@@ -653,7 +696,7 @@ public enum HitboxData {
|
||||
}
|
||||
} else {
|
||||
// Via replacement - 4 sided vine
|
||||
return new ComplexCollisionBox(
|
||||
return new ComplexCollisionBox(4,
|
||||
new HexCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 16.0D, 16.0D),
|
||||
new HexCollisionBox(15.0D, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D),
|
||||
new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, 1.0D),
|
||||
@@ -663,13 +706,19 @@ public enum HitboxData {
|
||||
}
|
||||
|
||||
private static int getHorizontalID(BlockFace facing) {
|
||||
return switch (facing) {
|
||||
case DOWN, UP -> -1;
|
||||
case NORTH -> 2;
|
||||
case SOUTH -> 0;
|
||||
case WEST -> 1;
|
||||
case EAST -> 3;
|
||||
default -> throw new IllegalStateException("Impossible blockface for getHorizontalID");
|
||||
};
|
||||
switch (facing) {
|
||||
case DOWN:
|
||||
case UP:
|
||||
return -1;
|
||||
case NORTH:
|
||||
return 2;
|
||||
case SOUTH:
|
||||
return 0;
|
||||
case WEST:
|
||||
return 1;
|
||||
case EAST:
|
||||
return 3;
|
||||
}
|
||||
throw new IllegalStateException("Impossible blockface for getHorizontalID");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package ac.grim.grimac.utils.collisions;
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.*;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
// Expansion to the CollisionData class, which is different than regular ray tracing hitboxes
|
||||
public enum RaycastData {
|
||||
|
||||
HOPPER((player, item, version, data, x, y, z) -> {
|
||||
HexCollisionBox insideShape = new HexCollisionBox(2.0, 11.0, 2.0, 14.0, 16.0, 14.0);
|
||||
switch (data.getFacing()) {
|
||||
case NORTH:
|
||||
new ComplexCollisionBox(insideShape, new HexCollisionBox(6.0, 8.0, 0.0, 10.0, 10.0, 4.0));
|
||||
case SOUTH:
|
||||
new ComplexCollisionBox(insideShape, new HexCollisionBox(6.0, 8.0, 12.0, 10.0, 10.0, 16.0));
|
||||
case WEST:
|
||||
new ComplexCollisionBox(insideShape, new HexCollisionBox(0.0, 8.0, 6.0, 4.0, 10.0, 10.0));
|
||||
case EAST:
|
||||
new ComplexCollisionBox(insideShape, new HexCollisionBox(12.0, 8.0, 6.0, 16.0, 10.0, 10.0));
|
||||
default:
|
||||
return insideShape;
|
||||
}
|
||||
}, StateTypes.HOPPER),
|
||||
|
||||
CAULDRON((player, item, version, data, x, y, z) -> {
|
||||
return new HexCollisionBox(2.0, 4.0, 2.0, 14.0, 16.0, 14.0);
|
||||
}, BlockTags.CAULDRONS.getStates().toArray(new StateType[0])),
|
||||
|
||||
FULL_BLOCK((player, item, version, data, x, y, z) -> {
|
||||
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
}, StateTypes.COMPOSTER, StateTypes.SCAFFOLDING);
|
||||
|
||||
|
||||
private static final Map<StateType, RaycastData> lookup = new HashMap<>();
|
||||
|
||||
static {
|
||||
for (RaycastData data : RaycastData.values()) {
|
||||
for (StateType type : data.materials) {
|
||||
lookup.put(type, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final StateType[] materials;
|
||||
private CollisionBox box;
|
||||
private HitBoxFactory dynamic;
|
||||
|
||||
RaycastData(CollisionBox box, StateType... materials) {
|
||||
this.box = box;
|
||||
Set<StateType> mList = new HashSet<>(Arrays.asList(materials));
|
||||
mList.remove(null); // Sets can contain one null
|
||||
this.materials = mList.toArray(new StateType[0]);
|
||||
}
|
||||
|
||||
RaycastData(HitBoxFactory dynamic, StateType... materials) {
|
||||
this.dynamic = dynamic;
|
||||
Set<StateType> mList = new HashSet<>(Arrays.asList(materials));
|
||||
mList.remove(null); // Sets can contain one null
|
||||
this.materials = mList.toArray(new StateType[0]);
|
||||
}
|
||||
|
||||
public static RaycastData getData(StateType material) {
|
||||
return lookup.get(material);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static CollisionBox getBlockHitbox(GrimPlayer player, StateType heldItem, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
|
||||
RaycastData data = getData(block.getType());
|
||||
|
||||
if (data == null) {
|
||||
// We explicitly do not want to fallback on HitBoxData or CollisionBox
|
||||
// Because we only ever use this as a 2nd ray trace
|
||||
return NoCollisionBox.INSTANCE;
|
||||
}
|
||||
|
||||
// Simple collision box to override
|
||||
if (data.box != null)
|
||||
return data.box.copy().offset(x, y, z);
|
||||
|
||||
// Allow this class to override collision boxes when they aren't the same as regular boxes
|
||||
HitBoxFactory hitBoxFactory = data.dynamic;
|
||||
CollisionBox collisionBox = hitBoxFactory.fetch(player, heldItem, version, block, x, y, z);
|
||||
collisionBox.offset(x, y, z);
|
||||
return collisionBox;
|
||||
}
|
||||
}
|
||||
@@ -21,14 +21,18 @@ public class DoorHandler implements CollisionFactory {
|
||||
|
||||
@Override
|
||||
public CollisionBox fetch(GrimPlayer player, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
|
||||
return switch (fetchDirection(player, version, block, x, y, z)) {
|
||||
case NORTH -> NORTH_AABB.copy();
|
||||
case SOUTH -> SOUTH_AABB.copy();
|
||||
case EAST -> EAST_AABB.copy();
|
||||
case WEST -> WEST_AABB.copy();
|
||||
default -> NoCollisionBox.INSTANCE;
|
||||
};
|
||||
switch (fetchDirection(player, version, block, x, y, z)) {
|
||||
case NORTH:
|
||||
return NORTH_AABB.copy();
|
||||
case SOUTH:
|
||||
return SOUTH_AABB.copy();
|
||||
case EAST:
|
||||
return EAST_AABB.copy();
|
||||
case WEST:
|
||||
return WEST_AABB.copy();
|
||||
}
|
||||
|
||||
return NoCollisionBox.INSTANCE;
|
||||
}
|
||||
|
||||
public BlockFace fetchDirection(GrimPlayer player, ClientVersion version, WrappedBlockState door, int x, int y, int z) {
|
||||
@@ -76,11 +80,16 @@ public class DoorHandler implements CollisionFactory {
|
||||
isRightHinge = door.getHinge() == Hinge.RIGHT;
|
||||
}
|
||||
|
||||
return switch (facingDirection) {
|
||||
case SOUTH -> isClosed ? BlockFace.SOUTH : (isRightHinge ? BlockFace.EAST : BlockFace.WEST);
|
||||
case WEST -> isClosed ? BlockFace.WEST : (isRightHinge ? BlockFace.SOUTH : BlockFace.NORTH);
|
||||
case NORTH -> isClosed ? BlockFace.NORTH : (isRightHinge ? BlockFace.WEST : BlockFace.EAST);
|
||||
default -> isClosed ? BlockFace.EAST : (isRightHinge ? BlockFace.NORTH : BlockFace.SOUTH);
|
||||
};
|
||||
switch (facingDirection) {
|
||||
case EAST:
|
||||
default:
|
||||
return isClosed ? BlockFace.EAST : (isRightHinge ? BlockFace.NORTH : BlockFace.SOUTH);
|
||||
case SOUTH:
|
||||
return isClosed ? BlockFace.SOUTH : (isRightHinge ? BlockFace.EAST : BlockFace.WEST);
|
||||
case WEST:
|
||||
return isClosed ? BlockFace.WEST : (isRightHinge ? BlockFace.SOUTH : BlockFace.NORTH);
|
||||
case NORTH:
|
||||
return isClosed ? BlockFace.NORTH : (isRightHinge ? BlockFace.WEST : BlockFace.EAST);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ public class DynamicChorusPlant implements CollisionFactory {
|
||||
float f = 0.5F - (float) 0.3125;
|
||||
float f1 = 0.5F + (float) 0.3125;
|
||||
SimpleCollisionBox baseShape = new SimpleCollisionBox(f, f, f, f1, f1, f1, false);
|
||||
CollisionBox[] avoxelshape = new CollisionBox[directions.length];
|
||||
SimpleCollisionBox[] avoxelshape = new SimpleCollisionBox[directions.length];
|
||||
|
||||
for (int i = 0; i < directions.length; ++i) {
|
||||
BlockFace direction = directions[i];
|
||||
@@ -41,7 +41,7 @@ public class DynamicChorusPlant implements CollisionFactory {
|
||||
CollisionBox[] avoxelshape1 = new CollisionBox[64];
|
||||
|
||||
for (int k = 0; k < 64; ++k) {
|
||||
ComplexCollisionBox directionalShape = new ComplexCollisionBox(baseShape);
|
||||
ComplexCollisionBox directionalShape = new ComplexCollisionBox(7, baseShape); // how big is this one??
|
||||
|
||||
for (int j = 0; j < directions.length; ++j) {
|
||||
if ((k & 1 << j) != 0) {
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
package ac.grim.grimac.utils.collisions.blocks;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.CollisionFactory;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.ComplexCollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.HexCollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.*;
|
||||
import ac.grim.grimac.utils.nmsutil.Materials;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
@@ -18,16 +15,16 @@ import com.github.retrooper.packetevents.protocol.world.states.enums.Shape;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class DynamicStair implements CollisionFactory {
|
||||
protected static final CollisionBox TOP_AABB = new HexCollisionBox(0, 8, 0, 16, 16, 16);
|
||||
protected static final CollisionBox BOTTOM_AABB = new HexCollisionBox(0, 0, 0, 16, 8, 16);
|
||||
protected static final CollisionBox OCTET_NNN = new HexCollisionBox(0.0D, 0.0D, 0.0D, 8.0D, 8.0D, 8.0D);
|
||||
protected static final CollisionBox OCTET_NNP = new HexCollisionBox(0.0D, 0.0D, 8.0D, 8.0D, 8.0D, 16.0D);
|
||||
protected static final CollisionBox OCTET_NPN = new HexCollisionBox(0.0D, 8.0D, 0.0D, 8.0D, 16.0D, 8.0D);
|
||||
protected static final CollisionBox OCTET_NPP = new HexCollisionBox(0.0D, 8.0D, 8.0D, 8.0D, 16.0D, 16.0D);
|
||||
protected static final CollisionBox OCTET_PNN = new HexCollisionBox(8.0D, 0.0D, 0.0D, 16.0D, 8.0D, 8.0D);
|
||||
protected static final CollisionBox OCTET_PNP = new HexCollisionBox(8.0D, 0.0D, 8.0D, 16.0D, 8.0D, 16.0D);
|
||||
protected static final CollisionBox OCTET_PPN = new HexCollisionBox(8.0D, 8.0D, 0.0D, 16.0D, 16.0D, 8.0D);
|
||||
protected static final CollisionBox OCTET_PPP = new HexCollisionBox(8.0D, 8.0D, 8.0D, 16.0D, 16.0D, 16.0D);
|
||||
protected static final SimpleCollisionBox TOP_AABB = new HexCollisionBox(0, 8, 0, 16, 16, 16);
|
||||
protected static final SimpleCollisionBox BOTTOM_AABB = new HexCollisionBox(0, 0, 0, 16, 8, 16);
|
||||
protected static final SimpleCollisionBox OCTET_NNN = new HexCollisionBox(0.0D, 0.0D, 0.0D, 8.0D, 8.0D, 8.0D);
|
||||
protected static final SimpleCollisionBox OCTET_NNP = new HexCollisionBox(0.0D, 0.0D, 8.0D, 8.0D, 8.0D, 16.0D);
|
||||
protected static final SimpleCollisionBox OCTET_NPN = new HexCollisionBox(0.0D, 8.0D, 0.0D, 8.0D, 16.0D, 8.0D);
|
||||
protected static final SimpleCollisionBox OCTET_NPP = new HexCollisionBox(0.0D, 8.0D, 8.0D, 8.0D, 16.0D, 16.0D);
|
||||
protected static final SimpleCollisionBox OCTET_PNN = new HexCollisionBox(8.0D, 0.0D, 0.0D, 16.0D, 8.0D, 8.0D);
|
||||
protected static final SimpleCollisionBox OCTET_PNP = new HexCollisionBox(8.0D, 0.0D, 8.0D, 16.0D, 8.0D, 16.0D);
|
||||
protected static final SimpleCollisionBox OCTET_PPN = new HexCollisionBox(8.0D, 8.0D, 0.0D, 16.0D, 16.0D, 8.0D);
|
||||
protected static final SimpleCollisionBox OCTET_PPP = new HexCollisionBox(8.0D, 8.0D, 8.0D, 16.0D, 16.0D, 16.0D);
|
||||
protected static final CollisionBox[] TOP_SHAPES = makeShapes(TOP_AABB, OCTET_NNN, OCTET_PNN, OCTET_NNP, OCTET_PNP);
|
||||
protected static final CollisionBox[] BOTTOM_SHAPES = makeShapes(BOTTOM_AABB, OCTET_NPN, OCTET_PPN, OCTET_NPP, OCTET_PPP);
|
||||
private static final int[] SHAPE_BY_STATE = new int[]{12, 5, 3, 10, 14, 13, 7, 11, 13, 7, 11, 14, 8, 4, 1, 2, 4, 1, 2, 8};
|
||||
@@ -77,20 +74,25 @@ public class DynamicStair implements CollisionFactory {
|
||||
}
|
||||
|
||||
private static BlockFace rotateYCCW(BlockFace face) {
|
||||
return switch (face) {
|
||||
case EAST -> BlockFace.NORTH;
|
||||
case SOUTH -> BlockFace.EAST;
|
||||
case WEST -> BlockFace.SOUTH;
|
||||
default -> BlockFace.WEST;
|
||||
};
|
||||
switch (face) {
|
||||
default:
|
||||
case NORTH:
|
||||
return BlockFace.WEST;
|
||||
case EAST:
|
||||
return BlockFace.NORTH;
|
||||
case SOUTH:
|
||||
return BlockFace.EAST;
|
||||
case WEST:
|
||||
return BlockFace.SOUTH;
|
||||
}
|
||||
}
|
||||
|
||||
private static CollisionBox[] makeShapes(CollisionBox p_199779_0_, CollisionBox p_199779_1_, CollisionBox p_199779_2_, CollisionBox p_199779_3_, CollisionBox p_199779_4_) {
|
||||
private static CollisionBox[] makeShapes(SimpleCollisionBox p_199779_0_, SimpleCollisionBox p_199779_1_, SimpleCollisionBox p_199779_2_, SimpleCollisionBox p_199779_3_, SimpleCollisionBox p_199779_4_) {
|
||||
return IntStream.range(0, 16).mapToObj((p_199780_5_) -> makeStairShape(p_199780_5_, p_199779_0_, p_199779_1_, p_199779_2_, p_199779_3_, p_199779_4_)).toArray(CollisionBox[]::new);
|
||||
}
|
||||
|
||||
private static CollisionBox makeStairShape(int p_199781_0_, CollisionBox p_199781_1_, CollisionBox p_199781_2_, CollisionBox p_199781_3_, CollisionBox p_199781_4_, CollisionBox p_199781_5_) {
|
||||
ComplexCollisionBox voxelshape = new ComplexCollisionBox(p_199781_1_);
|
||||
private static CollisionBox makeStairShape(int p_199781_0_, SimpleCollisionBox p_199781_1_, SimpleCollisionBox p_199781_2_, SimpleCollisionBox p_199781_3_, SimpleCollisionBox p_199781_4_, SimpleCollisionBox p_199781_5_) {
|
||||
ComplexCollisionBox voxelshape = new ComplexCollisionBox(5, p_199781_1_); // can we do better than 5?
|
||||
if ((p_199781_0_ & 1) != 0) {
|
||||
voxelshape.add(p_199781_2_);
|
||||
}
|
||||
@@ -129,23 +131,36 @@ public class DynamicStair implements CollisionFactory {
|
||||
}
|
||||
|
||||
private int directionToValue(BlockFace face) {
|
||||
return switch (face) {
|
||||
case NORTH -> 2;
|
||||
case SOUTH -> 0;
|
||||
case WEST -> 1;
|
||||
case EAST -> 3;
|
||||
default -> -1;
|
||||
};
|
||||
switch (face) {
|
||||
default:
|
||||
case UP:
|
||||
case DOWN:
|
||||
return -1;
|
||||
case NORTH:
|
||||
return 2;
|
||||
case SOUTH:
|
||||
return 0;
|
||||
case WEST:
|
||||
return 1;
|
||||
case EAST:
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
private EnumShape toEnumShape(Shape shape) {
|
||||
return switch (shape) {
|
||||
case INNER_LEFT -> EnumShape.INNER_LEFT;
|
||||
case INNER_RIGHT -> EnumShape.INNER_RIGHT;
|
||||
case OUTER_LEFT -> EnumShape.OUTER_LEFT;
|
||||
case OUTER_RIGHT -> EnumShape.OUTER_RIGHT;
|
||||
default -> EnumShape.STRAIGHT;
|
||||
};
|
||||
switch (shape) {
|
||||
default:
|
||||
case STRAIGHT:
|
||||
return EnumShape.STRAIGHT;
|
||||
case INNER_LEFT:
|
||||
return EnumShape.INNER_LEFT;
|
||||
case INNER_RIGHT:
|
||||
return EnumShape.INNER_RIGHT;
|
||||
case OUTER_LEFT:
|
||||
return EnumShape.OUTER_LEFT;
|
||||
case OUTER_RIGHT:
|
||||
return EnumShape.OUTER_RIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
enum EnumShape {
|
||||
|
||||
@@ -14,13 +14,20 @@ public class PistonBaseCollision implements CollisionFactory {
|
||||
public CollisionBox fetch(GrimPlayer player, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
|
||||
if (!block.isExtended()) return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
|
||||
return switch (block.getFacing()) {
|
||||
case UP -> new HexCollisionBox(0, 0, 0, 16, 12, 16);
|
||||
case NORTH -> new HexCollisionBox(0, 0, 4, 16, 16, 16);
|
||||
case SOUTH -> new HexCollisionBox(0, 0, 0, 16, 16, 12);
|
||||
case WEST -> new HexCollisionBox(4, 0, 0, 16, 16, 16);
|
||||
case EAST -> new HexCollisionBox(0, 0, 0, 12, 16, 16);
|
||||
default -> new HexCollisionBox(0, 4, 0, 16, 16, 16);
|
||||
};
|
||||
switch (block.getFacing()) {
|
||||
default:
|
||||
case DOWN:
|
||||
return new HexCollisionBox(0, 4, 0, 16, 16, 16);
|
||||
case UP:
|
||||
return new HexCollisionBox(0, 0, 0, 16, 12, 16);
|
||||
case NORTH:
|
||||
return new HexCollisionBox(0, 0, 4, 16, 16, 16);
|
||||
case SOUTH:
|
||||
return new HexCollisionBox(0, 0, 0, 16, 16, 12);
|
||||
case WEST:
|
||||
return new HexCollisionBox(4, 0, 0, 16, 16, 16);
|
||||
case EAST:
|
||||
return new HexCollisionBox(0, 0, 0, 12, 16, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,35 +38,46 @@ public class PistonHeadCollision implements CollisionFactory {
|
||||
longAmount = 0;
|
||||
|
||||
|
||||
return switch (block.getFacing()) {
|
||||
case UP -> new ComplexCollisionBox(new HexCollisionBox(0, 12, 0, 16, 16, 16),
|
||||
new HexCollisionBox(6, 0 - longAmount, 6, 10, 12, 10));
|
||||
case NORTH -> new ComplexCollisionBox(new HexCollisionBox(0, 0, 0, 16, 16, 4),
|
||||
new HexCollisionBox(6, 6, 4, 10, 10, 16 + longAmount));
|
||||
case SOUTH -> {
|
||||
switch (block.getFacing()) {
|
||||
case DOWN:
|
||||
default:
|
||||
return new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(0, 0, 0, 16, 4, 16),
|
||||
new HexCollisionBox(6, 4, 6, 10, 16 + longAmount, 10));
|
||||
case UP:
|
||||
return new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(0, 12, 0, 16, 16, 16),
|
||||
new HexCollisionBox(6, 0 - longAmount, 6, 10, 12, 10));
|
||||
case NORTH:
|
||||
return new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(0, 0, 0, 16, 16, 4),
|
||||
new HexCollisionBox(6, 6, 4, 10, 10, 16 + longAmount));
|
||||
case SOUTH:
|
||||
// SOUTH piston is glitched in 1.7 and 1.8, fixed in 1.9
|
||||
// Don't bother with short piston boxes as 1.7/1.8 clients don't have them
|
||||
if (version.isOlderThanOrEquals(ClientVersion.V_1_8))
|
||||
yield new ComplexCollisionBox(new HexCollisionBox(0, 0, 12, 16, 16, 16),
|
||||
return new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(0, 0, 12, 16, 16, 16),
|
||||
new HexCollisionBox(4, 6, 0, 12, 10, 12));
|
||||
|
||||
yield new ComplexCollisionBox(new HexCollisionBox(0, 0, 12, 16, 16, 16),
|
||||
return new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(0, 0, 12, 16, 16, 16),
|
||||
new HexCollisionBox(6, 6, 0 - longAmount, 10, 10, 12));
|
||||
}
|
||||
case WEST -> {
|
||||
case WEST:
|
||||
// WEST piston is glitched in 1.7 and 1.8, fixed in 1.9
|
||||
// Don't bother with short piston boxes as 1.7/1.8 clients don't have them
|
||||
if (version.isOlderThanOrEquals(ClientVersion.V_1_8))
|
||||
yield new ComplexCollisionBox(new HexCollisionBox(0, 0, 0, 4, 16, 16),
|
||||
return new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(0, 0, 0, 4, 16, 16),
|
||||
new HexCollisionBox(6, 4, 4, 10, 12, 16));
|
||||
|
||||
yield new ComplexCollisionBox(new HexCollisionBox(0, 0, 0, 4, 16, 16),
|
||||
return new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(0, 0, 0, 4, 16, 16),
|
||||
new HexCollisionBox(4, 6, 6, 16 + longAmount, 10, 10));
|
||||
}
|
||||
case EAST -> new ComplexCollisionBox(new HexCollisionBox(12, 0, 0, 16, 16, 16),
|
||||
new HexCollisionBox(0 - longAmount, 6, 4, 12, 10, 12));
|
||||
default -> new ComplexCollisionBox(new HexCollisionBox(0, 0, 0, 16, 4, 16),
|
||||
new HexCollisionBox(6, 4, 6, 10, 16 + longAmount, 10));
|
||||
};
|
||||
case EAST:
|
||||
return new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(12, 0, 0, 16, 16, 16),
|
||||
new HexCollisionBox(0 - longAmount, 6, 4, 12, 10, 12));
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -18,7 +18,7 @@ import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
|
||||
public class DynamicCollisionFence extends DynamicConnecting implements CollisionFactory {
|
||||
private static final CollisionBox[] COLLISION_BOXES = makeShapes(2.0F, 2.0F, 24.0F, 0.0F, 24.0F, true);
|
||||
private static final CollisionBox[] COLLISION_BOXES = makeShapes(2.0F, 2.0F, 24.0F, 0.0F, 24.0F, true, 1);
|
||||
|
||||
@Override
|
||||
public CollisionBox fetch(GrimPlayer player, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
|
||||
|
||||
+2
-2
@@ -21,7 +21,7 @@ import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
|
||||
public class DynamicCollisionPane extends DynamicConnecting implements CollisionFactory {
|
||||
|
||||
private static final CollisionBox[] COLLISION_BOXES = makeShapes(1.0F, 1.0F, 16.0F, 0.0F, 16.0F, true);
|
||||
private static final CollisionBox[] COLLISION_BOXES = makeShapes(1.0F, 1.0F, 16.0F, 0.0F, 16.0F, true, 1);
|
||||
|
||||
@Override
|
||||
public CollisionBox fetch(GrimPlayer player, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
|
||||
@@ -52,7 +52,7 @@ public class DynamicCollisionPane extends DynamicConnecting implements Collision
|
||||
if (version.isNewerThanOrEquals(ClientVersion.V_1_9)) {
|
||||
return COLLISION_BOXES[getAABBIndex(north, east, south, west)].copy();
|
||||
} else { // 1.8 and below clients have pane bounding boxes one pixel less
|
||||
ComplexCollisionBox boxes = new ComplexCollisionBox();
|
||||
ComplexCollisionBox boxes = new ComplexCollisionBox(2);
|
||||
if ((!west || !east) && (west || east || north || south)) {
|
||||
if (west) {
|
||||
boxes.add(new SimpleCollisionBox(0.0F, 0.0F, 0.4375F, 0.5F, 1.0F, 0.5625F));
|
||||
|
||||
+4
-7
@@ -16,10 +16,9 @@ import com.github.retrooper.packetevents.protocol.world.states.enums.West;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
|
||||
public class DynamicCollisionWall extends DynamicConnecting implements CollisionFactory {
|
||||
public static final CollisionBox[] BOXES = makeShapes(4.0F, 3.0F, 16.0F, 0.0F, 16.0F, false);
|
||||
// https://bugs.mojang.com/browse/MC-9565
|
||||
// https://bugs.mojang.com/browse/MC-94016
|
||||
private static final CollisionBox[] COLLISION_BOXES = makeShapes(4.0F, 3.0F, 24.0F, 0.0F, 24.0F, false);
|
||||
private static final CollisionBox[] COLLISION_BOXES = makeShapes(4.0F, 3.0F, 24.0F, 0.0F, 24.0F, false, 1);
|
||||
|
||||
/**
|
||||
* @deprecated use DynamicHitboxWall
|
||||
@@ -53,12 +52,11 @@ public class DynamicCollisionWall extends DynamicConnecting implements Collision
|
||||
|
||||
// On 1.13+ clients the bounding box is much more complicated
|
||||
if (version.isNewerThanOrEquals(ClientVersion.V_1_13)) {
|
||||
ComplexCollisionBox box = new ComplexCollisionBox();
|
||||
ComplexCollisionBox box = new ComplexCollisionBox(5);
|
||||
|
||||
// Proper and faster way would be to compute all this beforehand
|
||||
if (up == 1) {
|
||||
box.add(new HexCollisionBox(4, 0, 4, 12, 16, 12));
|
||||
return box;
|
||||
}
|
||||
|
||||
if (north == 1) {
|
||||
@@ -81,6 +79,7 @@ public class DynamicCollisionWall extends DynamicConnecting implements Collision
|
||||
} else if (east == 2) {
|
||||
box.add(new HexCollisionBox(5, 0, 5, 16, 16, 11));
|
||||
}
|
||||
return box;
|
||||
}
|
||||
|
||||
// Magic 1.8 code for walls that I copied over, 1.12 below uses this mess
|
||||
@@ -143,9 +142,7 @@ public class DynamicCollisionWall extends DynamicConnecting implements Collision
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13)) {
|
||||
// Proper and faster way would be to compute all this beforehand
|
||||
if (up) {
|
||||
ComplexCollisionBox box = new ComplexCollisionBox(COLLISION_BOXES[getAABBIndex(north, east, south, west)].copy());
|
||||
box.add(new HexCollisionBox(4, 0, 4, 12, 24, 12));
|
||||
return box;
|
||||
return COLLISION_BOXES[getAABBIndex(north, east, south, west)].copy().union(new HexCollisionBox(4, 0, 4, 12, 24, 12));
|
||||
}
|
||||
|
||||
return COLLISION_BOXES[getAABBIndex(north, east, south, west)].copy();
|
||||
|
||||
+12
-5
@@ -18,7 +18,7 @@ import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
|
||||
public class DynamicConnecting {
|
||||
|
||||
public static CollisionBox[] makeShapes(float p_196408_1_, float p_196408_2_, float p_196408_3_, float p_196408_4_, float p_196408_5_, boolean includeCenter) {
|
||||
public static CollisionBox[] makeShapes(float p_196408_1_, float p_196408_2_, float p_196408_3_, float p_196408_4_, float p_196408_5_, boolean includeCenter, int additionalMaxIndex) {
|
||||
float middleMin = 8.0F - p_196408_1_;
|
||||
float middleMax = 8.0F + p_196408_1_;
|
||||
float f2 = 8.0F - p_196408_2_;
|
||||
@@ -29,14 +29,21 @@ public class DynamicConnecting {
|
||||
SimpleCollisionBox voxelshape3 = new HexCollisionBox(0.0D, p_196408_4_, f2, f3, p_196408_5_, f3);
|
||||
SimpleCollisionBox voxelshape4 = new HexCollisionBox(f2, p_196408_4_, f2, 16.0D, p_196408_5_, f3);
|
||||
|
||||
ComplexCollisionBox voxelshape5 = new ComplexCollisionBox(voxelshape1, voxelshape4);
|
||||
ComplexCollisionBox voxelshape6 = new ComplexCollisionBox(voxelshape2, voxelshape3);
|
||||
ComplexCollisionBox voxelshape5 = new ComplexCollisionBox(2 + additionalMaxIndex, voxelshape1, voxelshape4);
|
||||
ComplexCollisionBox voxelshape6 = new ComplexCollisionBox(2 + additionalMaxIndex, voxelshape2, voxelshape3);
|
||||
|
||||
CollisionBox[] avoxelshape = new CollisionBox[]{NoCollisionBox.INSTANCE, voxelshape2, voxelshape3, voxelshape6, voxelshape1, new ComplexCollisionBox(voxelshape2, voxelshape1), new ComplexCollisionBox(voxelshape3, voxelshape1), new ComplexCollisionBox(voxelshape6, voxelshape1), voxelshape4, new ComplexCollisionBox(voxelshape2, voxelshape4), new ComplexCollisionBox(voxelshape3, voxelshape4), new ComplexCollisionBox(voxelshape6, voxelshape4), voxelshape5, new ComplexCollisionBox(voxelshape2, voxelshape5), new ComplexCollisionBox(voxelshape3, voxelshape5), new ComplexCollisionBox(voxelshape6, voxelshape5)};
|
||||
CollisionBox[] avoxelshape = new CollisionBox[]{
|
||||
NoCollisionBox.INSTANCE, voxelshape2, voxelshape3, voxelshape6, voxelshape1,
|
||||
new ComplexCollisionBox(2 + additionalMaxIndex, voxelshape2, voxelshape1), new ComplexCollisionBox(2 + additionalMaxIndex, voxelshape3, voxelshape1),
|
||||
new ComplexCollisionBox(3 + additionalMaxIndex, voxelshape2, voxelshape3, voxelshape1), voxelshape4,
|
||||
new ComplexCollisionBox(2 + additionalMaxIndex, voxelshape2, voxelshape4), new ComplexCollisionBox(2 + additionalMaxIndex, voxelshape3, voxelshape4),
|
||||
new ComplexCollisionBox(3 + additionalMaxIndex, voxelshape2, voxelshape3, voxelshape4), voxelshape5,
|
||||
new ComplexCollisionBox(3 + additionalMaxIndex, voxelshape2, voxelshape1, voxelshape4), new ComplexCollisionBox(3 + additionalMaxIndex, voxelshape3, voxelshape1, voxelshape4),
|
||||
new ComplexCollisionBox(4 + additionalMaxIndex, voxelshape1, voxelshape2, voxelshape3, voxelshape4)};
|
||||
|
||||
if (includeCenter) {
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
avoxelshape[i] = new ComplexCollisionBox(up, avoxelshape[i]);
|
||||
avoxelshape[i] = avoxelshape[i].union(up);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+15
-12
@@ -16,27 +16,30 @@ import com.github.retrooper.packetevents.protocol.world.states.enums.*;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DynamicHitboxFence extends DynamicConnecting implements HitBoxFactory {
|
||||
private static final CollisionBox[] MODERN_HITBOXES = makeShapes(2.0F, 2.0F, 24.0F, 0.0F, 24.0F, true);
|
||||
private static final CollisionBox[] MODERN_HITBOXES = makeShapes(2.0F, 2.0F, 24.0F, 0.0F, 24.0F, true, 1);
|
||||
|
||||
public static SimpleCollisionBox[] LEGACY_HITBOXES = new SimpleCollisionBox[] {new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D)};
|
||||
|
||||
static {
|
||||
for (int i = 0; i < MODERN_HITBOXES.length; i++) {
|
||||
CollisionBox collisionBox = MODERN_HITBOXES[i];
|
||||
List<SimpleCollisionBox> boxes = new ArrayList<>();
|
||||
collisionBox.downCast(boxes);
|
||||
// no ComplexCollisionBox produced by makeShapes is every larger than 5 SimpleCollisionBoxes
|
||||
private static final int MAX_MODERN_HITBOX_COMPLEX_COLLISION_BOX_SIZE = 5;
|
||||
|
||||
for (SimpleCollisionBox box : boxes) {
|
||||
if (box.maxY > 1) {
|
||||
box.maxY = 1;
|
||||
static {
|
||||
SimpleCollisionBox[] boxes = new SimpleCollisionBox[MAX_MODERN_HITBOX_COMPLEX_COLLISION_BOX_SIZE];
|
||||
|
||||
// we start from one because MODERN_HITBOXES[0] is a NoCollisionBox
|
||||
for (int i = 1; i < MODERN_HITBOXES.length; i++) {
|
||||
CollisionBox collisionBox = MODERN_HITBOXES[i];
|
||||
int size = collisionBox.downCast(boxes);
|
||||
|
||||
for (int j = 0; j < size; j++) {
|
||||
if (boxes[j].maxY > 1) {
|
||||
boxes[j].maxY = 1;
|
||||
}
|
||||
}
|
||||
|
||||
MODERN_HITBOXES[i] = new ComplexCollisionBox(boxes.toArray(new SimpleCollisionBox[0]));
|
||||
MODERN_HITBOXES[i] = size == 1 ? boxes[0] : new ComplexCollisionBox(size, boxes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
|
||||
public class DynamicHitboxPane extends DynamicConnecting implements HitBoxFactory {
|
||||
|
||||
private static final CollisionBox[] COLLISION_BOXES = makeShapes(1.0F, 1.0F, 16.0F, 0.0F, 16.0F, true);
|
||||
private static final CollisionBox[] COLLISION_BOXES = makeShapes(1.0F, 1.0F, 16.0F, 0.0F, 16.0F, true, 1);
|
||||
|
||||
@Override
|
||||
public CollisionBox fetch(GrimPlayer player, StateType item, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
|
||||
|
||||
+1
-3
@@ -13,8 +13,6 @@ import com.github.retrooper.packetevents.protocol.world.states.enums.*;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
|
||||
public class DynamicHitboxWall extends DynamicConnecting implements HitBoxFactory {
|
||||
private static final CollisionBox[] HIT_BOXES = makeShapes(4.0F, 3.0F, 16.0F, 0.0F, 16.0F, false);
|
||||
|
||||
@Override
|
||||
public CollisionBox fetch(GrimPlayer player, StateType heldItem, ClientVersion version, WrappedBlockState state, int x, int y, int z) {
|
||||
int[] connections = getConnections(player, version, state, x, y, z);
|
||||
@@ -60,7 +58,7 @@ public class DynamicHitboxWall extends DynamicConnecting implements HitBoxFactor
|
||||
}
|
||||
|
||||
private CollisionBox getModernHitBox(int north, int south, int west, int east, int up) {
|
||||
ComplexCollisionBox box = new ComplexCollisionBox();
|
||||
ComplexCollisionBox box = new ComplexCollisionBox(5);
|
||||
if (up == 1) {
|
||||
box.add(new HexCollisionBox(4, 0, 4, 12, 16, 12));
|
||||
}
|
||||
|
||||
Loaded 100 of 162 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user