Compare 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
125 changed files with 3128 additions and 760 deletions

No files matched your search

+173 -4
View File
@@ -30,12 +30,15 @@ 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(17))
toolchain.languageVersion.set(JavaLanguageVersion.of(18))
}
// Set to false for debug builds
// You cannot live reload classes if the jar relocates dependencies
var relocate = true;
// Checks Project properties -> environment variable -> defaults true
val relocate: Boolean = project.findProperty("relocate")?.toString()?.toBoolean()
?: System.getenv("RELOCATE_JAR")?.toBoolean()
?: true
repositories {
mavenLocal()
@@ -56,11 +59,18 @@ repositories {
// FastUtil, Discord-Webhooks
}
// Add JMH configuration
configurations {
create("jmh")
create("jmhAnnotationProcessor")
}
dependencies {
implementation("com.github.retrooper:packetevents-spigot:2.5.1-SNAPSHOT")
implementation("com.github.retrooper:packetevents-spigot:2.6.1-SNAPSHOT")
implementation("co.aikar:acf-paper:0.5.1-SNAPSHOT")
implementation("club.minnced:discord-webhooks:0.8.0") // Newer versions include kotlin-stdlib, which leads to incompatibility with plugins that use Kotlin
implementation("it.unimi.dsi:fastutil:8.5.13")
implementation("it.unimi.dsi:fastutil:8.5.15")
implementation("github.scarsz:configuralize:1.4.0")
//implementation("com.github.grimanticheat:grimapi:1193c4fa41")
@@ -73,6 +83,12 @@ dependencies {
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 {
@@ -140,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
}
}
}
@@ -156,6 +182,71 @@ publishing.publications.create<MavenPublication>("maven") {
artifact(tasks["shadowJar"])
}
java {
sourceSets {
create("java18") {
java {
srcDirs("src/main/java18")
}
compileClasspath += main.get().output
runtimeClasspath += main.get().output
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_17
targetCompatibility = JavaVersion.VERSION_17
toolchain {
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> {
manifest {
attributes("Multi-Release" to "true")
}
duplicatesStrategy = DuplicatesStrategy.INCLUDE // Important for multi-release JARs
from(sourceSets.main.get().output)
from(sourceSets.getByName("java18").output) {
into("META-INF/versions/18")
}
}
tasks.shadowJar {
minimize()
archiveFileName.set("${project.name}-${project.version}.jar")
@@ -179,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")
}
@@ -0,0 +1,148 @@
package ac.grim.grimac.utils.vector;
import jdk.incubator.vector.*;
public final class SIMDVector3D implements Vector3D {
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);
private static final DoubleVector FOURTH_COMPONENT_ZERO = DoubleVector.zero(SPECIES).withLane(3, 1.0);
private DoubleVector vector;
public SIMDVector3D(double x, double y, double z) {
this.vector = DoubleVector.fromArray(SPECIES, new double[]{x, y, z, 0}, 0, LENGTH_3_ARRAY_MASK);
}
@Override
public double getX() {
return this.vector.lane(0);
}
@Override
public double getY() {
return this.vector.lane(1);
}
@Override
public double getZ() {
return this.vector.lane(2);
}
@Override
public Vector3D setX(double x) {
this.vector = this.vector.withLane(0, x);
return this;
}
@Override
public Vector3D setY(double y) {
this.vector = this.vector.withLane(1, y);
return this;
}
@Override
public Vector3D setZ(double z) {
this.vector = this.vector.withLane(2, z);
return this;
}
@Override
public double length() {
return Math.sqrt(lengthSquared());
}
@Override
public double lengthSquared() {
return this.vector.mul(this.vector).reduceLanes(VectorOperators.ADD);
}
@Override
public Vector3D multiply(double m) {
this.vector = this.vector.mul(m);
return this;
}
@Override
public Vector3D normalize() {
// DoubleVector squared = this.vector.mul(this.vector);
// double sum = squared.reduceLanes(VectorOperators.ADD);
// double length = Math.sqrt(sum);
// this.vector = this.vector.div(length);
// return this;
this.vector = this.vector.div(length());
return this;
}
@Override
public Vector3D crossProduct(Vector3D other) {
SIMDVector3D o = (SIMDVector3D) other;
DoubleVector tmp0 = this.vector.rearrange(CROSS_PRODUCT_SHUFFLE1);
DoubleVector tmp1 = o.vector.rearrange(CROSS_PRODUCT_SHUFFLE2);
DoubleVector tmp2 = tmp0.mul(o.vector);
DoubleVector tmp3 = tmp0.mul(tmp1);
DoubleVector tmp4 = tmp2.rearrange(CROSS_PRODUCT_SHUFFLE1);
this.vector = tmp3.sub(tmp4).blend(FOURTH_COMPONENT_ZERO, LENGTH_3_ARRAY_MASK.not());
return this;
}
@Override
public Vector3D add(Vector3D o) {
this.vector = this.vector.add(((SIMDVector3D) o).vector);
return this;
}
@Override
public Vector3D subtract(Vector3D o) {
this.vector = this.vector.sub(((SIMDVector3D) o).vector);
return this;
}
@Override
public Vector3D multiply(Vector3D o) {
this.vector = this.vector.mul(((SIMDVector3D) o).vector);
return this;
}
@Override
public double distance(Vector3D o) {
return Math.sqrt(distanceSquared(o));
}
@Override
public double distanceSquared(Vector3D o) {
DoubleVector diff = this.vector.sub(((SIMDVector3D) o).vector);
return diff.mul(diff).reduceLanes(VectorOperators.ADD);
}
@Override
public double dot(Vector3D o) {
return this.vector.mul(((SIMDVector3D) o).vector).reduceLanes(VectorOperators.ADD);
}
@Override
public Vector3D clone() {
// I think this makes a shallow clone if I do it like this?
// try {
// return (Vector3D) super.clone();
// } catch (CloneNotSupportedException e) {
// throw new Error(e);
// }
try {
SIMDVector3D cloned = (SIMDVector3D) super.clone();
// Create a new DoubleVector with the same values
cloned.vector = DoubleVector.fromArray(SPECIES,
new double[]{this.getX(), this.getY(), this.getZ(), 0}, 0);
return cloned;
} catch (CloneNotSupportedException e) {
throw new Error(e);
}
}
}
@@ -0,0 +1,10 @@
package ac.grim.grimac.utils.vector;
public class VectorOperationsJava18 implements VectorOperations {
@Override
public Vector3D newVector(double x, double y, double z) {
return new SIMDVector3D(x, y, z);
}
}
@@ -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());
@@ -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;
}
}
@@ -43,7 +43,7 @@ public class BadPacketsH 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();
}
@@ -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);
}
@@ -24,6 +24,7 @@ import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
import ac.grim.grimac.utils.data.packetentity.dragon.PacketEntityEnderDragonPart;
import ac.grim.grimac.utils.nmsutil.ReachUtils;
import ac.grim.grimac.utils.vector.Vector3D;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
@@ -33,13 +34,14 @@ import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.*;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
// You may not copy the check unless you are licensed under GPL
@CheckData(name = "Reach", configName = "Reach", setback = 10)
public class Reach extends Check implements PacketCheck {
@@ -108,9 +110,7 @@ public class Reach extends Check implements PacketCheck {
}
// If the player set their look, or we know they have a new tick
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) ||
event.getPacketType() == PacketType.Play.Client.PONG ||
event.getPacketType() == PacketType.Play.Client.WINDOW_CONFIRMATION) {
if (isUpdate(event.getPacketType())) {
tickBetterReachCheckWithAngle();
}
}
@@ -185,7 +185,7 @@ public class Reach extends Check implements PacketCheck {
double minDistance = Double.MAX_VALUE;
// https://bugs.mojang.com/browse/MC-67665
List<Vector> possibleLookDirs = new ArrayList<>(Collections.singletonList(ReachUtils.getLook(player, player.xRot, player.yRot)));
List<Vector3D> possibleLookDirs = new ArrayList<>(Collections.singletonList(ReachUtils.getLook(player, player.xRot, player.yRot)));
// If we are a tick behind, we don't know their next look so don't bother doing this
if (!isPrediction) {
@@ -205,12 +205,12 @@ public class Reach extends Check implements PacketCheck {
// +3 would be 3 + 3 = 6, which is the pre-1.20.5 behaviour, preventing "Missed Hitbox"
final double distance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_ENTITY_INTERACTION_RANGE) + 3;
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
for (Vector lookVec : possibleLookDirs) {
for (Vector3D lookVec : possibleLookDirs) {
for (double eye : possibleEyeHeights) {
Vector eyePos = new Vector(from.getX(), from.getY() + eye, from.getZ());
Vector endReachPos = eyePos.clone().add(new Vector(lookVec.getX() * distance, lookVec.getY() * distance, lookVec.getZ() * distance));
ac.grim.grimac.utils.vector.Vector3D eyePos = newVector3D(from.getX(), from.getY() + eye, from.getZ());
ac.grim.grimac.utils.vector.Vector3D endReachPos = eyePos.clone().add(newVector3D(lookVec.getX() * distance, lookVec.getY() * distance, lookVec.getZ() * distance));
Vector intercept = ReachUtils.calculateIntercept(targetBox, eyePos, endReachPos).first();
Vector3D intercept = ReachUtils.calculateIntercept(targetBox, eyePos, endReachPos).first();
if (ReachUtils.isVecInside(targetBox, eyePos)) {
minDistance = 0;
@@ -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();
}
}
}
}
}
@@ -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();
}
}
@@ -15,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;
@@ -43,7 +42,7 @@ public class PostCheck extends Check implements PacketCheck, PostPredictionCheck
// Okay, the user might be cheating, let's double check
// 1.8 clients have the idle packet, and this shouldn't false on 1.8 clients
// 1.9+ clients have predictions, which will determine if hidden tick skipping occurred
if (player.isTickingReliablyFor(3)) {
if (player.isTickingReliablyFor(3) && !player.uncertaintyHandler.lastVehicleSwitch.hasOccurredSince(0)) {
for (String flag : flags) {
flagAndAlert(flag);
}
@@ -68,12 +67,7 @@ public class PostCheck extends Check implements PacketCheck, 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 {
@@ -9,7 +9,7 @@ import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import ac.grim.grimac.utils.lists.EvictingQueue;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.HashSet;
import java.util.Set;
@@ -45,8 +45,8 @@ public class DebugHandler extends Check implements PostPredictionCheck {
ChatColor color = pickColor(offset, offset);
Vector predicted = player.predictedVelocity.vector;
Vector actually = player.actualMovement;
Vector3D predicted = player.predictedVelocity.vector;
Vector3D actually = player.actualMovement;
ChatColor xColor = pickColor(Math.abs(predicted.getX() - actually.getX()), offset);
ChatColor yColor = pickColor(Math.abs(predicted.getY() - actually.getY()), offset);
@@ -9,7 +9,9 @@ import ac.grim.grimac.utils.math.VectorUtils;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.util.Vector3i;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
@CheckData(name = "FarPlace")
public class FarPlace extends BlockPlaceCheck {
@@ -27,8 +29,8 @@ public class FarPlace extends BlockPlaceCheck {
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
for (double d : possibleEyeHeights) {
SimpleCollisionBox box = new SimpleCollisionBox(blockPos);
Vector eyes = new Vector(player.x, player.y + d, player.z);
Vector best = VectorUtils.cutBoxToVector(eyes, box);
Vector3D eyes = newVector3D(player.x, player.y + d, player.z);
Vector3D best = VectorUtils.cutBoxToVector(eyes, box);
min = Math.min(min, eyes.distanceSquared(best));
}
@@ -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);
}
@@ -8,6 +8,7 @@ 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;
@@ -15,7 +16,6 @@ import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.util.Vector3f;
import org.bukkit.util.Vector;
import java.util.ArrayList;
import java.util.Arrays;
@@ -111,7 +111,7 @@ public class RotationPlace extends BlockPlaceCheck {
Vector3d starting = new Vector3d(player.x, player.y + d, player.z);
// xRot and yRot are a tick behind
Ray trace = new Ray(player, starting.getX(), starting.getY(), starting.getZ(), lookDir.getX(), lookDir.getY());
Pair<Vector, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
Pair<Vector3D, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
if (intercept.first() != null) return true;
}
@@ -19,12 +19,13 @@ import com.github.retrooper.packetevents.util.Vector3f;
import com.github.retrooper.packetevents.util.Vector3i;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerExplosion;
import lombok.Getter;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.Nullable;
import java.util.Deque;
import java.util.LinkedList;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
@CheckData(name = "AntiExplosion", configName = "Explosion", setback = 10)
public class ExplosionHandler extends Check implements PostPredictionCheck {
Deque<VelocityData> firstBreadMap = new LinkedList<>();
@@ -121,7 +122,7 @@ public class ExplosionHandler extends Check implements PostPredictionCheck {
}
public void addPlayerExplosion(int breadOne, Vector3f explosion) {
firstBreadMap.add(new VelocityData(-1, breadOne, player.getSetbackTeleportUtil().isSendingSetback, new Vector(explosion.getX(), explosion.getY(), explosion.getZ())));
firstBreadMap.add(new VelocityData(-1, breadOne, player.getSetbackTeleportUtil().isSendingSetback, newVector3D(explosion.getX(), explosion.getY(), explosion.getZ())));
}
public void setPointThree(boolean isPointThree) {
@@ -15,12 +15,14 @@ import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityVelocity;
import lombok.Getter;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import org.jetbrains.annotations.NotNull;
import java.util.Deque;
import java.util.LinkedList;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
// We are making a velocity sandwich between two pieces of transaction packets (bread)
@CheckData(name = "AntiKB", alternativeName = "AntiKnockback", configName = "Knockback", setback = 10, decay = 0.025)
public class KnockbackHandler extends Check implements PostPredictionCheck {
@@ -66,16 +68,17 @@ 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
player.sendTransaction();
addPlayerKnockback(entityId, player.lastTransactionSent.get(), new Vector(playerVelocity.getX(), playerVelocity.getY(), playerVelocity.getZ()));
addPlayerKnockback(entityId, player.lastTransactionSent.get(), newVector3D(playerVelocity.getX(), playerVelocity.getY(), playerVelocity.getZ()));
event.getTasksAfterSend().add(player::sendTransaction);
}
}
@NotNull public Pair<VelocityData, Vector> getFutureKnockback() {
@NotNull public Pair<VelocityData, Vector3D> getFutureKnockback() {
// Chronologically in the future
if (firstBreadMap.size() > 0) {
VelocityData data = firstBreadMap.peek();
@@ -97,7 +100,7 @@ public class KnockbackHandler extends Check implements PostPredictionCheck {
return new Pair<>(null, null);
}
private void addPlayerKnockback(int entityID, int breadOne, Vector knockback) {
private void addPlayerKnockback(int entityID, int breadOne, Vector3D knockback) {
firstBreadMap.add(new VelocityData(entityID, breadOne, player.getSetbackTeleportUtil().isSendingSetback, knockback));
}
@@ -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) {}
}
@@ -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,16 +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 player)) return;
if (!(sender instanceof Player)) return;
Player player = (Player) sender;
if (GrimAPI.INSTANCE.getSpectateManager().isSpectating(player.getUniqueId())) {
boolean teleportBack = string == null || !string.equalsIgnoreCase("here");
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.");
@@ -17,6 +17,8 @@ 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;
@@ -47,7 +49,6 @@ import com.github.retrooper.packetevents.wrapper.play.client.*;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerAcknowledgeBlockChanges;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSetSlot;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import org.bukkit.util.Vector;
import java.util.ArrayList;
import java.util.List;
@@ -371,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) {
@@ -455,43 +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(BadPacketsX.class).handle(event, dig, type);
player.checkManager.getPacketCheck(BadPacketsZ.class).handle(event, dig);
player.checkManager.getPacketCheck(BadPacketsX.class).handle(blockBreak);
player.checkManager.getPacketCheck(BadPacketsZ.class).handle(blockBreak);
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);
}
}
}
@@ -578,6 +577,15 @@ 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;
@@ -747,6 +755,10 @@ 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) {
@@ -782,10 +794,10 @@ public class CheckManagerListener extends PacketListenerAbstract {
private static HitData getNearestHitResult(GrimPlayer player, StateType heldItem, boolean sourcesHaveHitbox) {
Vector3d startingPos = new Vector3d(player.x, player.y + player.getEyeHeight(), player.z);
Vector startingVec = new Vector(startingPos.getX(), startingPos.getY(), startingPos.getZ());
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);
Vector endVec = trace.getPointAtDistance(distance);
Vector3D endVec = trace.getPointAtDistance(distance);
Vector3d endPos = new Vector3d(endVec.getX(), endVec.getY(), endVec.getZ());
return traverseBlocks(player, startingPos, endPos, (block, vector3i) -> {
@@ -794,14 +806,14 @@ public class CheckManagerListener extends PacketListenerAbstract {
data.downCast(boxes);
double bestHitResult = Double.MAX_VALUE;
Vector bestHitLoc = null;
Vector3D bestHitLoc = null;
BlockFace bestFace = null;
for (SimpleCollisionBox box : boxes) {
Pair<Vector, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
Pair<Vector3D, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
if (intercept.first() == null) continue; // No intercept
Vector hitLoc = intercept.first();
Vector3D hitLoc = intercept.first();
if (hitLoc.distanceSquared(startingVec) < bestHitResult) {
bestHitResult = hitLoc.distanceSquared(startingVec);
@@ -819,7 +831,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
double waterHeight = player.compensatedWorld.getFluidLevelAt(vector3i.getX(), vector3i.getY(), vector3i.getZ());
SimpleCollisionBox box = new SimpleCollisionBox(vector3i.getX(), vector3i.getY(), vector3i.getZ(), vector3i.getX() + 1, vector3i.getY() + waterHeight, vector3i.getZ() + 1);
Pair<Vector, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
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);
@@ -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.
@@ -241,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) {
@@ -259,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
@@ -269,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();
@@ -287,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
@@ -318,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();
@@ -413,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)
@@ -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,11 +20,12 @@ import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerJoinGame;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerRespawn;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateHealth;
import org.bukkit.util.Vector;
import java.util.List;
import java.util.Objects;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
/**
* PlayerRespawnS2CPacket info (1.20.2+):
* If the world is different (check via registry key), world is recreated (all entities etc destroyed).
@@ -131,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;
@@ -157,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)) {
@@ -185,7 +192,7 @@ public class PacketPlayerRespawn extends PacketListenerAbstract {
player.compensatedEntities.hasSprintingAttributeEnabled = false;
}
player.pose = Pose.STANDING;
player.clientVelocity = new Vector();
player.clientVelocity = newVector3D();
player.gamemode = respawn.getGameMode();
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_17)) {
player.compensatedWorld.setDimension(respawn.getDimensionType(), event.getUser());
@@ -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());
}
}
}
@@ -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
@@ -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,12 +11,14 @@ 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.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.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;
@@ -98,11 +100,15 @@ 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(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(TransactionOrder.class, new TransactionOrder(player))
.put(NoSlowB.class, new NoSlowB(player))
@@ -151,6 +157,7 @@ public class CheckManager {
.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))
@@ -161,6 +168,7 @@ public class CheckManager {
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))
@@ -171,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();
@@ -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;
@@ -19,6 +19,7 @@ import ac.grim.grimac.utils.math.VectorUtils;
import ac.grim.grimac.utils.nmsutil.Collisions;
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
import ac.grim.grimac.utils.nmsutil.ReachUtils;
import ac.grim.grimac.utils.vector.Vector3D;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.player.GameMode;
@@ -31,13 +32,15 @@ import lombok.Getter;
import lombok.Setter;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.Collections;
import java.util.HashSet;
import java.util.Random;
import java.util.concurrent.ConcurrentLinkedQueue;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
// Sync to netty
public final ConcurrentLinkedQueue<TeleportData> pendingTeleports = new ConcurrentLinkedQueue<>();
@@ -65,7 +68,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
@Override
public void onPredictionComplete(final PredictionComplete predictionComplete) {
// Grab friction now when we know player on ground and other variables
Vector afterTickFriction = player.clientVelocity.clone();
Vector3D afterTickFriction = player.clientVelocity.clone();
// We must first check if the player has accepted their setback
// If the setback isn't complete, then this position is illegitimate
@@ -115,7 +118,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
return false;
}
private void simulateFriction(Vector vector) {
private void simulateFriction(Vector3D vector) {
// We must always do this before simulating positions, as this is the last actual (safe) movement
// We must not do this for knockback or explosions, as they are at the start of the tick
if (player.wasTouchingWater) {
@@ -123,12 +126,13 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
} else if (player.wasTouchingLava) {
vector.multiply(0.5D);
if (player.hasGravity)
vector.add(new Vector(0.0D, -player.gravity / 4.0D, 0.0D));
vector.add(newVector3D(0.0D, -player.gravity / 4.0D, 0.0D));
} else if (player.isGliding) {
PredictionEngineElytra.getElytraMovement(player, vector, ReachUtils.getLook(player, player.xRot, player.yRot)).multiply(player.stuckSpeedMultiplier).multiply(new Vector(0.99F, 0.98F, 0.99F));
PredictionEngineElytra.getElytraMovement(player, vector, ReachUtils.getLook(player, player.xRot, player.yRot)).multiply(player.stuckSpeedMultiplier).multiply(newVector3D(0.99F, 0.98F, 0.99F));
vector.setY(vector.getY() - 0.05); // Make the player fall a bit
} else { // Gliding doesn't have friction, we handle it differently
PredictionEngineNormal.staticVectorEndOfTick(player, vector); // Lava and normal movement
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?
@@ -147,9 +151,9 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
lastWorldResync = System.currentTimeMillis();
}
Vector clientVel = lastKnownGoodPosition.vector.clone();
Vector3D clientVel = lastKnownGoodPosition.vector.clone();
Pair<VelocityData, Vector> futureKb = player.checkManager.getKnockbackHandler().getFutureKnockback();
Pair<VelocityData, Vector3D> futureKb = player.checkManager.getKnockbackHandler().getFutureKnockback();
VelocityData futureExplosion = player.checkManager.getExplosionHandler().getFutureExplosion();
// Velocity sets
@@ -169,7 +173,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
// Mini prediction engine - simulate collisions
if (simulateNextTickPosition) {
Vector collide = Collisions.collide(player, clientVel.getX(), clientVel.getY(), clientVel.getZ());
Vector3D collide = Collisions.collide(player, clientVel.getX(), clientVel.getY(), clientVel.getZ());
position = position.withX(position.getX() + collide.getX());
// 1.8 players need the collision epsilon to not phase into blocks when being setback
@@ -397,22 +401,22 @@ 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());
}
data = new TeleportData(safePosition, flags, transaction, teleportId);
requiredSetBack = new SetBackData(data, player.xRot, player.yRot, null, false, plugin);
this.lastKnownGoodPosition = new SetbackPosWithVector(safePosition, new Vector());
this.lastKnownGoodPosition = new SetbackPosWithVector(safePosition, newVector3D());
}
@AllArgsConstructor
@@ -420,6 +424,6 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
@Setter
public static class SetbackPosWithVector {
private Vector3d pos;
private Vector vector;
private Vector3D vector;
}
}
@@ -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.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);
}
}
@@ -10,26 +10,28 @@ import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import ac.grim.grimac.utils.data.VectorData;
import ac.grim.grimac.utils.lists.EvictingQueue;
import ac.grim.grimac.utils.math.GrimMath;
import ac.grim.grimac.utils.vector.Vector3D;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import lombok.AllArgsConstructor;
import org.bukkit.util.Vector;
import java.util.*;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public final class SuperDebug extends Check implements PostPredictionCheck {
private static final StringBuilder[] flags = new StringBuilder[256]; // 17 MB of logs in memory
Object2IntMap<StringBuilder> continuedDebug = new Object2IntOpenHashMap<>();
List<VectorData> predicted = new EvictingQueue<>(60);
List<Vector> actually = new EvictingQueue<>(60);
List<Vector3D> actually = new EvictingQueue<>(60);
List<Location> locations = new EvictingQueue<>(60);
List<Vector> startTickClientVel = new EvictingQueue<>(60);
List<Vector> baseTickAddition = new EvictingQueue<>(60);
List<Vector> baseTickWater = new EvictingQueue<>(60);
List<Vector3D> startTickClientVel = new EvictingQueue<>(60);
List<Vector3D> baseTickAddition = new EvictingQueue<>(60);
List<Vector3D> baseTickWater = new EvictingQueue<>(60);
public SuperDebug(GrimPlayer player) {
super(player);
@@ -80,11 +82,11 @@ public final class SuperDebug extends Check implements PostPredictionCheck {
for (int i = 0; i < predicted.size(); i++) {
VectorData predict = predicted.get(i);
Vector actual = actually.get(i);
Vector3D actual = actually.get(i);
Location loc = locations.get(i);
Vector startTickVel = startTickClientVel.get(i);
Vector addition = baseTickAddition.get(i);
Vector water = baseTickWater.get(i);
Vector3D startTickVel = startTickClientVel.get(i);
Vector3D addition = baseTickAddition.get(i);
Vector3D water = baseTickWater.get(i);
appendDebug(sb, predict, actual, loc, startTickVel, addition, water);
}
@@ -189,7 +191,7 @@ public final class SuperDebug extends Check implements PostPredictionCheck {
continuedDebug.put(sb, 40);
}
private void appendDebug(StringBuilder sb, VectorData predict, Vector actual, Location location, Vector startTick, Vector addition, Vector water) {
private void appendDebug(StringBuilder sb, VectorData predict, Vector3D actual, Location location, Vector3D startTick, Vector3D addition, Vector3D water) {
if (predict.isZeroPointZeroThree()) {
sb.append("Movement threshold/tick skipping\n");
}
@@ -225,11 +227,11 @@ public final class SuperDebug extends Check implements PostPredictionCheck {
// Apply 0.003/0.005 to make numbers more accurate
Set<VectorData> set = new HashSet<>(Collections.singletonList(new VectorData(startTick.clone(), VectorData.VectorType.BestVelPicked)));
new PredictionEngine().applyMovementThreshold(player, set);
Vector trueStartVel = ((VectorData) set.toArray()[0]).vector;
Vector3D trueStartVel = ((VectorData) set.toArray()[0]).vector;
Vector clientMovement = getPlayerMathMovement(player, actual.clone().subtract(trueStartVel), location.xRot);
Vector simulatedMovement = getPlayerMathMovement(player, predict.vector.clone().subtract(trueStartVel), location.xRot);
Vector offset = actual.clone().subtract(predict.vector);
Vector3D clientMovement = getPlayerMathMovement(player, actual.clone().subtract(trueStartVel), location.xRot);
Vector3D simulatedMovement = getPlayerMathMovement(player, predict.vector.clone().subtract(trueStartVel), location.xRot);
Vector3D offset = actual.clone().subtract(predict.vector);
trueStartVel.add(addition);
trueStartVel.add(water);
@@ -268,14 +270,14 @@ public final class SuperDebug extends Check implements PostPredictionCheck {
sb.append("\n\n");
}
private Vector getPlayerMathMovement(GrimPlayer player, Vector wantedMovement, float f2) {
private Vector3D getPlayerMathMovement(GrimPlayer player, Vector3D wantedMovement, float f2) {
float f3 = player.trigHandler.sin(f2 * 0.017453292f);
float f4 = player.trigHandler.cos(f2 * 0.017453292f);
float bestTheoreticalX = (float) (f3 * wantedMovement.getZ() + f4 * wantedMovement.getX()) / (f3 * f3 + f4 * f4);
float bestTheoreticalZ = (float) (-f3 * wantedMovement.getX() + f4 * wantedMovement.getZ()) / (f3 * f3 + f4 * f4);
return new Vector(bestTheoreticalX, 0, bestTheoreticalZ);
return newVector3D(bestTheoreticalX, 0, bestTheoreticalZ);
}
@AllArgsConstructor
@@ -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();
@@ -54,7 +54,7 @@ import net.kyori.adventure.text.TranslatableComponent;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import org.jetbrains.annotations.Nullable;
import java.util.*;
@@ -64,6 +64,9 @@ 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.Normal;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
// Everything in this class should be sync'd to the anticheat thread.
// Put variables sync'd to the netty thread in PacketStateData
// Variables that need lag compensation should have their own class
@@ -90,7 +93,7 @@ public class GrimPlayer implements GrimUser {
public MovementCheckRunner movementCheckRunner;
public SyncedTags tagManager;
// End manager like classes
public Vector clientVelocity = new Vector();
public ac.grim.grimac.utils.vector.Vector3D clientVelocity = newVector3D();
PacketTracker packetTracker;
private long transactionPing = 0;
public long lastTransSent = 0;
@@ -103,9 +106,9 @@ public class GrimPlayer implements GrimUser {
public boolean hasGravity = true;
public final long joinTime = System.currentTimeMillis();
public boolean playerEntityHasGravity = true;
public VectorData predictedVelocity = new VectorData(new Vector(), VectorData.VectorType.Normal);
public Vector actualMovement = new Vector();
public Vector stuckSpeedMultiplier = new Vector(1, 1, 1);
public VectorData predictedVelocity = new VectorData(newVector3D(), Normal);
public Vector3D actualMovement = newVector3D();
public Vector3D stuckSpeedMultiplier = newVector3D(1, 1, 1);
public UncertaintyHandler uncertaintyHandler;
public double gravity;
public float friction;
@@ -184,9 +187,9 @@ public class GrimPlayer implements GrimUser {
public TrigHandler trigHandler;
public PacketStateData packetStateData;
// Keep track of basetick stuff
public Vector baseTickAddition = new Vector();
public Vector baseTickWaterPushing = new Vector();
public Vector startTickClientVel = new Vector();
public Vector3D baseTickAddition = newVector3D();
public Vector3D baseTickWaterPushing = newVector3D();
public Vector3D startTickClientVel = newVector3D();
// For syncing the player's full swing in 1.9+
public int movementPackets = 0;
public VelocityData firstBreadKB = null;
@@ -242,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)) {
@@ -292,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));
}
@@ -360,15 +364,15 @@ public class GrimPlayer implements GrimUser {
return data != null && data.first() == id;
}
public void baseTickAddWaterPushing(Vector vector) {
public void baseTickAddWaterPushing(ac.grim.grimac.utils.vector.Vector3D vector) {
baseTickWaterPushing.add(vector);
}
public void baseTickAddVector(Vector vector) {
public void baseTickAddVector(Vector3D vector) {
clientVelocity.add(vector);
}
public void trackBaseTickAddition(Vector vector) {
public void trackBaseTickAddition(Vector3D vector) {
baseTickAddition.add(vector);
}
@@ -565,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));
}
@@ -697,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);
@@ -751,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) {
@@ -759,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
@@ -38,7 +38,10 @@ import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import static ac.grim.grimac.utils.data.VectorData.VectorType.Dead;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class MovementCheckRunner extends Check implements PositionCheck {
// Averaged over 500 predictions (Defaults set slightly above my 3600x results)
@@ -138,10 +141,10 @@ public class MovementCheckRunner extends Check implements PositionCheck {
double negX = Math.min(-0.05, GrimMath.clamp(player.actualMovement.getX(), -16, 16) - 0.05);
double negZ = Math.min(-0.05, GrimMath.clamp(player.actualMovement.getZ(), -16, 16) - 0.05);
Vector NE = Collisions.maybeBackOffFromEdge(new Vector(posX, 0, negZ), player, true);
Vector NW = Collisions.maybeBackOffFromEdge(new Vector(negX, 0, negZ), player, true);
Vector SE = Collisions.maybeBackOffFromEdge(new Vector(posX, 0, posZ), player, true);
Vector SW = Collisions.maybeBackOffFromEdge(new Vector(negX, 0, posZ), player, true);
Vector3D NE = Collisions.maybeBackOffFromEdge(newVector3D(posX, 0, negZ), player, true);
Vector3D NW = Collisions.maybeBackOffFromEdge(newVector3D(negX, 0, negZ), player, true);
Vector3D SE = Collisions.maybeBackOffFromEdge(newVector3D(posX, 0, posZ), player, true);
Vector3D SW = Collisions.maybeBackOffFromEdge(newVector3D(negX, 0, posZ), player, true);
boolean isEast = NE.getX() != posX || SE.getX() != posX;
boolean isWest = NW.getX() != negX || SW.getX() != negX;
@@ -184,7 +187,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
player.vehicleData.wasVehicleSwitch = false;
if (riding != null) {
Vector pos = new Vector(player.x, player.y, player.z);
Vector3D pos = newVector3D(player.x, player.y, player.z);
SimpleCollisionBox interTruePositions = riding.getPossibleCollisionBoxes();
// We shrink the expanded bounding box to what the packet positions can be, for a smaller box
@@ -194,7 +197,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
interTruePositions.expand(-width, 0, -width);
interTruePositions.expandMax(0, -height, 0);
Vector cutTo = VectorUtils.cutBoxToVector(pos, interTruePositions);
Vector3D cutTo = VectorUtils.cutBoxToVector(pos, interTruePositions);
// Now we need to simulate a tick starting at the most optimal position
// The start position is never sent, so we assume the most optimal start position
@@ -212,14 +215,14 @@ public class MovementCheckRunner extends Check implements PositionCheck {
} else {
// Server always teleports the player when they eject anyways,
// so just let the player control where they eject within reason, they get set back anyways
if (new Vector(player.lastX, player.lastY, player.lastZ).distance(new Vector(player.x, player.y, player.z)) > 3) {
if (newVector3D(player.lastX, player.lastY, player.lastZ).distance(newVector3D(player.x, player.y, player.z)) > 3) {
player.getSetbackTeleportUtil().executeForceResync(); // Too far! (I think this value is sane)
}
handleTeleport(update);
if (player.isClimbing) {
Vector ladder = player.clientVelocity.clone().setY(0.2);
Vector3D ladder = player.clientVelocity.clone().setY(0.2);
PredictionEngineNormal.staticVectorEndOfTick(player, ladder);
player.lastWasClimbing = ladder.getY();
}
@@ -299,7 +302,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
player.clientControlledVerticalCollision = Math.abs(player.y % (1 / 64D)) < 0.00001;
// This isn't the final velocity of the player in the tick, only the one applied to the player
player.actualMovement = new Vector(player.x - player.lastX, player.y - player.lastY, player.z - player.lastZ);
player.actualMovement = newVector3D(player.x - player.lastX, player.y - player.lastY, player.z - player.lastZ);
if (player.isSprinting != player.lastSprinting) {
player.compensatedEntities.hasSprintingAttributeEnabled = player.isSprinting;
@@ -347,7 +350,7 @@ 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.first();
if (data.getType() == StateTypes.SLIME_BLOCK && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_8)) {
@@ -411,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) {
@@ -426,9 +429,9 @@ public class MovementCheckRunner extends Check implements PositionCheck {
// Exempt if the player is dead or is riding a dead entity
if (player.compensatedEntities.getSelf().isDead || (riding != null && riding.isDead)) {
// Dead players can't cheat, if you find a way how they could, open an issue
player.predictedVelocity = new VectorData(new Vector(), VectorData.VectorType.Dead);
player.clientVelocity = new Vector();
} else if (player.disableGrim || (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_8) && player.gamemode == GameMode.SPECTATOR) || player.isFlying) {
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 || (player.isExemptElytra() && player.isGliding)) {
// We could technically check spectator but what's the point...
// Added complexity to analyze a gamemode used mainly by moderators
//
@@ -450,7 +453,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
// Riptiding while on the ground moves the hitbox upwards before any movement code runs
// It's a pain to support and this is my best attempt
if (player.lastOnGround && player.packetStateData.tryingToRiptide && !player.compensatedEntities.getSelf().inVehicle()) {
Vector pushingMovement = Collisions.collide(player, 0, 1.1999999F, 0);
Vector3D pushingMovement = Collisions.collide(player, 0, 1.1999999F, 0);
player.verticalCollision = pushingMovement.getY() != 1.1999999F;
double currentY = player.clientVelocity.getY();
@@ -462,7 +465,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
player.lastY += pushingMovement.getY();
new PlayerBaseTick(player).updatePlayerPose();
player.boundingBox = GetBoundingBox.getPlayerBoundingBox(player, player.lastX, player.lastY, player.lastZ);
player.actualMovement = new Vector(player.x - player.lastX, player.y - player.lastY, player.z - player.lastZ);
player.actualMovement = newVector3D(player.x - player.lastX, player.y - player.lastY, player.z - player.lastZ);
player.couldSkipTick = true;
@@ -514,7 +517,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
//
// Checking for oldClientVel being too high fixes BleachHack vertical scaffold
if (player.getSetbackTeleportUtil().getRequiredSetBack() != null && player.getSetbackTeleportUtil().getRequiredSetBack().getTicksComplete() == 1) {
Vector setbackVel = player.getSetbackTeleportUtil().getRequiredSetBack().getVelocity();
Vector3D setbackVel = player.getSetbackTeleportUtil().getRequiredSetBack().getVelocity();
// A player must have velocity going INTO the ground to be able to jump
// Otherwise they could ignore upwards velocity that isn't useful into more useful upwards velocity (towering)
// So if they are supposed to be going upwards, or are supposed to be off the ground, resync
@@ -543,7 +546,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
// Patch sprint jumping with elytra exploit
if (player.bukkitPlayer != null && player.isGliding && player.predictedVelocity.isJump() && player.isSprinting && !allowSprintJumpingWithElytra) {
SetbackTeleportUtil.SetbackPosWithVector lastKnownGoodPosition = player.getSetbackTeleportUtil().lastKnownGoodPosition;
lastKnownGoodPosition.setVector(lastKnownGoodPosition.getVector().multiply(new Vector(0.6 * 0.91, 1, 0.6 * 0.91)));
lastKnownGoodPosition.setVector(lastKnownGoodPosition.getVector().multiply(newVector3D(0.6 * 0.91, 1, 0.6 * 0.91)));
player.getSetbackTeleportUtil().executeNonSimulatingSetback();
}
@@ -620,7 +623,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
* @param pushingMovement The collision result when trying to move the player upwards by 1.2f
* @return Whether it is more likely that this player was on the ground the tick they riptided
*/
private boolean likelyGroundRiptide(Vector pushingMovement) {
private boolean likelyGroundRiptide(Vector3D pushingMovement) {
// Y velocity gets reset if the player collides vertically
double riptideYResult = Riptide.getRiptideVelocity(player).getY();
@@ -638,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);
}
}
@@ -9,16 +9,19 @@ import ac.grim.grimac.utils.enums.Pose;
import ac.grim.grimac.utils.latency.CompensatedEntities;
import ac.grim.grimac.utils.math.GrimMath;
import ac.grim.grimac.utils.nmsutil.*;
import ac.grim.grimac.utils.vector.Vector3D;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateAttributes;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.Optional;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class PlayerBaseTick {
GrimPlayer player;
@@ -40,11 +43,11 @@ public class PlayerBaseTick {
public void doBaseTick() {
// Keep track of basetick stuff
player.baseTickAddition = new Vector();
player.baseTickWaterPushing = new Vector();
player.baseTickAddition = newVector3D();
player.baseTickWaterPushing = newVector3D();
if (player.isFlying && player.isSneaking && !player.compensatedEntities.getSelf().inVehicle()) {
Vector flyingShift = new Vector(0, player.flySpeed * -3, 0);
Vector3D flyingShift = newVector3D(0, player.flySpeed * -3, 0);
player.baseTickAddVector(flyingShift);
player.trackBaseTickAddition(flyingShift);
}
@@ -60,7 +63,7 @@ public class PlayerBaseTick {
// You cannot crouch while flying, only shift - could be specific to 1.14?
// pre-1.13 clients don't have this code
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13) && player.wasTouchingWater && player.isSneaking && !player.isFlying && !player.compensatedEntities.getSelf().inVehicle()) {
Vector waterPushVector = new Vector(0, -0.04f, 0);
Vector3D waterPushVector = newVector3D(0, -0.04f, 0);
player.baseTickAddVector(waterPushVector);
player.trackBaseTickAddition(waterPushVector);
}
@@ -268,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
@@ -413,7 +417,7 @@ public class PlayerBaseTick {
}
boolean hasPushed = false;
Vector vec3 = new Vector();
Vector3D vec3 = newVector3D();
for (int x = floorX; x < ceilX; ++x) {
for (int y = floorY; y < ceilY; ++y) {
@@ -463,7 +467,7 @@ public class PlayerBaseTick {
}
double d2 = 0.0;
boolean hasTouched = false;
Vector vec3 = new Vector();
Vector3D vec3 = newVector3D();
int n7 = 0;
for (int x = floorX; x < ceilX; ++x) {
@@ -488,7 +492,7 @@ public class PlayerBaseTick {
d2 = Math.max(fluidHeightToWorld - aABB.minY, d2);
if (!player.isFlying) {
Vector vec32 = FluidTypeFlowing.getFlow(player, x, y, z);
Vector3D vec32 = FluidTypeFlowing.getFlow(player, x, y, z);
if (d2 < 0.4) {
vec32 = vec32.multiply(d2);
}
@@ -18,11 +18,13 @@ import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.OptionalInt;
import java.util.Set;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
/**
* A lot of care was put into handling all the stupid stuff occurring between events
* <p>
@@ -127,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))) {
@@ -140,7 +143,7 @@ public class PointThreeEstimator {
isNearBubbleColumn = true;
}
Vector fluidVector = FluidTypeFlowing.getFlow(player, x, y, z);
Vector3D fluidVector = FluidTypeFlowing.getFlow(player, x, y, z);
if (fluidVector.getX() != 0 || fluidVector.getZ() != 0) {
isNearHorizontalFlowingLiquid = true;
}
@@ -212,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;
@@ -222,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;
@@ -303,7 +307,7 @@ public class PointThreeEstimator {
player.boundingBox = player.boundingBox.copy().expand(0.03, 0, 0.03).offset(0, 0.03, 0);
// 0.16 magic value -> 0.03 plus gravity, plus some additional lenience
double searchDistance = -0.2 + Math.min(0, y);
Vector collisionResult = Collisions.collide(player, 0, searchDistance, 0);
Vector3D collisionResult = Collisions.collide(player, 0, searchDistance, 0);
player.boundingBox = playerBox;
return collisionResult.getY() != searchDistance;
}
@@ -312,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
}
@@ -337,9 +341,9 @@ public class PointThreeEstimator {
// Takes 0.01 millis, on average, to compute... this should be improved eventually
for (VectorData data : init) {
// Try to get the vector as close to zero as possible to give the best chance at 0.03...
Vector toZeroVec = new PredictionEngine().handleStartingVelocityUncertainty(player, data, new Vector());
Vector3D toZeroVec = new PredictionEngine().handleStartingVelocityUncertainty(player, data, newVector3D());
// Collide to handle mostly gravity, but other scenarios similar to this.
Vector collisionResult = Collisions.collide(player, toZeroVec.getX(), toZeroVec.getY(), toZeroVec.getZ(), Integer.MIN_VALUE, null);
Vector3D collisionResult = Collisions.collide(player, toZeroVec.getX(), toZeroVec.getY(), toZeroVec.getZ(), Integer.MIN_VALUE, null);
// If this tick is the tick after y velocity was by 0, a stepping movement is POSSIBLE to have been hidden
// A bit hacky... is there a better way? I'm unsure...
@@ -13,10 +13,12 @@ import ac.grim.grimac.utils.nmsutil.ReachUtils;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.*;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class UncertaintyHandler {
private final GrimPlayer player;
// Handles uncertainty when a piston could have pushed a player in a direction
@@ -64,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;
@@ -147,15 +151,15 @@ public class UncertaintyHandler {
entityBox.maxY -= height;
entityBox.expand(-width / 2, 0, -width / 2);
Vector maxLocation = new Vector(entityBox.maxX, entityBox.maxY, entityBox.maxZ);
Vector minLocation = new Vector(entityBox.minX, entityBox.minY, entityBox.minZ);
Vector3D maxLocation = newVector3D(entityBox.maxX, entityBox.maxY, entityBox.maxZ);
Vector3D minLocation = newVector3D(entityBox.minX, entityBox.minY, entityBox.minZ);
Vector diff = minLocation.subtract(new Vector(player.lastX, player.lastY + 0.8 * 1.8, player.lastZ)).multiply(0.1);
Vector3D diff = minLocation.subtract(newVector3D(player.lastX, player.lastY + 0.8 * 1.8, player.lastZ)).multiply(0.1);
fishingRodPullBox.minX = Math.min(0, diff.getX());
fishingRodPullBox.minY = Math.min(0, diff.getY());
fishingRodPullBox.minZ = Math.min(0, diff.getZ());
diff = maxLocation.subtract(new Vector(player.lastX, player.lastY + 0.8 * 1.8, player.lastZ)).multiply(0.1);
diff = maxLocation.subtract(newVector3D(player.lastX, player.lastY + 0.8 * 1.8, player.lastZ)).multiply(0.1);
fishingRodPullBox.maxX = Math.max(0, diff.getX());
fishingRodPullBox.maxY = Math.max(0, diff.getY());
fishingRodPullBox.maxZ = Math.max(0, diff.getZ());
@@ -170,8 +174,8 @@ public class UncertaintyHandler {
fireworksBox = new SimpleCollisionBox();
Vector currentLook = ReachUtils.getLook(player, player.xRot, player.yRot);
Vector lastLook = ReachUtils.getLook(player, player.lastXRot, player.lastYRot);
Vector3D currentLook = ReachUtils.getLook(player, player.xRot, player.yRot);
ac.grim.grimac.utils.vector.Vector3D lastLook = ReachUtils.getLook(player, player.lastXRot, player.lastYRot);
double antiTickSkipping = player.isPointThree() ? 0 : 0.05; // With 0.03, let that handle tick skipping
@@ -24,7 +24,9 @@ import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.util.Vector3d;
import com.viaversion.viaversion.api.Via;
import io.github.retrooper.packetevents.util.viaversion.ViaVersionUtil;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class MovementTicker {
public final GrimPlayer player;
@@ -41,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()) {
@@ -52,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++;
}
}
@@ -74,12 +84,13 @@ public class MovementTicker {
player.uncertaintyHandler.yPositiveUncertainty += 0.05;
}
player.uncertaintyHandler.riptideEntities.add(possibleRiptideEntities);
player.uncertaintyHandler.collidingEntities.add(possibleCollidingEntities);
}
public void move(Vector inputVel, Vector collide) {
public void move(Vector3D inputVel, Vector3D collide) {
if (player.stuckSpeedMultiplier.getX() < 0.99) {
player.clientVelocity = new Vector();
player.clientVelocity = newVector3D();
}
if (inputVel.getX() != collide.getX()) {
@@ -172,21 +183,21 @@ public class MovementTicker {
// The game disregards movements smaller than 1e-7 (such as in boats)
if (collide.lengthSquared() < 1e-7) {
collide = new Vector();
collide = newVector3D();
}
// This is where vanilla moves the bounding box and sets it
player.predictedVelocity = new VectorData(collide.clone(), player.predictedVelocity.lastVector, player.predictedVelocity.vectorType);
float f = BlockProperties.getBlockSpeedFactor(player, player.mainSupportingBlockData, new Vector3d(player.x, player.y, player.z));
player.clientVelocity.multiply(new Vector(f, 1, f));
player.clientVelocity.multiply(newVector3D(f, 1, f));
// Reset stuck speed so it can update
if (player.stuckSpeedMultiplier.getX() < 0.99) {
player.uncertaintyHandler.lastStuckSpeedMultiplier.reset();
}
player.stuckSpeedMultiplier = new Vector(1, 1, 1);
player.stuckSpeedMultiplier = newVector3D(1, 1, 1);
// 1.15 and older clients use the handleInsideBlocks method for lava
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_16))
@@ -201,7 +212,7 @@ public class MovementTicker {
// Flying players are not affected by cobwebs/sweet berry bushes
if (player.isFlying) {
player.stuckSpeedMultiplier = new Vector(1, 1, 1);
player.stuckSpeedMultiplier = newVector3D(1, 1, 1);
}
}
@@ -388,14 +399,14 @@ public class MovementTicker {
// Lava movement changed in 1.16
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_16) && player.slightlyTouchingLava) {
player.clientVelocity = player.clientVelocity.multiply(new Vector(0.5D, 0.800000011920929D, 0.5D));
player.clientVelocity = player.clientVelocity.multiply(newVector3D(0.5D, 0.800000011920929D, 0.5D));
player.clientVelocity = FluidFallingAdjustedMovement.getFluidFallingAdjustedMovement(player, playerGravity, isFalling, player.clientVelocity);
} else {
player.clientVelocity.multiply(0.5D);
}
if (player.hasGravity)
player.clientVelocity.add(new Vector(0.0D, -playerGravity / 4.0D, 0.0D));
player.clientVelocity.add(newVector3D(0.0D, -playerGravity / 4.0D, 0.0D));
} else if (player.isGliding) {
player.friction = 0.99F; // Not vanilla, just useful for other grim stuff
@@ -5,7 +5,8 @@ import ac.grim.grimac.utils.data.packetentity.PacketEntityHorse;
import ac.grim.grimac.utils.nmsutil.Collisions;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import org.bukkit.util.Vector;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class MovementTickerHorse extends MovementTickerLivingVehicle {
@@ -26,7 +27,7 @@ public class MovementTickerHorse extends MovementTickerLivingVehicle {
forwardsInput *= 0.25F;
}
this.movementInput = new Vector(horizInput, 0, forwardsInput);
this.movementInput = newVector3D(horizInput, 0, forwardsInput);
if (movementInput.lengthSquared() > 1) movementInput.normalize();
}
@@ -7,10 +7,13 @@ import ac.grim.grimac.predictionengine.predictions.rideable.PredictionEngineRide
import ac.grim.grimac.predictionengine.predictions.rideable.PredictionEngineRideableWaterLegacy;
import ac.grim.grimac.utils.nmsutil.BlockProperties;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import ac.grim.grimac.utils.vector.Vector3D;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class MovementTickerLivingVehicle extends MovementTicker {
Vector movementInput = new Vector();
Vector3D movementInput = newVector3D();
public MovementTickerLivingVehicle(GrimPlayer player) {
super(player);
@@ -3,12 +3,13 @@ package ac.grim.grimac.predictionengine.movementtick;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.data.packetentity.PacketEntityRideable;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import org.bukkit.util.Vector;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class MovementTickerPig extends MovementTickerRideable {
public MovementTickerPig(GrimPlayer player) {
super(player);
movementInput = new Vector(0, 0, 1);
movementInput = newVector3D(0, 0, 1);
}
@Override
@@ -11,15 +11,16 @@ import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateAttributes;
import org.bukkit.util.Vector;
import java.util.ArrayList;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class MovementTickerStrider extends MovementTickerRideable {
public MovementTickerStrider(GrimPlayer player) {
super(player);
movementInput = new Vector(0, 0, 1);
movementInput = newVector3D(0, 0, 1);
}
public static void floatStrider(GrimPlayer player) {
@@ -27,7 +28,7 @@ public class MovementTickerStrider extends MovementTickerRideable {
if (isAbove(player) && player.compensatedWorld.getLavaFluidLevelAt((int) Math.floor(player.x), (int) Math.floor(player.y + 1), (int) Math.floor(player.z)) == 0) {
player.onGround = true;
} else {
player.clientVelocity.multiply(0.5).add(new Vector(0, 0.05, 0));
player.clientVelocity.multiply(0.5).add(newVector3D(0, 0.05, 0));
}
}
}
@@ -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;
@@ -11,20 +12,29 @@ import ac.grim.grimac.utils.nmsutil.Collisions;
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
import ac.grim.grimac.utils.nmsutil.JumpPower;
import ac.grim.grimac.utils.nmsutil.Riptide;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import ac.grim.grimac.utils.vector.Vector3D;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.*;
import static ac.grim.grimac.utils.data.VectorData.VectorType.ZeroPointZeroThree;
import static ac.grim.grimac.utils.nmsutil.Collisions.isEmpty;
import static ac.grim.grimac.utils.nmsutil.GetBoundingBox.getBoundingBoxFromPosAndSize;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
import static com.github.retrooper.packetevents.protocol.attribute.Attributes.GENERIC_GRAVITY;
import static com.github.retrooper.packetevents.protocol.player.ClientVersion.V_1_13;
import static java.lang.Math.*;
import static java.util.Collections.max;
public class PredictionEngine {
public static Vector clampMovementToHardBorder(GrimPlayer player, Vector outputVel) {
public static Vector3D clampMovementToHardBorder(GrimPlayer player, Vector3D outputVel) {
// TODO: Reimplement
return outputVel;
}
public static Vector transformInputsToVector(GrimPlayer player, Vector theoreticalInput) {
public static Vector3D transformInputsToVector(GrimPlayer player, Vector3D theoreticalInput) {
float bestPossibleX;
float bestPossibleZ;
@@ -42,15 +52,15 @@ public class PredictionEngine {
bestPossibleZ *= 0.2F;
}
Vector inputVector = new Vector(bestPossibleX, 0, bestPossibleZ);
Vector3D inputVector = newVector3D(bestPossibleX, 0, bestPossibleZ);
inputVector.multiply(0.98F);
// Simulate float rounding imprecision
inputVector = new Vector((float) inputVector.getX(), (float) inputVector.getY(), (float) inputVector.getZ());
inputVector = newVector3D((float) inputVector.getX(), (float) inputVector.getY(), (float) inputVector.getZ());
if (inputVector.lengthSquared() > 1) {
double d0 = Math.sqrt(inputVector.getX() * inputVector.getX() + inputVector.getY() * inputVector.getY() + inputVector.getZ() * inputVector.getZ());
inputVector = new Vector(inputVector.getX() / d0, inputVector.getY() / d0, inputVector.getZ() / d0);
double d0 = sqrt(inputVector.getX() * inputVector.getX() + inputVector.getY() * inputVector.getY() + inputVector.getZ() * inputVector.getZ());
inputVector = newVector3D(inputVector.getX() / d0, inputVector.getY() / d0, inputVector.getZ() / d0);
}
return inputVector;
@@ -62,7 +72,7 @@ public class PredictionEngine {
if (player.uncertaintyHandler.influencedByBouncyBlock()) {
for (VectorData data : init) {
// Try to get the vector as close to zero as possible to give the best chance at 0.03...
Vector toZeroVec = new PredictionEngine().handleStartingVelocityUncertainty(player, data, new Vector(0, -1000000000, 0)); // Downwards without overflow risk
Vector3D toZeroVec = new PredictionEngine().handleStartingVelocityUncertainty(player, data, newVector3D(0, -1000000000, 0)); // Downwards without overflow risk
player.uncertaintyHandler.nextTickSlimeBlockUncertainty = Math.max(Math.abs(toZeroVec.getY()), player.uncertaintyHandler.nextTickSlimeBlockUncertainty);
}
@@ -105,20 +115,22 @@ public class PredictionEngine {
double bestInput = Double.MAX_VALUE;
VectorData bestCollisionVel = null;
Vector beforeCollisionMovement = null;
Vector originalClientVel = player.clientVelocity.clone();
Vector3D beforeCollisionMovement = null;
Vector3D originalClientVel = player.clientVelocity.clone();
SimpleCollisionBox originalBB = player.boundingBox;
// 0.03 doesn't exist with vehicles, thank god
// 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;
for (VectorData clientVelAfterInput : possibleVelocities) {
Vector primaryPushMovement = handleStartingVelocityUncertainty(player, clientVelAfterInput, player.actualMovement);
Vector3D primaryPushMovement = handleStartingVelocityUncertainty(player, clientVelAfterInput, player.actualMovement);
Vector bestTheoreticalCollisionResult = VectorUtils.cutBoxToVector(player.actualMovement, new SimpleCollisionBox(0, Math.min(0, primaryPushMovement.getY()), 0, primaryPushMovement.getX(), Math.max(0.6, primaryPushMovement.getY()), primaryPushMovement.getZ()).sort());
Vector3D bestTheoreticalCollisionResult = VectorUtils.cutBoxToVector(player.actualMovement, new SimpleCollisionBox(0, Math.min(0, primaryPushMovement.getY()), 0, primaryPushMovement.getX(), Math.max(0.6, primaryPushMovement.getY()), primaryPushMovement.getZ()).sort());
// Check if this vector could ever possible beat the last vector in terms of accuracy
// This is quite a good optimization :)
if (bestTheoreticalCollisionResult.distanceSquared(player.actualMovement) > bestInput && !clientVelAfterInput.isKnockback() && !clientVelAfterInput.isExplosion()) {
@@ -132,9 +144,9 @@ public class PredictionEngine {
}
// Returns pair of primary push movement, and then outputvel
Pair<Vector, Vector> output = doSeekingWallCollisions(player, primaryPushMovement, originalClientVel, clientVelAfterInput);
Pair<Vector3D, Vector3D> output = doSeekingWallCollisions(player, primaryPushMovement, originalClientVel, clientVelAfterInput);
primaryPushMovement = output.first();
Vector outputVel = clampMovementToHardBorder(player, output.second());
Vector3D outputVel = clampMovementToHardBorder(player, output.second());
double resultAccuracy = outputVel.distanceSquared(player.actualMovement);
@@ -208,7 +220,7 @@ public class PredictionEngine {
}
}
private Pair<Vector, Vector> doSeekingWallCollisions(GrimPlayer player, Vector primaryPushMovement, Vector originalClientVel, VectorData clientVelAfterInput) {
private Pair<Vector3D, Vector3D> doSeekingWallCollisions(GrimPlayer player, Vector3D primaryPushMovement, Vector3D originalClientVel, VectorData clientVelAfterInput) {
boolean vehicleKB = player.compensatedEntities.getSelf().inVehicle() && clientVelAfterInput.isKnockback() && clientVelAfterInput.vector.getY() == 0;
// Extra collision epsilon required for vehicles to be accurate
double xAdditional = Math.signum(primaryPushMovement.getX()) * SimpleCollisionBox.COLLISION_EPSILON;
@@ -221,9 +233,9 @@ public class PredictionEngine {
double testX = primaryPushMovement.getX() + xAdditional;
double testY = primaryPushMovement.getY() + yAdditional;
double testZ = primaryPushMovement.getZ() + zAdditional;
primaryPushMovement = new Vector(testX, testY, testZ);
primaryPushMovement = newVector3D(testX, testY, testZ);
Vector outputVel = Collisions.collide(player, primaryPushMovement.getX(), primaryPushMovement.getY(), primaryPushMovement.getZ(), originalClientVel.getY(), clientVelAfterInput);
Vector3D outputVel = Collisions.collide(player, primaryPushMovement.getX(), primaryPushMovement.getY(), primaryPushMovement.getZ(), originalClientVel.getY(), clientVelAfterInput);
if (testX == outputVel.getX()) { // the player didn't have X collision, don't ruin offset by collision epsilon
primaryPushMovement.setX(primaryPushMovement.getX() - xAdditional);
@@ -249,20 +261,20 @@ public class PredictionEngine {
// For now just let the player control their Y velocity within 0.03. Gravity should stop exploits.
// 0.03 - 0.784 < -0.03 = can't skip next tick
Vector pointThreeVector = new Vector();
Vector3D pointThreeVector = newVector3D();
// Stop a bypass (and fix falses) by carrying over the player's current velocity IF they couldn't have modified it
if (!player.pointThreeEstimator.controlsVerticalMovement()) {
pointThreeVector.setY(player.clientVelocity.getY());
} else { // Carry over the current Y velocity to try and help with gravity issues
pointThreePossibilities.add(new VectorData(new Vector(0, player.clientVelocity.getY(), 0), VectorData.VectorType.ZeroPointZeroThree));
pointThreePossibilities.add(new VectorData(newVector3D(0, player.clientVelocity.getY(), 0), ZeroPointZeroThree));
}
pointThreePossibilities.add(new VectorData(pointThreeVector, VectorData.VectorType.ZeroPointZeroThree));
// Swim hop
if (player.pointThreeEstimator.isNearFluid && !Collisions.isEmpty(player, player.boundingBox.copy().expand(0.4, 0, 0.4)) && !player.onGround) { // onGround can still be used here, else generic 0.03
pointThreePossibilities.add(new VectorData(new Vector(0, 0.3, 0), VectorData.VectorType.ZeroPointZeroThree));
pointThreePossibilities.add(new VectorData(newVector3D(0, 0.3, 0), ZeroPointZeroThree));
}
// Swimming vertically can add more Y velocity than normal
@@ -279,7 +291,7 @@ public class PredictionEngine {
player.clientVelocity.getX(), 0, player.clientVelocity.getZ()).expand(0.5, -SimpleCollisionBox.COLLISION_EPSILON, 0.5)))) {
// Calculate the Y velocity after friction
Vector hackyClimbVector = new Vector(0, 0.2, 0);
Vector3D hackyClimbVector = newVector3D(0, 0.2, 0);
PredictionEngineNormal.staticVectorEndOfTick(player, hackyClimbVector);
pointThreePossibilities.add(new VectorData(hackyClimbVector, VectorData.VectorType.ZeroPointZeroThree));
@@ -290,8 +302,8 @@ public class PredictionEngine {
addExplosionToPossibilities(player, pointThreePossibilities);
if (player.packetStateData.tryingToRiptide) {
Vector riptideAddition = Riptide.getRiptideVelocity(player);
pointThreePossibilities.add(new VectorData(player.clientVelocity.clone().add(riptideAddition), new VectorData(new Vector(), VectorData.VectorType.ZeroPointZeroThree), VectorData.VectorType.Trident));
Vector3D riptideAddition = Riptide.getRiptideVelocity(player);
pointThreePossibilities.add(new VectorData(player.clientVelocity.clone().add(riptideAddition), new VectorData(newVector3D(), ZeroPointZeroThree), VectorData.VectorType.Trident));
}
possibleVelocities.addAll(applyInputsToVelocityPossibilities(player, pointThreePossibilities, speed));
@@ -312,7 +324,7 @@ public class PredictionEngine {
// Water pushing movement is affected by initial velocity due to 0.003 eating pushing in the past
if (vectorData.isKnockback() && player.baseTickWaterPushing.lengthSquared() != 0) {
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13)) {
Vector vec3 = player.baseTickWaterPushing.clone();
Vector3D vec3 = player.baseTickWaterPushing.clone();
if (Math.abs(vectorData.vector.getX()) < 0.003 && Math.abs(vectorData.vector.getZ()) < 0.003 && player.baseTickWaterPushing.length() < 0.0045000000000000005D) {
vec3 = vec3.normalize().multiply(0.0045000000000000005);
}
@@ -332,7 +344,7 @@ public class PredictionEngine {
addExplosionToPossibilities(player, velocities);
if (player.packetStateData.tryingToRiptide) {
Vector riptideAddition = Riptide.getRiptideVelocity(player);
Vector3D riptideAddition = Riptide.getRiptideVelocity(player);
velocities.add(new VectorData(player.clientVelocity.clone().add(riptideAddition), VectorData.VectorType.Trident));
}
@@ -361,7 +373,7 @@ public class PredictionEngine {
private void addAttackSlowToPossibilities(GrimPlayer player, Set<VectorData> velocities) {
for (int x = 1; x <= Math.min(player.maxPlayerAttackSlow, 5); x++) {
for (VectorData data : new HashSet<>(velocities)) {
velocities.add(data.returnNewModified(data.vector.clone().multiply(new Vector(0.6, 1, 0.6)), VectorData.VectorType.AttackSlow));
velocities.add(data.returnNewModified(data.vector.clone().multiply(newVector3D(0.6, 1, 0.6)), VectorData.VectorType.AttackSlow));
}
}
}
@@ -464,15 +476,15 @@ public class PredictionEngine {
return Double.compare(a.vector.distanceSquared(player.actualMovement), b.vector.distanceSquared(player.actualMovement));
}
public Vector handleStartingVelocityUncertainty(GrimPlayer player, VectorData vector, Vector targetVec) {
double avgColliding = Collections.max(player.uncertaintyHandler.collidingEntities);
public Vector3D handleStartingVelocityUncertainty(GrimPlayer player, VectorData vector, Vector3D targetVec) {
double avgColliding = max(player.uncertaintyHandler.collidingEntities);
double additionHorizontal = player.uncertaintyHandler.getOffsetHorizontal(vector);
double additionVertical = player.uncertaintyHandler.getVerticalOffset(vector);
double pistonX = Collections.max(player.uncertaintyHandler.pistonX);
double pistonY = Collections.max(player.uncertaintyHandler.pistonY);
double pistonZ = Collections.max(player.uncertaintyHandler.pistonZ);
double pistonX = max(player.uncertaintyHandler.pistonX);
double pistonY = max(player.uncertaintyHandler.pistonY);
double pistonZ = max(player.uncertaintyHandler.pistonZ);
additionHorizontal += player.uncertaintyHandler.lastHorizontalOffset;
additionVertical += player.uncertaintyHandler.lastVerticalOffset;
@@ -515,13 +527,13 @@ public class PredictionEngine {
// 0.075 seems safe?
//
// Be somewhat careful as there is an antikb (for horizontal) that relies on this lenience
Vector uncertainty = new Vector(avgColliding * 0.08, additionVertical, avgColliding * 0.08);
Vector3D uncertainty = newVector3D(avgColliding * 0.08, additionVertical, avgColliding * 0.08);
Vector min = new Vector(player.uncertaintyHandler.xNegativeUncertainty - additionHorizontal, -bonusY + player.uncertaintyHandler.yNegativeUncertainty, player.uncertaintyHandler.zNegativeUncertainty - additionHorizontal);
Vector max = new Vector(player.uncertaintyHandler.xPositiveUncertainty + additionHorizontal, bonusY + player.uncertaintyHandler.yPositiveUncertainty, player.uncertaintyHandler.zPositiveUncertainty + additionHorizontal);
Vector3D min = newVector3D(player.uncertaintyHandler.xNegativeUncertainty - additionHorizontal, -bonusY + player.uncertaintyHandler.yNegativeUncertainty, player.uncertaintyHandler.zNegativeUncertainty - additionHorizontal);
Vector3D max = newVector3D(player.uncertaintyHandler.xPositiveUncertainty + additionHorizontal, bonusY + player.uncertaintyHandler.yPositiveUncertainty, player.uncertaintyHandler.zPositiveUncertainty + additionHorizontal);
Vector minVector = vector.vector.clone().add(min.subtract(uncertainty));
Vector maxVector = vector.vector.clone().add(max.add(uncertainty));
Vector3D minVector = vector.vector.clone().add(min.subtract(uncertainty));
Vector3D maxVector = vector.vector.clone().add(max.add(uncertainty));
// Handle the player landing within 0.03 movement, which resets Y velocity
if (player.uncertaintyHandler.onGroundUncertain && vector.vector.getY() < 0) {
@@ -548,7 +560,7 @@ public class PredictionEngine {
// We can't simulate the player's Y velocity, unknown number of ticks with a gravity change
// Feel free to simulate all 104857600000000000000000000 possibilities!
if (!player.pointThreeEstimator.canPredictNextVerticalMovement()) {
minVector.setY(minVector.getY() - player.compensatedEntities.getSelf().getAttributeValue(Attributes.GENERIC_GRAVITY));
minVector.setY(minVector.getY() - player.compensatedEntities.getSelf().getAttributeValue(GENERIC_GRAVITY));
}
// Hidden slime block bounces by missing idle tick and 0.03
@@ -588,9 +600,9 @@ public class PredictionEngine {
box.maxZ += sneaking.getSneakingPotentialHiddenVelocity().maxZ;
if (player.uncertaintyHandler.fireworksBox != null) {
double minXdiff = Math.min(0, player.uncertaintyHandler.fireworksBox.minX - originalVec.vector.getX());
double minYdiff = Math.min(0, player.uncertaintyHandler.fireworksBox.minY - originalVec.vector.getY());
double minZdiff = Math.min(0, player.uncertaintyHandler.fireworksBox.minZ - originalVec.vector.getZ());
double minXdiff = min(0, player.uncertaintyHandler.fireworksBox.minX - originalVec.vector.getX());
double minYdiff = min(0, player.uncertaintyHandler.fireworksBox.minY - originalVec.vector.getY());
double minZdiff = min(0, player.uncertaintyHandler.fireworksBox.minZ - originalVec.vector.getZ());
double maxXdiff = Math.max(0, player.uncertaintyHandler.fireworksBox.maxX - originalVec.vector.getX());
double maxYdiff = Math.max(0, player.uncertaintyHandler.fireworksBox.maxY - originalVec.vector.getY());
double maxZdiff = Math.max(0, player.uncertaintyHandler.fireworksBox.maxZ - originalVec.vector.getZ());
@@ -646,7 +658,7 @@ public class PredictionEngine {
//
// Or the player is switching in and out of controlling a vehicle, in which friction messes it up
//
if (player.uncertaintyHandler.lastVehicleSwitch.hasOccurredSince(0) || player.uncertaintyHandler.lastHardCollidingLerpingEntity.hasOccurredSince(3) || (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13) && vector.vector.getY() > 0 && vector.isZeroPointZeroThree() && !Collisions.isEmpty(player, GetBoundingBox.getBoundingBoxFromPosAndSize(player, player.lastX, vector.vector.getY() + player.lastY + 0.6, player.lastZ, 0.6f, 1.26f)))) {
if (player.uncertaintyHandler.lastVehicleSwitch.hasOccurredSince(0) || player.uncertaintyHandler.lastHardCollidingLerpingEntity.hasOccurredSince(3) || (player.getClientVersion().isNewerThanOrEquals(V_1_13) && vector.vector.getY() > 0 && vector.isZeroPointZeroThree() && !isEmpty(player, getBoundingBoxFromPosAndSize(player, player.lastX, vector.vector.getY() + player.lastY + 0.6, player.lastZ, 0.6f, 1.26f)))) {
box.expandToAbsoluteCoordinates(0, 0, 0);
}
@@ -658,9 +670,9 @@ public class PredictionEngine {
if (player.wasTouchingWater) trueFriction = 0.96;
double maxY = Math.max(box.maxY, box.maxY + ((box.maxY - player.gravity) * 0.91));
double minY = Math.min(box.minY, box.minY + ((box.minY - player.gravity) * 0.91));
double minX = Math.min(box.minX, box.minX + (-player.speed * trueFriction));
double minZ = Math.min(box.minZ, box.minZ + (-player.speed * trueFriction));
double minY = min(box.minY, box.minY + ((box.minY - player.gravity) * 0.91));
double minX = min(box.minX, box.minX + (-player.speed * trueFriction));
double minZ = min(box.minZ, box.minZ + (-player.speed * trueFriction));
double maxX = Math.max(box.maxX, box.maxX + (player.speed * trueFriction));
double maxZ = Math.max(box.maxZ, box.maxZ + (player.speed * trueFriction));
@@ -676,15 +688,15 @@ public class PredictionEngine {
maxVector = box.max();
if (pistonX != 0) {
minVector.setX(Math.min(minVector.getX() - pistonX, pistonX));
minVector.setX(min(minVector.getX() - pistonX, pistonX));
maxVector.setX(Math.max(maxVector.getX() + pistonX, pistonX));
}
if (pistonY != 0) {
minVector.setY(Math.min(minVector.getY() - pistonY, pistonY));
minVector.setY(min(minVector.getY() - pistonY, pistonY));
maxVector.setY(Math.max(maxVector.getY() + pistonY, pistonY));
}
if (pistonZ != 0) {
minVector.setZ(Math.min(minVector.getZ() - pistonZ, pistonZ));
minVector.setZ(min(minVector.getZ() - pistonZ, pistonZ));
maxVector.setZ(Math.max(maxVector.getZ() + pistonZ, pistonZ));
}
return VectorUtils.cutBoxToVector(targetVec, minVector, maxVector);
@@ -700,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
@@ -712,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(possibleLastTickOutput.vector.clone().add(getMovementResultFromInput(player, transformInputsToVector(player, new Vector(x, 0, z)), speed, player.xRot)), possibleLastTickOutput, VectorData.VectorType.InputResult);
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
@@ -781,21 +824,21 @@ public class PredictionEngine {
// This is just the vanilla equation, which accepts invalid inputs greater than 1
// We need it because of collision support when a player is using speed
public Vector getMovementResultFromInput(GrimPlayer player, Vector inputVector, float f, float f2) {
public Vector3D getMovementResultFromInput(GrimPlayer player, Vector3D inputVector, float f, float f2) {
float f3 = player.trigHandler.sin(f2 * 0.017453292f);
float f4 = player.trigHandler.cos(f2 * 0.017453292f);
double xResult = inputVector.getX() * f4 - inputVector.getZ() * f3;
double zResult = inputVector.getZ() * f4 + inputVector.getX() * f3;
return new Vector(xResult * f, 0, zResult * f);
return newVector3D(xResult * f, 0, zResult * f);
}
public Vector handleOnClimbable(Vector vector, GrimPlayer player) {
public Vector3D handleOnClimbable(Vector3D vector, GrimPlayer player) {
return vector;
}
public void doJump(GrimPlayer player, Vector vector) {
public void doJump(GrimPlayer player, Vector3D vector) {
if (!player.lastOnGround || player.onGround)
return;
@@ -3,28 +3,33 @@ package ac.grim.grimac.predictionengine.predictions;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.data.VectorData;
import ac.grim.grimac.utils.nmsutil.ReachUtils;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
import static com.github.retrooper.packetevents.protocol.attribute.Attributes.GENERIC_GRAVITY;
import static com.github.retrooper.packetevents.protocol.player.ClientVersion.V_1_18_2;
import static com.github.retrooper.packetevents.protocol.player.ClientVersion.V_1_20_5;
import static java.lang.Math.*;
public class PredictionEngineElytra extends PredictionEngine {
// Inputs have no effect on movement
@Override
public List<VectorData> applyInputsToVelocityPossibilities(GrimPlayer player, Set<VectorData> possibleVectors, float speed) {
List<VectorData> results = new ArrayList<>();
Vector currentLook = ReachUtils.getLook(player, player.xRot, player.yRot);
Vector3D currentLook = ReachUtils.getLook(player, player.xRot, player.yRot);
for (VectorData data : possibleVectors) {
Vector elytraResult = getElytraMovement(player, data.vector.clone(), currentLook).multiply(player.stuckSpeedMultiplier).multiply(new Vector(0.99F, 0.98F, 0.99F));
Vector3D elytraResult = getElytraMovement(player, data.vector.clone(), currentLook).multiply(player.stuckSpeedMultiplier).multiply(newVector3D(0.99F, 0.98F, 0.99F));
results.add(data.returnNewModified(elytraResult, VectorData.VectorType.InputResult));
// We must bruteforce Optifine ShitMath
player.trigHandler.toggleShitMath();
elytraResult = getElytraMovement(player, data.vector.clone(), ReachUtils.getLook(player, player.xRot, player.yRot)).multiply(player.stuckSpeedMultiplier).multiply(new Vector(0.99F, 0.98F, 0.99F));
elytraResult = getElytraMovement(player, data.vector.clone(), ReachUtils.getLook(player, player.xRot, player.yRot)).multiply(player.stuckSpeedMultiplier).multiply(newVector3D(0.99F, 0.98F, 0.99F));
player.trigHandler.toggleShitMath();
results.add(data.returnNewModified(elytraResult, VectorData.VectorType.InputResult));
}
@@ -32,42 +37,42 @@ public class PredictionEngineElytra extends PredictionEngine {
return results;
}
public static Vector getElytraMovement(GrimPlayer player, Vector vector, Vector lookVector) {
public static Vector3D getElytraMovement(GrimPlayer player, Vector3D vector, Vector3D lookVector) {
float yRotRadians = player.yRot * 0.017453292F;
double horizontalSqrt = Math.sqrt(lookVector.getX() * lookVector.getX() + lookVector.getZ() * lookVector.getZ());
double horizontalSqrt = sqrt(lookVector.getX() * lookVector.getX() + lookVector.getZ() * lookVector.getZ());
double horizontalLength = vector.clone().setY(0).length();
double length = lookVector.length();
// Mojang changed from using their math to using regular java math in 1.18.2 elytra movement
double vertCosRotation = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_18_2) ? Math.cos(yRotRadians) : player.trigHandler.cos(yRotRadians);
vertCosRotation = (float) (vertCosRotation * vertCosRotation * Math.min(1.0D, length / 0.4D));
double vertCosRotation = player.getClientVersion().isNewerThanOrEquals(V_1_18_2) ? cos(yRotRadians) : player.trigHandler.cos(yRotRadians);
vertCosRotation = (float) (vertCosRotation * vertCosRotation * min(1.0D, length / 0.4D));
// So we actually use the player's actual movement to get the gravity/slow falling status
// However, this is wrong with elytra movement because players can control vertical movement after gravity is calculated
// Yeah, slow falling needs a refactor in grim.
double recalculatedGravity = player.compensatedEntities.getSelf().getAttributeValue(Attributes.GENERIC_GRAVITY);
double recalculatedGravity = player.compensatedEntities.getSelf().getAttributeValue(GENERIC_GRAVITY);
if (player.clientVelocity.getY() <= 0 && player.compensatedEntities.getSlowFallingAmplifier().isPresent()) {
recalculatedGravity = player.getClientVersion().isOlderThan(ClientVersion.V_1_20_5) ? 0.01 : Math.min(recalculatedGravity, 0.01);
recalculatedGravity = player.getClientVersion().isOlderThan(V_1_20_5) ? 0.01 : min(recalculatedGravity, 0.01);
}
vector.add(new Vector(0.0D, recalculatedGravity * (-1.0D + vertCosRotation * 0.75D), 0.0D));
vector.add(newVector3D(0.0D, recalculatedGravity * (-1.0D + vertCosRotation * 0.75D), 0.0D));
double d5;
// Handle slowing the player down when falling
if (vector.getY() < 0.0D && horizontalSqrt > 0.0D) {
d5 = vector.getY() * -0.1D * vertCosRotation;
vector.add(new Vector(lookVector.getX() * d5 / horizontalSqrt, d5, lookVector.getZ() * d5 / horizontalSqrt));
vector.add(newVector3D(lookVector.getX() * d5 / horizontalSqrt, d5, lookVector.getZ() * d5 / horizontalSqrt));
}
// Handle accelerating the player when they are looking down
if (yRotRadians < 0.0F && horizontalSqrt > 0.0D) {
d5 = horizontalLength * (double) (-player.trigHandler.sin(yRotRadians)) * 0.04D;
vector.add(new Vector(-lookVector.getX() * d5 / horizontalSqrt, d5 * 3.2D, -lookVector.getZ() * d5 / horizontalSqrt));
vector.add(newVector3D(-lookVector.getX() * d5 / horizontalSqrt, d5 * 3.2D, -lookVector.getZ() * d5 / horizontalSqrt));
}
// Handle accelerating the player sideways
if (horizontalSqrt > 0) {
vector.add(new Vector((lookVector.getX() / horizontalSqrt * horizontalLength - vector.getX()) * 0.1D, 0.0D, (lookVector.getZ() / horizontalSqrt * horizontalLength - vector.getZ()) * 0.1D));
vector.add(newVector3D((lookVector.getX() / horizontalSqrt * horizontalLength - vector.getX()) * 0.1D, 0.0D, (lookVector.getZ() / horizontalSqrt * horizontalLength - vector.getZ()) * 0.1D));
}
return vector;
@@ -3,11 +3,13 @@ package ac.grim.grimac.predictionengine.predictions;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.data.VectorData;
import ac.grim.grimac.utils.math.GrimMath;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.HashSet;
import java.util.Set;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class PredictionEngineLava extends PredictionEngine {
@Override
public void addJumpsToPossibilities(GrimPlayer player, Set<VectorData> existingVelocities) {
@@ -16,11 +18,11 @@ public class PredictionEngineLava extends PredictionEngine {
double extraVelFromVertTickSkipUpwards = GrimMath.clamp(player.actualMovement.getY(), vector.vector.clone().getY(), vector.vector.clone().getY() + 0.05f);
existingVelocities.add(new VectorData(vector.vector.clone().setY(extraVelFromVertTickSkipUpwards), vector, VectorData.VectorType.Jump));
} else {
existingVelocities.add(new VectorData(vector.vector.clone().add(new Vector(0, 0.04f, 0)), vector, VectorData.VectorType.Jump));
existingVelocities.add(new VectorData(vector.vector.clone().add(newVector3D(0, 0.04f, 0)), vector, VectorData.VectorType.Jump));
}
if (player.slightlyTouchingLava && player.lastOnGround && !player.onGround) {
Vector withJump = vector.vector.clone();
Vector3D withJump = vector.vector.clone();
super.doJump(player, withJump);
existingVelocities.add(new VectorData(withJump, vector, VectorData.VectorType.Jump));
}
@@ -3,23 +3,28 @@ 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;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.potion.PotionTypes;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.HashSet;
import java.util.OptionalInt;
import java.util.Set;
import static ac.grim.grimac.utils.math.GrimMath.clamp;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
import static com.github.retrooper.packetevents.protocol.world.states.type.StateTypes.SCAFFOLDING;
import static java.lang.Math.max;
public class PredictionEngineNormal extends PredictionEngine {
public static void staticVectorEndOfTick(GrimPlayer player, Vector vector) {
public static void staticVectorEndOfTick(GrimPlayer player, Vector3D vector) {
double adjustedY = vector.getY();
final OptionalInt levitation = player.compensatedEntities.getPotionLevelForPlayer(PotionTypes.LEVITATION);
if (levitation.isPresent()) {
@@ -37,8 +42,12 @@ 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)) {
Vector jump = vector.vector.clone();
Vector3D jump = vector.vector.clone();
if (!player.isFlying) {
// Negative jump boost does not allow the player to leave the ground
@@ -52,9 +61,9 @@ public class PredictionEngineNormal extends PredictionEngine {
JumpPower.jumpFromGround(player, jump);
} else {
jump.add(new Vector(0, player.flySpeed * 3, 0));
jump.add(newVector3D(0, player.flySpeed * 3, 0));
if (!player.wasFlying) {
Vector edgeCaseJump = jump.clone();
Vector3D edgeCaseJump = jump.clone();
JumpPower.jumpFromGround(player, edgeCaseJump);
existingVelocities.add(vector.returnNewModified(edgeCaseJump, VectorData.VectorType.Jump));
}
@@ -82,7 +91,7 @@ public class PredictionEngineNormal extends PredictionEngine {
if (player.lastWasClimbing == 0 && (player.pointThreeEstimator.isNearClimbable() || player.isClimbing) && (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14)
|| !Collisions.isEmpty(player, player.boundingBox.copy().expand(
player.clientVelocity.getX(), 0, player.clientVelocity.getZ()).expand(0.5, -SimpleCollisionBox.COLLISION_EPSILON, 0.5))) || walkingOnPowderSnow) {
Vector ladderVelocity = player.clientVelocity.clone().setY(0.2);
Vector3D ladderVelocity = player.clientVelocity.clone().setY(0.2);
staticVectorEndOfTick(player, ladderVelocity);
player.lastWasClimbing = ladderVelocity.getY();
}
@@ -93,17 +102,17 @@ public class PredictionEngineNormal extends PredictionEngine {
}
@Override
public Vector handleOnClimbable(Vector vector, GrimPlayer player) {
public Vector3D handleOnClimbable(Vector3D vector, GrimPlayer player) {
if (player.isClimbing) {
// Reset fall distance when climbing
player.fallDistance = 0;
vector.setX(GrimMath.clamp(vector.getX(), -0.15F, 0.15F));
vector.setZ(GrimMath.clamp(vector.getZ(), -0.15F, 0.15F));
vector.setY(Math.max(vector.getY(), -0.15F));
vector.setX(clamp(vector.getX(), -0.15F, 0.15F));
vector.setZ(clamp(vector.getZ(), -0.15F, 0.15F));
vector.setY(max(vector.getY(), -0.15F));
// Yes, this uses shifting not crouching
if (vector.getY() < 0.0 && !(player.compensatedWorld.getStateTypeAt(player.lastX, player.lastY, player.lastZ) == StateTypes.SCAFFOLDING) && player.isSneaking && !player.isFlying) {
if (vector.getY() < 0.0 && !(player.compensatedWorld.getStateTypeAt(player.lastX, player.lastY, player.lastZ) == SCAFFOLDING) && player.isSneaking && !player.isFlying) {
vector.setY(0.0);
}
}
@@ -9,11 +9,14 @@ import ac.grim.grimac.utils.nmsutil.Collisions;
import ac.grim.grimac.utils.nmsutil.FluidFallingAdjustedMovement;
import ac.grim.grimac.utils.nmsutil.ReachUtils;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.HashSet;
import java.util.Set;
import static ac.grim.grimac.utils.data.VectorData.VectorType.SwimmingSpace;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class PredictionEngineWater extends PredictionEngine {
boolean isFalling;
double playerGravity;
@@ -21,9 +24,9 @@ public class PredictionEngineWater extends PredictionEngine {
float swimmingFriction;
double lastY;
public static void staticVectorEndOfTick(GrimPlayer player, Vector vector, float swimmingFriction, double playerGravity, boolean isFalling) {
vector.multiply(new Vector(swimmingFriction, 0.8F, swimmingFriction));
Vector fluidVector = FluidFallingAdjustedMovement.getFluidFallingAdjustedMovement(player, playerGravity, isFalling, vector);
public static void staticVectorEndOfTick(GrimPlayer player, Vector3D vector, float swimmingFriction, double playerGravity, boolean isFalling) {
vector.multiply(newVector3D(swimmingFriction, 0.8F, swimmingFriction));
Vector3D fluidVector = FluidFallingAdjustedMovement.getFluidFallingAdjustedMovement(player, playerGravity, isFalling, vector);
vector.setX(fluidVector.getX());
vector.setY(fluidVector.getY());
vector.setZ(fluidVector.getZ());
@@ -47,7 +50,7 @@ public class PredictionEngineWater extends PredictionEngine {
double scalar = lookYAmount < -0.2 ? 0.085 : 0.06;
// The player can always press jump and activate this
swimmingVelocities.add(vector.returnNewModified(new Vector(vector.vector.getX(), vector.vector.getY() + ((lookYAmount - vector.vector.getY()) * scalar), vector.vector.getZ()), VectorData.VectorType.SwimmingSpace));
swimmingVelocities.add(vector.returnNewModified(newVector3D(vector.vector.getX(), vector.vector.getY() + ((lookYAmount - vector.vector.getY()) * scalar), vector.vector.getZ()), SwimmingSpace));
// This scenario will occur if the player does not press jump and the other conditions are met
// Theoretically we should check this BEFORE allowing no look, but there isn't a cheat that takes advantage of this yet
@@ -80,11 +83,11 @@ public class PredictionEngineWater extends PredictionEngine {
double extraVelFromVertTickSkipUpwards = GrimMath.clamp(player.actualMovement.getY(), vector.vector.clone().getY(), vector.vector.clone().getY() + 0.05f);
existingVelocities.add(new VectorData(vector.vector.clone().setY(extraVelFromVertTickSkipUpwards), vector, VectorData.VectorType.Jump));
} else {
existingVelocities.add(new VectorData(vector.vector.clone().add(new Vector(0, 0.04f, 0)), vector, VectorData.VectorType.Jump));
existingVelocities.add(new VectorData(vector.vector.clone().add(newVector3D(0, 0.04f, 0)), vector, VectorData.VectorType.Jump));
}
if (player.slightlyTouchingWater && player.lastOnGround && !player.onGround) {
Vector withJump = vector.vector.clone();
Vector3D withJump = vector.vector.clone();
super.doJump(player, withJump);
existingVelocities.add(new VectorData(withJump, vector, VectorData.VectorType.Jump));
}
@@ -2,11 +2,15 @@ package ac.grim.grimac.predictionengine.predictions;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.data.VectorData;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.HashSet;
import java.util.Set;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
import static java.lang.Math.sqrt;
public class PredictionEngineWaterLegacy extends PredictionEngine {
double playerGravity;
float swimmingSpeed;
@@ -23,11 +27,11 @@ public class PredictionEngineWaterLegacy extends PredictionEngine {
// This is just the vanilla equation for legacy water movement
@Override
public Vector getMovementResultFromInput(GrimPlayer player, Vector inputVector, float f, float f2) {
public Vector3D getMovementResultFromInput(GrimPlayer player, Vector3D inputVector, float f, float f2) {
float lengthSquared = (float) inputVector.lengthSquared();
if (lengthSquared >= 1.0E-4F) {
lengthSquared = (float) Math.sqrt(lengthSquared);
lengthSquared = (float) sqrt(lengthSquared);
if (lengthSquared < 1.0F) {
lengthSquared = 1.0F;
@@ -38,21 +42,20 @@ public class PredictionEngineWaterLegacy extends PredictionEngine {
float sinResult = player.trigHandler.sin(player.xRot * 0.017453292F);
float cosResult = player.trigHandler.cos(player.xRot * 0.017453292F);
return new Vector(inputVector.getX() * cosResult - inputVector.getZ() * sinResult,
inputVector.getY(), inputVector.getZ() * cosResult + inputVector.getX() * sinResult);
return newVector3D(inputVector.getX() * cosResult - inputVector.getZ() * sinResult, inputVector.getY(), inputVector.getZ() * cosResult + inputVector.getX() * sinResult);
}
return new Vector();
return newVector3D();
}
@Override
public void addJumpsToPossibilities(GrimPlayer player, Set<VectorData> existingVelocities) {
for (VectorData vector : new HashSet<>(existingVelocities)) {
existingVelocities.add(new VectorData(vector.vector.clone().add(new Vector(0, 0.04f, 0)), vector, VectorData.VectorType.Jump));
existingVelocities.add(new VectorData(vector.vector.clone().add(newVector3D(0, 0.04f, 0)), vector, VectorData.VectorType.Jump));
if (player.skippedTickInActualMovement) {
existingVelocities.add(new VectorData(vector.vector.clone().add(new Vector(0, 0.02f, 0)), vector, VectorData.VectorType.Jump));
existingVelocities.add(new VectorData(vector.vector.clone().add(newVector3D(0, 0.02f, 0)), vector, VectorData.VectorType.Jump));
}
}
}
@@ -62,7 +65,7 @@ public class PredictionEngineWaterLegacy extends PredictionEngine {
super.endOfTick(player, playerGravity);
for (VectorData vector : player.getPossibleVelocitiesMinusKnockback()) {
vector.vector.multiply(new Vector(swimmingFriction, 0.8F, swimmingFriction));
vector.vector.multiply(newVector3D(swimmingFriction, 0.8F, swimmingFriction));
// Gravity
vector.vector.setY(vector.vector.getY() - 0.02D);
@@ -13,12 +13,15 @@ import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
import static java.lang.Math.PI;
public class BoatPredictionEngine extends PredictionEngine {
public BoatPredictionEngine(GrimPlayer player) {
player.uncertaintyHandler.collidingEntities.add(0); // We don't do collisions like living entities
@@ -147,7 +150,7 @@ public class BoatPredictionEngine extends PredictionEngine {
// so if a player tries to move in both directions, a packet will
// show that the player is staying, but the boat will move anyway
if (player.vehicleData.vehicleForward == 0) {
Vector vector = data.vector.clone();
Vector3D vector = data.vector.clone();
controlBoat(player, vector, true);
vector.multiply(player.stuckSpeedMultiplier);
vectors.add(data.returnNewModified(vector, VectorData.VectorType.InputResult));
@@ -184,7 +187,7 @@ public class BoatPredictionEngine extends PredictionEngine {
return false;
}
private void floatBoat(GrimPlayer player, Vector vector) {
private void floatBoat(GrimPlayer player, Vector3D vector) {
double d1 = player.hasGravity ? -0.04f : 0;
double d2 = 0.0D;
float invFriction = 0.05F;
@@ -194,7 +197,7 @@ public class BoatPredictionEngine extends PredictionEngine {
player.lastY = getWaterLevelAbove(player) - 0.5625F + 0.101D;
player.boundingBox = GetBoundingBox.getCollisionBoxForPlayer(player, player.lastX, player.lastY, player.lastZ);
player.actualMovement = new Vector(player.x - player.lastX, player.y - player.lastY, player.z - player.lastZ);
player.actualMovement = newVector3D(player.x - player.lastX, player.y - player.lastY, player.z - player.lastZ);
vector.setY(0);
player.vehicleData.lastYd = 0.0D;
@@ -260,7 +263,7 @@ public class BoatPredictionEngine extends PredictionEngine {
return (float) (l + 1);
}
private void controlBoat(GrimPlayer player, Vector vector, boolean intermediate) {
private void controlBoat(GrimPlayer player, Vector3D vector, boolean intermediate) {
float f = 0.0F;
if (player.vehicleData.vehicleHorizontal != 0 && (!intermediate && player.vehicleData.vehicleForward == 0)) {
f += 0.005F;
@@ -275,6 +278,6 @@ public class BoatPredictionEngine extends PredictionEngine {
f -= 0.005F;
}
vector.add(new Vector(player.trigHandler.sin(-player.xRot * ((float) Math.PI / 180F)) * f, 0, (double) (player.trigHandler.cos(player.xRot * ((float) Math.PI / 180F)) * f)));
vector.add(newVector3D(player.trigHandler.sin(-player.xRot * ((float) PI / 180F)) * f, 0, (double) (player.trigHandler.cos(player.xRot * ((float) PI / 180F)) * f)));
}
}
@@ -3,16 +3,16 @@ package ac.grim.grimac.predictionengine.predictions.rideable;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.predictionengine.predictions.PredictionEngineLava;
import ac.grim.grimac.utils.data.VectorData;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.List;
import java.util.Set;
public class PredictionEngineRideableLava extends PredictionEngineLava {
Vector movementVector;
Vector3D movementVector;
public PredictionEngineRideableLava(Vector movementVector) {
public PredictionEngineRideableLava(Vector3D movementVector) {
this.movementVector = movementVector;
}
@@ -3,16 +3,16 @@ package ac.grim.grimac.predictionengine.predictions.rideable;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.predictionengine.predictions.PredictionEngineNormal;
import ac.grim.grimac.utils.data.VectorData;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.List;
import java.util.Set;
public class PredictionEngineRideableNormal extends PredictionEngineNormal {
Vector movementVector;
Vector3D movementVector;
public PredictionEngineRideableNormal(Vector movementVector) {
public PredictionEngineRideableNormal(Vector3D movementVector) {
this.movementVector = movementVector;
}
@@ -8,13 +8,15 @@ import ac.grim.grimac.utils.data.packetentity.PacketEntityHorse;
import ac.grim.grimac.utils.nmsutil.JumpPower;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.potion.PotionTypes;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.ArrayList;
import java.util.List;
import java.util.OptionalInt;
import java.util.Set;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class PredictionEngineRideableUtils {
public static Set<VectorData> handleJumps(GrimPlayer player, Set<VectorData> possibleVectors) {
if (!(player.compensatedEntities.getSelf().getRiding() instanceof PacketEntityHorse horse)) return possibleVectors;
@@ -55,7 +57,7 @@ public class PredictionEngineRideableUtils {
for (VectorData vectorData : possibleVectors) {
vectorData.vector.setY(d1);
if (f1 > 0.0F) {
vectorData.vector.add(new Vector(-0.4F * f2 * player.vehicleData.horseJump, 0.0D, 0.4F * f3 * player.vehicleData.horseJump));
vectorData.vector.add(newVector3D(-0.4F * f2 * player.vehicleData.horseJump, 0.0D, 0.4F * f3 * player.vehicleData.horseJump));
}
}
@@ -71,7 +73,7 @@ public class PredictionEngineRideableUtils {
return possibleVectors;
}
public static List<VectorData> applyInputsToVelocityPossibilities(Vector movementVector, GrimPlayer player, Set<VectorData> possibleVectors, float speed) {
public static List<VectorData> applyInputsToVelocityPossibilities(Vector3D movementVector, GrimPlayer player, Set<VectorData> possibleVectors, float speed) {
List<VectorData> returnVectors = new ArrayList<>();
for (VectorData possibleLastTickOutput : possibleVectors) {
@@ -3,16 +3,16 @@ package ac.grim.grimac.predictionengine.predictions.rideable;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.predictionengine.predictions.PredictionEngineWater;
import ac.grim.grimac.utils.data.VectorData;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.List;
import java.util.Set;
public class PredictionEngineRideableWater extends PredictionEngineWater {
Vector movementVector;
Vector3D movementVector;
public PredictionEngineRideableWater(Vector movementVector) {
public PredictionEngineRideableWater(Vector3D movementVector) {
this.movementVector = movementVector;
}
@@ -3,15 +3,15 @@ package ac.grim.grimac.predictionengine.predictions.rideable;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.predictionengine.predictions.PredictionEngineWaterLegacy;
import ac.grim.grimac.utils.data.VectorData;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.List;
import java.util.Set;
public class PredictionEngineRideableWaterLegacy extends PredictionEngineWaterLegacy {
Vector movementVector;
Vector3D movementVector;
public PredictionEngineRideableWaterLegacy(Vector movementVector) {
public PredictionEngineRideableWaterLegacy(Vector3D movementVector) {
this.movementVector = movementVector;
}
@@ -0,0 +1,34 @@
package ac.grim.grimac.utils.anticheat.update;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.util.Vector3i;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
import lombok.Getter;
public class BlockBreak {
public final Vector3i position;
public final BlockFace face;
public final DiggingAction action;
@Getter
private boolean cancelled;
public final WrappedBlockState block;
public BlockBreak(WrapperPlayClientPlayerDigging packet, GrimPlayer player) {
this(packet.getBlockPosition(), packet.getBlockFace(), packet.getAction(), player.compensatedWorld.getWrappedBlockStateAt(packet.getBlockPosition()));
}
public BlockBreak(Vector3i position, BlockFace face, DiggingAction action, WrappedBlockState block) {
this.position = position;
this.face = face;
this.action = action;
this.block = block;
}
public void cancel() {
this.cancelled = true;
}
}
@@ -16,7 +16,7 @@ import ac.grim.grimac.utils.math.GrimMath;
import ac.grim.grimac.utils.nmsutil.BoundingBoxSize;
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
import ac.grim.grimac.utils.nmsutil.Materials;
import ac.grim.grimac.utils.nmsutil.ReachUtils;
import ac.grim.grimac.utils.vector.Vector3D;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
@@ -35,18 +35,23 @@ import com.github.retrooper.packetevents.util.Vector3f;
import com.github.retrooper.packetevents.util.Vector3i;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
import static ac.grim.grimac.utils.nmsutil.ReachUtils.calculateIntercept;
import static ac.grim.grimac.utils.nmsutil.ReachUtils.getLook;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
import static com.github.retrooper.packetevents.protocol.attribute.Attributes.PLAYER_BLOCK_INTERACTION_RANGE;
public class BlockPlace {
protected static final BlockFace[] UPDATE_SHAPE_ORDER = new BlockFace[]{BlockFace.WEST, BlockFace.EAST, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.DOWN, BlockFace.UP};
private static final BlockFace[] BY_2D = new BlockFace[]{BlockFace.SOUTH, BlockFace.WEST, BlockFace.NORTH, BlockFace.EAST};
static final BlockFace[] BY_3D = new BlockFace[]{BlockFace.DOWN, BlockFace.UP, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.WEST, BlockFace.EAST};
@Setter
Vector3i blockPosition;
public Vector3i blockPosition;
@Getter
InteractionHand hand;
@Getter
@@ -661,18 +666,18 @@ public class BlockPlace {
// You also have the desync caused by eye height as apparently tracking the player's ticks wasn't important to you
// No mojang, you really do need to track client ticks to get their accurate eye height.
// another damn desync added... maybe next decade it will get fixed and double the amount of issues.
public Vector getClickedLocation() {
public Vector3D getClickedLocation() {
SimpleCollisionBox box = new SimpleCollisionBox(getPlacedAgainstBlockLocation());
Vector look = ReachUtils.getLook(player, player.xRot, player.yRot);
Vector3D look = getLook(player, player.xRot, player.yRot);
final double distance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_BLOCK_INTERACTION_RANGE) + 3;
Vector eyePos = new Vector(player.x, player.y + player.getEyeHeight(), player.z);
Vector endReachPos = eyePos.clone().add(new Vector(look.getX() * distance, look.getY() * distance, look.getZ() * distance));
Vector intercept = ReachUtils.calculateIntercept(box, eyePos, endReachPos).first();
final double distance = player.compensatedEntities.getSelf().getAttributeValue(PLAYER_BLOCK_INTERACTION_RANGE) + 3;
Vector3D eyePos = newVector3D(player.x, player.y + player.getEyeHeight(), player.z);
Vector3D endReachPos = eyePos.clone().add(newVector3D(look.getX() * distance, look.getY() * distance, look.getZ() * distance));
Vector3D intercept = calculateIntercept(box, eyePos, endReachPos).first();
// Bring this back to relative to the block
// The player didn't even click the block... (we should force resync BEFORE we get here!)
if (intercept == null) return new Vector();
if (intercept == null) return newVector3D();
intercept.setX(intercept.getX() - box.minX);
intercept.setY(intercept.getY() - box.minY);
@@ -21,11 +21,13 @@ import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.protocol.world.states.type.StateValue;
import com.github.retrooper.packetevents.util.Vector3i;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.*;
import java.util.stream.Collectors;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public enum BlockPlaceResult {
// If the block only has directional data
@@ -80,7 +82,7 @@ public enum BlockPlaceResult {
}, ItemTypes.SNOW),
SLAB((player, place) -> {
Vector clickedPos = place.getClickedLocation();
Vector3D clickedPos = place.getClickedLocation();
WrappedBlockState slabData = place.getMaterial().createBlockState(CompensatedWorld.blockVersion);
WrappedBlockState existing = place.getExistingBlockData();
@@ -909,7 +911,7 @@ public enum BlockPlaceResult {
WrappedBlockState ccwState = place.getDirectionalState(ccw);
CollisionBox ccwBox = CollisionData.getData(ccwState.getType()).getMovementCollisionBox(player, player.getClientVersion(), ccwState);
Vector aboveCCWPos = place.getClickedLocation().add(new Vector(ccw.getModX(), ccw.getModY(), ccw.getModZ())).add(new Vector(0, 1, 0));
Vector3D aboveCCWPos = place.getClickedLocation().add(newVector3D(ccw.getModX(), ccw.getModY(), ccw.getModZ())).add(newVector3D(0, 1, 0));
WrappedBlockState aboveCCWState = player.compensatedWorld.getWrappedBlockStateAt(aboveCCWPos);
CollisionBox aboveCCWBox = CollisionData.getData(aboveCCWState.getType()).getMovementCollisionBox(player, player.getClientVersion(), aboveCCWState);
@@ -917,7 +919,7 @@ public enum BlockPlaceResult {
WrappedBlockState cwState = place.getDirectionalState(cw);
CollisionBox cwBox = CollisionData.getData(cwState.getType()).getMovementCollisionBox(player, player.getClientVersion(), cwState);
Vector aboveCWPos = place.getClickedLocation().add(new Vector(cw.getModX(), cw.getModY(), cw.getModZ())).add(new Vector(0, 1, 0));
Vector3D aboveCWPos = place.getClickedLocation().add(newVector3D(cw.getModX(), cw.getModY(), cw.getModZ())).add(newVector3D(0, 1, 0));
WrappedBlockState aboveCWState = player.compensatedWorld.getWrappedBlockStateAt(aboveCWPos);
CollisionBox aboveCWBox = CollisionData.getData(aboveCWState.getType()).getMovementCollisionBox(player, player.getClientVersion(), aboveCWState);
@@ -934,7 +936,7 @@ public enum BlockPlaceResult {
if ((!isCWLower || isCCWLower) && i >= 0) {
int j = playerFacing.getModX();
int k = playerFacing.getModZ();
Vector vec3 = place.getClickedLocation();
Vector3D vec3 = place.getClickedLocation();
double d0 = vec3.getX();
double d1 = vec3.getY();
hinge = (j >= 0 || d1 >= 0.5D) && (j <= 0 || d1 <= 0.5D) && (k >= 0 || d0 <= 0.5D) && (k <= 0 || d0 >= 0.5D) ? Hinge.LEFT : Hinge.RIGHT;
@@ -1,7 +1,8 @@
package ac.grim.grimac.utils.blockstate.helper;
import ac.grim.grimac.utils.vector.Vector3D;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
public class BlockFaceHelper {
public static boolean isFaceVertical(BlockFace face) {
@@ -56,7 +57,7 @@ public class BlockFaceHelper {
};
}
public Vector offset(Vector toOffset, BlockFace face) {
public Vector3D offset(Vector3D toOffset, BlockFace face) {
toOffset.setX(toOffset.getX() + face.getModX());
toOffset.setY(toOffset.getY() + face.getModY());
toOffset.setZ(toOffset.getZ() + face.getModZ());
@@ -680,7 +680,7 @@ public enum CollisionData {
}, StateTypes.END_ROD, StateTypes.LIGHTNING_ROD),
CAULDRON((player, version, data, x, y, z) -> {
if (version.isNewerThan(ClientVersion.getById(467))) { // changed in 19w13a, 1.14 Snapshot
if (version.isNewerThan(ClientVersion.V_1_13_2)) { // changed in 19w13a, 1.14 Snapshot
return new ComplexCollisionBox(15,
new SimpleCollisionBox(0.0, 0.0, 0.0, 0.125, 1.0, 0.25, false),
new SimpleCollisionBox(0.0, 0.0, 0.75, 0.125, 1.0, 1.0, false),
@@ -16,7 +16,6 @@ import com.github.retrooper.packetevents.protocol.world.states.enums.*;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
public class DynamicHitboxFence extends DynamicConnecting implements HitBoxFactory {
private static final CollisionBox[] MODERN_HITBOXES = makeShapes(2.0F, 2.0F, 24.0F, 0.0F, 24.0F, true, 1);
@@ -1,6 +1,9 @@
package ac.grim.grimac.utils.collisions.datatypes;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import ac.grim.grimac.utils.vector.Vector3D;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class BoundingBox {
@@ -15,7 +18,7 @@ public class BoundingBox {
this.maxZ = maxZ;
}
public BoundingBox(Vector min, Vector max) {
public BoundingBox(Vector3D min, Vector3D max) {
this.minX = (float) Math.min(min.getX(), max.getX());
this.minY = (float) Math.min(min.getY(), max.getY());
this.minZ = (float) Math.min(min.getZ(), max.getZ());
@@ -44,7 +47,7 @@ public class BoundingBox {
return new BoundingBox(newMinX, newMinY, newMinZ, newMaxX, newMaxY, newMaxZ);
}
public BoundingBox add(Vector vector) {
public BoundingBox add(Vector3D vector) {
float x = (float) vector.getX(), y = (float) vector.getY(), z = (float) vector.getZ();
float newMinX = minX + x;
@@ -87,27 +90,27 @@ public class BoundingBox {
return new BoundingBox(this.minX - minX, this.minY - minY, this.minZ - minZ, this.maxX - maxX, this.maxY - maxY, this.maxZ - maxZ);
}
public boolean intersectsWithBox(Vector vector) {
public boolean intersectsWithBox(Vector3D vector) {
return (vector.getX() > this.minX && vector.getX() < this.maxX) && ((vector.getY() > this.minY && vector.getY() < this.maxY) && (vector.getZ() > this.minZ && vector.getZ() < this.maxZ));
}
public Vector getMinimum() {
return new Vector(minX, minY, minZ);
public Vector3D getMinimum() {
return newVector3D(minX, minY, minZ);
}
public Vector getMaximum() {
return new Vector(maxX, maxY, maxZ);
public Vector3D getMaximum() {
return newVector3D(maxX, maxY, maxZ);
}
public boolean collides(Vector vector) {
public boolean collides(Vector3D vector) {
return (vector.getX() >= this.minX && vector.getX() <= this.maxX) && ((vector.getY() >= this.minY && vector.getY() <= this.maxY) && (vector.getZ() >= this.minZ && vector.getZ() <= this.maxZ));
}
public boolean collidesHorizontally(Vector vector) {
public boolean collidesHorizontally(Vector3D vector) {
return (vector.getX() >= this.minX && vector.getX() <= this.maxX) && ((vector.getY() > this.minY && vector.getY() < this.maxY) && (vector.getZ() >= this.minZ && vector.getZ() <= this.maxZ));
}
public boolean collidesVertically(Vector vector) {
public boolean collidesVertically(Vector3D vector) {
return (vector.getX() > this.minX && vector.getX() < this.maxX) && ((vector.getY() >= this.minY && vector.getY() <= this.maxY) && (vector.getZ() > this.minZ && vector.getZ() < this.maxZ));
}
@@ -20,8 +20,8 @@ public class ComplexCollisionBox implements CollisionBox {
public ComplexCollisionBox(int maxIndex, SimpleCollisionBox... boxes) {
this.boxes = new SimpleCollisionBox[maxIndex];
System.arraycopy(boxes, 0, this.boxes, 0, Math.min(maxIndex, boxes.length));
currentLength = boxes.length;
currentLength = Math.min(maxIndex, boxes.length);
System.arraycopy(boxes, 0, this.boxes, 0, this.currentLength);
}
public boolean add(SimpleCollisionBox collisionBox) {
@@ -1,6 +1,7 @@
package ac.grim.grimac.utils.collisions.datatypes;
import ac.grim.grimac.utils.nmsutil.Ray;
import ac.grim.grimac.utils.vector.Vector3D;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.util.Vector3i;
@@ -8,10 +9,12 @@ import it.unimi.dsi.fastutil.doubles.AbstractDoubleList;
import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
import it.unimi.dsi.fastutil.doubles.DoubleList;
import org.bukkit.Location;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.List;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class SimpleCollisionBox implements CollisionBox {
public static final double COLLISION_EPSILON = 1.0E-7;
@@ -46,7 +49,7 @@ public class SimpleCollisionBox implements CollisionBox {
isFullBlock = fullBlock;
}
public SimpleCollisionBox(Vector min, Vector max) {
public SimpleCollisionBox(Vector3D min, Vector3D max) {
this(min.getX(), min.getY(), min.getZ(), max.getX(), max.getY(), max.getZ());
}
@@ -85,10 +88,10 @@ public class SimpleCollisionBox implements CollisionBox {
}
public SimpleCollisionBox(Location loc, double width, double height) {
this(loc.toVector(), width, height);
this(newVector3D(loc.getX(), loc.getY(), loc.getZ()), width, height);
}
public SimpleCollisionBox(Vector vec, double width, double height) {
public SimpleCollisionBox(Vector3D vec, double width, double height) {
this(vec.getX(), vec.getY(), vec.getZ(), vec.getX(), vec.getY(), vec.getZ());
expand(width / 2, 0, width / 2);
@@ -151,16 +154,16 @@ public class SimpleCollisionBox implements CollisionBox {
return this;
}
public Vector[] corners() {
Vector[] vectors = new Vector[8];
vectors[0] = new Vector(minX, minY, minZ);
vectors[1] = new Vector(minX, minY, maxZ);
vectors[2] = new Vector(maxX, minY, minZ);
vectors[3] = new Vector(maxX, minY, maxZ);
vectors[4] = new Vector(minX, maxY, minZ);
vectors[5] = new Vector(minX, maxY, maxZ);
vectors[6] = new Vector(maxX, maxY, minZ);
vectors[7] = new Vector(maxX, maxY, maxZ);
public Vector3D[] corners() {
Vector3D[] vectors = new Vector3D[8];
vectors[0] = newVector3D(minX, minY, minZ);
vectors[1] = newVector3D(minX, minY, maxZ);
vectors[2] = newVector3D(maxX, minY, minZ);
vectors[3] = newVector3D(maxX, minY, maxZ);
vectors[4] = newVector3D(minX, maxY, minZ);
vectors[5] = newVector3D(minX, maxY, maxZ);
vectors[6] = newVector3D(maxX, maxY, minZ);
vectors[7] = newVector3D(maxX, maxY, maxZ);
return vectors;
}
@@ -408,14 +411,14 @@ public class SimpleCollisionBox implements CollisionBox {
*/
// Copied from hawk lol
// I would like to point out that this is magic to me and I have not attempted to understand this code
public Vector intersectsRay(Ray ray, float minDist, float maxDist) {
Vector invDir = new Vector(1f / ray.calculateDirection().getX(), 1f / ray.calculateDirection().getY(), 1f / ray.calculateDirection().getZ());
public Vector3D intersectsRay(Ray ray, float minDist, float maxDist) {
Vector3D invDir = newVector3D(1f / ray.calculateDirection().getX(), 1f / ray.calculateDirection().getY(), 1f / ray.calculateDirection().getZ());
boolean signDirX = invDir.getX() < 0;
boolean signDirY = invDir.getY() < 0;
boolean signDirZ = invDir.getZ() < 0;
Vector bbox = signDirX ? max() : min();
Vector3D bbox = signDirX ? max() : min();
double tmin = (bbox.getX() - ray.getOrigin().getX()) * invDir.getX();
bbox = signDirX ? min() : max();
double tmax = (bbox.getX() - ray.getOrigin().getX()) * invDir.getX();
@@ -454,12 +457,12 @@ public class SimpleCollisionBox implements CollisionBox {
return null;
}
public Vector max() {
return new Vector(maxX, maxY, maxZ);
public Vector3D max() {
return newVector3D(maxX, maxY, maxZ);
}
public Vector min() {
return new Vector(minX, minY, minZ);
public Vector3D min() {
return newVector3D(minX, minY, minZ);
}
public DoubleList getYPointPositions() {
@@ -1,22 +1,22 @@
package ac.grim.grimac.utils.data;
import ac.grim.grimac.utils.vector.Vector3D;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.util.Vector3i;
import lombok.Getter;
import lombok.ToString;
import org.bukkit.util.Vector;
@Getter
@ToString
public class HitData {
Vector3i position;
Vector blockHitLocation;
Vector3D blockHitLocation;
WrappedBlockState state;
BlockFace closestDirection;
public HitData(Vector3i position, Vector blockHitLocation, BlockFace closestDirection, WrappedBlockState state) {
public HitData(Vector3i position, Vector3D blockHitLocation, BlockFace closestDirection, WrappedBlockState state) {
this.position = position;
this.blockHitLocation = blockHitLocation;
this.closestDirection = closestDirection;
@@ -0,0 +1,4 @@
package ac.grim.grimac.utils.data;
public record KnownInput(boolean forward, boolean backward, boolean left, boolean right, boolean jump, boolean shift, boolean sprint) {
}
@@ -24,6 +24,9 @@ public class PacketStateData {
// This works on 1.8 only
public boolean didLastLastMovementIncludePosition = false;
public boolean didLastMovementIncludePosition = false;
// This works on 1.21.2+ only
public boolean didSendMovementBeforeTickEnd = false;
public KnownInput knownInput = new KnownInput(false, false, false, false, false, false, false);
public Vector3d lastClaimedPosition = new Vector3d(0, 0, 0);
public float lastHealth, lastSaturation;
@@ -18,20 +18,4 @@
package ac.grim.grimac.utils.data;
import java.util.Objects;
public record Pair<A, B>(A first, B second) {
public static <T, K> Pair<T, K> of(T a, K b) {
return new Pair<>(a, b);
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Pair)) {
return false;
}
Pair b = (Pair) o;
return Objects.equals(this.first, b.first) && Objects.equals(this.second, b.second);
}
}
public record Pair<A, B>(A first, B second) {}
@@ -3,7 +3,7 @@ package ac.grim.grimac.utils.data;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
@Getter
@Setter
@@ -11,14 +11,14 @@ import org.bukkit.util.Vector;
public class SetBackData {
TeleportData teleportData;
float xRot, yRot;
Vector velocity;
Vector3D velocity;
boolean vehicle;
boolean isComplete = false;
// TODO: Rethink when we block movements for teleports, perhaps after 10 ticks or 5 blocks?
boolean isPlugin = false;
int ticksComplete = 0;
public SetBackData(TeleportData teleportData, float xRot, float yRot, Vector velocity, boolean vehicle, boolean isPlugin) {
public SetBackData(TeleportData teleportData, float xRot, float yRot, Vector3D velocity, boolean vehicle, boolean isPlugin) {
this.teleportData = teleportData;
this.xRot = xRot;
this.yRot = yRot;
@@ -1,7 +1,7 @@
package ac.grim.grimac.utils.data;
import lombok.Getter;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.Objects;
@@ -9,13 +9,13 @@ public class VectorData {
public VectorType vectorType;
public VectorData lastVector;
public VectorData preUncertainty;
public Vector vector;
public Vector3D vector;
@Getter
private boolean isKnockback, firstBreadKb, isExplosion, firstBreadExplosion, isTrident, isZeroPointZeroThree, isSwimHop, isFlipSneaking, isFlipItem, isJump, isAttackSlow = false;
// For handling replacing the type of vector it is while keeping data
public VectorData(Vector vector, VectorData lastVector, VectorType vectorType) {
public VectorData(Vector3D vector, VectorData lastVector, VectorType vectorType) {
this.vector = vector;
this.lastVector = lastVector;
this.vectorType = vectorType;
@@ -38,7 +38,7 @@ public class VectorData {
addVectorType(vectorType);
}
public VectorData(Vector vector, VectorType vectorType) {
public VectorData(Vector3D vector, VectorType vectorType) {
this.vector = vector;
this.vectorType = vectorType;
addVectorType(vectorType);
@@ -48,7 +48,7 @@ public class VectorData {
return new VectorData(vector, this, type);
}
public VectorData returnNewModified(Vector newVec, VectorType type) {
public VectorData returnNewModified(Vector3D newVec, VectorType type) {
return new VectorData(newVec, this, type);
}
@@ -65,7 +65,7 @@ public class VectorData {
return Objects.hash(vectorType, lastVector, preUncertainty, vector, isKnockback, firstBreadKb, isExplosion, firstBreadExplosion, isTrident, isZeroPointZeroThree, isSwimHop, isFlipSneaking, isFlipItem, isJump, isAttackSlow);
}
private void addVectorType(VectorType type) {
protected void addVectorType(VectorType type) {
switch (type) {
case Knockback:
isKnockback = true;
@@ -1,15 +1,15 @@
package ac.grim.grimac.utils.data;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
public class VelocityData {
public final Vector vector;
public final Vector3D vector;
public final int entityID;
public final int transaction;
public double offset = Integer.MAX_VALUE;
public boolean isSetback;
public VelocityData(int entityID, int transaction, boolean isSetback, Vector vector) {
public VelocityData(int entityID, int transaction, boolean isSetback, Vector3D vector) {
this.entityID = entityID;
this.vector = vector;
this.transaction = transaction;
@@ -17,7 +17,7 @@ public class VelocityData {
}
// First bread last tick -> Required this tick = don't require kb twice
public VelocityData(int entityID, int transaction, Vector vector, boolean isSetback, double offset) {
public VelocityData(int entityID, int transaction, Vector3D vector, boolean isSetback, double offset) {
this.entityID = entityID;
this.vector = vector;
this.transaction = transaction;
@@ -0,0 +1,19 @@
package ac.grim.grimac.utils.data.packetentity;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.world.Direction;
import lombok.Getter;
import java.util.UUID;
@Getter
public class PacketEntityPainting extends PacketEntity {
private final Direction direction;
public PacketEntityPainting(GrimPlayer player, UUID uuid, double x, double y, double z, Direction direction) {
super(player, uuid, EntityTypes.PAINTING, x, y, z);
this.direction = direction;
}
}
@@ -39,23 +39,47 @@ public enum MenuType {
private final int id;
//TODO: could be optimized
public static MenuType getMenuType(int id) {
if (id < 0) return UNKNOWN;
private static final MenuType[] MENU_BY_ID_ARRAY;
static {
ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion();
// versions under 1.20.3
MenuType[] menuTypes = MenuType.values();
int menuIdLimit;
if (version.isOlderThan(ServerVersion.V_1_20_3)) {
if (id > 23) return UNKNOWN;
MenuType[] values = MenuType.values();
if (id >= 7) id++;
return values[id];
// versions under 1.20.3
menuIdLimit = 23;
} else {
// 1.20.3 & greater
menuIdLimit = menuTypes.length - 1; // Don't iterate the UNKNOWN menu type
}
MENU_BY_ID_ARRAY = new MenuType[menuIdLimit];
for (int itr = 0; itr < menuIdLimit; itr++) {
MENU_BY_ID_ARRAY[itr] = menuTypes[itr];
}
// 1.20.3 & greater
MenuType[] values = MenuType.values();
if (id >= values.length) return UNKNOWN;
return MenuType.values()[id];
}
public static MenuType getMenuType(int id) {
if (id < 0) {
return UNKNOWN;
}
ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion();
// versions under 1.20.3
if (version.isOlderThan(ServerVersion.V_1_20_3)) { // TODO: Can this be moved to the static block?
if (id >= 7) {
id++;
}
}
if (id >= MENU_BY_ID_ARRAY.length) {
return UNKNOWN;
}
return MENU_BY_ID_ARRAY[id];
}
public static AbstractContainerMenu getMenuFromID(GrimPlayer player, Inventory playerInventory, MenuType type) {
return switch (type) {
@@ -20,6 +20,7 @@ import com.github.retrooper.packetevents.protocol.player.UserProfile;
import com.github.retrooper.packetevents.protocol.potion.PotionType;
import com.github.retrooper.packetevents.protocol.potion.PotionTypes;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.protocol.world.Direction;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateAttributes;
@@ -168,22 +169,22 @@ public class CompensatedEntities {
packetEntity = new PacketEntityHorse(player, uuid, entityType, position.getX(), position.getY(), position.getZ(), xRot);
} else if (entityType == EntityTypes.SLIME || entityType == EntityTypes.MAGMA_CUBE || entityType == EntityTypes.PHANTOM) {
packetEntity = new PacketEntitySizeable(player, uuid, entityType, position.getX(), position.getY(), position.getZ());
} else if (EntityTypes.PIG.equals(entityType)) {
packetEntity = new PacketEntityRideable(player, uuid, entityType, position.getX(), position.getY(), position.getZ());
} else if (EntityTypes.SHULKER.equals(entityType)) {
packetEntity = new PacketEntityShulker(player, uuid, entityType, position.getX(), position.getY(), position.getZ());
} else if (EntityTypes.STRIDER.equals(entityType)) {
packetEntity = new PacketEntityStrider(player, uuid, entityType, position.getX(), position.getY(), position.getZ());
} else if (EntityTypes.isTypeInstanceOf(entityType, EntityTypes.BOAT) || EntityTypes.CHICKEN.equals(entityType)) {
packetEntity = new PacketEntityTrackXRot(player, uuid, entityType, position.getX(), position.getY(), position.getZ(), xRot);
} else if (EntityTypes.FISHING_BOBBER.equals(entityType)) {
packetEntity = new PacketEntityHook(player, uuid, entityType, position.getX(), position.getY(), position.getZ(), data);
} else if (EntityTypes.ENDER_DRAGON.equals(entityType)) {
packetEntity = new PacketEntityEnderDragon(player, uuid, entityID, position.getX(), position.getY(), position.getZ());
} else if (EntityTypes.PAINTING.equals(entityType)) {
packetEntity = new PacketEntityPainting(player, uuid, position.x, position.y, position.z, Direction.getByHorizontalIndex(data));
} else {
if (EntityTypes.PIG.equals(entityType)) {
packetEntity = new PacketEntityRideable(player, uuid, entityType, position.getX(), position.getY(), position.getZ());
} else if (EntityTypes.SHULKER.equals(entityType)) {
packetEntity = new PacketEntityShulker(player, uuid, entityType, position.getX(), position.getY(), position.getZ());
} else if (EntityTypes.STRIDER.equals(entityType)) {
packetEntity = new PacketEntityStrider(player, uuid, entityType, position.getX(), position.getY(), position.getZ());
} else if (EntityTypes.isTypeInstanceOf(entityType, EntityTypes.BOAT) || EntityTypes.CHICKEN.equals(entityType)) {
packetEntity = new PacketEntityTrackXRot(player, uuid, entityType, position.getX(), position.getY(), position.getZ(), xRot);
} else if (EntityTypes.FISHING_BOBBER.equals(entityType)) {
packetEntity = new PacketEntityHook(player, uuid, entityType, position.getX(), position.getY(), position.getZ(), data);
} else if (EntityTypes.ENDER_DRAGON.equals(entityType)) {
packetEntity = new PacketEntityEnderDragon(player, uuid, entityID, position.getX(), position.getY(), position.getZ());
} else {
packetEntity = new PacketEntity(player, uuid, entityType, position.getX(), position.getY(), position.getZ());
}
packetEntity = new PacketEntity(player, uuid, entityType, position.getX(), position.getY(), position.getZ());
}
entityMap.put(entityID, packetEntity);
@@ -49,7 +49,7 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
import org.bukkit.Bukkit;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.*;
@@ -686,7 +686,7 @@ public class CompensatedWorld {
return maxHeight;
}
public WrappedBlockState getWrappedBlockStateAt(Vector aboveCCWPos) {
public WrappedBlockState getWrappedBlockStateAt(Vector3D aboveCCWPos) {
return getWrappedBlockStateAt(aboveCCWPos.getX(), aboveCCWPos.getY(), aboveCCWPos.getZ());
}
}
@@ -7,7 +7,6 @@ import ac.grim.grimac.utils.inventory.InventoryStorage;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import io.github.retrooper.packetevents.util.folia.FoliaScheduler;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import org.bukkit.inventory.InventoryView;
import java.util.*;
@@ -44,7 +43,7 @@ public class CorrectingPlayerInventoryStorage extends InventoryStorage {
// The key for this map is the inventory slot ID
// The value for this map is the transaction that we care about
// Returns -1 if the entry is null
Int2IntOpenHashMap serverIsCurrentlyProcessingThesePredictions = new Int2IntOpenHashMap();
Map<Integer, Integer> serverIsCurrentlyProcessingThesePredictions = new ConcurrentHashMap<>();
// A list of predictions the client has made for inventory changes
// Remove if the server rejects these changes
Map<Integer, Integer> pendingFinalizedSlot = new ConcurrentHashMap<>();
@@ -56,7 +55,6 @@ public class CorrectingPlayerInventoryStorage extends InventoryStorage {
public CorrectingPlayerInventoryStorage(GrimPlayer player, int size) {
super(size);
this.player = player;
this.serverIsCurrentlyProcessingThesePredictions.defaultReturnValue(-1);
}
// 1.17+ clients send what slots they have changed. This makes our jobs much easier.
@@ -78,7 +76,7 @@ public class CorrectingPlayerInventoryStorage extends InventoryStorage {
@Override
public void setItem(int item, ItemStack stack) {
// If there is a more recent change to this one, don't override it
int finalTransaction = serverIsCurrentlyProcessingThesePredictions.get(item);
int finalTransaction = serverIsCurrentlyProcessingThesePredictions.getOrDefault(item, -1);
// If the server is currently sending a packet to the player AND it is the final change to the slot
// OR, the client was in control of setting this slot
@@ -1,9 +1,14 @@
package ac.grim.grimac.utils.math;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.vector.Vector3D;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import lombok.Getter;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
import static com.github.retrooper.packetevents.protocol.player.ClientVersion.V_1_8;
import static java.lang.Math.abs;
public class TrigHandler {
GrimPlayer player;
@@ -19,24 +24,24 @@ public class TrigHandler {
isVanillaMath = !isVanillaMath;
}
public Vector getVanillaMathMovement(Vector wantedMovement, float f, float f2) {
public Vector3D getVanillaMathMovement(Vector3D wantedMovement, float f, float f2) {
float f3 = VanillaMath.sin(f2 * 0.017453292f);
float f4 = VanillaMath.cos(f2 * 0.017453292f);
float bestTheoreticalX = (float) (f3 * wantedMovement.getZ() + f4 * wantedMovement.getX()) / (f3 * f3 + f4 * f4) / f;
float bestTheoreticalZ = (float) (-f3 * wantedMovement.getX() + f4 * wantedMovement.getZ()) / (f3 * f3 + f4 * f4) / f;
return new Vector(bestTheoreticalX, 0, bestTheoreticalZ);
return newVector3D(bestTheoreticalX, 0, bestTheoreticalZ);
}
public Vector getShitMathMovement(Vector wantedMovement, float f, float f2) {
float f3 = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_8) ? OptifineFastMath.sin(f2 * 0.017453292f) : LegacyFastMath.sin(f2 * 0.017453292f);
float f4 = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_8) ? OptifineFastMath.cos(f2 * 0.017453292f) : LegacyFastMath.cos(f2 * 0.017453292f);
public Vector3D getShitMathMovement(Vector3D wantedMovement, float f, float f2) {
float f3 = player.getClientVersion().isNewerThanOrEquals(V_1_8) ? OptifineFastMath.sin(f2 * 0.017453292f) : LegacyFastMath.sin(f2 * 0.017453292f);
float f4 = player.getClientVersion().isNewerThanOrEquals(V_1_8) ? OptifineFastMath.cos(f2 * 0.017453292f) : LegacyFastMath.cos(f2 * 0.017453292f);
float bestTheoreticalX = (float) (f3 * wantedMovement.getZ() + f4 * wantedMovement.getX()) / (f3 * f3 + f4 * f4) / f;
float bestTheoreticalZ = (float) (-f3 * wantedMovement.getX() + f4 * wantedMovement.getZ()) / (f3 * f3 + f4 * f4) / f;
return new Vector(bestTheoreticalX, 0, bestTheoreticalZ);
return newVector3D(bestTheoreticalX, 0, bestTheoreticalZ);
}
public void setOffset(double offset) {
@@ -49,12 +54,12 @@ public class TrigHandler {
}
if (offset > 1e-5) {
Vector trueMovement = player.actualMovement.clone().subtract(player.startTickClientVel);
Vector correctMath = getVanillaMathMovement(trueMovement, 0.1f, player.xRot);
Vector fastMath = getShitMathMovement(trueMovement, 0.1f, player.xRot);
Vector3D trueMovement = player.actualMovement.clone().subtract(player.startTickClientVel);
Vector3D correctMath = getVanillaMathMovement(trueMovement, 0.1f, player.xRot);
Vector3D fastMath = getShitMathMovement(trueMovement, 0.1f, player.xRot);
correctMath = new Vector(Math.abs(correctMath.getX()), 0, Math.abs(correctMath.getZ()));
fastMath = new Vector(Math.abs(fastMath.getX()), 0, Math.abs(fastMath.getZ()));
correctMath = newVector3D(abs(correctMath.getX()), 0, abs(correctMath.getZ()));
fastMath = newVector3D(abs(fastMath.getX()), 0, abs(fastMath.getZ()));
double minCorrectHorizontal = Math.min(correctMath.getX(), correctMath.getZ());
// Support diagonal inputs
@@ -1,23 +1,25 @@
package ac.grim.grimac.utils.math;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.vector.Vector3D;
import com.github.retrooper.packetevents.util.Vector3d;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import static ac.grim.grimac.utils.math.GrimMath.clamp;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class VectorUtils {
public static Vector cutBoxToVector(Vector vectorToCutTo, Vector min, Vector max) {
public static Vector3D cutBoxToVector(Vector3D vectorToCutTo, Vector3D min, Vector3D max) {
SimpleCollisionBox box = new SimpleCollisionBox(min, max).sort();
return cutBoxToVector(vectorToCutTo, box);
}
public static Vector cutBoxToVector(Vector vectorCutTo, SimpleCollisionBox box) {
return new Vector(GrimMath.clamp(vectorCutTo.getX(), box.minX, box.maxX),
GrimMath.clamp(vectorCutTo.getY(), box.minY, box.maxY),
GrimMath.clamp(vectorCutTo.getZ(), box.minZ, box.maxZ));
public static Vector3D cutBoxToVector(Vector3D vectorCutTo, SimpleCollisionBox box) {
return newVector3D(clamp(vectorCutTo.getX(), box.minX, box.maxX), clamp(vectorCutTo.getY(), box.minY, box.maxY), clamp(vectorCutTo.getZ(), box.minZ, box.maxZ));
}
public static Vector fromVec3d(Vector3d vector3d) {
return new Vector(vector3d.getX(), vector3d.getY(), vector3d.getZ());
public static Vector3D fromVec3d(Vector3d vector3d) {
return newVector3D(vector3d.getX(), vector3d.getY(), vector3d.getZ());
}
// Clamping stops the player from causing an integer overflow and crashing the netty thread
@@ -45,8 +45,8 @@ public class BlockProperties {
}
// In 1.19.4, air sprinting is based on current sprinting, not last sprinting
if (player.getClientVersion().getProtocolVersion() > ClientVersion.V_1_19_3.getProtocolVersion()) {
return player.isSprinting ? (float) ((double) 0.02f + 0.005999999865889549D) : 0.02f;
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_19_4)) {
return player.isSprinting ? 0.025999999F : 0.02f;
}
return player.lastSprintingForSpeed ? (float) ((double) 0.02f + 0.005999999865889549D) : 0.02f;
@@ -0,0 +1 @@
@@ -12,6 +12,7 @@ import ac.grim.grimac.utils.data.tags.SyncedTags;
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.vector.Vector3D;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
@@ -26,7 +27,7 @@ import it.unimi.dsi.fastutil.floats.FloatArraySet;
import it.unimi.dsi.fastutil.floats.FloatArrays;
import it.unimi.dsi.fastutil.floats.FloatSet;
import org.bukkit.Location;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.ArrayList;
import java.util.Arrays;
@@ -34,6 +35,13 @@ import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
import static ac.grim.grimac.utils.nmsutil.Collisions.Axis.*;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
import static com.github.retrooper.packetevents.protocol.player.ClientVersion.*;
import static java.lang.Double.MAX_VALUE;
import static java.lang.Math.max;
import static java.lang.Math.min;
public class Collisions {
private static final double COLLISION_EPSILON = 1.0E-7;
@@ -61,22 +69,23 @@ public class Collisions {
SimpleCollisionBox oldBB = player.boundingBox;
player.boundingBox = GetBoundingBox.getBoundingBoxFromPosAndSize(player, x, y, z, 0.6f, 0.06f);
double posXZ = Collisions.collide(player, 0.03, -0.03, 0.03).getY();
double negXNegZ = Collisions.collide(player, -0.03, -0.03, -0.03).getY();
double posXNegZ = Collisions.collide(player, 0.03, -0.03, -0.03).getY();
double posZNegX = Collisions.collide(player, -0.03, -0.03, 0.03).getY();
double movementThreshold = player.getMovementThreshold();
double posXZ = Collisions.collide(player, movementThreshold, -movementThreshold, movementThreshold).getY();
double negXNegZ = Collisions.collide(player, -movementThreshold, -movementThreshold, -movementThreshold).getY();
double posXNegZ = Collisions.collide(player, movementThreshold, -movementThreshold, -movementThreshold).getY();
double posZNegX = Collisions.collide(player, -movementThreshold, -movementThreshold, movementThreshold).getY();
player.boundingBox = oldBB;
return negXNegZ != -0.03 || posXNegZ != -0.03 || posXZ != -0.03 || posZNegX != -0.03;
return negXNegZ != -movementThreshold || posXNegZ != -movementThreshold || posXZ != -movementThreshold || posZNegX != -movementThreshold;
}
// Call this when there isn't uncertainty on the Y axis
public static Vector collide(GrimPlayer player, double desiredX, double desiredY, double desiredZ) {
public static Vector3D collide(GrimPlayer player, double desiredX, double desiredY, double desiredZ) {
return collide(player, desiredX, desiredY, desiredZ, desiredY, null);
}
public static Vector collide(GrimPlayer player, double desiredX, double desiredY, double desiredZ, double clientVelY, VectorData data) {
if (desiredX == 0 && desiredY == 0 && desiredZ == 0) return new Vector();
public static Vector3D collide(GrimPlayer player, double desiredX, double desiredY, double desiredZ, double clientVelY, VectorData data) {
if (desiredX == 0 && desiredY == 0 && desiredZ == 0) return newVector3D();
final SimpleCollisionBox grabBoxesBB = player.boundingBox.copy();
final double stepUpHeight = player.getMaxUpStep();
@@ -94,7 +103,7 @@ public class Collisions {
grabBoxesBB.expandToCoordinate(desiredX, desiredY, desiredZ);
grabBoxesBB.maxY += stepUpHeight;
} else {
grabBoxesBB.expandToCoordinate(desiredX, Math.max(stepUpHeight, desiredY), desiredZ);
grabBoxesBB.expandToCoordinate(desiredX, max(stepUpHeight, desiredY), desiredZ);
}
} else {
grabBoxesBB.expandToCoordinate(desiredX, desiredY, desiredZ);
@@ -104,14 +113,14 @@ public class Collisions {
List<SimpleCollisionBox> desiredMovementCollisionBoxes = new ArrayList<>();
getCollisionBoxes(player, grabBoxesBB, desiredMovementCollisionBoxes, false);
double bestInput = Double.MAX_VALUE;
Vector bestOrderResult = null;
double bestInput = MAX_VALUE;
Vector3D bestOrderResult = null;
Vector bestTheoreticalCollisionResult = VectorUtils.cutBoxToVector(player.actualMovement, new SimpleCollisionBox(0, Math.min(0, desiredY), 0, desiredX, Math.max(stepUpHeight, desiredY), desiredZ).sort());
Vector3D bestTheoreticalCollisionResult = VectorUtils.cutBoxToVector(player.actualMovement, new SimpleCollisionBox(0, min(0, desiredY), 0, desiredX, max(stepUpHeight, desiredY), desiredZ).sort());
int zeroCount = (desiredX == 0 ? 1 : 0) + (desiredY == 0 ? 1 : 0) + (desiredZ == 0 ? 1 : 0);
for (List<Axis> order : (data != null && data.isZeroPointZeroThree() ? allAxisCombinations : nonStupidityCombinations)) {
Vector collisionResult = collideBoundingBoxLegacy(new Vector(desiredX, desiredY, desiredZ), player.boundingBox, desiredMovementCollisionBoxes, order);
Vector3D collisionResult = collideBoundingBoxLegacy(newVector3D(desiredX, desiredY, desiredZ), player.boundingBox, desiredMovementCollisionBoxes, order);
// While running up stairs and holding space, the player activates the "lastOnGround" part without otherwise being able to step
// 0.03 movement must compensate for stepping elsewhere. Too much of a hack to include in this met5hod.
@@ -123,7 +132,7 @@ public class Collisions {
if (stepUpHeight > 0.0F && movingIntoGround && (collisionResult.getX() != desiredX || collisionResult.getZ() != desiredZ)) {
player.uncertaintyHandler.isStepMovement = true;
// 1.21 significantly refactored this
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21)) {
if (player.getClientVersion().isNewerThanOrEquals(V_1_21)) {
SimpleCollisionBox box2 = movingIntoGroundReal ? player.boundingBox.copy().offset(0.0, collisionResult.getY(), 0.0) : player.boundingBox.copy();
SimpleCollisionBox box3 = box2.copy().expandToCoordinate(desiredX, stepUpHeight, desiredZ);
if (!movingIntoGroundReal) {
@@ -135,21 +144,21 @@ public class Collisions {
final float[] stepHeights = collectStepHeights(box2, list2, (float) stepUpHeight, (float) collisionResult.getY());
for (float stepHeight : stepHeights) {
Vector vec3d2 = collideBoundingBoxLegacy(new Vector(desiredX, stepHeight, desiredZ), box2, list2, order);
Vector3D vec3d2 = collideBoundingBoxLegacy(newVector3D(desiredX, stepHeight, desiredZ), box2, list2, order);
if (getHorizontalDistanceSqr(vec3d2) > getHorizontalDistanceSqr(collisionResult)) {
final double d = player.boundingBox.minY - box2.minY;
collisionResult = vec3d2.add(new Vector(0.0, -d, 0.0));
collisionResult = vec3d2.add(newVector3D(0.0, -d, 0.0));
break;
}
}
} else {
Vector regularStepUp = collideBoundingBoxLegacy(new Vector(desiredX, stepUpHeight, desiredZ), player.boundingBox, desiredMovementCollisionBoxes, order);
Vector3D regularStepUp = collideBoundingBoxLegacy(newVector3D(desiredX, stepUpHeight, desiredZ), player.boundingBox, desiredMovementCollisionBoxes, order);
// 1.7 clients do not have this stepping bug fix
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_8)) {
Vector stepUpBugFix = collideBoundingBoxLegacy(new Vector(0, stepUpHeight, 0), player.boundingBox.copy().expandToCoordinate(desiredX, 0, desiredZ), desiredMovementCollisionBoxes, order);
if (player.getClientVersion().isNewerThanOrEquals(V_1_8)) {
Vector3D stepUpBugFix = collideBoundingBoxLegacy(newVector3D(0, stepUpHeight, 0), player.boundingBox.copy().expandToCoordinate(desiredX, 0, desiredZ), desiredMovementCollisionBoxes, order);
if (stepUpBugFix.getY() < stepUpHeight) {
Vector stepUpBugFixResult = collideBoundingBoxLegacy(new Vector(desiredX, 0, desiredZ), player.boundingBox.copy().offset(0, stepUpBugFix.getY(), 0), desiredMovementCollisionBoxes, order).add(stepUpBugFix);
Vector3D stepUpBugFixResult = collideBoundingBoxLegacy(newVector3D(desiredX, 0, desiredZ), player.boundingBox.copy().offset(0, stepUpBugFix.getY(), 0), desiredMovementCollisionBoxes, order).add(stepUpBugFix);
if (getHorizontalDistanceSqr(stepUpBugFixResult) > getHorizontalDistanceSqr(regularStepUp)) {
regularStepUp = stepUpBugFixResult;
}
@@ -157,7 +166,7 @@ public class Collisions {
}
if (getHorizontalDistanceSqr(regularStepUp) > getHorizontalDistanceSqr(collisionResult)) {
collisionResult = regularStepUp.add(collideBoundingBoxLegacy(new Vector(0, -regularStepUp.getY() + (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14) ? desiredY : 0), 0), player.boundingBox.copy().offset(regularStepUp.getX(), regularStepUp.getY(), regularStepUp.getZ()), desiredMovementCollisionBoxes, order));
collisionResult = regularStepUp.add(collideBoundingBoxLegacy(newVector3D(0, -regularStepUp.getY() + (player.getClientVersion().isNewerThanOrEquals(V_1_14) ? desiredY : 0), 0), player.boundingBox.copy().offset(regularStepUp.getX(), regularStepUp.getY(), regularStepUp.getZ()), desiredMovementCollisionBoxes, order));
}
}
}
@@ -342,7 +351,7 @@ public class Collisions {
return false;
}
public static Vector collideBoundingBoxLegacy(Vector toCollide, SimpleCollisionBox
public static Vector3D collideBoundingBoxLegacy(Vector3D toCollide, SimpleCollisionBox
box, List<SimpleCollisionBox> desiredMovementCollisionBoxes, List<Axis> order) {
double x = toCollide.getX();
double y = toCollide.getY();
@@ -351,17 +360,17 @@ public class Collisions {
SimpleCollisionBox setBB = box.copy();
for (Axis axis : order) {
if (axis == Axis.X) {
if (axis == X) {
for (SimpleCollisionBox bb : desiredMovementCollisionBoxes) {
x = bb.collideX(setBB, x);
}
setBB.offset(x, 0.0D, 0.0D);
} else if (axis == Axis.Y) {
} else if (axis == Y) {
for (SimpleCollisionBox bb : desiredMovementCollisionBoxes) {
y = bb.collideY(setBB, y);
}
setBB.offset(0.0D, y, 0.0D);
} else if (axis == Axis.Z) {
} else if (axis == Z) {
for (SimpleCollisionBox bb : desiredMovementCollisionBoxes) {
z = bb.collideZ(setBB, z);
}
@@ -369,23 +378,23 @@ public class Collisions {
}
}
return new Vector(x, y, z);
return newVector3D(x, y, z);
}
public static boolean isEmpty(GrimPlayer player, SimpleCollisionBox playerBB) {
return !getCollisionBoxes(player, playerBB, null, true);
}
private static double getHorizontalDistanceSqr(Vector vector) {
private static double getHorizontalDistanceSqr(Vector3D vector) {
return vector.getX() * vector.getX() + vector.getZ() * vector.getZ();
}
public static Vector maybeBackOffFromEdge(Vector vec3, GrimPlayer player, boolean overrideVersion) {
public static Vector3D maybeBackOffFromEdge(Vector3D vec3, GrimPlayer player, boolean overrideVersion) {
if (!player.isFlying && player.isSneaking && isAboveGround(player)) {
double x = vec3.getX();
double z = vec3.getZ();
double maxStepDown = overrideVersion || player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_11) ? -player.getMaxUpStep() : -1 + COLLISION_EPSILON;
double maxStepDown = overrideVersion || player.getClientVersion().isNewerThanOrEquals(V_1_11) ? -player.getMaxUpStep() : -1 + COLLISION_EPSILON;
while (x != 0.0 && isEmpty(player, player.boundingBox.copy().offset(x, maxStepDown, 0.0))) {
if (x < 0.05D && x >= -0.05D) {
@@ -422,7 +431,7 @@ public class Collisions {
z += 0.05D;
}
}
vec3 = new Vector(x, vec3.getY(), z);
vec3 = newVector3D(x, vec3.getY(), z);
}
return vec3;
@@ -453,20 +462,20 @@ public class Collisions {
if (blockType == StateTypes.COBWEB) {
if (player.compensatedEntities.hasPotionEffect(PotionTypes.WEAVING)) {
player.stuckSpeedMultiplier = new Vector(0.5, 0.25, 0.5);
player.stuckSpeedMultiplier = newVector3D(0.5, 0.25, 0.5);
} else {
player.stuckSpeedMultiplier = new Vector(0.25, 0.05000000074505806, 0.25);
player.stuckSpeedMultiplier = newVector3D(0.25, 0.05000000074505806, 0.25);
}
}
if (blockType == StateTypes.SWEET_BERRY_BUSH
&& player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14)) {
player.stuckSpeedMultiplier = new Vector(0.800000011920929, 0.75, 0.800000011920929);
player.stuckSpeedMultiplier = newVector3D(0.800000011920929, 0.75, 0.800000011920929);
}
if (blockType == StateTypes.POWDER_SNOW && i == Math.floor(player.x) && j == Math.floor(player.y) && k == Math.floor(player.z)
&& player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_17)) {
player.stuckSpeedMultiplier = new Vector(0.8999999761581421, 1.5, 0.8999999761581421);
player.stuckSpeedMultiplier = newVector3D(0.8999999761581421, 1.5, 0.8999999761581421);
}
if (blockType == StateTypes.SOUL_SAND && player.getClientVersion().isOlderThan(ClientVersion.V_1_15)) {
@@ -533,7 +542,7 @@ public class Collisions {
}
}
private static boolean isSlidingDown(Vector vector, GrimPlayer player, int locationX, int locationY,
private static boolean isSlidingDown(Vector3D vector, GrimPlayer player, int locationX, int locationY,
int locationZ) {
if (player.onGround) {
return false;
Loaded 100 of 125 files, more files were not shown because too many files have changed in this diff. Show more