Compare commits

..
122 Commits
Author SHA1 Message Date
Axionize 513e42c58e Incomplete Nalim Impl 2024-12-05 14:04:35 -05:00
Axionize e1281c24fa Incomplete Nalim Impl 2024-12-05 13:54:24 -05:00
Axionize 82bb743120 migrate to using natives and poinetrs 2024-12-05 12:25:39 -05:00
Axionize 85ed18e108 Nalim working tests 2024-12-05 11:30:35 -05:00
Axionize 61a9bae878 Working native code 2024-12-05 07:49:06 -05:00
Axionize d0ff246b03 Java 21 source 2024-12-05 06:55:30 -05:00
Axionize f84321b7e7 Stash changes 2024-12-05 06:32:51 -05:00
Axionize 72b1ec2665 Fix implementation error 2024-12-05 05:47:43 -05:00
Axionize 2337386b37 Fix cloning vector 2024-12-05 05:41:52 -05:00
Axionize d1288bf4af Fix running with Vector API 2024-12-05 05:36:00 -05:00
Axionize 0072bfdee5 Remove debug messages 2024-12-05 05:34:51 -05:00
Axionize 6b2153f03e JMH Working 2024-12-05 05:34:19 -05:00
Axionize ab86e65a65 JMH first attempt 2024-12-05 04:44:04 -05:00
Axionize 1f2b6977b9 Merge remote-tracking branch 'origin/2.0' into simd-upstream
# Conflicts:
#	src/main/java/ac/grim/grimac/predictionengine/MovementCheckRunner.java
#	src/main/java/ac/grim/grimac/predictionengine/predictions/PredictionEngine.java
#	src/main/java/ac/grim/grimac/utils/nmsutil/JumpPower.java
2024-12-05 03:53:57 -05:00
Axionize d5847f73d3 Replace Bukkit Vector with new Vector impl 2024-12-05 03:45:14 -05:00
Axionize 4e5e20280f Implement Factory, interface, fallback, and Scalar and SIMD methods 2024-12-05 03:39:56 -05:00
Axionize baba4d6f6e Expand API definitions 2024-12-05 03:39:56 -05:00
AoElite 97abaaf8ad Merge pull request #1846 from Axionize/remove-use-of-var
Remove Usage of Var
2024-12-04 15:36:17 -05:00
SamB440 1f3ac2a174 Merge pull request #1831 from GrimAnticheat/update/1.21.3
1.21.3 update
2024-12-04 13:37:52 +00:00
SamB440 ff0629d0b7 Merging really sucks 2024-12-04 13:36:17 +00:00
SamB440 9936984534 Merge branch '2.0' into update/1.21.3 2024-12-04 13:34:06 +00:00
SamB440 ca56975136 Revert BadPacketsE changes 2024-12-04 13:31:49 +00:00
AoElite 348daf6a52 Merge pull request #1850 from SergioK29/fix-missing-should-cancel-packets
Fix missing shouldModifyPackets
2024-12-04 08:05:46 -05:00
AoElite d67b470ff6 better BadPackets E & R support for 1.21.2+ 2024-12-04 08:00:12 -05:00
SamB440 130f9e1023 Merge pull request #1597 from ManInMyVan/multiactions
Add MultiActions Checks
2024-12-03 22:13:59 +00:00
SamB440 eaac8ad908 Merge pull request #1835 from Axionize/fix-riptide-teams-and-non-pushable
Fix riptide trident attacks on entities that are non-pushable or the same team as the attacker
2024-12-03 21:12:19 +00:00
SamB440 89f8696d88 Merge pull request #1715 from ManInMyVan/elytra-fly
Allow exempting elytras
2024-12-03 18:01:08 +00:00
AoElite 940a1f819f punishment manager performance improvements 2024-12-03 12:07:33 -05:00
AoElite 3f01edba9d fixed paintings not being registered 2024-12-03 11:37:58 -05:00
SergioK29 24409a5771 Fix missing shouldModifyPackets 2024-12-03 14:00:27 +00:00
ManInMyVan 88e0f98241 fix swimming false 2024-12-02 18:11:48 -07:00
SamB440 fde99a7cbf Remove fastutil usage from CorrectingPlayerInventoryStorage 2024-12-02 13:54:46 +00:00
Axionize 1c3929834a Remove usage of var 2024-12-01 17:08:01 -05:00
ManInMyVan 3a839d7240 add parenthesis 2024-12-01 12:51:19 -07:00
SamB440 7737337a86 Don't let setbacks bypass stuck speed 2024-12-01 18:34:52 +00:00
MachineBreaker 88dc6a42a9 Merge pull request #1832 from Axionize/replace-var-with-explicit-types
Make var type in ExploitC explicitly String
2024-12-01 14:34:09 -04:00
MachineBreaker bcc93aab4a Merge pull request #1804 from GrimAnticheat/2.0-optimise-menu-type
Optimise MenuType#getMenuType
2024-12-01 14:30:33 -04:00
SamB440 b88561284c Disable verbose enable-on-join by default even for OPs 2024-12-01 17:38:41 +00:00
SamB440 5fd8230884 Fix timer ping-abuse-limit-threshold 2024-12-01 17:37:19 +00:00
SamB440 ca0fa88a77 Merge pull request #932 from SamB440/fix/BadPacketsG
Reimplement BadPacketsG
2024-12-01 15:01:56 +00:00
SamB440 cbb3c1cc0f Fix BadPacketsG falses & a sneak desync 2024-12-01 14:59:02 +00:00
SamB440 519968b85a Fix BadPacketsG teleport status 2024-12-01 14:58:38 +00:00
SamB440 6314b78e47 Merge pull request #1837 from ManInMyVan/cleanup/packetcheck
remove unused method from PacketCheck
2024-12-01 14:49:25 +00:00
SamB440 b791897dbc Merge pull request #1833 from Axionize/verbose-on-join
Add permission to enable verbose on join
2024-12-01 14:46:53 +00:00
SamB440 61af1d1e72 Merge pull request #1830 from ManInMyVan/blockbreak-class
add BlockBreak class
2024-12-01 14:42:08 +00:00
SamB440 bf3fc94d92 More big changes 2024-12-01 14:14:07 +00:00
Axionize 1bb0bf7e76 Java 8 support for main sources -> Java 17 2024-11-30 00:25:55 -05:00
Axionize c6cabe7ed6 Example SIMD code 2024-11-30 00:24:43 -05:00
Axionize 4f2fe70300 Build with Java 21 toolchain to prevent requiring downloading JDK 18 for contributors 2024-11-30 00:24:43 -05:00
Axionize 52a611bf94 Clean up Gradle configuration 2024-11-30 00:24:43 -05:00
Axionize 354564485a Create initial project structure proof of concept for adding SIMD support 2024-11-30 00:24:43 -05:00
Axionize 4887415912 Add verbose to appropiate places; makes sure player have alert for auto activation of verbose 2024-11-29 22:50:29 -05:00
SamB440 7f0838832d Merge pull request #1839 from Cyramek/limit-jump-possibilities
[1.21.3] Limit jump possibilities
2024-11-29 18:06:24 +00:00
cyramek 336efafbd3 limit jump possibilities 2024-11-29 18:46:47 +01:00
SamB440 782f8f8a09 Big changes 2024-11-29 17:15:37 +00:00
SamB440 c1858e49d8 Merge pull request #1815 from Axionize/explicit-flush-detection
Warn users who want extra reach accuracy that they need to add paper.explicit-flush=true
2024-11-29 12:24:00 +00:00
SamB440 7dce3a7a12 Merge pull request #1836 from ManInMyVan/cleanup/pair
cleanup Pair class
2024-11-29 12:22:22 +00:00
Axionize 7786da7296 Fix evicting queue for riptide entities 2024-11-28 21:46:43 -05:00
ManInMyVan bc365176b1 remove unused method from PacketCheck 2024-11-28 17:29:45 -07:00
ManInMyVan 7fe224c206 cleanup Pair class 2024-11-28 17:27:32 -07:00
Axionize 48e5f54341 Fix riptide trident attacks on entities that are non-pushable or the same team as the attacker 2024-11-28 17:14:56 -05:00
SamB440 5edf92f124 Merge pull request #1834 from Cyramek/fix-known-input
[1.21.3] fix looping through invalid values caused by known input
2024-11-28 14:33:13 +00:00
cyramek 1c3e938f0f fix looping through invalid values caused by known input 2024-11-28 15:09:54 +01:00
Axionize ec71581eae Add permission to enable verbose on join 2024-11-27 23:46:10 -05:00
Axionize 0904b61df0 Make var type in ExploitC explicitly String 2024-11-27 22:04:50 -05:00
Axionize c9e082257c Put -Dpaper.explicit-flush=true in quotes 2024-11-27 22:01:27 -05:00
SamB440 8875bbf991 Re-encode this 2024-11-27 17:53:49 +00:00
SamB440 e933186d2e determineCanSkipTick idle for 1.21.2+ 2024-11-27 16:53:50 +00:00
SamB440 89bec80b34 Fix 1.21.2+ jump false 2024-11-27 16:07:11 +00:00
SamB440 cc78eca02f Fix offground precision loss to make us consistent 1E-15 offset 2024-11-27 16:06:58 +00:00
SamB440 77a91ec627 1.21.3 update 2024-11-27 16:06:07 +00:00
ManInMyVan 1036563b24 add BlockBreak class 2024-11-26 17:48:45 -07:00
AoElite dd5ea74ae4 update fastutils 2024-11-26 12:43:55 -05:00
AoElite aa7a4dc155 Merge pull request #1828 from ManInMyVan/paper-dupe-patch
patch paper dupe
2024-11-25 21:49:57 -05:00
ManInMyVan 3387365732 remove unused import 2024-11-25 17:55:25 -07:00
ManInMyVan 411aa4f2b2 patch paper dupe 2024-11-25 17:48:30 -07:00
SamB440 63a8e394a9 Merge pull request #1827 from ItsClairton/fix/cauldron
fix: Legacy cauldron collision box.
2024-11-26 00:02:59 +00:00
Clairton Santos 9804b8b62f fix: Legacy cauldron collision box. 2024-11-25 20:44:55 -03:00
SamB440 f82cce32f6 Merge pull request #1724 from K-Lqrs/2.0
[Improved] Add full Japanese translations for all GrimAC configuration files
2024-11-25 15:29:13 +00:00
AoElite 474ae7dd27 small ResyncWorldUtil refactor 2024-11-25 07:16:47 -05:00
SamB440 d85c1d267b Merge pull request #1825 from Cyramek/fix-reach
fix wrongly ported reach changes from grim 3.0
2024-11-25 09:02:36 +00:00
cyramek 6ce2bcb9e9 fix wrongly ported changes from grim 3.0 2024-11-25 09:30:21 +01:00
AoElite 48eff610c3 fixed BadPackets E & R falsing on 1.21.2+ players in vehicles 2024-11-25 00:04:00 -05:00
AoElite 677dd044ff refactor bstats for packetevents 2024-11-25 00:02:34 -05:00
AoElite 87f30eea37 added a todo 2024-11-25 00:02:02 -05:00
AoElite 370da407ce update packetevents 2024-11-25 00:01:54 -05:00
SamB440 1ef4b4eec9 Merge pull request #1821 from Axionize/fix-complex-fence-boxes
Fix CCB constructor for DynamicHitboxFence
2024-11-24 14:00:36 +00:00
AoElite 954eb8b51c Merge pull request #1783 from ManInMyVan/track-paintings
track paintings
2024-11-24 06:52:30 -05:00
AoElite 84ceaa29dc Merge pull request #1797 from ManInMyVan/stopspectating-here-permission
add permission for /grim stopspectating here
2024-11-24 06:49:33 -05:00
AoElite 6974d61b9d Merge pull request #1817 from Axionize/badpacketsx
Fix BadPacketsX false and mark it as no longer experimental
2024-11-24 06:46:58 -05:00
AoElite d9a3a890c8 Merge pull request #1818 from Axionize/badpacketsz
Mark BadPacketsZ as no longer experimental
2024-11-24 06:46:29 -05:00
AoElite c4495d995a Merge pull request #1820 from Axionize/use-movement-threshold
Use movementThreshold() instead of 0.03 where appropiate
2024-11-24 06:42:44 -05:00
Axionize 9bd5c2db0b Fix CCB constructor for DynamicHitboxFence 2024-11-24 02:38:55 -05:00
Axionize 2518b23f8b Use movementThreshold() instead of 0.03 where appropiate 2024-11-24 01:37:53 -05:00
Axionize bd2ceeeed4 Style fix 2024-11-24 00:28:46 -05:00
Axionize a6d082b355 Mark BadPacketsX as no longer experimental 2024-11-24 00:00:47 -05:00
Axionize 8af0dbc46d Mark BadPacketsZ as no longer experimental 2024-11-23 23:59:56 -05:00
Axionize 3fedd558d6 Fix BadPacketsX falses 2024-11-23 23:58:02 -05:00
Axionize 547a2965dd Warn players who want extra reach accuracy that they need to add paper.explicit-flush=true 2024-11-23 22:31:31 -05:00
ManInMyVan c19914e83f fix MutliActionsF false 2024-11-23 16:37:45 -07:00
ManInMyVan 6c14faa51d fix MutliActionsF falses 2024-11-23 16:37:06 -07:00
ManInMyVan 6b4060e0c4 fix MultiActionsF false 2024-11-23 16:37:06 -07:00
ManInMyVan 483706151b check sprinting in MultiActionsC and MultiActionsD 2024-11-23 16:37:06 -07:00
ManInMyVan 9dc128cc1d add MultiActionsF 2024-11-23 16:37:06 -07:00
ManInMyVan 50b7e84f58 offhand 2024-11-23 16:37:06 -07:00
ManInMyVan a77067ffe5 Add MultiActionsE 2024-11-23 16:37:06 -07:00
ManInMyVan 2a49635497 Add MutliActionsD 2024-11-23 16:37:05 -07:00
ManInMyVan a158019d66 Add MultiActions Checks 2024-11-23 16:37:05 -07:00
SamB440 5f7d296b4d Merge pull request #1814 from Axionize/dynamically-configurable-relocate
Make relocate read from project properties --> environment variable --> default to true
2024-11-23 19:11:32 +00:00
Axionize c4cd8136bf Ensure ComplexCollisionBoxes can only contain SimpleCollisionBoxes (#1805)
* Ensure ComplexCollisionBoxes can only contain SimpleCollisionBoxes

* Optimize ComplexCollisionBoxes by using primitives internally; Make new more efficient constructors available

* Port everything to new internal .downCast() API besides getCollisionBoxes

* implement new downCast() API for Dynamic, Simple, and No Collision Boxes

* Manually size ComplexCollisionBoxes Pt. 1

* Manually size ComplexCollisionBoxes Pt. 2

* Fix new downCast() for SimpleCollisionBox

* Fix logic error in CCB constructor

* Optimize DynamicHitboxFence

* Fix logic error again in CCB constructor

* Remove debug print statement

* Fix copying CCBs in edge case

* Rename variable for clarity

* Make CCD length private var

* Make makeShapes() CCD extra maxIndex explicit; Fix DynamicCollisionWall error

* Remove unused calls to makeShapes()
2024-11-23 10:57:15 +00:00
SamB440 dbc0c11795 Merge pull request #1813 from ManInMyVan/fix/falses
fix falses
2024-11-23 10:56:35 +00:00
Axionize d30b024bd3 Make whether or not to relocate deps read from project properties -> environment variable"
Prevents having to modify build.gradle.kts when disabling relocating.
2024-11-22 21:23:12 -05:00
ManInMyVan a3e979abc8 fix falses 2024-11-22 14:33:57 -07:00
MachineBreaker 93f14a5b40 Optimise MenuType#getMenuType 2024-11-17 11:15:07 -04:00
ManInMyVan 2de0f49af9 add permission for /grim stopspectating here 2024-11-15 15:04:04 -07:00
ManInMyVan 39a53db067 track paintings 2024-11-09 17:08:10 -07:00
Lars 9bfc7d7d9b わずかな間違いを修正・2 2024-11-02 23:41:34 +13:00
Lars 7c93eda985 わずかな間違いを修正 2024-11-02 19:13:51 +13:00
Lars 1e7a858aa0 Japanese向けの翻訳を修正 2024-11-02 19:10:17 +13:00
Lars 11fba1b6bd configのja.ymlを修正済み 2024-11-02 19:06:11 +13:00
ManInMyVan ce9d66c199 Allow exempting elytras 2024-10-27 12:51:33 -06:00
Ruxy 330a9782bc Add full Japanese translations for all GrimAC configuration files 2024-09-17 10:31:53 +12:00
171 changed files with 2986 additions and 4504 deletions

No files matched your search

+139 -6
View File
@@ -26,9 +26,13 @@ spotless {
}
group = "ac.grim.grimac"
version = "2.3.69"
version = "2.3.68"
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(18))
}
// Set to false for debug builds
// You cannot live reload classes if the jar relocates dependencies
// Checks Project properties -> environment variable -> defaults true
@@ -55,6 +59,13 @@ repositories {
// FastUtil, Discord-Webhooks
}
// Add JMH configuration
configurations {
create("jmh")
create("jmhAnnotationProcessor")
}
dependencies {
implementation("com.github.retrooper:packetevents-spigot:2.6.1-SNAPSHOT")
implementation("co.aikar:acf-paper:0.5.1-SNAPSHOT")
@@ -67,11 +78,17 @@ dependencies {
implementation("com.github.grimanticheat:grimapi:fc5634e444")
implementation("org.jetbrains:annotations:24.1.0")
compileOnly("org.geysermc.floodgate:api:2.2.3-SNAPSHOT")
compileOnly("org.geysermc.floodgate:api:2.0-SNAPSHOT")
compileOnly("org.spigotmc:spigot-api:1.18.2-R0.1-SNAPSHOT")
compileOnly("com.viaversion:viaversion-api:5.1.1-SNAPSHOT")
compileOnly("com.viaversion:viaversion-api:5.0.4-SNAPSHOT")
//
compileOnly("io.netty:netty-all:4.1.85.Final")
// Replace jmhImplementation with the new configuration
"jmh"("org.openjdk.jmh:jmh-core:1.37")
"jmhAnnotationProcessor"("org.openjdk.jmh:jmh-generator-annprocess:1.37")
implementation(files("./nalim.jar"))
}
bukkit {
@@ -139,6 +156,16 @@ bukkit {
description = "Exempt from all checks"
default = Permission.Default.FALSE
}
register("grim.verbose") {
description = "Receive verbose alerts for violations. Requires grim.alerts"
default = Permission.Default.OP
}
register("grim.verbose.enable-on-join") {
description = "Enable verbose alerts on join. Requires grim.alerts and grim.alerts.enable-on-join"
default = Permission.Default.FALSE
}
}
}
@@ -163,22 +190,50 @@ java {
}
compileClasspath += main.get().output
runtimeClasspath += main.get().output
dependencies {
implementation(files("./nalim.jar")) // Add nalim.jar to java18 source set
}
}
create("jmh") {
java {
srcDir("src/jmh/java")
}
compileClasspath += sourceSets.main.get().output +
sourceSets.getByName("java18").output +
configurations["jmh"]
runtimeClasspath += sourceSets.main.get().output +
sourceSets.getByName("java18").output +
configurations["jmh"]
}
}
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
languageVersion.set(JavaLanguageVersion.of(18))
}
}
tasks.withType<JavaCompile> {
if (name == "compileJava") {
options.compilerArgs.addAll(listOf("--add-exports","java.base/jdk.internal=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED"))
}
if (name == "compileJava18Java") {
options.compilerArgs.addAll(listOf("--add-modules", "jdk.incubator.vector", "-Xlint:unchecked"))
sourceCompatibility = "18"
targetCompatibility = "18"
}
if (name == "compileJava21Java") {
options.compilerArgs.addAll(listOf("--enable-preview", "--add-modules", "jdk.incubator.vector", "-Xlint:unchecked"))
sourceCompatibility = "21"
targetCompatibility = "21"
}
if (name == "compileJmhJava") {
options.compilerArgs.addAll(listOf("--add-modules", "jdk.incubator.vector"))
sourceCompatibility = "18"
targetCompatibility = "18"
}
}
tasks.withType<Jar> {
@@ -215,3 +270,81 @@ tasks.shadowJar {
relocate("org.jetbrains", "ac.grim.grimac.shaded.jetbrains")
}
}
tasks.register<Jar>("jmhJar") {
dependsOn("compileGeneratedJmh")
from(sourceSets["main"].output)
from(sourceSets["java18"].output)
from(sourceSets["jmh"].output)
from("${buildDir}/classes/java/jmh")
from(configurations["jmh"].map { if (it.isDirectory) it else zipTree(it) })
manifest {
attributes(
"Main-Class" to "org.openjdk.jmh.Main",
"Add-Opens" to "java.base/java.lang java.base/java.io java.base/java.util java.base/java.util.concurrent java.base/java.net",
"Add-Modules" to "jdk.incubator.vector"
)
}
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
archiveClassifier.set("benchmarks")
}
tasks.register<JavaCompile>("compileGeneratedJmh") {
dependsOn("compileJmhJava")
source = fileTree("${buildDir}/generated-sources/jmh")
classpath = sourceSets["jmh"].compileClasspath +
sourceSets["java18"].output +
sourceSets["java18"].compileClasspath +
sourceSets.main.get().output +
sourceSets.main.get().compileClasspath +
files("${buildDir}/classes/java/jmh")
destinationDirectory.set(file("${buildDir}/classes/java/jmh"))
sourceCompatibility = "18"
targetCompatibility = "18"
options.compilerArgs.addAll(listOf("--add-modules", "jdk.incubator.vector"))
}
tasks.register("jmh") {
dependsOn("jmhJar")
doLast {
val includes = System.getProperty("includes", "")
javaexec {
classpath = files(tasks.named("jmhJar").get().outputs.files)
mainClass.set("org.openjdk.jmh.Main")
jvmArgs = listOf("--enable-preview", "--add-modules", "jdk.incubator.vector", "-XX:+UnlockExperimentalVMOptions", "-XX:+EnableJVMCI", "-javaagent:nalim.jar")
// Initialize the args list with default settings
args = mutableListOf<String>().apply {
// Add any default arguments here if needed
if (includes.isNotEmpty()) {
addAll(listOf(includes))
}
}
}
}
}
tasks.named<JavaCompile>("compileJmhJava") {
source = fileTree("src/jmh/java")
classpath = sourceSets["jmh"].compileClasspath +
sourceSets.main.get().output +
sourceSets["java18"].output
destinationDirectory.set(file("${buildDir}/classes/java/jmh"))
sourceCompatibility = "18"
targetCompatibility = "18"
options.compilerArgs.addAll(listOf("--add-modules", "jdk.incubator.vector"))
}
tasks.named("jmhJar") {
dependsOn("compileJmhJava")
}
@@ -4,7 +4,7 @@ import jdk.incubator.vector.*;
public final class SIMDVector3D implements Vector3D {
private static final VectorSpecies<Double> SPECIES = DoubleVector.SPECIES_PREFERRED;
private static final VectorSpecies<Double> SPECIES = DoubleVector.SPECIES_256;
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);
@@ -13,7 +13,7 @@ public final class SIMDVector3D implements Vector3D {
private DoubleVector vector;
public SIMDVector3D(double x, double y, double z) {
this.vector = DoubleVector.fromArray(SPECIES, new double[]{x, y, z, 0}, 0);
this.vector = DoubleVector.fromArray(SPECIES, new double[]{x, y, z, 0}, 0, LENGTH_3_ARRAY_MASK);
}
@Override
@@ -72,8 +72,7 @@ public final class SIMDVector3D implements Vector3D {
// 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());
this.vector = this.vector.div(length());
return this;
}
@@ -1,8 +1,5 @@
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 {
@@ -0,0 +1,114 @@
//package ac.grim.grimac.benchmark.vector;
//
//import ac.grim.grimac.utils.vector.Java21SIMDVector3D;
//import org.openjdk.jmh.annotations.*;
//
//import java.util.concurrent.TimeUnit;
//
//@BenchmarkMode(Mode.AverageTime)
//@OutputTimeUnit(TimeUnit.NANOSECONDS)
//@State(Scope.Thread)
//@Warmup(iterations = 2, time = 1)
//@Measurement(iterations = 3, time = 1)
//@Fork(1)
//public class Java21SIMDVector3DBenchmark {
//
// private Java21SIMDVector3D vector1;
// private Java21SIMDVector3D vector2;
//
// @Setup
// public void setup() {
// vector1 = new Java21SIMDVector3D(1.0, 2.0, 3.0);
// vector2 = new Java21SIMDVector3D(4.0, 5.0, 6.0);
// }
//
// @Benchmark
// public double benchmarkGetX() {
// return vector1.getX();
// }
//
// @Benchmark
// public double benchmarkGetY() {
// return vector1.getY();
// }
//
// @Benchmark
// public double benchmarkGetZ() {
// return vector1.getZ();
// }
//
// @Benchmark
// public Java21SIMDVector3D benchmarkSetX() {
// return (Java21SIMDVector3D) vector1.setX(10.0);
// }
//
// @Benchmark
// public Java21SIMDVector3D benchmarkSetY() {
// return (Java21SIMDVector3D) vector1.setY(20.0);
// }
//
// @Benchmark
// public Java21SIMDVector3D benchmarkSetZ() {
// return (Java21SIMDVector3D) vector1.setZ(30.0);
// }
//
// @Benchmark
// public double benchmarkLength() {
// return vector1.length();
// }
//
// @Benchmark
// public double benchmarkLengthSquared() {
// return vector1.lengthSquared();
// }
//
// @Benchmark
// public Java21SIMDVector3D benchmarkMultiplyScalar() {
// return (Java21SIMDVector3D) vector1.multiply(2.0);
// }
//
// @Benchmark
// public Java21SIMDVector3D benchmarkNormalize() {
// return (Java21SIMDVector3D) vector1.normalize();
// }
//
// @Benchmark
// public Java21SIMDVector3D benchmarkCrossProduct() {
// return (Java21SIMDVector3D) vector1.crossProduct(vector2);
// }
//
// @Benchmark
// public Java21SIMDVector3D benchmarkAdd() {
// return (Java21SIMDVector3D) vector1.add(vector2);
// }
//
// @Benchmark
// public Java21SIMDVector3D benchmarkSubtract() {
// return (Java21SIMDVector3D) vector1.subtract(vector2);
// }
//
// @Benchmark
// public Java21SIMDVector3D benchmarkMultiplyVector() {
// return (Java21SIMDVector3D) vector1.multiply(vector2);
// }
//
// @Benchmark
// public double benchmarkDistance() {
// return vector1.distance(vector2);
// }
//
// @Benchmark
// public double benchmarkDistanceSquared() {
// return vector1.distanceSquared(vector2);
// }
//
// @Benchmark
// public double benchmarkDotProduct() {
// return vector1.dot(vector2);
// }
//
// @Benchmark
// public Java21SIMDVector3D benchmarkClone() {
// return (Java21SIMDVector3D) vector1.clone();
// }
//}
@@ -0,0 +1,114 @@
package ac.grim.grimac.benchmark.vector;
import ac.grim.grimac.utils.vector.NalimVector3D;
import org.openjdk.jmh.annotations.*;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
@Warmup(iterations = 2, time = 1)
@Measurement(iterations = 3, time = 1)
@Fork(1)
public class NalimVector3DBenchmark {
private NalimVector3D vector1;
private NalimVector3D vector2;
@Setup
public void setup() {
vector1 = new NalimVector3D(1.0, 2.0, 3.0);
vector2 = new NalimVector3D(4.0, 5.0, 6.0);
}
@Benchmark
public double benchmarkGetX() {
return vector1.getX();
}
@Benchmark
public double benchmarkGetY() {
return vector1.getY();
}
@Benchmark
public double benchmarkGetZ() {
return vector1.getZ();
}
@Benchmark
public NalimVector3D benchmarkSetX() {
return (NalimVector3D) vector1.setX(10.0);
}
@Benchmark
public NalimVector3D benchmarkSetY() {
return (NalimVector3D) vector1.setY(20.0);
}
@Benchmark
public NalimVector3D benchmarkSetZ() {
return (NalimVector3D) vector1.setZ(30.0);
}
@Benchmark
public double benchmarkLength() {
return vector1.length();
}
@Benchmark
public double benchmarkLengthSquared() {
return vector1.lengthSquared();
}
@Benchmark
public NalimVector3D benchmarkMultiplyScalar() {
return (NalimVector3D) vector1.multiply(2.0);
}
@Benchmark
public NalimVector3D benchmarkNormalize() {
return (NalimVector3D) vector1.normalize();
}
@Benchmark
public NalimVector3D benchmarkCrossProduct() {
return (NalimVector3D) vector1.crossProduct(vector2);
}
@Benchmark
public NalimVector3D benchmarkAdd() {
return (NalimVector3D) vector1.add(vector2);
}
@Benchmark
public NalimVector3D benchmarkSubtract() {
return (NalimVector3D) vector1.subtract(vector2);
}
@Benchmark
public NalimVector3D benchmarkMultiplyVector() {
return (NalimVector3D) vector1.multiply(vector2);
}
@Benchmark
public double benchmarkDistance() {
return vector1.distance(vector2);
}
@Benchmark
public double benchmarkDistanceSquared() {
return vector1.distanceSquared(vector2);
}
@Benchmark
public double benchmarkDotProduct() {
return vector1.dot(vector2);
}
@Benchmark
public NalimVector3D benchmarkClone() {
return (NalimVector3D) vector1.clone();
}
}
@@ -0,0 +1,114 @@
//package ac.grim.grimac.benchmark.vector;
//
//import ac.grim.grimac.utils.vector.NativeVector3D;
//import org.openjdk.jmh.annotations.*;
//
//import java.util.concurrent.TimeUnit;
//
//@BenchmarkMode(Mode.AverageTime)
//@OutputTimeUnit(TimeUnit.NANOSECONDS)
//@State(Scope.Thread)
//@Warmup(iterations = 2, time = 1)
//@Measurement(iterations = 3, time = 1)
//@Fork(1)
//public class NativeVector3DBenchmark {
//
// private NativeVector3D vector1;
// private NativeVector3D vector2;
//
// @Setup
// public void setup() {
// vector1 = new NativeVector3D(1.0, 2.0, 3.0);
// vector2 = new NativeVector3D(4.0, 5.0, 6.0);
// }
//
// @Benchmark
// public double benchmarkGetX() {
// return vector1.getX();
// }
//
// @Benchmark
// public double benchmarkGetY() {
// return vector1.getY();
// }
//
// @Benchmark
// public double benchmarkGetZ() {
// return vector1.getZ();
// }
//
// @Benchmark
// public NativeVector3D benchmarkSetX() {
// return (NativeVector3D) vector1.setX(10.0);
// }
//
// @Benchmark
// public NativeVector3D benchmarkSetY() {
// return (NativeVector3D) vector1.setY(20.0);
// }
//
// @Benchmark
// public NativeVector3D benchmarkSetZ() {
// return (NativeVector3D) vector1.setZ(30.0);
// }
//
// @Benchmark
// public double benchmarkLength() {
// return vector1.length();
// }
//
// @Benchmark
// public double benchmarkLengthSquared() {
// return vector1.lengthSquared();
// }
//
// @Benchmark
// public NativeVector3D benchmarkMultiplyScalar() {
// return (NativeVector3D) vector1.multiply(2.0);
// }
//
// @Benchmark
// public NativeVector3D benchmarkNormalize() {
// return (NativeVector3D) vector1.normalize();
// }
//
// @Benchmark
// public NativeVector3D benchmarkCrossProduct() {
// return (NativeVector3D) vector1.crossProduct(vector2);
// }
//
// @Benchmark
// public NativeVector3D benchmarkAdd() {
// return (NativeVector3D) vector1.add(vector2);
// }
//
// @Benchmark
// public NativeVector3D benchmarkSubtract() {
// return (NativeVector3D) vector1.subtract(vector2);
// }
//
// @Benchmark
// public NativeVector3D benchmarkMultiplyVector() {
// return (NativeVector3D) vector1.multiply(vector2);
// }
//
// @Benchmark
// public double benchmarkDistance() {
// return vector1.distance(vector2);
// }
//
// @Benchmark
// public double benchmarkDistanceSquared() {
// return vector1.distanceSquared(vector2);
// }
//
// @Benchmark
// public double benchmarkDotProduct() {
// return vector1.dot(vector2);
// }
//
// @Benchmark
// public NativeVector3D benchmarkClone() {
// return (NativeVector3D) vector1.clone();
// }
//}
@@ -0,0 +1,114 @@
package ac.grim.grimac.benchmark.vector;
import ac.grim.grimac.utils.vector.SIMDVector3D;
import org.openjdk.jmh.annotations.*;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
@Warmup(iterations = 2, time = 1)
@Measurement(iterations = 3, time = 1)
@Fork(1)
public class SIMDVector3DBenchmark {
private SIMDVector3D vector1;
private SIMDVector3D vector2;
@Setup
public void setup() {
vector1 = new SIMDVector3D(1.0, 2.0, 3.0);
vector2 = new SIMDVector3D(4.0, 5.0, 6.0);
}
@Benchmark
public double benchmarkGetX() {
return vector1.getX();
}
@Benchmark
public double benchmarkGetY() {
return vector1.getY();
}
@Benchmark
public double benchmarkGetZ() {
return vector1.getZ();
}
@Benchmark
public SIMDVector3D benchmarkSetX() {
return (SIMDVector3D) vector1.setX(10.0);
}
@Benchmark
public SIMDVector3D benchmarkSetY() {
return (SIMDVector3D) vector1.setY(20.0);
}
@Benchmark
public SIMDVector3D benchmarkSetZ() {
return (SIMDVector3D) vector1.setZ(30.0);
}
@Benchmark
public double benchmarkLength() {
return vector1.length();
}
@Benchmark
public double benchmarkLengthSquared() {
return vector1.lengthSquared();
}
@Benchmark
public SIMDVector3D benchmarkMultiplyScalar() {
return (SIMDVector3D) vector1.multiply(2.0);
}
@Benchmark
public SIMDVector3D benchmarkNormalize() {
return (SIMDVector3D) vector1.normalize();
}
@Benchmark
public SIMDVector3D benchmarkCrossProduct() {
return (SIMDVector3D) vector1.crossProduct(vector2);
}
@Benchmark
public SIMDVector3D benchmarkAdd() {
return (SIMDVector3D) vector1.add(vector2);
}
@Benchmark
public SIMDVector3D benchmarkSubtract() {
return (SIMDVector3D) vector1.subtract(vector2);
}
@Benchmark
public SIMDVector3D benchmarkMultiplyVector() {
return (SIMDVector3D) vector1.multiply(vector2);
}
@Benchmark
public double benchmarkDistance() {
return vector1.distance(vector2);
}
@Benchmark
public double benchmarkDistanceSquared() {
return vector1.distanceSquared(vector2);
}
@Benchmark
public double benchmarkDotProduct() {
return vector1.dot(vector2);
}
@Benchmark
public SIMDVector3D benchmarkClone() {
return (SIMDVector3D) vector1.clone();
}
}
@@ -0,0 +1,114 @@
package ac.grim.grimac.benchmark.vector;
import ac.grim.grimac.utils.vector.ScalarVector3D;
import org.openjdk.jmh.annotations.*;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
@Warmup(iterations = 2, time = 1)
@Measurement(iterations = 3, time = 1)
@Fork(1)
public class ScalarVector3DBenchmark {
private ScalarVector3D vector1;
private ScalarVector3D vector2;
@Setup
public void setup() {
vector1 = new ScalarVector3D(1.0, 2.0, 3.0);
vector2 = new ScalarVector3D(4.0, 5.0, 6.0);
}
@Benchmark
public double benchmarkGetX() {
return vector1.getX();
}
@Benchmark
public double benchmarkGetY() {
return vector1.getY();
}
@Benchmark
public double benchmarkGetZ() {
return vector1.getZ();
}
@Benchmark
public ScalarVector3D benchmarkSetX() {
return (ScalarVector3D) vector1.setX(10.0);
}
@Benchmark
public ScalarVector3D benchmarkSetY() {
return (ScalarVector3D) vector1.setY(20.0);
}
@Benchmark
public ScalarVector3D benchmarkSetZ() {
return (ScalarVector3D) vector1.setZ(30.0);
}
@Benchmark
public double benchmarkLength() {
return vector1.length();
}
@Benchmark
public double benchmarkLengthSquared() {
return vector1.lengthSquared();
}
@Benchmark
public ScalarVector3D benchmarkMultiplyScalar() {
return (ScalarVector3D) vector1.multiply(2.0);
}
@Benchmark
public ScalarVector3D benchmarkNormalize() {
return (ScalarVector3D) vector1.normalize();
}
@Benchmark
public ScalarVector3D benchmarkCrossProduct() {
return (ScalarVector3D) vector1.crossProduct(vector2);
}
@Benchmark
public ScalarVector3D benchmarkAdd() {
return (ScalarVector3D) vector1.add(vector2);
}
@Benchmark
public ScalarVector3D benchmarkSubtract() {
return (ScalarVector3D) vector1.subtract(vector2);
}
@Benchmark
public ScalarVector3D benchmarkMultiplyVector() {
return (ScalarVector3D) vector1.multiply(vector2);
}
@Benchmark
public double benchmarkDistance() {
return vector1.distance(vector2);
}
@Benchmark
public double benchmarkDistanceSquared() {
return vector1.distanceSquared(vector2);
}
@Benchmark
public double benchmarkDotProduct() {
return vector1.dot(vector2);
}
@Benchmark
public ScalarVector3D benchmarkClone() {
return (ScalarVector3D) vector1.clone();
}
}
@@ -8,6 +8,8 @@ import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.common.ConfigReloadObserver;
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.wrapper.play.client.WrapperPlayClientPlayerFlying;
import io.github.retrooper.packetevents.util.folia.FoliaScheduler;
import lombok.Getter;
import lombok.Setter;
@@ -150,6 +152,39 @@ public class Check implements AbstractCheck, ConfigReloadObserver {
packetType == PacketType.Play.Client.WINDOW_CONFIRMATION;
}
public boolean isFlying(PacketTypeCommon packetType) {
return WrapperPlayClientPlayerFlying.isFlying(packetType);
}
public boolean isUpdate(PacketTypeCommon packetType) {
return isFlying(packetType)
|| packetType == PacketType.Play.Client.CLIENT_TICK_END
|| isTransaction(packetType);
}
public boolean isTickPacket(PacketTypeCommon packetType) {
if (isTickPacketIncludingNonMovement(packetType)) {
if (isFlying(packetType)) {
return !player.packetStateData.lastPacketWasTeleport && !player.packetStateData.lastPacketWasOnePointSeventeenDuplicate;
}
return true;
}
return false;
}
public boolean isTickPacketIncludingNonMovement(PacketTypeCommon packetType) {
// On 1.21.2+ fall back to the TICK_END packet IF the player did not send a movement packet for their tick
// TickTimer checks to see if player did not send a tick end packet before new flying packet is sent
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_2)
&& !player.packetStateData.didSendMovementBeforeTickEnd) {
if (packetType == PacketType.Play.Client.CLIENT_TICK_END) {
return true;
}
}
return isFlying(packetType);
}
@Override
public void reload() {
reload(GrimAPI.INSTANCE.getConfigManager().getConfig());
@@ -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.getSecond() > SIGNIFICANT_SAMPLES_THRESHOLD) {
this.modeX = modeX.getFirst();
if (modeX.second() > SIGNIFICANT_SAMPLES_THRESHOLD) {
this.modeX = modeX.first();
this.sensitivityX = convertToSensitivity(this.modeX);
}
}
if (this.yRotMode.size() > SIGNIFICANT_SAMPLES_THRESHOLD) {
Pair<Double, Integer> modeY = this.yRotMode.getMode();
if (modeY.getSecond() > SIGNIFICANT_SAMPLES_THRESHOLD) {
this.modeY = modeY.getFirst();
if (modeY.second() > SIGNIFICANT_SAMPLES_THRESHOLD) {
this.modeY = modeY.first();
this.sensitivityY = convertToSensitivity(this.modeY);
}
}
@@ -6,6 +6,7 @@ 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.wrapper.play.client.WrapperPlayClientPlayerFlying;
@CheckData(name = "BadPacketsE")
@@ -23,10 +24,12 @@ 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) {
} else if (event.getPacketType() == PacketType.Play.Client.STEER_VEHICLE
|| (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_2) && player.compensatedEntities.getSelf().inVehicle())) {
noReminderTicks = 0; // Exempt vehicles
}
if (noReminderTicks > 20) {
flagAndAlert("ticks=" + noReminderTicks); // ban? I don't know how this would false
}
@@ -10,8 +10,7 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEn
@CheckData(name = "BadPacketsG")
public class BadPacketsG extends Check implements PacketCheck {
boolean wasTeleport;
boolean lastSneaking;
private boolean lastSneaking, respawn;
public BadPacketsG(GrimPlayer player) {
super(player);
@@ -19,13 +18,12 @@ public class BadPacketsG extends Check implements PacketCheck {
@Override
public void onPacketReceive(PacketReceiveEvent event) {
wasTeleport = player.packetStateData.lastPacketWasTeleport || wasTeleport;
if (event.getPacketType() == PacketType.Play.Client.ENTITY_ACTION) {
WrapperPlayClientEntityAction packet = new WrapperPlayClientEntityAction(event);
if (packet.getAction() == WrapperPlayClientEntityAction.Action.START_SNEAKING) {
if (lastSneaking && !wasTeleport) {
// The player may send two START_SNEAKING packets if they respawned
if (lastSneaking && !respawn) {
if (flagAndAlert("state=true") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
@@ -33,8 +31,9 @@ public class BadPacketsG extends Check implements PacketCheck {
} else {
lastSneaking = true;
}
respawn = false;
} else if (packet.getAction() == WrapperPlayClientEntityAction.Action.STOP_SNEAKING) {
if (!lastSneaking && !wasTeleport) {
if (!lastSneaking && !respawn) {
if (flagAndAlert("state=false") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
@@ -42,7 +41,13 @@ public class BadPacketsG extends Check implements PacketCheck {
} else {
lastSneaking = false;
}
respawn = false;
}
}
}
public void handleRespawn() {
// Clients could potentially not send a STOP_SNEAKING packet when they die, so we need to track it
respawn = true;
}
}
@@ -1,4 +1,4 @@
package ac.grim.grimac.checks.impl.packetorder;
package ac.grim.grimac.checks.impl.badpackets;
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 = "PacketOrderB")
public class PacketOrderB extends Check implements PacketCheck {
@CheckData(name = "BadPacketsH")
public class BadPacketsH 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 PacketOrderB(final GrimPlayer player) {
public BadPacketsH(final GrimPlayer player) {
super(player);
}
@@ -43,7 +43,7 @@ public class PacketOrderB extends Check implements PacketCheck {
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_9)
&& PacketEvents.getAPI().getServerManager().getVersion().isNewerThan(ServerVersion.V_1_8)) return;
if (!sentAnimation && flagAndAlert()) {
if (!sentAnimation && flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
@@ -1,4 +1,4 @@
package ac.grim.grimac.checks.impl.packetorder;
package ac.grim.grimac.checks.impl.badpackets;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
@@ -9,33 +9,25 @@ 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 = "PacketOrderC", experimental = true)
public class PacketOrderC extends Check implements PacketCheck {
public PacketOrderC(final GrimPlayer player) {
@CheckData(name = "BadPacketsM", experimental = true)
public class BadPacketsM extends Check implements PacketCheck {
public BadPacketsM(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 (exempt) {
return;
}
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY && !exempt) {
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
final WrapperPlayClientInteractEntity packet = new WrapperPlayClientInteractEntity(event);
final WrapperPlayClientInteractEntity wrapper = new WrapperPlayClientInteractEntity(event);
final PacketEntity entity = player.compensatedEntities.entityMap.get(packet.getEntityId());
final PacketEntity entity = player.compensatedEntities.entityMap.get(wrapper.getEntityId());
// For armor stands, vanilla clients send:
// - when renaming the armor stand or in spectator mode: INTERACT_AT + INTERACT
@@ -43,49 +35,27 @@ public class PacketOrderC extends Check implements PacketCheck {
// Just exempt armor stands to be safe
if (entity != null && entity.getType() == EntityTypes.ARMOR_STAND) return;
final boolean sneaking = packet.isSneaking().orElse(false);
switch (packet.getAction()) {
switch (wrapper.getAction()) {
// INTERACT_AT then INTERACT
case INTERACT:
if (!sentInteractAt) {
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()) {
if (flagAndAlert("Missed Interact-At") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
sentInteractAt = false;
break;
case INTERACT_AT:
if (sentInteractAt) {
if (flagAndAlert("Skipped Interact") && shouldModifyPackets()) {
if (flagAndAlert("Missed 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)");
}
}
}
}
@@ -39,7 +39,7 @@ public class BadPacketsO extends Check implements PacketCheck {
boolean hasID = false;
for (Pair<Long, Long> iterator : keepaliveMap) {
if (iterator.getFirst() == id) {
if (iterator.first() == 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.getFirst() != id);
} while (data.first() != id);
}
}
}
@@ -41,7 +41,8 @@ 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 && player.compensatedEntities.getSelf().inVehicle()) {
} else if ((event.getPacketType() == PacketType.Play.Client.STEER_VEHICLE || event.getPacketType() == PacketType.Play.Client.VEHICLE_MOVE)
&& player.inVehicle()) {
positions++;
}
}
@@ -1,31 +0,0 @@
package ac.grim.grimac.checks.impl.badpackets;
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.item.ItemStack;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
@CheckData(name = "BadPacketsV", experimental = true)
public class BadPacketsV extends Check implements PacketCheck {
public BadPacketsV(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
WrapperPlayClientInteractEntity interactEntity = new WrapperPlayClientInteractEntity(event);
if (interactEntity.getAction() != WrapperPlayClientInteractEntity.InteractAction.ATTACK) return;
if (!player.packetStateData.isSlowedByUsingItem()) return;
ItemStack itemInUse = player.getInventory().getItemInHand(player.packetStateData.eatingHand);
if (flagAndAlert("UseItem=" + itemInUse.getType().getName().getKey()) && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
}
@@ -1,29 +1,53 @@
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;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
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.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
import com.github.retrooper.packetevents.util.Vector3i;
@CheckData(name = "BadPacketsX", experimental = true)
@CheckData(name = "BadPacketsX")
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)
public final void handle(BlockBreak blockBreak) {
if (blockBreak.action != DiggingAction.START_DIGGING && blockBreak.action != DiggingAction.FINISHED_DIGGING)
return;
final StateType block = blockBreak.block.getType();
// 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(blockBreak.position)
&& !didLastFlag
&& lastBlockType.getHardness() == 0.0F
&& lastBlockType.getBlastResistance() == 0.0F
&& block == StateTypes.WATER
) return;
lastTick = newTick;
lastBreakLoc = blockBreak.position;
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()
@@ -31,13 +55,15 @@ public class BadPacketsX extends Check implements PacketCheck {
|| block == StateTypes.LAVA
|| block == StateTypes.BUBBLE_COLUMN
|| block == StateTypes.MOVING_PISTON
|| (block == StateTypes.FIRE && noFireHitbox)
|| block == StateTypes.FIRE && noFireHitbox
// or the client claims to have broken an unbreakable block
|| block.getHardness() == -1.0f && dig.getAction() == DiggingAction.FINISHED_DIGGING;
|| block.getHardness() == -1.0f && blockBreak.action == DiggingAction.FINISHED_DIGGING;
if (invalid && flagAndAlert("block=" + block.getName() + ", type=" + dig.getAction()) && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
if (invalid && flagAndAlert("block=" + block.getName() + ", type=" + blockBreak.action) && shouldModifyPackets()) {
didLastFlag = true;
blockBreak.cancel();
} else {
didLastFlag = false;
}
}
}
@@ -5,18 +5,17 @@ import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.MessageUtil;
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
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.DiggingAction;
import com.github.retrooper.packetevents.util.Vector3i;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
import static ac.grim.grimac.events.packets.patch.ResyncWorldUtil.resyncPosition;
import static ac.grim.grimac.utils.nmsutil.BlockBreakSpeed.getBlockDamage;
@CheckData(name = "BadPacketsZ", experimental = true)
@CheckData(name = "BadPacketsZ")
public class BadPacketsZ extends Check implements PacketCheck {
public BadPacketsZ(final GrimPlayer player) {
super(player);
@@ -40,9 +39,9 @@ public class BadPacketsZ extends Check implements PacketCheck {
return player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4) || getBlockDamage(player, pos) < 1;
}
public void handle(PacketReceiveEvent event, WrapperPlayClientPlayerDigging dig) {
if (dig.getAction() == DiggingAction.START_DIGGING) {
final Vector3i pos = dig.getBlockPosition();
public void handle(BlockBreak blockBreak) {
if (blockBreak.action == DiggingAction.START_DIGGING) {
final Vector3i pos = blockBreak.position;
lastBlockWasInstantBreak = getBlockDamage(player, pos) >= 1;
lastCancelledBlock = null;
@@ -50,8 +49,8 @@ public class BadPacketsZ extends Check implements PacketCheck {
lastBlock = pos;
}
if (dig.getAction() == DiggingAction.CANCELLED_DIGGING) {
final Vector3i pos = dig.getBlockPosition();
if (blockBreak.action == DiggingAction.CANCELLED_DIGGING) {
final Vector3i pos = blockBreak.position;
if (shouldExempt(pos)) {
lastCancelledBlock = pos;
@@ -65,8 +64,7 @@ public class BadPacketsZ extends Check implements PacketCheck {
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4) || (!lastBlockWasInstantBreak && pos.equals(lastCancelledBlock))) {
if (flagAndAlert("action=CANCELLED_DIGGING" + ", last=" + MessageUtil.toUnlabledString(lastBlock) + ", pos=" + MessageUtil.toUnlabledString(pos))) {
if (shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
blockBreak.cancel();
resyncPosition(player, pos);
}
}
@@ -79,15 +77,14 @@ public class BadPacketsZ extends Check implements PacketCheck {
return;
}
if (dig.getAction() == DiggingAction.FINISHED_DIGGING) {
final Vector3i pos = dig.getBlockPosition();
if (blockBreak.action == DiggingAction.FINISHED_DIGGING) {
final Vector3i pos = blockBreak.position;
// when a player looks away from the mined block, they send a cancel, and if they look at it again, they don't send another start. (thanks mojang!)
if (!pos.equals(lastCancelledBlock) && (!lastBlockWasInstantBreak || player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4)) && !pos.equals(lastBlock)) {
if (flagAndAlert("action=FINISHED_DIGGING" + ", last=" + MessageUtil.toUnlabledString(lastBlock) + ", pos=" + MessageUtil.toUnlabledString(pos))) {
if (shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
blockBreak.cancel();
resyncPosition(player, pos);
}
}
@@ -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)) {
if (player.isTickingReliablyFor(3) && !player.uncertaintyHandler.lastVehicleSwitch.hasOccurredSince(0)) {
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)) {
if (player.isTickingReliablyFor(3) && !player.uncertaintyHandler.lastVehicleSwitch.hasOccurredSince(0)) {
for (String verbose : flags) {
flagAndAlert(verbose);
}
@@ -21,10 +21,7 @@ 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;
@@ -35,22 +32,13 @@ 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 ac.grim.grimac.utils.vector.Vector3D;
import org.jetbrains.annotations.Nullable;
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 java.util.*;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
@@ -60,15 +48,11 @@ 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
@@ -102,10 +86,6 @@ 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;
@@ -130,11 +110,8 @@ public class Reach extends Check implements PacketCheck {
}
// If the player set their look, or we know they have a new tick
final boolean isFlying = WrapperPlayClientPlayerFlying.isFlying(event.getPacketType());
if (isFlying ||
event.getPacketType() == PacketType.Play.Client.PONG ||
event.getPacketType() == PacketType.Play.Client.WINDOW_CONFIRMATION) {
tickBetterReachCheckWithAngle(isFlying);
if (isUpdate(event.getPacketType())) {
tickBetterReachCheckWithAngle();
}
}
@@ -166,7 +143,7 @@ public class Reach extends Check implements PacketCheck {
}
}
private void tickBetterReachCheckWithAngle(boolean isFlying) {
private void tickBetterReachCheckWithAngle() {
for (Int2ObjectMap.Entry<Vector3d> attack : playerAttackQueue.int2ObjectEntrySet()) {
PacketEntity reachEntity = player.compensatedEntities.entityMap.get(attack.getIntKey());
if (reachEntity != null) {
@@ -181,9 +158,6 @@ 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) {
@@ -236,7 +210,7 @@ public class Reach extends Check implements PacketCheck {
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));
Vector3D intercept = ReachUtils.calculateIntercept(targetBox, eyePos, endReachPos).getFirst();
Vector3D intercept = ReachUtils.calculateIntercept(targetBox, eyePos, endReachPos).first();
if (ReachUtils.isVecInside(targetBox, eyePos)) {
minDistance = 0;
@@ -249,25 +223,9 @@ 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.MIN_VALUE && foundHitData != null) {
cancelBuffer = 1;
return "Hit block block=" + foundHitData.getState().getType().getName();
} else if (minDistance == Double.MAX_VALUE) {
if (minDistance == Double.MAX_VALUE) {
cancelBuffer = 1;
return "Missed hitbox";
} else if (minDistance > player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_ENTITY_INTERACTION_RANGE)) {
@@ -281,48 +239,8 @@ 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,32 @@
package ac.grim.grimac.checks.impl.exploit;
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.wrapper.play.client.WrapperPlayClientEditBook;
@CheckData(name = "ExploitC")
public class ExploitC extends Check implements PacketCheck {
public ExploitC(GrimPlayer playerData) {
super(playerData);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.EDIT_BOOK) {
final String title = new WrapperPlayClientEditBook(event).getTitle();
if (title == null) return; // book was not signed
if (title.length() > 15) {
if (flagAndAlert("title=" + title) && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
}
}
@@ -1,41 +0,0 @@
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();
}
}
}
}
@@ -1,33 +0,0 @@
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();
}
}
}
@@ -1,28 +0,0 @@
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();
}
}
}
@@ -1,84 +0,0 @@
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;
}
}
@@ -1,48 +0,0 @@
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();
}
}
}
@@ -1,44 +0,0 @@
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();
}
}
}
}
@@ -1,40 +0,0 @@
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();
}
}
}
}
@@ -0,0 +1,16 @@
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,34 @@
package ac.grim.grimac.checks.impl.movement;
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 com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
@CheckData(name = "TickTimer", setback = 1)
public class TickTimer extends Check implements PostPredictionCheck {
private boolean receivedTickEnd = true;
private int flyingPackets = 0;
public TickTimer(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (!player.supportsEndTick()) return;
if (isFlying(event.getPacketType()) && !player.packetStateData.lastPacketWasTeleport) {
if (!receivedTickEnd) flagAndAlert("type=flying, packets=" + flyingPackets);
receivedTickEnd = false;
flyingPackets++;
} else if (event.getPacketType() == PacketType.Play.Client.CLIENT_TICK_END) {
receivedTickEnd = true;
if (flyingPackets > 1) flagAndAlert("type=end, packets=" + flyingPackets);
flyingPackets = 0;
}
}
}
@@ -8,7 +8,6 @@ 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.packettype.PacketTypeCommon;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
@CheckData(name = "Timer", configName = "TimerA", setback = 10)
public class TimerCheck extends Check implements PacketCheck {
@@ -18,10 +17,10 @@ public class TimerCheck extends Check implements PacketCheck {
long knownPlayerClockTime = (long) (System.nanoTime() - 6e10);
long lastMovementPlayerClock = (long) (System.nanoTime() - 6e10);
// How long should the player be able to fall back behind their ping?
// How long should the player be able to fall back behind their ping? (nanos)
// Default: 120 milliseconds
long clockDrift;
// At what ping should we start to limit the balance advantage? (nanos)
long limitAbuseOverPing;
boolean hasGottenMovementAfterTransaction = false;
@@ -73,35 +72,31 @@ public class TimerCheck extends Check implements PacketCheck {
doCheck(event);
}
public void doCheck(final PacketReceiveEvent event) {
final double transactionPing = player.getTransactionPing();
// Limit using transaction ping if over 1000ms (default)
final boolean needsAdjustment = limitAbuseOverPing != -1 && transactionPing >= limitAbuseOverPing;
final boolean wouldFailNormal = timerBalanceRealTime > System.nanoTime();
final boolean failsAdjusted = needsAdjustment && (timerBalanceRealTime + ((transactionPing * 1e6) - clockDrift - 50e6)) > System.nanoTime();
if (wouldFailNormal || failsAdjusted) {
if (timerBalanceRealTime > System.nanoTime()) {
if (flag()) {
// Cancel the packet
// Only cancel if not an adjustment setback
if (wouldFailNormal && shouldModifyPackets()) {
if (shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
if (isAboveSetbackVl()) player.getSetbackTeleportUtil().executeNonSimulatingSetback();
if (wouldFailNormal) {
// Only alert if we would fail without adjusted limit
alert("");
}
alert("");
}
// Reset the violation by 1 movement
timerBalanceRealTime -= 50e6;
}
timerBalanceRealTime = Math.max(timerBalanceRealTime, lastMovementPlayerClock - clockDrift);
// Limit using transaction ping if over 1000ms (default)
long playerClock = lastMovementPlayerClock;
if (System.nanoTime() - playerClock > limitAbuseOverPing) {
playerClock = System.nanoTime() - limitAbuseOverPing;
}
timerBalanceRealTime = Math.max(timerBalanceRealTime, playerClock - clockDrift);
}
public boolean checkForTransaction(PacketTypeCommon packetType) {
@@ -111,13 +106,12 @@ public class TimerCheck extends Check implements PacketCheck {
public boolean shouldCountPacketForTimer(PacketTypeCommon packetType) {
// If not flying, or this was a teleport, or this was a duplicate 1.17 mojang stupidity packet
return WrapperPlayClientPlayerFlying.isFlying(packetType) &&
!player.packetStateData.lastPacketWasTeleport && !player.packetStateData.lastPacketWasOnePointSeventeenDuplicate;
return isTickPacket(packetType);
}
@Override
public void onReload(ConfigManager config) {
clockDrift = (long) (config.getDoubleElse(getConfigName() + ".drift", 120.0) * 1e6);
limitAbuseOverPing = (long) (config.getDoubleElse(getConfigName() + ".ping-abuse-limit-threshold", 1000));
limitAbuseOverPing = (long) (config.getDoubleElse(getConfigName() + ".ping-abuse-limit-threshold", 1000) * 1e6);
}
}
@@ -0,0 +1,29 @@
package ac.grim.grimac.checks.impl.multiactions;
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.InteractionHand;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
@CheckData(name = "MultiActionsA", experimental = true)
public class MultiActionsA extends Check implements PacketCheck {
public MultiActionsA(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (player.packetStateData.isSlowedByUsingItem() && (player.packetStateData.lastSlotSelected == player.packetStateData.getSlowedByUsingItemSlot() || player.packetStateData.eatingHand == InteractionHand.OFF_HAND) && event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
if (new WrapperPlayClientInteractEntity(event).getAction() == WrapperPlayClientInteractEntity.InteractAction.ATTACK) {
if (flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
}
}
@@ -0,0 +1,41 @@
package ac.grim.grimac.checks.impl.multiactions;
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.DiggingAction;
import com.github.retrooper.packetevents.protocol.player.InteractionHand;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
import static ac.grim.grimac.events.packets.patch.ResyncWorldUtil.resyncPosition;
@CheckData(name = "MultiActionsB", experimental = true)
public class MultiActionsB extends Check implements PacketCheck {
public MultiActionsB(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (player.packetStateData.isSlowedByUsingItem() && (player.packetStateData.lastSlotSelected == player.packetStateData.getSlowedByUsingItemSlot() || player.packetStateData.eatingHand == InteractionHand.OFF_HAND) && event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING) {
// this is vanilla on 1.7
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_7_10)) {
return;
}
final WrapperPlayClientPlayerDigging packet = new WrapperPlayClientPlayerDigging(event);
if (packet.getAction() == DiggingAction.START_DIGGING || packet.getAction() == DiggingAction.CANCELLED_DIGGING || packet.getAction() == DiggingAction.FINISHED_DIGGING) {
if (flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
resyncPosition(player, packet.getBlockPosition());
}
}
}
}
}
@@ -0,0 +1,31 @@
package ac.grim.grimac.checks.impl.multiactions;
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;
@CheckData(name = "MultiActionsC", experimental = true)
public class MultiActionsC extends Check implements PacketCheck {
public MultiActionsC(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW) {
String verbose = player.isSprinting && !player.isSwimming ? "sprinting" : "";
if (player.packetStateData.isSlowedByUsingItem()) {
verbose += (verbose.isEmpty() ? "" : ", ") + "using";
}
if (!verbose.isEmpty() && flagAndAlert(verbose) && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
}
@@ -0,0 +1,31 @@
package ac.grim.grimac.checks.impl.multiactions;
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;
@CheckData(name = "MultiActionsD", experimental = true)
public class MultiActionsD extends Check implements PacketCheck {
public MultiActionsD(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.CLOSE_WINDOW) {
String verbose = player.isSprinting && !player.isSwimming ? "sprinting" : "";
if (player.packetStateData.isSlowedByUsingItem()) {
verbose += (verbose.isEmpty() ? "" : ", ") + "using";
}
if (!verbose.isEmpty() && flagAndAlert(verbose) && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
}
@@ -0,0 +1,32 @@
package ac.grim.grimac.checks.impl.multiactions;
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;
@CheckData(name = "MultiActionsE", experimental = true)
public class MultiActionsE extends Check implements PacketCheck {
public MultiActionsE(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (player.packetStateData.isSlowedByUsingItem() && (player.packetStateData.lastSlotSelected == player.packetStateData.getSlowedByUsingItemSlot() || player.packetStateData.eatingHand == InteractionHand.OFF_HAND) && event.getPacketType() == PacketType.Play.Client.ANIMATION) {
// this is possible to false on 1.7
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_7_10)) {
return;
}
if (flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
}
@@ -0,0 +1,92 @@
package ac.grim.grimac.checks.impl.multiactions;
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.player.DiggingAction;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
import java.util.ArrayList;
import java.util.List;
import static ac.grim.grimac.events.packets.patch.ResyncWorldUtil.resyncPosition;
@CheckData(name = "MultiActionsF", experimental = true)
public class MultiActionsF extends BlockPlaceCheck {
public MultiActionsF(GrimPlayer player) {
super(player);
}
private final List<String> flags = new ArrayList<>();
boolean entity, block;
@Override
public void onBlockPlace(BlockPlace place) {
block = true;
if (entity) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert("place") && shouldModifyPackets() && shouldCancel()) {
place.resync();
}
} else {
flags.add("place");
}
}
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
entity = true;
if (block) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert("entity") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else {
flags.add("entity");
}
}
}
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING) {
WrapperPlayClientPlayerDigging packet = new WrapperPlayClientPlayerDigging(event);
if (packet.getAction() == DiggingAction.START_DIGGING || packet.getAction() == DiggingAction.FINISHED_DIGGING) {
block = true;
if (entity) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert("dig") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
resyncPosition(player, packet.getBlockPosition());
}
} else {
flags.add("dig");
}
}
}
}
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && !player.packetStateData.lastPacketWasTeleport && !player.packetStateData.lastPacketWasOnePointSeventeenDuplicate) {
block = entity = false;
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
if (player.getClientVersion().isNewerThan(ClientVersion.V_1_8) && player.isTickingReliablyFor(3) && !player.uncertaintyHandler.lastVehicleSwitch.hasOccurredSince(0)) {
for (String verbose : flags) {
flagAndAlert(verbose);
}
}
flags.clear();
}
}
@@ -1,54 +0,0 @@
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;
}
}
@@ -1,13 +0,0 @@
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);
}
}
@@ -1,66 +0,0 @@
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;
}
}
@@ -1,61 +0,0 @@
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;
}
}
@@ -1,69 +0,0 @@
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;
}
}
@@ -1,51 +0,0 @@
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;
}
}
@@ -1,130 +0,0 @@
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);
}
}
@@ -1,49 +0,0 @@
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;
}
}
@@ -1,64 +0,0 @@
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();
}
}
@@ -1,64 +0,0 @@
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();
}
}
@@ -1,72 +0,0 @@
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;
}
}
@@ -1,69 +0,0 @@
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;
}
}
@@ -1,60 +0,0 @@
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);
}
}
@@ -1,63 +0,0 @@
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;
}
}
}
@@ -1,178 +0,0 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.wrapper.play.client.*;
import lombok.Getter;
import org.jetbrains.annotations.Contract;
@Getter
public final class PacketOrderProcessor extends Check implements PostPredictionCheck {
public PacketOrderProcessor(final GrimPlayer player) {
super(player);
}
private boolean openingInventory; // only pre 1.9 clients on pre 1.9 servers
private boolean swapping;
private boolean dropping;
private boolean interacting;
private boolean attacking;
private boolean releasing;
private boolean digging;
private boolean sprinting;
private boolean sneaking;
private boolean placing;
private boolean using;
private boolean picking;
private boolean clickingInInventory;
private boolean closingInventory;
private boolean quickMoveClicking;
private boolean pickUpClicking;
private boolean leavingBed;
private boolean startingToGlide;
private boolean jumpingWithMount;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
final PacketTypeCommon packetType = event.getPacketType();
if (packetType == PacketType.Play.Client.CLIENT_STATUS) {
if (new WrapperPlayClientClientStatus(event).getAction() == WrapperPlayClientClientStatus.Action.OPEN_INVENTORY_ACHIEVEMENT) {
openingInventory = true;
}
}
if (packetType == PacketType.Play.Client.INTERACT_ENTITY) {
if (new WrapperPlayClientInteractEntity(event).getAction() == WrapperPlayClientInteractEntity.InteractAction.ATTACK) {
attacking = true;
} else {
interacting = true;
}
}
if (packetType == PacketType.Play.Client.PLAYER_DIGGING) {
switch (new WrapperPlayClientPlayerDigging(event).getAction()) {
case SWAP_ITEM_WITH_OFFHAND:
swapping = true;
break;
case DROP_ITEM:
case DROP_ITEM_STACK:
dropping = true;
break;
case RELEASE_USE_ITEM:
releasing = true;
break;
case FINISHED_DIGGING:
case CANCELLED_DIGGING:
case START_DIGGING:
digging = true;
break;
}
}
if (packetType == PacketType.Play.Client.ENTITY_ACTION) {
switch (new WrapperPlayClientEntityAction(event).getAction()) {
case START_SPRINTING:
case STOP_SPRINTING:
sprinting = true;
break;
case STOP_SNEAKING:
case START_SNEAKING:
sneaking = true;
break;
case LEAVE_BED:
leavingBed = true;
break;
case START_FLYING_WITH_ELYTRA:
startingToGlide = true;
break;
case OPEN_HORSE_INVENTORY:
openingInventory = true;
break;
case START_JUMPING_WITH_HORSE:
case STOP_JUMPING_WITH_HORSE:
jumpingWithMount = true;
break;
}
}
if (packetType == PacketType.Play.Client.USE_ITEM) {
using = true;
}
if (packetType == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT) {
if (new WrapperPlayClientPlayerBlockPlacement(event).getFace() == BlockFace.OTHER) {
using = true;
} else {
placing = true;
}
}
if (packetType == PacketType.Play.Client.PICK_ITEM) {
picking = true;
}
if (packetType == PacketType.Play.Client.CLICK_WINDOW) {
clickingInInventory = true;
switch (new WrapperPlayClientClickWindow(event).getWindowClickType()) {
case QUICK_MOVE:
quickMoveClicking = true;
break;
case PICKUP:
case PICKUP_ALL:
pickUpClicking = true;
break;
}
}
if (packetType == PacketType.Play.Client.CLOSE_WINDOW) {
closingInventory = true;
}
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8) && !player.packetStateData.lastPacketWasTeleport) {
onTick();
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
onTick();
}
}
private void onTick() {
openingInventory = false;
swapping = false;
dropping = false;
attacking = false;
interacting = false;
releasing = false;
digging = false;
placing = false;
using = false;
picking = false;
sprinting = false;
sneaking = false;
clickingInInventory = false;
closingInventory = false;
quickMoveClicking = false;
pickUpClicking = false;
leavingBed = false;
startingToGlide = false;
jumpingWithMount = false;
}
@Contract(pure = true)
public boolean isRightClicking() {
return placing || using || interacting;
}
}
@@ -2,6 +2,7 @@ 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;
@@ -14,7 +15,6 @@ 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.wrapper.play.client.WrapperPlayClientEntityAction;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityAnimation;
import java.util.ArrayDeque;
@@ -24,7 +24,7 @@ import java.util.Locale;
import static com.github.retrooper.packetevents.protocol.packettype.PacketType.Play.Client.*;
@CheckData(name = "Post")
public class Post extends Check implements PostPredictionCheck {
public class PostCheck extends Check implements PacketCheck, 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
@@ -32,7 +32,7 @@ public class Post extends Check implements PostPredictionCheck {
private boolean sentFlying = false;
private int isExemptFromSwingingCheck = Integer.MIN_VALUE;
public Post(GrimPlayer playerData) {
public PostCheck(GrimPlayer playerData) {
super(playerData);
}
@@ -42,7 +42,7 @@ public class Post extends Check implements 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)) {
if (player.isTickingReliablyFor(3) && !player.uncertaintyHandler.lastVehicleSwitch.hasOccurredSince(0)) {
for (String flag : flags) {
flagAndAlert(flag);
}
@@ -67,12 +67,7 @@ public class Post extends Check implements PostPredictionCheck {
@Override
public void onPacketReceive(final PacketReceiveEvent event) {
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType())) {
// Don't count teleports or duplicates as movements
if (player.packetStateData.lastPacketWasTeleport || player.packetStateData.lastPacketWasOnePointSeventeenDuplicate) {
return;
}
if (isTickPacket(event.getPacketType())) { // Don't count teleports or duplicates as movements
post.clear();
sentFlying = true;
} else {
@@ -1,224 +0,0 @@
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));
}
}
}
@@ -11,7 +11,6 @@ import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.util.Vector3f;
import com.github.retrooper.packetevents.util.Vector3i;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
import java.util.ArrayList;
import java.util.List;
@@ -56,14 +55,14 @@ public class MultiPlace extends BlockPlaceCheck {
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && !player.packetStateData.lastPacketWasTeleport && !player.packetStateData.lastPacketWasOnePointSeventeenDuplicate) {
if (isTickPacket(event.getPacketType())) {
hasPlaced = false;
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
if (player.getClientVersion().isNewerThan(ClientVersion.V_1_8) && player.isTickingReliablyFor(3)) {
if (player.getClientVersion().isNewerThan(ClientVersion.V_1_8) && player.isTickingReliablyFor(3) && !player.uncertaintyHandler.lastVehicleSwitch.hasOccurredSince(0)) {
for (String verbose : flags) {
flagAndAlert(verbose);
}
@@ -48,27 +48,21 @@ 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 = 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;
}
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;
};
if (flag) {
if (flagAndAlert() && shouldModifyPackets() && shouldCancel()) {
@@ -1 +1,122 @@
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 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.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 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<Vector3D, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
if (intercept.first() != null) return true;
}
}
return false;
}
}
@@ -19,7 +19,6 @@ 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 ac.grim.grimac.utils.vector.Vector3D;
import org.jetbrains.annotations.Nullable;
import java.util.Deque;
@@ -68,6 +68,7 @@ public class KnockbackHandler extends Check implements PostPredictionCheck {
if (playerVelocity.getY() == -0.04) {
velocity.setVelocity(playerVelocity.add(new Vector3d(0, 1 / 8000D, 0)));
playerVelocity = velocity.getVelocity();
event.markForReEncode(true);
}
// Wrap velocity between two transactions
@@ -1,67 +0,0 @@
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();
}
}
@@ -1,19 +1,10 @@
package ac.grim.grimac.checks.type;
import ac.grim.grimac.api.AbstractCheck;
import ac.grim.grimac.utils.anticheat.update.PositionUpdate;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.event.PacketSendEvent;
public interface PacketCheck extends AbstractCheck {
default void onPacketReceive(final PacketReceiveEvent event) {
}
default void onPacketSend(final PacketSendEvent event) {
}
default void onPositionUpdate(final PositionUpdate positionUpdate) {
}
default void onPacketReceive(final PacketReceiveEvent event) {}
default void onPacketSend(final PacketSendEvent event) {}
}
@@ -23,8 +23,7 @@ public class GrimSpectate extends BaseCommand {
@CommandPermission("grim.spectate")
@CommandCompletion("@players")
public void onSpectate(CommandSender sender, @Optional OnlinePlayer target) {
if (!(sender instanceof Player)) return;
Player player = (Player) sender;
if (!(sender instanceof Player player)) return;
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,6 +3,8 @@ 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;
@@ -10,17 +12,21 @@ 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("here")
@CommandCompletion("@stopspectating")
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");
boolean teleportBack = string == null || !string.equalsIgnoreCase("here") || !sender.hasPermission("grim.spectate.stophere");
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,17 +3,22 @@ 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 ac.grim.grimac.utils.vector.Vector3D;
import ac.grim.grimac.utils.vector.VectorFactory;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
import com.github.retrooper.packetevents.event.PacketListenerPriority;
@@ -21,6 +26,7 @@ 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;
@@ -44,6 +50,9 @@ import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerAc
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSetSlot;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
public class CheckManagerListener extends PacketListenerAbstract {
@@ -52,8 +61,79 @@ 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 = BlockRayTrace.getNearestHitResult(player, StateTypes.AIR, false);
HitData data = getNearestHitResult(player, StateTypes.AIR, false);
if (data != null) {
BlockPlace blockPlace = new BlockPlace(player, hand, data.getPosition(), data.getClosestDirection().getFaceValue(), data.getClosestDirection(), held, data);
@@ -148,9 +228,8 @@ public class CheckManagerListener extends PacketListenerAbstract {
private static void handleBlockPlaceOrUseItem(PacketWrapper<?> packet, GrimPlayer player) {
// Legacy "use item" packet
if (packet instanceof WrapperPlayClientPlayerBlockPlacement &&
if (packet instanceof WrapperPlayClientPlayerBlockPlacement place &&
PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_9)) {
WrapperPlayClientPlayerBlockPlacement place = (WrapperPlayClientPlayerBlockPlacement) packet;
if (player.gamemode == GameMode.SPECTATOR || player.gamemode == GameMode.ADVENTURE) return;
@@ -165,9 +244,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
}
}
if (packet instanceof WrapperPlayClientUseItem) {
WrapperPlayClientUseItem place = (WrapperPlayClientUseItem) packet;
if (packet instanceof WrapperPlayClientUseItem place) {
if (player.gamemode == GameMode.SPECTATOR || player.gamemode == GameMode.ADVENTURE) return;
ItemStack placedWith = player.getInventory().getHeldItem();
@@ -179,9 +256,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
}
// Check for interactable first (door, etc)
if (packet instanceof WrapperPlayClientPlayerBlockPlacement) {
WrapperPlayClientPlayerBlockPlacement place = (WrapperPlayClientPlayerBlockPlacement) packet;
if (packet instanceof WrapperPlayClientPlayerBlockPlacement place) {
ItemStack placedWith = player.getInventory().getHeldItem();
ItemStack offhand = player.getInventory().getOffHand();
@@ -190,7 +265,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, BlockRayTrace.getNearestHitResult(player, null, true));
BlockPlace blockPlace = new BlockPlace(player, place.getHand(), blockPosition, place.getFaceId(), place.getFace(), placedWith, getNearestHitResult(player, null, true));
// Right-clicking a trapdoor/door/etc.
StateType placedAgainst = blockPlace.getPlacedAgainstMaterial();
@@ -217,8 +292,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
}
}
if (packet instanceof WrapperPlayClientPlayerBlockPlacement) {
WrapperPlayClientPlayerBlockPlacement place = (WrapperPlayClientPlayerBlockPlacement) packet;
if (packet instanceof WrapperPlayClientPlayerBlockPlacement place) {
Vector3i blockPosition = place.getBlockPosition();
BlockFace face = place.getFace();
@@ -230,7 +304,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
placedWith = player.getInventory().getOffHand();
}
BlockPlace blockPlace = new BlockPlace(player, place.getHand(), blockPosition, place.getFaceId(), face, placedWith, BlockRayTrace.getNearestHitResult(player, null, true));
BlockPlace blockPlace = new BlockPlace(player, place.getHand(), blockPosition, place.getFaceId(), face, placedWith, 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);
@@ -298,9 +372,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
}
// Manual filter on FINISH_DIGGING to prevent clients setting non-breakable blocks to air
private static final Function<StateType, Boolean> BREAKABLE = type -> {
return !type.isAir() && type.getHardness() != -1.0f && type != StateTypes.WATER && type != StateTypes.LAVA;
};
private static final Function<StateType, Boolean> BREAKABLE = type -> !type.isAir() && type.getHardness() != -1.0f && type != StateTypes.WATER && type != StateTypes.LAVA;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
@@ -382,45 +454,43 @@ public class CheckManagerListener extends PacketListenerAbstract {
}
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING) {
WrapperPlayClientPlayerDigging dig = new WrapperPlayClientPlayerDigging(event);
final Vector3i digPosition = dig.getBlockPosition();
WrappedBlockState block = player.compensatedWorld.getWrappedBlockStateAt(digPosition);
final StateType type = block.getType();
final WrapperPlayClientPlayerDigging packet = new WrapperPlayClientPlayerDigging(event);
final BlockBreak blockBreak = new BlockBreak(packet, player);
player.checkManager.getPacketCheck(InventoryB.class).handle(event, dig);
player.checkManager.getPacketCheck(BadPacketsX.class).handle(blockBreak);
player.checkManager.getPacketCheck(BadPacketsZ.class).handle(blockBreak);
player.checkManager.getPacketCheck(BadPacketsX.class).handle(event, dig, type);
player.checkManager.getPacketCheck(BadPacketsZ.class).handle(event, dig);
if (dig.getAction() == DiggingAction.FINISHED_DIGGING) {
// Not unbreakable
if (BREAKABLE.apply(type) && !event.isCancelled()) {
player.compensatedWorld.startPredicting();
player.compensatedWorld.updateBlock(digPosition.getX(), digPosition.getY(), digPosition.getZ(), 0);
player.compensatedWorld.stopPredicting(dig);
}
}
if (dig.getAction() == DiggingAction.START_DIGGING && !event.isCancelled()) {
double damage = BlockBreakSpeed.getBlockDamage(player, digPosition);
//Instant breaking, no damage means it is unbreakable by creative players (with swords)
if (damage >= 1) {
player.compensatedWorld.startPredicting();
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13) && Materials.isWaterSource(player.getClientVersion(), block)) {
// Vanilla uses a method to grab water flowing, but as you can't break flowing water
// We can simply treat all waterlogged blocks or source blocks as source blocks
player.compensatedWorld.updateBlock(digPosition, StateTypes.WATER.createBlockState(CompensatedWorld.blockVersion));
} else {
player.compensatedWorld.updateBlock(digPosition.getX(), digPosition.getY(), digPosition.getZ(), 0);
}
player.compensatedWorld.stopPredicting(dig);
}
if (blockBreak.isCancelled()) {
event.setCancelled(true);
player.onPacketCancel();
}
if (!event.isCancelled()) {
if (dig.getAction() == DiggingAction.START_DIGGING || dig.getAction() == DiggingAction.FINISHED_DIGGING || dig.getAction() == DiggingAction.CANCELLED_DIGGING) {
player.compensatedWorld.handleBlockBreakPrediction(dig);
if (blockBreak.action == DiggingAction.FINISHED_DIGGING && BREAKABLE.apply(blockBreak.block.getType())) {
player.compensatedWorld.startPredicting();
player.compensatedWorld.updateBlock(blockBreak.position.x, blockBreak.position.y, blockBreak.position.z, 0);
player.compensatedWorld.stopPredicting(packet);
}
if (blockBreak.action == DiggingAction.START_DIGGING) {
double damage = BlockBreakSpeed.getBlockDamage(player, blockBreak.position);
// Instant breaking, no damage means it is unbreakable by creative players (with swords)
if (damage >= 1) {
player.compensatedWorld.startPredicting();
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13) && Materials.isWaterSource(player.getClientVersion(), blockBreak.block)) {
// Vanilla uses a method to grab water flowing, but as you can't break flowing water
// We can simply treat all waterlogged blocks or source blocks as source blocks
player.compensatedWorld.updateBlock(blockBreak.position, StateTypes.WATER.createBlockState(CompensatedWorld.blockVersion));
} else {
player.compensatedWorld.updateBlock(blockBreak.position.x, blockBreak.position.y, blockBreak.position.z, 0);
}
player.compensatedWorld.stopPredicting(packet);
}
}
if (blockBreak.action == DiggingAction.START_DIGGING || blockBreak.action == DiggingAction.FINISHED_DIGGING || blockBreak.action == DiggingAction.CANCELLED_DIGGING) {
player.compensatedWorld.handleBlockBreakPrediction(packet);
}
}
}
@@ -439,7 +509,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, BlockRayTrace.getNearestHitResult(player, null, true));
BlockPlace blockPlace = new BlockPlace(player, packet.getHand(), packet.getBlockPosition(), packet.getFaceId(), packet.getFace(), placedWith, 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)) {
@@ -507,13 +577,22 @@ public class CheckManagerListener extends PacketListenerAbstract {
player.packetStateData.cancelDuplicatePacket = false;
}
if (event.getPacketType() == PacketType.Play.Client.CLIENT_TICK_END) {
if (!player.packetStateData.didSendMovementBeforeTickEnd) {
// The player didn't send a movement packet, so we can predict this like we had idle tick on 1.8
player.packetStateData.didLastLastMovementIncludePosition = player.packetStateData.didLastMovementIncludePosition;
player.packetStateData.didLastMovementIncludePosition = false;
}
player.packetStateData.didSendMovementBeforeTickEnd = false;
}
// Finally, remove the packet state variables on this packet
player.packetStateData.lastPacketWasOnePointSeventeenDuplicate = false;
player.packetStateData.lastPacketWasTeleport = false;
}
private static void placeBucket(GrimPlayer player, InteractionHand hand) {
HitData data = BlockRayTrace.getNearestHitResult(player, null, true);
HitData data = getNearestHitResult(player, null, true);
if (data != null) {
BlockPlace blockPlace = new BlockPlace(player, hand, data.getPosition(), data.getClosestDirection().getFaceValue(), data.getClosestDirection(), ItemStack.EMPTY, data);
@@ -676,10 +755,14 @@ public class CheckManagerListener extends PacketListenerAbstract {
player.packetStateData.didLastLastMovementIncludePosition = player.packetStateData.didLastMovementIncludePosition;
player.packetStateData.didLastMovementIncludePosition = hasPosition;
if (!player.packetStateData.lastPacketWasTeleport) {
player.packetStateData.didSendMovementBeforeTickEnd = true;
}
}
private static void placeLilypad(GrimPlayer player, InteractionHand hand) {
HitData data = BlockRayTrace.getNearestHitResult(player, null, true);
HitData data = getNearestHitResult(player, null, true);
if (data != null) {
// A lilypad cannot replace a fluid
@@ -709,6 +792,56 @@ 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);
Vector3D startingVec = VectorFactory.newVector3D(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);
Vector3D 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;
Vector3D bestHitLoc = null;
BlockFace bestFace = null;
for (SimpleCollisionBox box : boxes) {
Pair<Vector3D, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
if (intercept.first() == null) continue; // No intercept
Vector3D 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<Vector3D, 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;
@@ -13,6 +13,7 @@ import com.github.retrooper.packetevents.PacketEvents;
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.entity.EntityPositionData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
@@ -21,7 +22,6 @@ import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.UserProfile;
import com.github.retrooper.packetevents.protocol.potion.PotionType;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
import com.github.retrooper.packetevents.wrapper.play.server.*;
import io.github.retrooper.packetevents.util.viaversion.ViaVersionUtil;
@@ -60,22 +60,19 @@ public class PacketEntityReplication extends Check implements PacketCheck {
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType())) {
// Teleports don't interpolate, duplicate 1.17 packets don't interpolate
if (player.packetStateData.lastPacketWasTeleport || player.packetStateData.lastPacketWasOnePointSeventeenDuplicate)
return;
// Teleports don't interpolate, duplicate 1.17 packets don't interpolate
if (!isTickPacket(event.getPacketType())) return;
boolean isTickingReliably = player.isTickingReliablyFor(3);
boolean isTickingReliably = player.isTickingReliablyFor(3);
PacketEntity playerVehicle = player.compensatedEntities.getSelf().getRiding();
for (PacketEntity entity : player.compensatedEntities.entityMap.values()) {
if (entity == playerVehicle && !player.vehicleData.lastDummy) {
// The player has this as their vehicle, so they aren't interpolating it.
// And it isn't a dummy position
entity.setPositionRaw(entity.getPossibleCollisionBoxes());
} else {
entity.onMovement(isTickingReliably);
}
PacketEntity playerVehicle = player.compensatedEntities.getSelf().getRiding();
for (PacketEntity entity : player.compensatedEntities.entityMap.values()) {
if (entity == playerVehicle && !player.vehicleData.lastDummy) {
// The player has this as their vehicle, so they aren't interpolating it.
// And it isn't a dummy position
entity.setPositionRaw(entity.getPossibleCollisionBoxes());
} else {
entity.onMovement(isTickingReliably);
}
}
}
@@ -86,44 +83,55 @@ public class PacketEntityReplication extends Check implements PacketCheck {
if ((event.getPacketType() == PacketType.Play.Server.PING || event.getPacketType() == PacketType.Play.Server.WINDOW_CONFIRMATION) && player.packetStateData.lastServerTransWasValid) {
despawnedEntitiesThisTransaction.clear();
}
if (event.getPacketType() == PacketType.Play.Server.SPAWN_LIVING_ENTITY) {
else if (event.getPacketType() == PacketType.Play.Server.SPAWN_LIVING_ENTITY) {
WrapperPlayServerSpawnLivingEntity packetOutEntity = new WrapperPlayServerSpawnLivingEntity(event);
addEntity(packetOutEntity.getEntityId(), packetOutEntity.getEntityUUID(), packetOutEntity.getEntityType(), packetOutEntity.getPosition(), packetOutEntity.getYaw(), packetOutEntity.getPitch(), packetOutEntity.getEntityMetadata(), 0);
}
if (event.getPacketType() == PacketType.Play.Server.SPAWN_ENTITY) {
else if (event.getPacketType() == PacketType.Play.Server.SPAWN_ENTITY) {
WrapperPlayServerSpawnEntity packetOutEntity = new WrapperPlayServerSpawnEntity(event);
addEntity(packetOutEntity.getEntityId(), packetOutEntity.getUUID().orElse(null), packetOutEntity.getEntityType(), packetOutEntity.getPosition(), packetOutEntity.getYaw(), packetOutEntity.getPitch(), null, packetOutEntity.getData());
}
if (event.getPacketType() == PacketType.Play.Server.SPAWN_PLAYER) {
else if (event.getPacketType() == PacketType.Play.Server.SPAWN_PLAYER) {
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.ENTITY_RELATIVE_MOVE) {
else if (event.getPacketType() == PacketType.Play.Server.SPAWN_PAINTING) {
WrapperPlayServerSpawnPainting packetOutEntity = new WrapperPlayServerSpawnPainting(event);
addEntity(packetOutEntity.getEntityId(), packetOutEntity.getUUID(), EntityTypes.PAINTING, packetOutEntity.getPosition().toVector3d(), 0, 0f, null, packetOutEntity.getDirection().getHorizontalIndex());
}
else if (event.getPacketType() == PacketType.Play.Server.ENTITY_RELATIVE_MOVE) {
WrapperPlayServerEntityRelativeMove move = new WrapperPlayServerEntityRelativeMove(event);
handleMoveEntity(event, move.getEntityId(), move.getDeltaX(), move.getDeltaY(), move.getDeltaZ(), null, null, true, true);
}
if (event.getPacketType() == PacketType.Play.Server.ENTITY_RELATIVE_MOVE_AND_ROTATION) {
else if (event.getPacketType() == PacketType.Play.Server.ENTITY_RELATIVE_MOVE_AND_ROTATION) {
WrapperPlayServerEntityRelativeMoveAndRotation move = new WrapperPlayServerEntityRelativeMoveAndRotation(event);
handleMoveEntity(event, move.getEntityId(), move.getDeltaX(), move.getDeltaY(), move.getDeltaZ(), move.getYaw() * 0.7111111F, move.getPitch() * 0.7111111F, true, true);
}
if (event.getPacketType() == PacketType.Play.Server.ENTITY_TELEPORT) {
else if (event.getPacketType() == PacketType.Play.Server.ENTITY_TELEPORT) {
WrapperPlayServerEntityTeleport move = new WrapperPlayServerEntityTeleport(event);
Vector3d pos = move.getPosition();
handleMoveEntity(event, move.getEntityId(), pos.getX(), pos.getY(), pos.getZ(), move.getYaw(), move.getPitch(), false, true);
}
if (event.getPacketType() == PacketType.Play.Server.ENTITY_ROTATION) { // Affects interpolation
else if (event.getPacketType() == PacketType.Play.Server.ENTITY_POSITION_SYNC) {
// ENTITY_TELEPORT but without relative flags
WrapperPlayServerEntityPositionSync move = new WrapperPlayServerEntityPositionSync(event);
final EntityPositionData values = move.getValues();
final Vector3d pos = values.getPosition();
// TODO this isn't technically correct
// If the position sync is to a pos > 4096 from the entity pos, client does some special stuff without interpolation
handleMoveEntity(event, move.getId(), pos.getX(), pos.getY(), pos.getZ(), values.getYaw(), values.getPitch(), false, true);
}
else if (event.getPacketType() == PacketType.Play.Server.ENTITY_ROTATION) { // Affects interpolation
WrapperPlayServerEntityRotation move = new WrapperPlayServerEntityRotation(event);
handleMoveEntity(event, move.getEntityId(), 0, 0, 0, move.getYaw() * 0.7111111F, move.getPitch() * 0.7111111F, true, false);
}
if (event.getPacketType() == PacketType.Play.Server.ENTITY_METADATA) {
else if (event.getPacketType() == PacketType.Play.Server.ENTITY_METADATA) {
WrapperPlayServerEntityMetadata entityMetadata = new WrapperPlayServerEntityMetadata(event);
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(), () -> player.compensatedEntities.updateEntityMetadata(entityMetadata.getEntityId(), entityMetadata.getEntityMetadata()));
}
// 1.19.3+
if (event.getPacketType() == PacketType.Play.Server.PLAYER_INFO_UPDATE) {
else if (event.getPacketType() == PacketType.Play.Server.PLAYER_INFO_UPDATE) {
WrapperPlayServerPlayerInfoUpdate info = new WrapperPlayServerPlayerInfoUpdate(event);
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(), () -> {
for (WrapperPlayServerPlayerInfoUpdate.PlayerInfo entry : info.getEntries()) {
@@ -150,7 +158,7 @@ public class PacketEntityReplication extends Check implements PacketCheck {
});
}
if (event.getPacketType() == PacketType.Play.Server.ENTITY_EFFECT) {
else if (event.getPacketType() == PacketType.Play.Server.ENTITY_EFFECT) {
WrapperPlayServerEntityEffect effect = new WrapperPlayServerEntityEffect(event);
PotionType type = effect.getPotionType();
@@ -182,7 +190,7 @@ public class PacketEntityReplication extends Check implements PacketCheck {
});
}
if (event.getPacketType() == PacketType.Play.Server.REMOVE_ENTITY_EFFECT) {
else if (event.getPacketType() == PacketType.Play.Server.REMOVE_ENTITY_EFFECT) {
WrapperPlayServerRemoveEntityEffect effect = new WrapperPlayServerRemoveEntityEffect(event);
if (isDirectlyAffectingPlayer(player, effect.getEntityId())) player.sendTransaction();
@@ -195,7 +203,7 @@ public class PacketEntityReplication extends Check implements PacketCheck {
});
}
if (event.getPacketType() == PacketType.Play.Server.UPDATE_ATTRIBUTES) {
else if (event.getPacketType() == PacketType.Play.Server.UPDATE_ATTRIBUTES) {
WrapperPlayServerUpdateAttributes attributes = new WrapperPlayServerUpdateAttributes(event);
int entityID = attributes.getEntityId();
@@ -207,7 +215,7 @@ public class PacketEntityReplication extends Check implements PacketCheck {
() -> player.compensatedEntities.updateAttributes(entityID, attributes.getProperties()));
}
if (event.getPacketType() == PacketType.Play.Server.ENTITY_STATUS) {
else if (event.getPacketType() == PacketType.Play.Server.ENTITY_STATUS) {
WrapperPlayServerEntityStatus status = new WrapperPlayServerEntityStatus(event);
// This hasn't changed from 1.7.2 to 1.17
// Needed to exempt players on dead vehicles, as dead entities have strange physics.
@@ -227,9 +235,8 @@ public class PacketEntityReplication extends Check implements PacketCheck {
if (status.getStatus() == 31) {
PacketEntity hook = player.compensatedEntities.getEntity(status.getEntityId());
if (!(hook instanceof PacketEntityHook)) return;
if (!(hook instanceof PacketEntityHook hookEntity)) 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.
@@ -242,7 +249,7 @@ public class PacketEntityReplication extends Check implements PacketCheck {
}
}
if (event.getPacketType() == PacketType.Play.Server.SET_SLOT) {
else if (event.getPacketType() == PacketType.Play.Server.SET_SLOT) {
WrapperPlayServerSetSlot slot = new WrapperPlayServerSetSlot(event);
if (slot.getWindowId() == 0) {
@@ -260,7 +267,7 @@ public class PacketEntityReplication extends Check implements PacketCheck {
}
}
if (event.getPacketType() == PacketType.Play.Server.WINDOW_ITEMS) {
else if (event.getPacketType() == PacketType.Play.Server.WINDOW_ITEMS) {
WrapperPlayServerWindowItems items = new WrapperPlayServerWindowItems(event);
if (items.getWindowId() == 0) { // Player inventory
@@ -270,16 +277,16 @@ public class PacketEntityReplication extends Check implements PacketCheck {
}
// 1.8 clients fail to send the RELEASE_USE_ITEM packet when a window is opened client sided while using an item
if (event.getPacketType() == PacketType.Play.Server.OPEN_WINDOW) {
else if (event.getPacketType() == PacketType.Play.Server.OPEN_WINDOW) {
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(), () -> player.packetStateData.setSlowedByUsingItem(false));
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get() + 1, () -> player.packetStateData.setSlowedByUsingItem(false));
}
if (event.getPacketType() == PacketType.Play.Server.OPEN_HORSE_WINDOW) {
else if (event.getPacketType() == PacketType.Play.Server.OPEN_HORSE_WINDOW) {
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(), () -> player.packetStateData.setSlowedByUsingItem(false));
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get() + 1, () -> player.packetStateData.setSlowedByUsingItem(false));
}
if (event.getPacketType() == PacketType.Play.Server.SET_PASSENGERS) {
else if (event.getPacketType() == PacketType.Play.Server.SET_PASSENGERS) {
WrapperPlayServerSetPassengers mount = new WrapperPlayServerSetPassengers(event);
int vehicleID = mount.getEntityId();
@@ -288,7 +295,7 @@ public class PacketEntityReplication extends Check implements PacketCheck {
handleMountVehicle(event, vehicleID, passengers);
}
if (event.getPacketType() == PacketType.Play.Server.ATTACH_ENTITY) {
else if (event.getPacketType() == PacketType.Play.Server.ATTACH_ENTITY) {
WrapperPlayServerAttachEntity attach = new WrapperPlayServerAttachEntity(event);
// This packet was replaced by the mount packet on 1.9+ servers - to support multiple passengers on one vehicle
@@ -319,7 +326,7 @@ public class PacketEntityReplication extends Check implements PacketCheck {
}
}
if (event.getPacketType() == PacketType.Play.Server.DESTROY_ENTITIES) {
else if (event.getPacketType() == PacketType.Play.Server.DESTROY_ENTITIES) {
WrapperPlayServerDestroyEntities destroy = new WrapperPlayServerDestroyEntities(event);
int[] destroyEntityIds = destroy.getEntityIds();
@@ -414,9 +421,13 @@ public class PacketEntityReplication extends Check implements PacketCheck {
// As we don't want vehicles to fly, we need to replace it with a teleport if it is player vehicle
//
// Don't bother with client controlled vehicles though
boolean vanillaVehicleFlight = player.compensatedEntities.serverPlayerVehicle != null && player.compensatedEntities.serverPlayerVehicle == entityId
&& player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) &&
PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9);
boolean vanillaVehicleFlight = player.compensatedEntities.serverPlayerVehicle != null
&& player.compensatedEntities.serverPlayerVehicle == entityId
&& player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)
// TODO: https://discord.com/channels/721686193061888071/721686193515003966/1310659538831020123
// Why does the server now send an entity rel move packet matching the player's vehicle movement every time?
&& PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_21_2)
&& PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9);
// ViaVersion sends two relative packets when moving more than 4 blocks
// This is broken and causes the client to interpolate like (0, 4) and (1, 3) instead of (1, 7)
@@ -454,8 +465,7 @@ 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 && yaw != null) {
PacketEntityTrackXRot xRotEntity = (PacketEntityTrackXRot) entity;
if (entity instanceof PacketEntityTrackXRot xRotEntity && yaw != null) {
xRotEntity.packetYaw = yaw;
xRotEntity.steps = entity.isBoat() ? 10 : 3;
}
@@ -36,6 +36,9 @@ public class PacketPlayerJoinQuit extends PacketListenerAbstract {
}
if (player.hasPermission("grim.alerts") && player.hasPermission("grim.alerts.enable-on-join")) {
GrimAPI.INSTANCE.getAlertManager().toggleAlerts(player);
if (player.hasPermission("grim.verbose") && player.hasPermission("grim.verbose.enable-on-join")) {
GrimAPI.INSTANCE.getAlertManager().toggleVerbose(player);
}
}
if (player.hasPermission("grim.spectate") && GrimAPI.INSTANCE.getConfigManager().getConfig().getBooleanElse("spectators.hide-regardless", false)) {
GrimAPI.INSTANCE.getSpectateManager().onLogin(player);
@@ -3,6 +3,7 @@ package ac.grim.grimac.events.packets;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.checks.impl.badpackets.BadPacketsE;
import ac.grim.grimac.checks.impl.badpackets.BadPacketsF;
import ac.grim.grimac.checks.impl.badpackets.BadPacketsG;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.data.TrackerData;
import ac.grim.grimac.utils.data.packetentity.PacketEntitySelf;
@@ -19,7 +20,6 @@ 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 ac.grim.grimac.utils.vector.Vector3D;
import java.util.List;
import java.util.Objects;
@@ -133,7 +133,11 @@ public class PacketPlayerRespawn extends PacketListenerAbstract {
}
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get() + 1, () -> {
player.isSneaking = false;
// From 1.16 to 1.19, this doesn't get set to false for whatever reason
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_16) || player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_20)) {
player.isSneaking = false;
}
player.lastOnGround = false;
player.onGround = false;
player.isInBed = false;
@@ -159,6 +163,7 @@ public class PacketPlayerRespawn extends PacketListenerAbstract {
}
player.checkManager.getPacketCheck(BadPacketsE.class).handleRespawn(); // Reminder ticks reset
player.checkManager.getPacketCheck(BadPacketsG.class).handleRespawn();
// compensate for immediate respawn gamerule
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_15)) {
@@ -3,6 +3,7 @@ package ac.grim.grimac.events.packets;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.KnownInput;
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
import ac.grim.grimac.utils.data.packetentity.PacketEntityHorse;
import com.github.retrooper.packetevents.PacketEvents;
@@ -12,6 +13,7 @@ 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;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerInput;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientSteerVehicle;
public class PacketPlayerSteer extends PacketListenerAbstract {
@@ -22,13 +24,12 @@ public class PacketPlayerSteer extends PacketListenerAbstract {
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.STEER_VEHICLE) {
WrapperPlayClientSteerVehicle steer = new WrapperPlayClientSteerVehicle(event);
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
if (player == null) return;
WrapperPlayClientSteerVehicle steer = new WrapperPlayClientSteerVehicle(event);
float forwards = steer.getForward();
float sideways = steer.getSideways();
@@ -99,6 +100,33 @@ public class PacketPlayerSteer extends PacketListenerAbstract {
}
player.packetStateData.receivedSteerVehicle = true;
} else if (event.getPacketType() == PacketType.Play.Client.PLAYER_INPUT) {
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
if (player == null) return;
WrapperPlayClientPlayerInput input = new WrapperPlayClientPlayerInput(event);
byte forward = 0;
byte sideways = 0;
if (input.isForward()) {
forward++;
}
if (input.isBackward()) {
forward--;
}
if (input.isLeft()) {
sideways++;
}
if (input.isRight()) {
sideways--;
}
player.vehicleData.nextVehicleForward = forward * 0.98f;
player.vehicleData.nextVehicleHorizontal = sideways * 0.98f;
player.packetStateData.knownInput = new KnownInput(input.isForward(), input.isBackward(), input.isLeft(), input.isRight(), input.isJump(), input.isShift(), input.isSprint());
}
}
}
@@ -1,165 +0,0 @@
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,6 +12,7 @@ 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;
@@ -35,19 +36,20 @@ 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(), player.bukkitPlayer.getWorld(),
FoliaScheduler.getRegionScheduler().execute(GrimAPI.INSTANCE.getPlugin(), world,
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 (!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))
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))
return;
// This is based on Tuinity's code, thanks leaf. Now merged into paper.
@@ -76,7 +78,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 = player.bukkitPlayer.getWorld().getChunkAt(currChunkX, currChunkZ);
Chunk chunk = world.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();
@@ -6,7 +6,6 @@ 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.WrapperPlayClientPlayerFlying;
import lombok.Getter;
@Getter
@@ -27,7 +26,7 @@ public class ActionManager extends Check implements PacketCheck {
attacking = true;
lastAttack = System.currentTimeMillis();
}
} else if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType())) {
} else if (isTickPacketIncludingNonMovement(event.getPacketType())) {
player.totalFlyingPacketsSent++;
attacking = false;
}
@@ -11,14 +11,15 @@ import ac.grim.grimac.checks.impl.combat.Reach;
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.exploit.ExploitC;
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.packetorder.*;
import ac.grim.grimac.checks.impl.post.Post;
import ac.grim.grimac.checks.impl.multiactions.*;
import ac.grim.grimac.checks.impl.post.PostCheck;
import ac.grim.grimac.checks.impl.prediction.DebugHandler;
import ac.grim.grimac.checks.impl.prediction.NoFallB;
import ac.grim.grimac.checks.impl.prediction.OffsetHandler;
@@ -86,10 +87,12 @@ 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))
@@ -97,21 +100,17 @@ public class CheckManager {
.put(BadPacketsS.class, new BadPacketsS(player))
.put(BadPacketsT.class, new BadPacketsT(player))
.put(BadPacketsU.class, new BadPacketsU(player))
.put(BadPacketsV.class, new BadPacketsV(player))
.put(BadPacketsW.class, new BadPacketsW(player))
.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(MultiActionsA.class, new MultiActionsA(player))
.put(MultiActionsB.class, new MultiActionsB(player))
.put(MultiActionsC.class, new MultiActionsC(player))
.put(MultiActionsD.class, new MultiActionsD(player))
.put(MultiActionsE.class, new MultiActionsE(player))
.put(FastBreak.class, new FastBreak(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(TransactionOrder.class, new TransactionOrder(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();
@@ -134,20 +133,8 @@ 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(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(PostCheck.class, new PostCheck(player))
.put(NoFallB.class, new NoFallB(player))
.put(OffsetHandler.class, new OffsetHandler(player))
.put(SuperDebug.class, new SuperDebug(player))
@@ -166,23 +153,22 @@ 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))
.put(MultiPlace.class, new MultiPlace(player))
.put(MultiActionsF.class, new MultiActionsF(player))
.put(FarPlace.class, new FarPlace(player))
.put(FabricatedPlace.class, new FabricatedPlace(player))
.put(PositionPlace.class, new PositionPlace(player))
.put(PacketOrderN.class, new PacketOrderN(player))
.put(PacketOrderO.class, new PacketOrderO(player))
.put(RotationPlace.class, new RotationPlace(player))
.put(DuplicateRotPlace.class, new DuplicateRotPlace(player))
.put(LineOfSightPlace.class, new LineOfSightPlace(player))
.put(GhostBlockMitigation.class, new GhostBlockMitigation(player))
.build();
prePredictionChecks = new ImmutableClassToInstanceMap.Builder<PacketCheck>()
.put(TimerCheck.class, new TimerCheck(player))
.put(TickTimer.class, new TickTimer(player))
.put(CrashA.class, new CrashA(player))
.put(CrashB.class, new CrashB(player))
.put(CrashC.class, new CrashC(player))
@@ -193,6 +179,7 @@ public class CheckManager {
.put(CrashH.class, new CrashH(player))
.put(ExploitA.class, new ExploitA(player))
.put(ExploitB.class, new ExploitB(player))
.put(ExploitC.class, new ExploitC(player))
.put(VehicleTimer.class, new VehicleTimer(player))
.build();
@@ -308,17 +295,11 @@ 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() {
@@ -70,12 +70,12 @@ public class DiscordManager implements Initable {
return list;
}
public void sendAlert(GrimPlayer player, String verbose, String checkName, String violations) {
public void sendAlert(GrimPlayer player, String verbose, String checkName, int violations) {
if (client != null) {
String content = staticContent;
content = content.replace("%check%", checkName);
content = content.replace("%violations%", violations);
content = content.replace("%violations%", Integer.toString(violations));
content = GrimAPI.INSTANCE.getExternalAPI().replaceVariables(player, content, false);
content = content.replace("_", "\\_");
@@ -11,7 +11,6 @@ import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.LogUtil;
import ac.grim.grimac.utils.anticheat.MessageUtil;
import io.github.retrooper.packetevents.util.folia.FoliaScheduler;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.Bukkit;
@@ -99,16 +98,14 @@ public class PunishmentManager implements ConfigReloadable {
}
}
private String replaceAlertPlaceholders(String original, PunishGroup group, Check check, String alertString, String verbose) {
// Streams are slow but this isn't a hot path... it's fine.
String vl = group.violations.values().stream().filter((e) -> e == check).count() + "";
private String replaceAlertPlaceholders(String original, int vl, PunishGroup group, Check check, String alertString, String verbose) {
original = MessageUtil.format(original
.replace("[alert]", alertString)
.replace("[proxy]", alertString)
.replace("%check_name%", check.getDisplayName())
.replace("%experimental%", check.isExperimental() ? experimentalSymbol : "")
.replace("%vl%", vl)
.replace("%vl%", Integer.toString(vl))
.replace("%verbose%", verbose)
.replace("%description%", check.getDescription())
);
@@ -124,12 +121,13 @@ public class PunishmentManager implements ConfigReloadable {
// Check commands
for (PunishGroup group : groups) {
if (group.getChecks().contains(check)) {
int violationCount = group.getViolations().size();
final int vl = getViolations(group, check);
final int violationCount = group.getViolations().size();
for (ParsedCommand command : group.getCommands()) {
String cmd = replaceAlertPlaceholders(command.getCommand(), group, check, alertString, verbose);
String cmd = replaceAlertPlaceholders(command.getCommand(), vl, group, check, alertString, verbose);
// Verbose that prints all flags
if (GrimAPI.INSTANCE.getAlertManager().getEnabledVerbose().size() > 0 && command.command.equals("[alert]")) {
if (!GrimAPI.INSTANCE.getAlertManager().getEnabledVerbose().isEmpty() && command.command.equals("[alert]")) {
sentDebug = true;
for (Player bukkitPlayer : GrimAPI.INSTANCE.getAlertManager().getEnabledVerbose()) {
bukkitPlayer.sendMessage(cmd);
@@ -149,10 +147,9 @@ public class PunishmentManager implements ConfigReloadable {
if (executeEvent.isCancelled()) continue;
if (command.command.equals("[webhook]")) {
String vl = group.violations.values().stream().filter((e) -> e == check).count() + "";
GrimAPI.INSTANCE.getDiscordManager().sendAlert(player, verbose, check.getDisplayName(), vl);
} else if (command.command.equals("[proxy]")) {
ProxyAlertMessenger.sendPluginMessage(replaceAlertPlaceholders(command.getCommand(), group, check, proxyAlertString, verbose));
ProxyAlertMessenger.sendPluginMessage(replaceAlertPlaceholders(command.getCommand(), vl, group, check, proxyAlertString, verbose));
} else {
if (command.command.equals("[alert]")) {
sentDebug = true;
@@ -184,10 +181,19 @@ public class PunishmentManager implements ConfigReloadable {
group.violations.put(currentTime, check);
// Remove violations older than the defined time in the config
group.violations.long2ObjectEntrySet().removeIf(time -> currentTime - time.getLongKey() > group.removeViolationsAfter);
group.violations.entrySet().removeIf(time -> currentTime - time.getKey() > group.removeViolationsAfter);
}
}
}
private int getViolations(PunishGroup group, Check check) {
int vl = 0;
for (Check value : group.violations.values()) {
if (value == check) vl++;
}
return vl;
}
}
class PunishGroup {
@@ -196,7 +202,7 @@ class PunishGroup {
@Getter
List<ParsedCommand> commands;
@Getter
Long2ObjectOpenHashMap<Check> violations = new Long2ObjectOpenHashMap<>();
public Map<Long, Check> violations = new HashMap<>();
@Getter
int removeViolationsAfter;
@@ -132,6 +132,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
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
vector.multiply(player.stuckSpeedMultiplier); // Prevent abusing setbacks to move out of blocks like webs
}
// stop 1.8 players from stepping onto 1.25 high blocks, because why not?
@@ -156,12 +157,12 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
VelocityData futureExplosion = player.checkManager.getExplosionHandler().getFutureExplosion();
// Velocity sets
if (futureKb.getFirst() != null) {
clientVel = futureKb.getSecond();
if (futureKb.first() != null) {
clientVel = futureKb.second();
}
// Explosion adds
if (futureExplosion != null && (futureKb.getFirst() == null || futureKb.getFirst().transaction < futureExplosion.transaction)) {
if (futureExplosion != null && (futureKb.first() == null || futureKb.first().transaction < futureExplosion.transaction)) {
clientVel.add(futureExplosion.vector);
}
@@ -331,16 +332,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.getFirst()) {
if (lastTransaction < teleportPos.first()) {
break;
}
Vector3d position = teleportPos.getSecond();
Vector3d position = teleportPos.second();
if (position.getX() == x && position.getY() == y && position.getZ() == z) {
player.vehicleData.vehicleTeleports.poll();
return true;
} else if (lastTransaction > teleportPos.getFirst() + 1) {
} else if (lastTransaction > teleportPos.first() + 1) {
player.vehicleData.vehicleTeleports.poll();
// Vehicles have terrible netcode so just ignore it if the teleport wasn't from us setting the player back
@@ -400,15 +401,15 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
Vector3d safePosition = new Vector3d(position.getX(), position.getY(), position.getZ());
// We must convert relative teleports to avoid them becoming client controlled in the case of setback
if (flags.isSet(RelativeFlag.X.getMask())) {
if (flags.has(RelativeFlag.X)) {
safePosition = safePosition.withX(safePosition.getX() + lastKnownGoodPosition.pos.getX());
}
if (flags.isSet(RelativeFlag.Y.getMask())) {
if (flags.has(RelativeFlag.Y)) {
safePosition = safePosition.withY(safePosition.getY() + lastKnownGoodPosition.pos.getY());
}
if (flags.isSet(RelativeFlag.Z.getMask())) {
if (flags.has(RelativeFlag.Z)) {
safePosition = safePosition.withZ(safePosition.getZ() + lastKnownGoodPosition.pos.getZ());
}
@@ -71,14 +71,8 @@ public class SpectateManager implements Initable {
spectatingPlayers.remove(uuid);
}
private static class PreviousState {
public PreviousState(org.bukkit.GameMode gameMode, Location location) {
this.gameMode = gameMode;
this.location = location;
}
private record PreviousState(org.bukkit.GameMode gameMode, Location location) {
private final org.bukkit.GameMode gameMode;
private final Location location;
}
}
@@ -4,7 +4,6 @@ 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;
@@ -23,7 +22,6 @@ 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,7 +32,6 @@ 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"));
@@ -82,7 +81,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: 10");
configString = configString.replaceAll("config-version: " + configStringVersion, "config-version: 9");
Files.write(config.toPath(), configString.getBytes());
upgradeModernConfig(config, configString, configVersion);
@@ -124,9 +123,6 @@ public class ConfigManagerFileImpl implements ConfigManager, BasicReloadable {
if (configVersion < 9) {
newOffsetHandlingAntiKB(config, configString);
}
if (configVersion < 10) {
addInventoryPunishments();
}
}
private void removeLegacyTwoPointOne(File config) throws IOException {
@@ -177,28 +173,6 @@ 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,13 +2,14 @@ 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 io.github.retrooper.packetevents.bstats.bukkit.Metrics(GrimAPI.INSTANCE.getPlugin(), pluginId);
new Metrics(GrimAPI.INSTANCE.getPlugin(), pluginId);
} catch (Exception ignored) {
}
}
@@ -23,5 +23,7 @@ public class CommandRegister implements Initable {
commandManager.registerCommand(new GrimStopSpectating());
commandManager.registerCommand(new GrimLog());
commandManager.registerCommand(new GrimVerbose());
commandManager.getCommandCompletions().registerCompletion("stopspectating", GrimStopSpectating.completionHandler);
}
}
@@ -19,7 +19,6 @@ 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());
@@ -13,7 +13,6 @@ 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;
@@ -3,7 +3,10 @@ package ac.grim.grimac.manager.init.start;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.manager.init.Initable;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.LogUtil;
import ac.grim.grimac.utils.lists.HookedListWrapper;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.util.reflection.Reflection;
import io.github.retrooper.packetevents.util.SpigotReflectionUtil;
import sun.misc.Unsafe;
@@ -30,6 +33,10 @@ public class TickEndEvent implements Initable {
return;
}
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThan(ServerVersion.V_1_11_2) && !Boolean.getBoolean("paper.explicit-flush")) {
LogUtil.warn("Reach.enable-post-packet=true but paper.explicit-flush=false, add \"-Dpaper.explicit-flush=true\" to your server's startup flags for fully functional extra reach accuracy.");
}
// Inject so we can add the final transaction pre-flush event
try {
Object connection = SpigotReflectionUtil.getMinecraftServerConnectionInstance();
@@ -42,7 +49,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<Object>(endOfTickObject) {
List<?> wrapper = Collections.synchronizedList(new HookedListWrapper<>(endOfTickObject) {
@Override
public void onIterator() {
hasTicked = true;
@@ -1,15 +0,0 @@
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,8 +7,7 @@ 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.packetorder.PacketOrderD;
import ac.grim.grimac.checks.impl.packetorder.PacketOrderProcessor;
import ac.grim.grimac.checks.impl.misc.TransactionOrder;
import ac.grim.grimac.events.packets.CheckManagerListener;
import ac.grim.grimac.manager.*;
import ac.grim.grimac.predictionengine.MovementCheckRunner;
@@ -22,7 +21,6 @@ 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;
@@ -66,7 +64,6 @@ 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;
@@ -98,7 +95,6 @@ public class GrimPlayer implements GrimUser {
// End manager like classes
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;
@@ -220,9 +216,6 @@ 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!
@@ -252,6 +245,7 @@ public class GrimPlayer implements GrimUser {
packetStateData = new PacketStateData();
uncertaintyHandler.riptideEntities.add(0);
uncertaintyHandler.collidingEntities.add(0);
if (getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14)) {
@@ -302,7 +296,7 @@ public class GrimPlayer implements GrimUser {
// If the player has that client sided riptide thing and has colliding with an entity
// This was determined in the previous tick but whatever just include the 2 ticks around it
// for a bit of safety as I doubt people will try to bypass this, it would be a very useless cheat
if (riptideSpinAttackTicks >= 0 && Collections.max(uncertaintyHandler.collidingEntities) > 0) {
if (riptideSpinAttackTicks >= 0 && Collections.max(uncertaintyHandler.riptideEntities) > 0) {
possibleMovements.add(new VectorData(clientVelocity.clone().multiply(-0.2), VectorData.VectorType.Trident));
}
@@ -336,7 +330,7 @@ public class GrimPlayer implements GrimUser {
boolean hasID = false;
int skipped = 0;
for (Pair<Short, Long> iterator : transactionsSent) {
if (iterator.getFirst() == id) {
if (iterator.first() == id) {
hasID = true;
break;
}
@@ -348,7 +342,7 @@ public class GrimPlayer implements GrimUser {
if (packetTracker != null) packetTracker.setIntervalPackets(packetTracker.getIntervalPackets() - 1);
if (skipped > 0 && System.currentTimeMillis() - joinTime > 5000)
checkManager.getPacketCheck(PacketOrderD.class).flagAndAlert("skipped: " + skipped);
checkManager.getPacketCheck(TransactionOrder.class).flagAndAlert("skipped: " + skipped);
do {
data = transactionsSent.poll();
@@ -357,9 +351,9 @@ public class GrimPlayer implements GrimUser {
lastTransactionReceived.incrementAndGet();
lastTransReceived = System.currentTimeMillis();
transactionPing = (System.nanoTime() - data.getSecond());
playerClockAtLeast = data.getSecond();
} while (data.getFirst() != id);
transactionPing = (System.nanoTime() - data.second());
playerClockAtLeast = data.second();
} while (data.first() != id);
// A transaction means a new tick, so apply any block places
CheckManagerListener.handleQueuedPlaces(this, false, 0, 0, System.currentTimeMillis());
@@ -367,7 +361,7 @@ public class GrimPlayer implements GrimUser {
}
// Were we the ones who sent the packet?
return data != null && data.getFirst() == id;
return data != null && data.first() == id;
}
public void baseTickAddWaterPushing(ac.grim.grimac.utils.vector.Vector3D vector) {
@@ -453,8 +447,7 @@ public class GrimPlayer implements GrimUser {
public void disconnect(Component reason) {
String textReason;
if (reason instanceof TranslatableComponent) {
TranslatableComponent translatableComponent = (TranslatableComponent) reason;
if (reason instanceof TranslatableComponent translatableComponent) {
textReason = translatableComponent.key();
} else {
textReason = LegacyComponentSerializer.legacySection().serialize(reason);
@@ -576,11 +569,18 @@ public class GrimPlayer implements GrimUser {
// - 3 ticks is a magic value, but it should buffer out incorrect predictions somewhat.
// 2. The player is in a vehicle
public boolean isTickingReliablyFor(int ticks) {
return (getClientVersion().isOlderThan(ClientVersion.V_1_9)
|| !uncertaintyHandler.lastPointThree.hasOccurredSince(ticks))
// 1.21.2+: Tick end packet, on servers 1.21.2+
// 1.8-: Flying packet
return supportsEndTick()
|| getClientVersion().isOlderThan(ClientVersion.V_1_9)
|| !uncertaintyHandler.lastPointThree.hasOccurredSince(ticks)
|| compensatedEntities.getSelf().inVehicle();
}
public boolean inVehicle() {
return compensatedEntities.getSelf().inVehicle();
}
public boolean canThePlayerBeCloseToZeroMovement(int ticks) {
return (!uncertaintyHandler.lastPointThree.hasOccurredSince(ticks));
}
@@ -708,6 +708,11 @@ public class GrimPlayer implements GrimUser {
return getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_10) || (gamemode == GameMode.CREATIVE && compensatedEntities.getSelf().getOpLevel() >= 2);
}
public boolean supportsEndTick() {
// TODO: Bypass viaversion
return getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_2) && PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_21_2);
}
@Override
public void runSafely(Runnable runnable) {
ChannelHelper.runInEventLoop(this.user.getChannel(), runnable);
@@ -762,6 +767,7 @@ public class GrimPlayer implements GrimUser {
@Getter private boolean ignoreDuplicatePacketRotation = false;
@Getter private boolean experimentalChecks = false;
@Getter private boolean cancelDuplicatePacket = true;
@Getter private boolean exemptElytra = false;
@Override
public void reload(ConfigManager config) {
@@ -770,6 +776,7 @@ public class GrimPlayer implements GrimUser {
experimentalChecks = config.getBooleanElse("experimental-checks", false);
ignoreDuplicatePacketRotation = config.getBooleanElse("ignore-duplicate-packet-rotation", false);
cancelDuplicatePacket = config.getBooleanElse("cancel-duplicate-packet", true);
exemptElytra = config.getBooleanElse("exempt-elytra", false);
// reload all checks
for (AbstractCheck value : checkManager.allChecks.values()) value.reload(config);
// reload punishment manager
@@ -40,7 +40,6 @@ import com.github.retrooper.packetevents.protocol.world.states.defaulttags.Block
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
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;
@@ -262,8 +261,7 @@ 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) {
PacketEntityTrackXRot boat = (PacketEntityTrackXRot) riding;
if (riding instanceof PacketEntityTrackXRot boat) {
boat.packetYaw = player.xRot;
boat.interpYaw = player.xRot;
boat.steps = 0;
@@ -343,7 +341,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.getFirst()));
boolean isInWater = player.compensatedWorld.isRaining || Collisions.hasMaterial(player, player.boundingBox.copy().expand(0.1f), (block) -> Materials.isWater(CompensatedWorld.blockVersion, block.first()));
if (currentTime - player.packetStateData.lastRiptide < 450 || !isInWater) {
player.packetStateData.tryingToRiptide = false;
@@ -352,9 +350,9 @@ public class MovementCheckRunner extends Check implements PositionCheck {
player.packetStateData.lastRiptide = currentTime;
}
SimpleCollisionBox steppingOnBB = GetBoundingBox.getCollisionBoxForPlayer(player, player.x, player.y, player.z).expand(0.03).offset(0, -1, 0);
SimpleCollisionBox steppingOnBB = GetBoundingBox.getCollisionBoxForPlayer(player, player.x, player.y, player.z).expand(player.getMovementThreshold()).offset(0, -1, 0);
Collisions.hasMaterial(player, steppingOnBB, (pair) -> {
WrappedBlockState data = pair.getFirst();
WrappedBlockState data = pair.first();
if (data.getType() == StateTypes.SLIME_BLOCK && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_8)) {
player.uncertaintyHandler.isSteppingOnSlime = true;
player.uncertaintyHandler.isSteppingOnBouncyBlock = true;
@@ -400,8 +398,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.getFirst().getType())
|| checkData.getFirst().getType() == StateTypes.CHEST || checkData.getFirst().getType() == StateTypes.TRAPPED_CHEST);
checkData -> BlockTags.ANVIL.contains(checkData.first().getType())
|| checkData.first().getType() == StateTypes.CHEST || checkData.first().getType() == StateTypes.TRAPPED_CHEST);
player.uncertaintyHandler.isOrWasNearGlitchyBlock = isGlitchy || player.uncertaintyHandler.isNearGlitchyBlock;
player.uncertaintyHandler.checkForHardCollision();
@@ -416,8 +414,8 @@ public class MovementCheckRunner extends Check implements PositionCheck {
player.uncertaintyHandler.lastUnderwaterFlyingHack.reset();
}
boolean couldBeStuckSpeed = Collisions.checkStuckSpeed(player, 0.03);
boolean couldLeaveStuckSpeed = player.isPointThree() && Collisions.checkStuckSpeed(player, -0.03);
boolean couldBeStuckSpeed = Collisions.checkStuckSpeed(player, player.getMovementThreshold());
boolean couldLeaveStuckSpeed = player.isPointThree() && Collisions.checkStuckSpeed(player, -player.getMovementThreshold());
player.uncertaintyHandler.claimingLeftStuckSpeed = !player.compensatedEntities.getSelf().inVehicle() && player.stuckSpeedMultiplier.getX() < 1 && !couldLeaveStuckSpeed;
if (couldBeStuckSpeed) {
@@ -433,7 +431,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
// Dead players can't cheat, if you find a way how they could, open an issue
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) {
} else if (player.disableGrim || (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_8) && player.gamemode == GameMode.SPECTATOR) || player.isFlying || (player.isExemptElytra() && player.isGliding)) {
// We could technically check spectator but what's the point...
// Added complexity to analyze a gamemode used mainly by moderators
//
@@ -643,5 +641,4 @@ public class MovementCheckRunner extends Check implements PositionCheck {
public void onReload(ConfigManager config) {
allowSprintJumpingWithElytra = config.getBooleanElse("exploit.allow-sprint-jumping-when-using-elytra", true);
}
}
@@ -154,7 +154,7 @@ public class PlayerBaseTick {
// Might be null after respawn?
final Optional<WrapperPlayServerUpdateAttributes.Property> property = playerSpeed.property();
if (!property.isPresent()) return;
if (property.isEmpty()) 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"));
@@ -271,13 +271,14 @@ public class PlayerBaseTick {
private void moveTowardsClosestSpace(double xPosition, double zPosition) {
player.boundingBox = player.boundingBox.expand(0.03, 0, 0.03); // 0.03... thanks mojang!
double movementThreshold = player.getMovementThreshold();
player.boundingBox = player.boundingBox.expand(movementThreshold, 0, movementThreshold); // 0.03... thanks mojang!
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14)) {
moveTowardsClosestSpaceModern(xPosition, zPosition);
} else {
moveTowardsClosestSpaceLegacy(xPosition, zPosition);
}
player.boundingBox = player.boundingBox.expand(-0.03, 0, -0.03);
player.boundingBox = player.boundingBox.expand(-movementThreshold, 0, -movementThreshold);
}
// Mojang is incompetent and this will push the player out a lot when using elytras
@@ -363,22 +364,12 @@ 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:
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;
}
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);
};
if (d6 >= lowestValue || doesSuffocate) continue;
lowestValue = d6;
@@ -1,7 +1,5 @@
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;
@@ -18,7 +16,6 @@ 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 ac.grim.grimac.utils.vector.Vector3D;
@@ -109,7 +106,6 @@ 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
@@ -133,12 +129,13 @@ 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.
SimpleCollisionBox slightlyExpanded = normalBox.copy().expand(0.03, 0, 0.03);
if (!slightlyExpanded.isIntersected(data) && slightlyExpanded.offset(0, 0.03, 0).isIntersected(data)) {
double movementThreshold = player.getMovementThreshold();
SimpleCollisionBox slightlyExpanded = normalBox.copy().expand(movementThreshold, 0, movementThreshold);
if (!slightlyExpanded.isIntersected(data) && slightlyExpanded.offset(0, movementThreshold, 0).isIntersected(data)) {
headHitter = true;
}
SimpleCollisionBox pointThreeBox = GetBoundingBox.getBoundingBoxFromPosAndSize(player, player.x, player.y - 0.03, player.z, 0.66f, 1.86f);
SimpleCollisionBox pointThreeBox = GetBoundingBox.getBoundingBoxFromPosAndSize(player, player.x, player.y - movementThreshold, player.z, 0.66f, 1.86f);
if ((Materials.isWater(player.getClientVersion(), state) || stateType == StateTypes.LAVA) &&
pointThreeBox.isIntersected(new SimpleCollisionBox(x, y, z))) {
@@ -157,10 +154,6 @@ 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;
@@ -222,7 +215,8 @@ public class PointThreeEstimator {
}
public void endOfTickTick() {
SimpleCollisionBox pointThreeBox = GetBoundingBox.getBoundingBoxFromPosAndSize(player, player.x, player.y - 0.03, player.z, 0.66f, 1.86f);
double movementThreshold = player.getMovementThreshold();
SimpleCollisionBox pointThreeBox = GetBoundingBox.getBoundingBoxFromPosAndSize(player, player.x, player.y - movementThreshold, player.z, 0.66f, 1.86f);
// Determine the head hitter using the current Y position
SimpleCollisionBox oldBB = player.boundingBox;
@@ -232,7 +226,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, 0.03, 0).getY() != 0.03;
headHitter = headHitter || Collisions.collide(player, 0, movementThreshold, 0).getY() != movementThreshold;
}
player.boundingBox = oldBB;
@@ -255,32 +249,27 @@ public class PointThreeEstimator {
isNearClimbable = false;
isNearBubbleColumn = false;
isNearFluid = false;
isNearNetherPortal = false;
// Check for flowing water
Collisions.hasMaterial(player, pointThreeBox, (pair) -> {
final WrappedBlockState state = pair.getFirst();
final WrappedBlockState state = pair.first();
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.getSecond().getX(), pair.getSecond().getY(), pair.getSecond().getZ(), state);
isNearClimbable = isNearClimbable || Collisions.trapdoorUsableAsLadder(player, pair.second().getX(), pair.second().getY(), pair.second().getZ(), state);
}
if (stateType == StateTypes.BUBBLE_COLUMN) {
isNearBubbleColumn = true;
}
if (Materials.isWater(player.getClientVersion(), pair.getFirst()) || pair.getFirst().getType() == StateTypes.LAVA) {
if (Materials.isWater(player.getClientVersion(), pair.first()) || pair.first().getType() == StateTypes.LAVA) {
isNearFluid = true;
}
if (state.getType() == StateTypes.NETHER_PORTAL) {
isNearNetherPortal = true;
}
return false;
});
}
@@ -327,7 +316,7 @@ public class PointThreeEstimator {
public boolean determineCanSkipTick(float speed, Set<VectorData> init) {
// If possible, check for idle packet
// TODO: Find a better way to fix slime without forcing 0.03 where there is none
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_9) && player.packetStateData.didLastMovementIncludePosition && !player.uncertaintyHandler.isSteppingOnSlime) {
if ((player.supportsEndTick() || player.getClientVersion().isOlderThan(ClientVersion.V_1_9)) && player.packetStateData.didLastMovementIncludePosition && !player.uncertaintyHandler.isSteppingOnSlime) {
return false; // Last packet included a position so not 0.03
}
@@ -66,8 +66,10 @@ public class UncertaintyHandler {
public boolean lastMovementWasUnknown003VectorReset = false;
// Handles 0.03 vertical false where actual velocity is greater than predicted because of previous lenience
public boolean wasZeroPointThreeVertically = false;
// How many entities are within 0.5 blocks of the player's bounding box?
// How many entities are within 0.5 blocks of the player's bounding box that are pushable?
public EvictingQueue<Integer> collidingEntities = new EvictingQueue<>(3);
// How many entities are within 0.5 blocks of the player's bounding box? Should only exclude entities in spectator
public EvictingQueue<Integer> riptideEntities = new EvictingQueue<>(3);
// Fishing rod pulling is another method of adding to a player's velocity
public List<Integer> fishingRodPulls = new ArrayList<>();
public SimpleCollisionBox fireworksBox = null;
@@ -305,8 +307,7 @@ 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) {
PacketEntityRideable vehicle = (PacketEntityRideable) player.compensatedEntities.getSelf().getRiding();
if (player.compensatedEntities.getSelf().getRiding() instanceof PacketEntityRideable vehicle) {
if (vehicle.currentBoostTime < vehicle.boostTimeMax + 20)
offset -= 0.01;
}
@@ -43,6 +43,7 @@ public class MovementTicker {
&& (!ViaVersionUtil.isAvailable() || Via.getConfig().isPreventCollision()))) return;
int possibleCollidingEntities = 0;
int possibleRiptideEntities = 0;
// Players in vehicles do not have collisions
if (!player.compensatedEntities.getSelf().inVehicle()) {
@@ -54,16 +55,23 @@ public class MovementTicker {
final TeamHandler teamHandler = player.checkManager.getPacketCheck(TeamHandler.class);
for (PacketEntity entity : player.compensatedEntities.entityMap.values()) {
if (!entity.isPushable())
continue;
// TODO actually handle entity collisions instead of this awfulness
SimpleCollisionBox entityBox = entity.getPossibleCollisionBoxes();
// 1.9+ player on 1.8- server with ViaVersion prevent-collision disabled.
boolean isCollided = playerBox.isCollided(entityBox);
if (isCollided) {
possibleRiptideEntities++;
}
// Filters out entities that can't be pushed/collided because of team collision rules
// Also handles 1.9+ player on 1.8- server with ViaVersion prevent-collision disabled.
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9)
&& !EntityPredicates.canBePushedBy(player, entity, teamHandler).test(player)) continue;
SimpleCollisionBox entityBox = entity.getPossibleCollisionBoxes();
if (!entity.isPushable())
continue;
if (playerBox.isCollided(entityBox)) {
if (isCollided) {
possibleCollidingEntities++;
}
}
@@ -76,6 +84,7 @@ public class MovementTicker {
player.uncertaintyHandler.yPositiveUncertainty += 0.05;
}
player.uncertaintyHandler.riptideEntities.add(possibleRiptideEntities);
player.uncertaintyHandler.collidingEntities.add(possibleCollidingEntities);
}
@@ -5,7 +5,6 @@ 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 ac.grim.grimac.utils.vector.Vector3D;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
@@ -3,7 +3,6 @@ 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 ac.grim.grimac.utils.vector.Vector3D;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
@@ -11,7 +11,6 @@ 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 ac.grim.grimac.utils.vector.Vector3D;
import java.util.ArrayList;
@@ -4,6 +4,7 @@ import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.predictionengine.SneakingEstimator;
import ac.grim.grimac.predictionengine.movementtick.MovementTickerPlayer;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.KnownInput;
import ac.grim.grimac.utils.data.Pair;
import ac.grim.grimac.utils.data.VectorData;
import ac.grim.grimac.utils.math.VectorUtils;
@@ -12,13 +13,11 @@ 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 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;
@@ -122,7 +121,9 @@ public class PredictionEngine {
SimpleCollisionBox originalBB = player.boundingBox;
// 0.03 doesn't exist with vehicles, thank god
// 1.13+ clients have stupid poses that desync because mojang brilliantly removed the idle packet in 1.9
SimpleCollisionBox pointThreeThanksMojang = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13) ? GetBoundingBox.getBoundingBoxFromPosAndSize(player, player.lastX, player.lastY, player.lastZ, 0.6f, 0.6f) : originalBB;
SimpleCollisionBox pointThreeThanksMojang = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13)
? GetBoundingBox.getBoundingBoxFromPosAndSize(player, player.lastX, player.lastY, player.lastZ, 0.6f, 0.6f)
: originalBB;
player.skippedTickInActualMovement = false;
@@ -144,8 +145,8 @@ public class PredictionEngine {
// Returns pair of primary push movement, and then outputvel
Pair<Vector3D, Vector3D> output = doSeekingWallCollisions(player, primaryPushMovement, originalClientVel, clientVelAfterInput);
primaryPushMovement = output.getFirst();
Vector3D outputVel = clampMovementToHardBorder(player, output.getSecond());
primaryPushMovement = output.first();
Vector3D outputVel = clampMovementToHardBorder(player, output.second());
double resultAccuracy = outputVel.distanceSquared(player.actualMovement);
@@ -422,63 +423,52 @@ 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 -= 10;
aScore -= 5;
if (a.isKnockback())
aScore -= 10;
aScore -= 5;
if (b.isExplosion())
bScore -= 10;
bScore -= 5;
if (b.isKnockback())
bScore -= 10;
bScore -= 5;
if (a.isFirstBreadExplosion())
aScore += 2;
aScore += 1;
if (b.isFirstBreadExplosion())
bScore += 2;
bScore += 1;
if (a.isFirstBreadKb())
aScore += 2;
aScore += 1;
if (b.isFirstBreadKb())
bScore += 2;
bScore += 1;
if (a.isFlipItem())
aScore += 6;
aScore += 3;
if (b.isFlipItem())
bScore += 6;
bScore += 3;
if (a.isZeroPointZeroThree())
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
if (b.isZeroPointZeroThree())
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 += 4;
aScore += 2;
if ((player.compensatedEntities.getSelf().inVehicle() ? player.clientControlledVerticalCollision : player.onGround) && b.vector.getY() >= 0)
bScore += 4;
bScore += 2;
if (aScore != bScore)
return Integer.compare(aScore, bScore);
@@ -722,7 +712,36 @@ public class PredictionEngine {
// Optimization - Also cuts down scenarios by 2/3
// For some reason the player sprints while swimming no matter what
// Probably as a way to tell the server it is swimming
int zMin = player.isSprinting && !player.isSwimming ? 1 : -1;
int forwardMin = player.isSprinting && !player.isSwimming ? 1 : -1;
int forwardMax = 1;
int strafeMin = -1;
int strafeMax = 1;
// Calculate inputs by the players known inputs on 1.21.2+
if (player.supportsEndTick()) {
forwardMin = forwardMax = strafeMin = strafeMax = 0;
final KnownInput knownInput = player.packetStateData.knownInput;
if (knownInput.forward()) {
forwardMax++;
forwardMin++;
}
if (knownInput.backward() && (!player.isSprinting || player.isSwimming)) {
forwardMax--;
forwardMin--;
}
if (knownInput.left()) {
strafeMax++;
strafeMin++;
}
if (knownInput.right()) {
strafeMax--;
strafeMin--;
}
}
for (int loopSlowed = 0; loopSlowed <= 1; loopSlowed++) {
// Loop twice for the using item status if the player is using a trident
@@ -734,9 +753,11 @@ public class PredictionEngine {
for (VectorData possibleLastTickOutput : possibleVectors) {
// Only do this when there is tick skipping
if (loopSlowed == 1 && !possibleLastTickOutput.isZeroPointZeroThree()) continue;
for (int x = -1; x <= 1; x++) {
for (int z = zMin; z <= 1; z++) {
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);
for (int strafe = strafeMin; strafe <= strafeMax; strafe++) {
for (int forward = forwardMin; forward <= forwardMax; forward++) {
VectorData result = new VectorData(possibleLastTickOutput.vector.clone()
.add(getMovementResultFromInput(player, transformInputsToVector(player, newVector3D(strafe, 0, forward)), speed, player.xRot)),
possibleLastTickOutput, VectorData.VectorType.InputResult);
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
@@ -4,8 +4,6 @@ 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 ac.grim.grimac.utils.vector.Vector3D;
import java.util.ArrayList;
@@ -3,7 +3,6 @@ package ac.grim.grimac.predictionengine.predictions;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
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;
@@ -43,6 +42,10 @@ public class PredictionEngineNormal extends PredictionEngine {
@Override
public void addJumpsToPossibilities(GrimPlayer player, Set<VectorData> existingVelocities) {
if (player.supportsEndTick() && !player.packetStateData.knownInput.jump()) {
return;
}
for (VectorData vector : new HashSet<>(existingVelocities)) {
Vector3D jump = vector.vector.clone();
@@ -53,7 +56,7 @@ 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.isPresent() || jumpBoost.getAsInt() >= 0) && player.onGround) || !player.lastOnGround)
if (((jumpBoost.isEmpty() || jumpBoost.getAsInt() >= 0) && player.onGround) || !player.lastOnGround)
return;
JumpPower.jumpFromGround(player, jump);
@@ -14,7 +14,6 @@ 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;
Loaded 100 of 171 files, more files were not shown because too many files have changed in this diff. Show more