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
+41
-8
@@ -26,13 +26,9 @@ 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
|
||||
// Checks Project properties -> environment variable -> defaults true
|
||||
@@ -63,7 +59,7 @@ dependencies {
|
||||
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")
|
||||
@@ -71,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")
|
||||
}
|
||||
@@ -159,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,7 @@ public class BadPacketsE extends Check implements PacketCheck {
|
||||
noReminderTicks = 0;
|
||||
} else if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType())) {
|
||||
noReminderTicks++;
|
||||
} else if (event.getPacketType() == PacketType.Play.Client.STEER_VEHICLE ||
|
||||
event.getPacketType() == PacketType.Play.Client.VEHICLE_MOVE) {
|
||||
} else if (event.getPacketType() == PacketType.Play.Client.STEER_VEHICLE) {
|
||||
noReminderTicks = 0; // Exempt vehicles
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,9 +41,7 @@ public class BadPacketsR extends Check implements PacketCheck {
|
||||
if ((event.getPacketType() == PacketType.Play.Client.PLAYER_POSITION_AND_ROTATION ||
|
||||
event.getPacketType() == PacketType.Play.Client.PLAYER_POSITION) && !player.compensatedEntities.getSelf().inVehicle()) {
|
||||
positions++;
|
||||
} else if ((event.getPacketType() == PacketType.Play.Client.STEER_VEHICLE ||
|
||||
event.getPacketType() == PacketType.Play.Client.VEHICLE_MOVE)
|
||||
&& player.compensatedEntities.getSelf().inVehicle()) {
|
||||
} else if (event.getPacketType() == PacketType.Play.Client.STEER_VEHICLE && player.compensatedEntities.getSelf().inVehicle()) {
|
||||
positions++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package ac.grim.grimac.checks.impl.badpackets;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
@@ -9,48 +8,22 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
|
||||
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 com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
|
||||
|
||||
@CheckData(name = "BadPacketsX")
|
||||
@CheckData(name = "BadPacketsX", experimental = true)
|
||||
public class BadPacketsX extends Check implements PacketCheck {
|
||||
public BadPacketsX(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private int lastTick;
|
||||
private boolean didLastFlag;
|
||||
private Vector3i lastBreakLoc;
|
||||
private StateType lastBlockType;
|
||||
|
||||
public final boolean noFireHitbox = player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_15_2);
|
||||
|
||||
public final void handle(PacketReceiveEvent event, WrapperPlayClientPlayerDigging dig, StateType block) {
|
||||
if (dig.getAction() != DiggingAction.START_DIGGING && dig.getAction() != DiggingAction.FINISHED_DIGGING)
|
||||
return;
|
||||
|
||||
// Fixes false from breaking kelp underwater
|
||||
// The client sends two start digging packets to the server both in the same tick. BadPacketsX gets called twice, doesn't false the first time, but falses the second
|
||||
// One ends up breaking the kelp, the other ends up doing nothing besides falsing this check because we think they're trying to mine water
|
||||
// I am explicitly making this patch as narrow and specific as possible to potentially discover other blocks that exhibit similar behaviour
|
||||
int newTick = GrimAPI.INSTANCE.getTickManager().currentTick;
|
||||
if (lastTick == newTick
|
||||
&& lastBreakLoc.equals(dig.getBlockPosition())
|
||||
&& !didLastFlag
|
||||
&& lastBlockType.getHardness() == 0.0F
|
||||
&& lastBlockType.getBlastResistance() == 0.0F
|
||||
&& block == StateTypes.WATER
|
||||
) {
|
||||
return;
|
||||
}
|
||||
lastTick = newTick;
|
||||
lastBreakLoc = dig.getBlockPosition();
|
||||
lastBlockType = block;
|
||||
|
||||
// the block does not have a hitbox
|
||||
boolean invalid = (block == StateTypes.LIGHT && !(player.getInventory().getHeldItem().is(ItemTypes.LIGHT) || player.getInventory().getOffHand().is(ItemTypes.LIGHT)))
|
||||
|| block.isAir()
|
||||
@@ -63,11 +36,8 @@ public class BadPacketsX extends Check implements PacketCheck {
|
||||
|| block.getHardness() == -1.0f && dig.getAction() == DiggingAction.FINISHED_DIGGING;
|
||||
|
||||
if (invalid && flagAndAlert("block=" + block.getName() + ", type=" + dig.getAction()) && shouldModifyPackets()) {
|
||||
didLastFlag = true;
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
} else {
|
||||
didLastFlag = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPl
|
||||
import static ac.grim.grimac.events.packets.patch.ResyncWorldUtil.resyncPosition;
|
||||
import static ac.grim.grimac.utils.nmsutil.BlockBreakSpeed.getBlockDamage;
|
||||
|
||||
@CheckData(name = "BadPacketsZ")
|
||||
@CheckData(name = "BadPacketsZ", experimental = true)
|
||||
public class BadPacketsZ extends Check implements PacketCheck {
|
||||
public BadPacketsZ(final GrimPlayer player) {
|
||||
super(player);
|
||||
|
||||
@@ -59,7 +59,7 @@ public class MultiInteractA extends Check implements PostPredictionCheck {
|
||||
// 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) && !player.uncertaintyHandler.lastVehicleSwitch.hasOccurredSince(0)) {
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (String verbose : flags) {
|
||||
flagAndAlert(verbose);
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ public class MultiInteractB extends Check implements PostPredictionCheck {
|
||||
// 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) && !player.uncertaintyHandler.lastVehicleSwitch.hasOccurredSince(0)) {
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (String verbose : flags) {
|
||||
flagAndAlert(verbose);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
+3
-4
@@ -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);
|
||||
}
|
||||
|
||||
@@ -43,7 +42,7 @@ public class PostCheck extends Check implements PacketCheck, PostPredictionCheck
|
||||
// Okay, the user might be cheating, let's double check
|
||||
// 1.8 clients have the idle packet, and this shouldn't false on 1.8 clients
|
||||
// 1.9+ clients have predictions, which will determine if hidden tick skipping occurred
|
||||
if (player.isTickingReliablyFor(3) && !player.uncertaintyHandler.lastVehicleSwitch.hasOccurredSince(0)) {
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (String flag : flags) {
|
||||
flagAndAlert(flag);
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,7 @@ public class MultiPlace extends BlockPlaceCheck {
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
if (player.getClientVersion().isNewerThan(ClientVersion.V_1_8) && player.isTickingReliablyFor(3) && !player.uncertaintyHandler.lastVehicleSwitch.hasOccurredSince(0)) {
|
||||
if (player.getClientVersion().isNewerThan(ClientVersion.V_1_8) && player.isTickingReliablyFor(3)) {
|
||||
for (String verbose : flags) {
|
||||
flagAndAlert(verbose);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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!");
|
||||
|
||||
@@ -3,8 +3,6 @@ package ac.grim.grimac.commands;
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import co.aikar.commands.BaseCommand;
|
||||
import co.aikar.commands.BukkitCommandCompletionContext;
|
||||
import co.aikar.commands.CommandCompletions.CommandCompletionHandler;
|
||||
import co.aikar.commands.annotation.CommandAlias;
|
||||
import co.aikar.commands.annotation.CommandCompletion;
|
||||
import co.aikar.commands.annotation.CommandPermission;
|
||||
@@ -12,21 +10,17 @@ import co.aikar.commands.annotation.Subcommand;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@CommandAlias("grim|grimac")
|
||||
public class GrimStopSpectating extends BaseCommand {
|
||||
public static final CommandCompletionHandler<BukkitCommandCompletionContext> completionHandler = context -> context.getSender().hasPermission("grim.spectate.stophere") ? List.of("here") : List.of();
|
||||
|
||||
@Subcommand("stopspectating")
|
||||
@CommandPermission("grim.spectate")
|
||||
@CommandCompletion("@stopspectating")
|
||||
@CommandCompletion("here")
|
||||
public void onStopSpectate(CommandSender sender, String[] args) {
|
||||
String string = args.length > 0 ? args[0] : null;
|
||||
if (!(sender instanceof Player)) return;
|
||||
Player player = (Player) sender;
|
||||
if (GrimAPI.INSTANCE.getSpectateManager().isSpectating(player.getUniqueId())) {
|
||||
boolean teleportBack = string == null || !string.equalsIgnoreCase("here") || !sender.hasPermission("grim.spectate.stophere");
|
||||
boolean teleportBack = string == null || !string.equalsIgnoreCase("here");
|
||||
GrimAPI.INSTANCE.getSpectateManager().disable(player, teleportBack);
|
||||
} else {
|
||||
String message = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("cannot-spectate-return", "%prefix% &cYou can only do this after spectating a player.");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -98,10 +98,6 @@ public class PacketEntityReplication extends Check implements PacketCheck {
|
||||
WrapperPlayServerSpawnPlayer packetOutEntity = new WrapperPlayServerSpawnPlayer(event);
|
||||
addEntity(packetOutEntity.getEntityId(), packetOutEntity.getUUID(), EntityTypes.PLAYER, packetOutEntity.getPosition(), packetOutEntity.getYaw(), packetOutEntity.getPitch(), packetOutEntity.getEntityMetadata(), 0);
|
||||
}
|
||||
if (event.getPacketType() == PacketType.Play.Server.SPAWN_PAINTING) {
|
||||
WrapperPlayServerSpawnPainting packetOutEntity = new WrapperPlayServerSpawnPainting(event);
|
||||
addEntity(packetOutEntity.getEntityId(), packetOutEntity.getUUID(), EntityTypes.PAINTING, packetOutEntity.getPosition().toVector3d(), packetOutEntity.getDirection().getHorizontalIndex(), 0f, null, packetOutEntity.getType().isPresent() ? packetOutEntity.getType().get().getId() : -1);
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Server.ENTITY_RELATIVE_MOVE) {
|
||||
WrapperPlayServerEntityRelativeMove move = new WrapperPlayServerEntityRelativeMove(event);
|
||||
@@ -231,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.
|
||||
@@ -457,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());
|
||||
|
||||
@@ -22,7 +22,7 @@ public class PacketPlayerSteer extends PacketListenerAbstract {
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
//TODO: add support for 1.21.2 with new vehicle move packet
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.STEER_VEHICLE) {
|
||||
WrapperPlayClientSteerVehicle steer = new WrapperPlayClientSteerVehicle(event);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerMultiBlockChange;
|
||||
import io.github.retrooper.packetevents.util.folia.FoliaScheduler;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
@@ -36,20 +35,19 @@ public class ResyncWorldUtil {
|
||||
|| !player.compensatedWorld.isChunkLoaded(maxBlockX >> 4, minBlockZ >> 4) || !player.compensatedWorld.isChunkLoaded(maxBlockX >> 4, maxBlockZ >> 4))
|
||||
return;
|
||||
|
||||
if (player.bukkitPlayer == null) return;
|
||||
World world = player.bukkitPlayer.getWorld();
|
||||
|
||||
// Takes 0.15ms or so to complete. Not bad IMO. Unsure how I could improve this other than sending packets async.
|
||||
// But that's on PacketEvents.
|
||||
FoliaScheduler.getRegionScheduler().execute(GrimAPI.INSTANCE.getPlugin(), world,
|
||||
FoliaScheduler.getRegionScheduler().execute(GrimAPI.INSTANCE.getPlugin(), player.bukkitPlayer.getWorld(),
|
||||
minBlockX >> 4, minBlockZ >> 4, () -> {
|
||||
boolean flat = PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13);
|
||||
|
||||
if (player.bukkitPlayer == null) return;
|
||||
// Player hasn't spawned, don't spam packets
|
||||
if (!player.getSetbackTeleportUtil().hasAcceptedSpawnTeleport) return;
|
||||
|
||||
// Check the 4 corners of the BB for loaded chunks, don't freeze main thread to load chunks.
|
||||
if (!world.isChunkLoaded(minBlockX >> 4, minBlockZ >> 4) || !world.isChunkLoaded(minBlockX >> 4, maxBlockZ >> 4)
|
||||
|| !world.isChunkLoaded(maxBlockX >> 4, minBlockZ >> 4) || !world.isChunkLoaded(maxBlockX >> 4, maxBlockZ >> 4))
|
||||
if (!player.bukkitPlayer.getWorld().isChunkLoaded(minBlockX >> 4, minBlockZ >> 4) || !player.bukkitPlayer.getWorld().isChunkLoaded(minBlockX >> 4, maxBlockZ >> 4)
|
||||
|| !player.bukkitPlayer.getWorld().isChunkLoaded(maxBlockX >> 4, minBlockZ >> 4) || !player.bukkitPlayer.getWorld().isChunkLoaded(maxBlockX >> 4, maxBlockZ >> 4))
|
||||
return;
|
||||
|
||||
// This is based on Tuinity's code, thanks leaf. Now merged into paper.
|
||||
@@ -78,7 +76,7 @@ public class ResyncWorldUtil {
|
||||
int minX = currChunkX == minChunkX ? minBlockX & 15 : 0; // coordinate in chunk
|
||||
int maxX = currChunkX == maxChunkX ? maxBlockX & 15 : 15; // coordinate in chunk
|
||||
|
||||
Chunk chunk = world.getChunkAt(currChunkX, currChunkZ);
|
||||
Chunk chunk = player.bukkitPlayer.getWorld().getChunkAt(currChunkX, currChunkZ);
|
||||
|
||||
for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) {
|
||||
int minY = currChunkY == minChunkY ? minBlockY & 15 : 0; // coordinate in chunk
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -2,14 +2,13 @@ package ac.grim.grimac.manager.init.start;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.manager.init.Initable;
|
||||
import io.github.retrooper.packetevents.bstats.bukkit.Metrics;
|
||||
|
||||
public class BStats implements Initable {
|
||||
@Override
|
||||
public void start() {
|
||||
int pluginId = 12820; // <-- Replace with the id of your plugin!
|
||||
try {
|
||||
new Metrics(GrimAPI.INSTANCE.getPlugin(), pluginId);
|
||||
new io.github.retrooper.packetevents.bstats.bukkit.Metrics(GrimAPI.INSTANCE.getPlugin(), pluginId);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,5 @@ public class CommandRegister implements Initable {
|
||||
commandManager.registerCommand(new GrimStopSpectating());
|
||||
commandManager.registerCommand(new GrimLog());
|
||||
commandManager.registerCommand(new GrimVerbose());
|
||||
|
||||
commandManager.getCommandCompletions().registerCompletion("stopspectating", GrimStopSpectating.completionHandler);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -347,9 +352,9 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
player.packetStateData.lastRiptide = currentTime;
|
||||
}
|
||||
|
||||
SimpleCollisionBox steppingOnBB = GetBoundingBox.getCollisionBoxForPlayer(player, player.x, player.y, player.z).expand(player.getMovementThreshold()).offset(0, -1, 0);
|
||||
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();
|
||||
@@ -411,8 +416,8 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
player.uncertaintyHandler.lastUnderwaterFlyingHack.reset();
|
||||
}
|
||||
|
||||
boolean couldBeStuckSpeed = Collisions.checkStuckSpeed(player, player.getMovementThreshold());
|
||||
boolean couldLeaveStuckSpeed = player.isPointThree() && Collisions.checkStuckSpeed(player, -player.getMovementThreshold());
|
||||
boolean couldBeStuckSpeed = Collisions.checkStuckSpeed(player, 0.03);
|
||||
boolean couldLeaveStuckSpeed = player.isPointThree() && Collisions.checkStuckSpeed(player, -0.03);
|
||||
player.uncertaintyHandler.claimingLeftStuckSpeed = !player.compensatedEntities.getSelf().inVehicle() && player.stuckSpeedMultiplier.getX() < 1 && !couldLeaveStuckSpeed;
|
||||
|
||||
if (couldBeStuckSpeed) {
|
||||
@@ -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"));
|
||||
@@ -268,14 +271,13 @@ public class PlayerBaseTick {
|
||||
|
||||
|
||||
private void moveTowardsClosestSpace(double xPosition, double zPosition) {
|
||||
double movementThreshold = player.getMovementThreshold();
|
||||
player.boundingBox = player.boundingBox.expand(movementThreshold, 0, movementThreshold); // 0.03... thanks mojang!
|
||||
player.boundingBox = player.boundingBox.expand(0.03, 0, 0.03); // 0.03... thanks mojang!
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14)) {
|
||||
moveTowardsClosestSpaceModern(xPosition, zPosition);
|
||||
} else {
|
||||
moveTowardsClosestSpaceLegacy(xPosition, zPosition);
|
||||
}
|
||||
player.boundingBox = player.boundingBox.expand(-movementThreshold, 0, -movementThreshold);
|
||||
player.boundingBox = player.boundingBox.expand(-0.03, 0, -0.03);
|
||||
}
|
||||
|
||||
// Mojang is incompetent and this will push the player out a lot when using elytras
|
||||
@@ -361,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;
|
||||
@@ -414,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) {
|
||||
@@ -464,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) {
|
||||
@@ -489,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
|
||||
@@ -127,13 +133,12 @@ public class PointThreeEstimator {
|
||||
|
||||
// Calculate head hitters. Take a shortcut by checking if the player doesn't intersect with this block, but does
|
||||
// when the player vertically moves upwards by 0.03! This is equivalent to the move method, but MUCH faster.
|
||||
double movementThreshold = player.getMovementThreshold();
|
||||
SimpleCollisionBox slightlyExpanded = normalBox.copy().expand(movementThreshold, 0, movementThreshold);
|
||||
if (!slightlyExpanded.isIntersected(data) && slightlyExpanded.offset(0, movementThreshold, 0).isIntersected(data)) {
|
||||
SimpleCollisionBox slightlyExpanded = normalBox.copy().expand(0.03, 0, 0.03);
|
||||
if (!slightlyExpanded.isIntersected(data) && slightlyExpanded.offset(0, 0.03, 0).isIntersected(data)) {
|
||||
headHitter = true;
|
||||
}
|
||||
|
||||
SimpleCollisionBox pointThreeBox = GetBoundingBox.getBoundingBoxFromPosAndSize(player, player.x, player.y - movementThreshold, player.z, 0.66f, 1.86f);
|
||||
SimpleCollisionBox pointThreeBox = GetBoundingBox.getBoundingBoxFromPosAndSize(player, player.x, player.y - 0.03, player.z, 0.66f, 1.86f);
|
||||
if ((Materials.isWater(player.getClientVersion(), state) || stateType == StateTypes.LAVA) &&
|
||||
pointThreeBox.isIntersected(new SimpleCollisionBox(x, y, z))) {
|
||||
|
||||
@@ -141,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;
|
||||
}
|
||||
@@ -152,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;
|
||||
@@ -213,8 +222,7 @@ public class PointThreeEstimator {
|
||||
}
|
||||
|
||||
public void endOfTickTick() {
|
||||
double movementThreshold = player.getMovementThreshold();
|
||||
SimpleCollisionBox pointThreeBox = GetBoundingBox.getBoundingBoxFromPosAndSize(player, player.x, player.y - movementThreshold, player.z, 0.66f, 1.86f);
|
||||
SimpleCollisionBox pointThreeBox = GetBoundingBox.getBoundingBoxFromPosAndSize(player, player.x, player.y - 0.03, player.z, 0.66f, 1.86f);
|
||||
|
||||
// Determine the head hitter using the current Y position
|
||||
SimpleCollisionBox oldBB = player.boundingBox;
|
||||
@@ -224,7 +232,7 @@ public class PointThreeEstimator {
|
||||
for (float sizes : (player.skippedTickInActualMovement ? new float[]{0.6f, 1.5f, 1.8f} : new float[]{player.pose.height})) {
|
||||
// Try to limit collisions to be as small as possible, for maximum performance
|
||||
player.boundingBox = GetBoundingBox.getBoundingBoxFromPosAndSize(player, player.x, player.y + (sizes - 0.01f), player.z, 0.6f, 0.01f);
|
||||
headHitter = headHitter || Collisions.collide(player, 0, movementThreshold, 0).getY() != movementThreshold;
|
||||
headHitter = headHitter || Collisions.collide(player, 0, 0.03, 0).getY() != 0.03;
|
||||
}
|
||||
|
||||
player.boundingBox = oldBB;
|
||||
@@ -247,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;
|
||||
});
|
||||
}
|
||||
@@ -305,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;
|
||||
}
|
||||
@@ -339,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,10 +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 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
|
||||
@@ -147,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());
|
||||
@@ -170,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
|
||||
|
||||
@@ -303,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,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;
|
||||
@@ -35,18 +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.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
|
||||
@@ -540,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) {
|
||||
@@ -661,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -174,22 +174,30 @@ 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])),
|
||||
@@ -245,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))
|
||||
@@ -350,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) -> {
|
||||
@@ -532,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])),
|
||||
@@ -675,9 +689,7 @@ 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
|
||||
@@ -723,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)
|
||||
@@ -752,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)
|
||||
@@ -816,37 +833,50 @@ 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());
|
||||
@@ -936,27 +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(2,
|
||||
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),
|
||||
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)) {
|
||||
@@ -969,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) -> {
|
||||
@@ -1010,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);
|
||||
@@ -1052,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) {
|
||||
@@ -1068,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) {
|
||||
@@ -1129,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,24 +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(2,
|
||||
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 -> new ComplexCollisionBox(2,
|
||||
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));
|
||||
};
|
||||
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) ->
|
||||
@@ -247,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,
|
||||
@@ -398,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) -> {
|
||||
@@ -638,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) {
|
||||
@@ -666,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,12 +74,17 @@ 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(SimpleCollisionBox p_199779_0_, SimpleCollisionBox p_199779_1_, SimpleCollisionBox p_199779_2_, SimpleCollisionBox p_199779_3_, SimpleCollisionBox p_199779_4_) {
|
||||
@@ -126,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loaded 100 of 165 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user