Compare commits

...
Author SHA1 Message Date
Axionize 0904b61df0 Make var type in ExploitC explicitly String 2024-11-27 22:04:50 -05: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
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
SamB440 497d72e13b Merge pull request #1812 from Axionize/fix-pink-petal-hitbox-1.12.2-1.19.3
Fix Pink Petals Hitbox (Coral Fan) for > 1.12.2 and =< 1.19.3 players
2024-11-22 10:30:31 +00:00
Axionize 02c5021ce2 Fix Pink Petals Hitbox (Coral Fan) for > 1.12.2 and =< 1.19.3 players 2024-11-21 19:21:57 -05:00
AoElite e5bd42c15a Merge pull request #1811 from crimilo/2.0
Fix race condition and exception when registering check permissions during rapid consecutive player joins on server start
2024-11-20 19:22:44 -05:00
Cristian Milani 67a25de202 Fix race condition and exception when registering check permissions during rapid consecutive player joins on server start 2024-11-21 00:26:33 +01:00
AoElite 187d70d1f1 Merge pull request #1809 from Rothes/folia
Fix reading world asynchronously on Folia
2024-11-18 14:47:05 -05:00
Rothes 31fd590968 Fix reading world asynchronously on Folia 2024-11-19 02:21:43 +08:00
SamB440 2f57704bae Merge pull request #1807 from ManInMyVan/fix/multiplace-and-multiinteract-falses
fix MultiInteract and MultiPlace falses
2024-11-18 13:30:53 +00:00
ManInMyVan 4385073234 fix MultiInteract and MultiPlace falses 2024-11-17 18:05:10 -07:00
MachineBreaker 39a812c5ff Merge pull request #1803 from GrimAnticheat/2.0-fastutil-usage
Use FastUtil collections where possible
2024-11-17 10:19:14 -04:00
MachineBreaker 367aac0274 Use FastUtil collections where possible 2024-11-17 10:06:22 -04:00
ManInMyVan 2de0f49af9 add permission for /grim stopspectating here 2024-11-15 15:04:04 -07:00
SamB440 c749a560fd Merge pull request #1795 from Axionize/button-pre-1.13-fix
Fix Button hitboxes for pre-1.13 players
2024-11-15 09:33:31 +00:00
Axionize 3c6742d040 Fix Button hitboxes for pre-1.13 players 2024-11-15 03:13:42 -05:00
SamB440 5dc3e08f22 This might fix aim modulo teleport falses 2024-11-14 16:39:12 +00:00
SamB440 1fb5762351 Gradle 8.10.2 2024-11-14 16:26:24 +00:00
SamB440 b51b661e23 Merge pull request #1788 from ManInMyVan/fix/noslow-alerts
fix NoSlowA alerting when flag returns false
2024-11-14 16:20:28 +00:00
SamB440 a540a9abb7 Merge pull request #1735 from GrimAnticheat/java-17
Java 17 update
2024-11-14 16:15:26 +00:00
SamB440 9035180db8 Update for HitboxData and CollisionData 2024-11-14 16:12:26 +00:00
SamB440 5a6f87fe22 Java 17 update 2024-11-14 15:59:51 +00:00
SamB440 fe95424e28 Merge pull request #1725 from Axionize/simulation-changes
All HitBox and CollisionBox Changes made for LineOfSightPlace
2024-11-14 15:57:12 +00:00
SamB440 d70e778f2d Merge pull request #1789 from ManInMyVan/folia
fix folia error
2024-11-14 15:50:40 +00:00
SamB440 8faa0db0ed Merge pull request #1793 from ManInMyVan/fix/shields
fix shields breaking causing noslow falses
2024-11-14 15:50:15 +00:00
MachineBreaker c4f133891d Merge pull request #1785 from jonesdevelopment/fix/dont-setback-crashb
Don't setback on CrashB
2024-11-14 06:43:08 -04:00
ManInMyVan 4b6eeadcc9 fix shields breaking causing noslow falses 2024-11-13 17:25:14 -07:00
ManInMyVan 9028135f61 fix folia error 2024-11-12 15:20:58 -07:00
ManInMyVan 8307e58449 fix NoSlowA alerting when flag returns false 2024-11-11 22:33:45 -07:00
SamB440 33766b11bc Limit when 1.9+ players can increase attack slow 2024-11-10 16:18:57 +00:00
SamB440 44fa35e6c7 Filter lava and water on FINISH_DIGGING 2024-11-10 15:53:25 +00:00
SamB440 26f205173e Merge pull request #1728 from ManInMyVan/multiaura
Add MultiInteract Checks
2024-11-10 15:46:16 +00:00
SamB440 7ff1ed0307 Merge pull request #1718 from ManInMyVan/exempt-MC-152728
exempt 1.14.2+ clients in NoSlowC and NoSlowD
2024-11-10 15:44:44 +00:00
SamB440 e50348ee32 Add spotless 2024-11-10 14:59:35 +00:00
Jones aee84e92cb Don't setback on CrashB 2024-11-10 15:58:59 +01:00
SamB440 a1f376bdb1 Merge pull request #1784 from jonesdevelopment/fix/strip-client-brand-colors
Strip color codes from client brand
2024-11-10 14:39:53 +00:00
SamB440 6483debd0a Merge pull request #1767 from BadPackage/patch-1
small doc fix
2024-11-10 14:39:39 +00:00
Jones 946c7a97a9 Strip color codes from client brand 2024-11-10 15:08:02 +01:00
ManInMyVan 39a53db067 track paintings 2024-11-09 17:08:10 -07:00
ManInMyVan b2db9ed9ff check sneaking in MutliInteractA 2024-11-08 13:54:03 -07:00
Axionize 3059f21ce6 Revert "Remove unncessary NO_COLLISION entry for torchcrop"
This reverts commit e149ca6be8.
2024-11-03 19:30:42 -05:00
Axionize b48f898c77 Revert "Remove unncessary NO_COLLISION entries"
This reverts commit 1761d853de.
2024-11-03 19:30:42 -05:00
Axionize 6f63743bd8 Fix scaffolding hitbox for pre-1.14 clients 2024-11-03 07:06:25 -05:00
Axionize 21e15f9b8a Fix Weeping Vines and Vine Plant for all versions; Fix Weeping + Twisted for pre 1.16 players 2024-11-03 06:39:40 -05:00
Axionize e505d2b2cd Fix pitcher crop for pre-1.20 players 2024-11-03 06:39:40 -05:00
Axionize c83b374830 Fix sniffer egg for pre 1.20 players 2024-11-03 06:39:40 -05:00
Axionize 1bdca2dae5 Fix frogspawn for pre-1.19 players 2024-11-03 06:39:38 -05:00
Axionize 91241dec7d Fix Sculk Shrieker hit box for all versions and collision box for pre 1.18.2 2024-11-03 06:39:38 -05:00
Axionize 651b0bd83e Fix Spore Blossom hitbox on < 1.17 2024-11-03 06:39:38 -05:00
Axionize 7f71f809c0 Fix glow lichen and sculk vein boxes 2024-11-03 06:39:38 -05:00
Axionize 05444e6f56 Fix Cauldron Hit/Collision Box 2024-11-03 03:52:09 -05:00
Axionize e9e84f5548 Fix Pink Petals Boxes 2024-11-03 02:58:19 -05:00
Axionize 62cb82de6a Add new union() function to collision boxes 2024-11-03 02:58:19 -05:00
Axionize d10f7619e4 Fix Coral Fan Collision box 2024-11-03 02:58:19 -05:00
Axionize 16e464719b Remove unncessary NO_COLLISION entries 2024-11-03 02:58:15 -05:00
Axionize 21f22a8446 Remove unncessary NO_COLLISION entry for torchcrop 2024-11-03 00:51:52 -04: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
Axionize f02d1383c8 Fix falses on dead corals 2024-11-01 21:28:15 -04:00
ManInMyVan b65f42a480 add check displaynames (#1669)
* add check displaynames

* oops
2024-11-01 16:32:34 +00:00
SamB440 e829c25c82 Merge pull request #1727 from ManInMyVan/negativetimer-setbacks
allow setbacks in NegativeTimer
2024-11-01 16:24:11 +00:00
SamB440 30c2d361a3 Merge pull request #1732 from Axionize/optimize-iteration-order
Optimizing getPossibleEyeHeights()
2024-10-31 21:54:17 +00:00
BadPackage d0eb36c87a small doc fix 2024-10-26 13:04:13 +02:00
ManInMyVan 504e667d40 fix MultiInteractB false 2024-10-05 22:38:09 -06:00
ManInMyVan f8aa611a4b fix MultiInteractA false 2024-10-05 22:35:55 -06:00
Axionize 28be03b978 Document SimpleCollisionBox and HexCollisionBox with Javadoc 2024-10-03 03:40:01 -04:00
Axionize e3ccbe2449 Fix spelling mistake 2024-10-03 00:09:51 -04:00
Axionize 5782d356fb Replace RuntimeException in HitBox.java with IllegalStateException 2024-10-03 00:09:51 -04:00
AxionizeandManInMyVan 999d7c7016 Button Styling Change
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-10-03 00:03:40 -04:00
AxionizeandManInMyVan 270db6995c Sapling Styling Change
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-10-03 00:03:23 -04:00
AxionizeandManInMyVan bafb169b76 TALL_FLOWER Styling Change
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-10-03 00:03:08 -04:00
AxionizeandManInMyVan 5bc0f06500 Redstone Wire Comment Styling
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-10-03 00:02:42 -04:00
AxionizeandManInMyVan 1e244ed37b Inline Hitbox Calculation
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-10-03 00:02:25 -04:00
AxionizeandManInMyVan 8b8a9b5ce7 getPropaguleMinHeight Spacing
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-10-03 00:02:03 -04:00
AxionizeandManInMyVan 129549bf2d Small Flower Line Spacing/Styling
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-10-03 00:01:34 -04:00
AxionizeandManInMyVan ed7f9d80f8 Coral Fan Line Spacing/Styling
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-10-03 00:01:15 -04:00
AxionizeandManInMyVan c1054142e0 Banner Line Spacing/Styling
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-10-03 00:01:05 -04:00
AxionizeandManInMyVan 9ee0b82eab Simplify TALL_FLOWERS handling
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-10-03 00:00:45 -04:00
AxionizeandManInMyVan 76f5f81f6a Rails Styling
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-10-03 00:00:21 -04:00
AxionizeandManInMyVan 0c51152477 Mark function as Deprecated
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-10-02 23:59:48 -04:00
Axionize 9b8a0b4969 Port over PR to Fix Chains for 1.16 2024-10-02 20:57:10 -04:00
Axionize a2528beaf9 Merge remote-tracking branch 'origin/2.0' into simulation-changes
# Conflicts:
#	src/main/java/ac/grim/grimac/utils/collisions/CollisionData.java
2024-10-02 20:54:23 -04:00
Axionize a3de70d498 Compact all NO_COLLISION states together 2024-10-02 20:51:44 -04:00
Axionize bb08c78e8a Fix 1.11 Ascending rails hitboxes 2024-09-28 02:01:45 -04:00
Axionize 4da8ddbd12 Fix HitBox of ascending powered rail on 1.9 2024-09-28 02:01:45 -04:00
Axionize 8f89d3615b Fix HitBox of ascending powered rails on 1.7.10 2024-09-28 02:01:45 -04:00
Axionize 8a09be435e Remove dynamicfence; implement hashcode() function for offset hitboxes 2024-09-25 01:33:24 -04:00
Axionize 7af6cfbb3b Fix Cacti, inline lever hitboxes, fix fence handling 2024-09-25 01:29:47 -04:00
Axionize ce8aee5b8c Groundwork classes for better fence handling 2024-09-25 01:28:56 -04:00
ManInMyVan a38f341965 Add MultiInteract Checks 2024-09-19 16:11:51 -06:00
ManInMyVan 262de24cbf allow setbacks in NegativeTimer 2024-09-18 20:14:11 -06:00
Axionize 16bb09aee0 All HitBox and CollisionBox Changes made for LineOfSightPlace 2024-09-17 04:08:23 -04:00
Ruxy 330a9782bc Add full Japanese translations for all GrimAC configuration files 2024-09-17 10:31:53 +12:00
ManInMyVan f6ef1df29d exempt 1.14.2+ clients in NoSlowC and NoSlowD 2024-09-13 13:02:40 -06:00
145 changed files with 2496 additions and 1364 deletions

No files matched your search

+1 -1
View File
@@ -7,7 +7,7 @@ GrimAC is an open source Minecraft anticheat designed for 1.21 and supports 1.8-
### Compiling through terminal/command prompt
1. git clone https://github.com/GrimAnticheat/Grim.git (or click the green code button, download ZIP, then unzip it.)
2. cd Grim
3. gradlew build
3. ./gradlew build
4. The final jar is located in build/libs
### API Information
+29 -6
View File
@@ -6,17 +6,39 @@ plugins {
id("com.github.johnrengelman.shadow") version "8.1.1"
id("io.freefair.lombok") version "8.6"
id("net.minecrell.plugin-yml.bukkit") version "0.6.0"
id("com.diffplug.spotless") version "6.25.0"
}
spotless {
java {
endWithNewline()
indentWithSpaces(4)
removeUnusedImports()
trimTrailingWhitespace()
targetExclude("build/generated/**/*")
}
kotlinGradle {
endWithNewline()
indentWithSpaces(4)
trimTrailingWhitespace()
}
}
group = "ac.grim.grimac"
version = "2.3.68"
description = "Libre simulation anticheat designed for 1.21 with 1.8-1.21 support, powered by PacketEvents 2.0."
java.sourceCompatibility = JavaVersion.VERSION_17
java.targetCompatibility = JavaVersion.VERSION_17
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
}
// Set to false for debug builds
// You cannot live reload classes if the jar relocates dependencies
var relocate = true;
// Checks Project properties -> environment variable -> defaults true
val relocate: Boolean = project.findProperty("relocate")?.toString()?.toBoolean()
?: System.getenv("RELOCATE_JAR")?.toBoolean()
?: true
repositories {
mavenLocal()
@@ -38,10 +60,10 @@ repositories {
}
dependencies {
implementation("com.github.retrooper:packetevents-spigot:2.5.1-SNAPSHOT")
implementation("com.github.retrooper:packetevents-spigot:2.6.1-SNAPSHOT")
implementation("co.aikar:acf-paper:0.5.1-SNAPSHOT")
implementation("club.minnced:discord-webhooks:0.8.0") // Newer versions include kotlin-stdlib, which leads to incompatibility with plugins that use Kotlin
implementation("it.unimi.dsi:fastutil:8.5.13")
implementation("it.unimi.dsi:fastutil:8.5.15")
implementation("github.scarsz:configuralize:1.4.0")
//implementation("com.github.grimanticheat:grimapi:1193c4fa41")
@@ -126,6 +148,7 @@ bukkit {
tasks.build {
dependsOn(tasks.shadowJar)
dependsOn(tasks.spotlessApply)
}
tasks.withType<JavaCompile> {
@@ -158,4 +181,4 @@ tasks.shadowJar {
relocate("org.intellij", "ac.grim.grimac.shaded.intellij")
relocate("org.jetbrains", "ac.grim.grimac.shaded.jetbrains")
}
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
@@ -25,6 +25,7 @@ public class Check implements AbstractCheck, ConfigReloadObserver {
private String checkName;
private String configName;
private String alternativeName;
private String displayName;
private String description;
private boolean experimental;
@@ -53,6 +54,7 @@ public class Check implements AbstractCheck, ConfigReloadObserver {
this.alternativeName = checkData.alternativeName();
this.experimental = checkData.experimental();
this.description = checkData.description();
this.displayName = this.checkName;
}
//
reload();
@@ -112,6 +114,8 @@ public class Check implements AbstractCheck, ConfigReloadObserver {
public void reload(ConfigManager configuration) {
decay = configuration.getDoubleElse(configName + ".decay", decay);
setbackVL = configuration.getDoubleElse(configName + ".setbackvl", setbackVL);
displayName = configuration.getStringElse(configName + ".displayname", checkName);
if (setbackVL == -1) setbackVL = Double.MAX_VALUE;
updateExempted();
onReload(configuration);
@@ -152,4 +156,3 @@ public class Check implements AbstractCheck, ConfigReloadObserver {
}
}
@@ -11,7 +11,8 @@ import ac.grim.grimac.utils.anticheat.update.RotationUpdate;
// It works on clients who % 360 their rotation.
@CheckData(name = "AimModulo360", decay = 0.005)
public class AimModulo360 extends Check implements RotationCheck {
float lastDeltaYaw;
private float lastDeltaYaw;
public AimModulo360(GrimPlayer playerData) {
super(playerData);
@@ -19,7 +20,10 @@ public class AimModulo360 extends Check implements RotationCheck {
@Override
public void process(final RotationUpdate rotationUpdate) {
if (player.packetStateData.lastPacketWasTeleport) return;
if (player.packetStateData.lastPacketWasTeleport) {
lastDeltaYaw = rotationUpdate.getDeltaXRot();
return;
}
if (player.xRot < 360 && player.xRot > -360 && Math.abs(rotationUpdate.getDeltaXRot()) > 320 && Math.abs(lastDeltaYaw) < 30) {
flagAndAlert();
@@ -59,15 +59,15 @@ public class AimProcessor extends Check implements RotationCheck {
if (this.xRotMode.size() > SIGNIFICANT_SAMPLES_THRESHOLD) {
Pair<Double, Integer> modeX = this.xRotMode.getMode();
if (modeX.getSecond() > SIGNIFICANT_SAMPLES_THRESHOLD) {
this.modeX = modeX.getFirst();
if (modeX.second() > SIGNIFICANT_SAMPLES_THRESHOLD) {
this.modeX = modeX.first();
this.sensitivityX = convertToSensitivity(this.modeX);
}
}
if (this.yRotMode.size() > SIGNIFICANT_SAMPLES_THRESHOLD) {
Pair<Double, Integer> modeY = this.yRotMode.getMode();
if (modeY.getSecond() > SIGNIFICANT_SAMPLES_THRESHOLD) {
this.modeY = modeY.getFirst();
if (modeY.second() > SIGNIFICANT_SAMPLES_THRESHOLD) {
this.modeY = modeY.first();
this.sensitivityY = convertToSensitivity(this.modeY);
}
}
@@ -81,4 +81,4 @@ public class AimProcessor extends Check implements RotationCheck {
double var9 = Math.cbrt(var11);
return (var9 - 0.2f) / 0.6f;
}
}
}
@@ -23,7 +23,8 @@ 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 ||
event.getPacketType() == PacketType.Play.Client.VEHICLE_MOVE) {
noReminderTicks = 0; // Exempt vehicles
}
@@ -10,7 +10,6 @@ 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.WrapperPlayClientInteractEntity;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
@CheckData(name = "BadPacketsH")
public class BadPacketsH extends Check implements PacketCheck {
@@ -39,7 +39,7 @@ public class BadPacketsO extends Check implements PacketCheck {
boolean hasID = false;
for (Pair<Long, Long> iterator : keepaliveMap) {
if (iterator.getFirst() == id) {
if (iterator.first() == id) {
hasID = true;
break;
}
@@ -55,7 +55,7 @@ public class BadPacketsO extends Check implements PacketCheck {
do {
data = keepaliveMap.poll();
if (data == null) break;
} while (data.getFirst() != id);
} while (data.first() != id);
}
}
}
@@ -41,7 +41,9 @@ 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.compensatedEntities.getSelf().inVehicle()) {
positions++;
}
}
@@ -6,7 +6,6 @@ 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.item.type.ItemType;
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
@@ -1,30 +1,54 @@
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 ac.grim.grimac.utils.nmsutil.BlockBreakSpeed;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.util.Vector3i;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
@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)
return;
// Fixes false from breaking kelp underwater
// The client sends two start digging packets to the server both in the same tick. BadPacketsX gets called twice, doesn't false the first time, but falses the second
// One ends up breaking the kelp, the other ends up doing nothing besides falsing this check because we think they're trying to mine water
// I am explicitly making this patch as narrow and specific as possible to potentially discover other blocks that exhibit similar behaviour
int newTick = GrimAPI.INSTANCE.getTickManager().currentTick;
if (lastTick == newTick
&& lastBreakLoc.equals(dig.getBlockPosition())
&& !didLastFlag
&& lastBlockType.getHardness() == 0.0F
&& lastBlockType.getBlastResistance() == 0.0F
&& block == StateTypes.WATER
) {
return;
}
lastTick = newTick;
lastBreakLoc = dig.getBlockPosition();
lastBlockType = block;
// the block does not have a hitbox
boolean invalid = (block == StateTypes.LIGHT && !(player.getInventory().getHeldItem().is(ItemTypes.LIGHT) || player.getInventory().getOffHand().is(ItemTypes.LIGHT)))
|| block.isAir()
@@ -37,8 +61,11 @@ public class BadPacketsX extends Check implements PacketCheck {
|| block.getHardness() == -1.0f && dig.getAction() == DiggingAction.FINISHED_DIGGING;
if (invalid && flagAndAlert("block=" + block.getName() + ", type=" + dig.getAction()) && shouldModifyPackets()) {
didLastFlag = true;
event.setCancelled(true);
player.onPacketCancel();
} else {
didLastFlag = false;
}
}
}
@@ -16,7 +16,7 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPl
import static ac.grim.grimac.events.packets.patch.ResyncWorldUtil.resyncPosition;
import static ac.grim.grimac.utils.nmsutil.BlockBreakSpeed.getBlockDamage;
@CheckData(name = "BadPacketsZ", experimental = true)
@CheckData(name = "BadPacketsZ")
public class BadPacketsZ extends Check implements PacketCheck {
public BadPacketsZ(final GrimPlayer player) {
super(player);
@@ -0,0 +1,71 @@
package ac.grim.grimac.checks.impl.combat;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
import java.util.ArrayList;
@CheckData(name = "MultiInteractA", experimental = true)
public class MultiInteractA extends Check implements PostPredictionCheck {
public MultiInteractA(final GrimPlayer player) {
super(player);
}
private final ArrayList<String> flags = new ArrayList<>();
private int lastEntity;
private boolean lastSneaking;
private boolean hasInteracted = false;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
WrapperPlayClientInteractEntity packet = new WrapperPlayClientInteractEntity(event);
int entity = packet.getEntityId();
boolean sneaking = packet.isSneaking().orElse(false);
if (hasInteracted && entity != lastEntity) {
String verbose = "lastEntity=" + lastEntity + ", entity=" + entity
+ ", lastSneaking=" + lastSneaking + ", sneaking=" + sneaking;
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert(verbose) && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else {
flags.add(verbose);
}
}
lastEntity = entity;
lastSneaking = sneaking;
hasInteracted = true;
}
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8) && !player.packetStateData.lastPacketWasTeleport) {
hasInteracted = false;
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
// we don't need to check pre-1.9 players here (no tick skipping)
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
if (player.isTickingReliablyFor(3) && !player.uncertaintyHandler.lastVehicleSwitch.hasOccurredSince(0)) {
for (String verbose : flags) {
flagAndAlert(verbose);
}
}
flags.clear();
hasInteracted = false;
}
}
@@ -0,0 +1,72 @@
package ac.grim.grimac.checks.impl.combat;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.MessageUtil;
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.util.Vector3f;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
import java.util.ArrayList;
@CheckData(name = "MultiInteractB", experimental = true)
public class MultiInteractB extends Check implements PostPredictionCheck {
public MultiInteractB(final GrimPlayer player) {
super(player);
}
private final ArrayList<String> flags = new ArrayList<>();
private Vector3f lastPos;
private boolean hasInteracted = false;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
Vector3f pos = new WrapperPlayClientInteractEntity(event).getTarget().orElse(null);
if (pos == null) {
return;
}
if (hasInteracted && !pos.equals(lastPos)) {
String verbose = "pos=" + MessageUtil.toUnlabledString(pos) + ", lastPos=" + MessageUtil.toUnlabledString(lastPos);
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert(verbose) && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else {
flags.add(verbose);
}
}
lastPos = pos;
hasInteracted = true;
}
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8) && !player.packetStateData.lastPacketWasTeleport) {
hasInteracted = false;
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
// we don't need to check pre-1.9 players here (no tick skipping)
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
if (player.isTickingReliablyFor(3) && !player.uncertaintyHandler.lastVehicleSwitch.hasOccurredSince(0)) {
for (String verbose : flags) {
flagAndAlert(verbose);
}
}
flags.clear();
hasInteracted = false;
}
}
@@ -34,6 +34,8 @@ 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 java.util.*;
@@ -43,7 +45,7 @@ import java.util.*;
public class Reach extends Check implements PacketCheck {
// Only one flag per reach attack, per entity, per tick.
// We store position because lastX isn't reliable on teleports.
private final Map<Integer, Vector3d> playerAttackQueue = new HashMap<>();
private final Int2ObjectMap<Vector3d> playerAttackQueue = new Int2ObjectOpenHashMap<>();
private static final List<EntityType> blacklisted = Arrays.asList(
EntityTypes.BOAT,
EntityTypes.CHEST_BOAT,
@@ -81,7 +83,7 @@ public class Reach extends Check implements PacketCheck {
}
return;
}
// Dead entities cause false flags (https://github.com/GrimAnticheat/Grim/issues/546)
if (entity.isDead) return;
@@ -142,8 +144,8 @@ public class Reach extends Check implements PacketCheck {
}
private void tickBetterReachCheckWithAngle() {
for (Map.Entry<Integer, Vector3d> attack : playerAttackQueue.entrySet()) {
PacketEntity reachEntity = player.compensatedEntities.entityMap.get(attack.getKey().intValue());
for (Int2ObjectMap.Entry<Vector3d> attack : playerAttackQueue.int2ObjectEntrySet()) {
PacketEntity reachEntity = player.compensatedEntities.entityMap.get(attack.getIntKey());
if (reachEntity != null) {
String result = checkReach(reachEntity, attack.getValue(), false);
if (result != null) {
@@ -208,7 +210,7 @@ public class Reach extends Check implements PacketCheck {
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));
Vector intercept = ReachUtils.calculateIntercept(targetBox, eyePos, endReachPos).getFirst();
Vector intercept = ReachUtils.calculateIntercept(targetBox, eyePos, endReachPos).first();
if (ReachUtils.isVecInside(targetBox, eyePos)) {
minDistance = 0;
@@ -18,7 +18,6 @@ public class CrashB extends Check implements PacketCheck {
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.CREATIVE_INVENTORY_ACTION) {
if (player.gamemode != GameMode.CREATIVE) {
player.getSetbackTeleportUtil().executeViolationSetback();
event.setCancelled(true);
player.onPacketCancel();
flagAndAlert(); // Could be transaction split, no need to setback though
@@ -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();
}
}
}
}
}
@@ -9,6 +9,7 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPluginMessage;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
public class ClientBrand extends Check implements PacketCheck {
@@ -38,6 +39,7 @@ public class ClientBrand extends Check implements PacketCheck {
System.arraycopy(data, 1, minusLength, 0, minusLength.length);
brand = new String(minusLength).replace(" (Velocity)", ""); //removes velocity's brand suffix
brand = ChatColor.stripColor(brand); //strip color codes from client brand
if (player.checkManager.getPrePredictionCheck(ExploitA.class).checkString(brand)) brand = "sent log4j";
if (!GrimAPI.INSTANCE.getConfigManager().isIgnoredClient(brand)) {
String message = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("client-brand-format", "%prefix% &f%player% joined using %brand%");
@@ -63,15 +63,15 @@ public class FastBreak extends Check implements PacketCheck {
if (digging.getAction() == DiggingAction.START_DIGGING) {
WrappedBlockState block = player.compensatedWorld.getWrappedBlockStateAt(blockPosition);
// Exempt all blocks that do not exist in the player version
if (WrappedBlockState.getDefaultState(player.getClientVersion(), block.getType()).getType() == StateTypes.AIR) {
return;
}
startBreak = System.currentTimeMillis() - (targetBlock == null ? 50 : 0); // ???
targetBlock = blockPosition;
maximumBlockDamage = BlockBreakSpeed.getBlockDamage(player, targetBlock);
double breakDelay = System.currentTimeMillis() - lastFinishBreak;
@@ -5,7 +5,6 @@ import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import java.util.ArrayList;
@CheckData(name = "TransactionOrder", experimental = false)
public class TransactionOrder extends Check implements PacketCheck {
@@ -14,4 +13,4 @@ public class TransactionOrder extends Check implements PacketCheck {
super(player);
}
}
}
@@ -7,7 +7,7 @@ import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
@CheckData(name = "NegativeTimer", configName = "NegativeTimer", setback = 10, experimental = true)
@CheckData(name = "NegativeTimer", configName = "NegativeTimer", setback = -1, experimental = true)
public class NegativeTimerCheck extends TimerCheck implements PostPredictionCheck {
public NegativeTimerCheck(GrimPlayer player) {
@@ -24,7 +24,7 @@ public class NegativeTimerCheck extends TimerCheck implements PostPredictionChec
if (timerBalanceRealTime < lastMovementPlayerClock - clockDrift) {
int lostMS = (int) ((System.nanoTime() - timerBalanceRealTime) / 1e6);
flagAndAlert("-" + lostMS);
if (flagAndAlert("-" + lostMS)) setbackIfAboveSetbackVL();
timerBalanceRealTime += 50e6;
}
}
@@ -34,8 +34,7 @@ public class NoSlowA extends Check implements PostPredictionCheck {
}
if (bestOffset > offsetToFlag) {
if (flaggedLastTick) {
flagWithSetback();
if (flaggedLastTick && flagWithSetback()) {
alert("");
}
flaggedLastTick = true;
@@ -35,4 +35,4 @@ public class NoSlowB extends Check implements PacketCheck {
}
}
}
}
}
@@ -2,32 +2,17 @@ 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.PacketCheck;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
@CheckData(name = "NoSlowC", setback = 5, experimental = true)
public class NoSlowC extends Check implements PostPredictionCheck, PacketCheck {
public class NoSlowC extends Check implements PostPredictionCheck {
public NoSlowC(GrimPlayer player) {
super(player);
}
public boolean startedSprintingBeforeSlowMovement = false;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.ENTITY_ACTION) {
if (new WrapperPlayClientEntityAction(event).getAction() == WrapperPlayClientEntityAction.Action.START_SPRINTING) {
startedSprintingBeforeSlowMovement = false;
}
}
}
@Override
public void onPredictionComplete(final PredictionComplete predictionComplete) {
if (!predictionComplete.isChecked()) return;
@@ -36,8 +21,7 @@ public class NoSlowC extends Check implements PostPredictionCheck, PacketCheck {
ClientVersion client = player.getClientVersion();
// https://bugs.mojang.com/browse/MC-152728
if (startedSprintingBeforeSlowMovement && client.isNewerThanOrEquals(ClientVersion.V_1_14_2)) {
reward();
if (client.isNewerThanOrEquals(ClientVersion.V_1_14_2)) {
return;
}
@@ -2,42 +2,26 @@ 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.PacketCheck;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
@CheckData(name = "NoSlowD", setback = 5, experimental = true)
public class NoSlowD extends Check implements PostPredictionCheck, PacketCheck {
public class NoSlowD extends Check implements PostPredictionCheck {
public NoSlowD(GrimPlayer player) {
super(player);
}
public boolean startedSprintingBeforeUse = false;
private boolean flaggedLastTick = false;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.ENTITY_ACTION) {
if (new WrapperPlayClientEntityAction(event).getAction() == WrapperPlayClientEntityAction.Action.START_SPRINTING) {
startedSprintingBeforeUse = false;
}
}
}
@Override
public void onPredictionComplete(final PredictionComplete predictionComplete) {
if (!predictionComplete.isChecked()) return;
if (player.packetStateData.isSlowedByUsingItem()) {
// https://bugs.mojang.com/browse/MC-152728
if (startedSprintingBeforeUse && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14_2)) {
reward();
flaggedLastTick = false;
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14_2)) {
return;
}
@@ -2,7 +2,6 @@ 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.PacketCheck;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
@@ -13,7 +12,7 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEn
import static com.github.retrooper.packetevents.protocol.potion.PotionTypes.BLINDNESS;
@CheckData(name = "NoSlowE", setback = 5, experimental = true)
public class NoSlowE extends Check implements PostPredictionCheck, PacketCheck {
public class NoSlowE extends Check implements PostPredictionCheck {
public NoSlowE(GrimPlayer player) {
super(player);
}
@@ -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.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
public class SetbackBlocker extends Check implements PacketCheck {
@@ -43,7 +43,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);
}
@@ -7,8 +7,6 @@ import ac.grim.grimac.utils.anticheat.update.BlockPlace;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.math.VectorUtils;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.util.Vector3i;
import org.bukkit.util.Vector;
@@ -63,7 +63,7 @@ public class MultiPlace extends BlockPlaceCheck {
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
if (player.getClientVersion().isNewerThan(ClientVersion.V_1_8) && !player.skippedTickInActualMovement && predictionComplete.isChecked()) {
if (player.getClientVersion().isNewerThan(ClientVersion.V_1_8) && player.isTickingReliablyFor(3) && !player.uncertaintyHandler.lastVehicleSwitch.hasOccurredSince(0)) {
for (String verbose : flags) {
flagAndAlert(verbose);
}
@@ -8,7 +8,6 @@ import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import java.util.Collections;
@CheckData(name = "PositionPlace")
public class PositionPlace extends BlockPlaceCheck {
@@ -49,27 +48,21 @@ public class PositionPlace extends BlockPlaceCheck {
// So now we have the player's possible eye positions
// So then look at the face that the player has clicked
boolean flag = false;
switch (place.getDirection()) {
case NORTH: // Z- face
flag = eyePositions.minZ > combined.minZ;
break;
case SOUTH: // Z+ face
flag = eyePositions.maxZ < combined.maxZ;
break;
case EAST: // X+ face
flag = eyePositions.maxX < combined.maxX;
break;
case WEST: // X- face
flag = eyePositions.minX > combined.minX;
break;
case UP: // Y+ face
flag = eyePositions.maxY < combined.maxY;
break;
case DOWN: // Y- face
flag = eyePositions.minY > combined.minY;
break;
}
boolean flag = switch (place.getDirection()) {
case NORTH -> // Z- face
eyePositions.minZ > combined.minZ;
case SOUTH -> // Z+ face
eyePositions.maxZ < combined.maxZ;
case EAST -> // X+ face
eyePositions.maxX < combined.maxX;
case WEST -> // X- face
eyePositions.minX > combined.minX;
case UP -> // Y+ face
eyePositions.maxY < combined.maxY;
case DOWN -> // Y- face
eyePositions.minY > combined.minY;
default -> false;
};
if (flag) {
if (flagAndAlert() && shouldModifyPackets() && shouldCancel()) {
@@ -113,7 +113,7 @@ public class RotationPlace extends BlockPlaceCheck {
Ray trace = new Ray(player, starting.getX(), starting.getY(), starting.getZ(), lookDir.getX(), lookDir.getY());
Pair<Vector, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
if (intercept.getFirst() != null) return true;
if (intercept.first() != null) return true;
}
}
@@ -6,6 +6,7 @@ import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
import ac.grim.grimac.utils.collisions.HitboxData;
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.ComplexCollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
@@ -18,6 +19,7 @@ import java.util.List;
public class BlockPlaceCheck extends Check implements RotationCheck, PostPredictionCheck {
private static final List<StateType> weirdBoxes = new ArrayList<>();
private static final List<StateType> buggyBoxes = new ArrayList<>();
private final SimpleCollisionBox[] boxes = new SimpleCollisionBox[ComplexCollisionBox.DEFAULT_MAX_COLLISION_BOX_SIZE];
protected int cancelVL;
@@ -69,11 +71,11 @@ public class BlockPlaceCheck extends Check implements RotationCheck, PostPredict
Vector3i clicked = place.getPlacedAgainstBlockLocation();
CollisionBox placedOn = HitboxData.getBlockHitbox(player, place.getMaterial(), player.getClientVersion(), player.compensatedWorld.getWrappedBlockStateAt(clicked), clicked.getX(), clicked.getY(), clicked.getZ());
List<SimpleCollisionBox> boxes = new ArrayList<>();
placedOn.downCast(boxes);
int size = placedOn.downCast(boxes);
SimpleCollisionBox combined = new SimpleCollisionBox(clicked.getX(), clicked.getY(), clicked.getZ());
for (SimpleCollisionBox box : boxes) {
for (int i = 0; i < size; i++) {
SimpleCollisionBox box = boxes[i];
double minX = Math.max(box.minX, combined.minX);
double minY = Math.max(box.minY, combined.minY);
double minZ = Math.max(box.minZ, combined.minZ);
@@ -1,8 +1,6 @@
package ac.grim.grimac.checks.type;
import ac.grim.grimac.api.AbstractCheck;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.RotationUpdate;
public interface RotationCheck extends AbstractCheck {
@@ -1,8 +1,6 @@
package ac.grim.grimac.checks.type;
import ac.grim.grimac.api.AbstractCheck;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.VehiclePositionUpdate;
public interface VehicleCheck extends AbstractCheck {
@@ -70,4 +70,4 @@ public class GrimDebug extends BaseCommand {
sender.sendMessage("Console output for " + (grimPlayer.bukkitPlayer == null ? grimPlayer.user.getProfile().getName() : grimPlayer.bukkitPlayer.getName()) + " is now " + isOutput);
}
}
}
@@ -1,12 +1,9 @@
package ac.grim.grimac.commands;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.checks.impl.aim.processor.AimProcessor;
import ac.grim.grimac.checks.impl.misc.ClientBrand;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.MessageUtil;
import ac.grim.grimac.utils.anticheat.MultiLibUtil;
import ac.grim.grimac.utils.math.GrimMath;
import co.aikar.commands.BaseCommand;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
@@ -23,8 +23,7 @@ public class GrimSpectate extends BaseCommand {
@CommandPermission("grim.spectate")
@CommandCompletion("@players")
public void onSpectate(CommandSender sender, @Optional OnlinePlayer target) {
if (!(sender instanceof Player)) return;
Player player = (Player) sender;
if (!(sender instanceof Player player)) return;
if (target != null && target.getPlayer().getUniqueId().equals(player.getUniqueId())) {
String message = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("cannot-run-on-self", "%prefix% &cYou cannot use this command on yourself!");
@@ -3,6 +3,8 @@ package ac.grim.grimac.commands;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.utils.anticheat.MessageUtil;
import co.aikar.commands.BaseCommand;
import co.aikar.commands.BukkitCommandCompletionContext;
import co.aikar.commands.CommandCompletions.CommandCompletionHandler;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
@@ -10,17 +12,21 @@ import co.aikar.commands.annotation.Subcommand;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
@CommandAlias("grim|grimac")
public class GrimStopSpectating extends BaseCommand {
public static final CommandCompletionHandler<BukkitCommandCompletionContext> completionHandler = context -> context.getSender().hasPermission("grim.spectate.stophere") ? List.of("here") : List.of();
@Subcommand("stopspectating")
@CommandPermission("grim.spectate")
@CommandCompletion("here")
@CommandCompletion("@stopspectating")
public void onStopSpectate(CommandSender sender, String[] args) {
String string = args.length > 0 ? args[0] : null;
if (!(sender instanceof Player)) return;
Player player = (Player) sender;
if (GrimAPI.INSTANCE.getSpectateManager().isSpectating(player.getUniqueId())) {
boolean teleportBack = string == null || !string.equalsIgnoreCase("here");
boolean teleportBack = string == null || !string.equalsIgnoreCase("here") || !sender.hasPermission("grim.spectate.stophere");
GrimAPI.INSTANCE.getSpectateManager().disable(player, teleportBack);
} else {
String message = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("cannot-spectate-return", "%prefix% &cYou can only do this after spectating a player.");
@@ -28,4 +34,3 @@ public class GrimStopSpectating extends BaseCommand {
}
}
}
@@ -52,6 +52,7 @@ import org.bukkit.util.Vector;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
public class CheckManagerListener extends PacketListenerAbstract {
@@ -224,11 +225,10 @@ public class CheckManagerListener extends PacketListenerAbstract {
}
}
private static void handleBlockPlaceOrUseItem(PacketWrapper packet, GrimPlayer player) {
private static void handleBlockPlaceOrUseItem(PacketWrapper<?> packet, GrimPlayer player) {
// Legacy "use item" packet
if (packet instanceof WrapperPlayClientPlayerBlockPlacement &&
if (packet instanceof WrapperPlayClientPlayerBlockPlacement place &&
PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_9)) {
WrapperPlayClientPlayerBlockPlacement place = (WrapperPlayClientPlayerBlockPlacement) packet;
if (player.gamemode == GameMode.SPECTATOR || player.gamemode == GameMode.ADVENTURE) return;
@@ -243,9 +243,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
}
}
if (packet instanceof WrapperPlayClientUseItem) {
WrapperPlayClientUseItem place = (WrapperPlayClientUseItem) packet;
if (packet instanceof WrapperPlayClientUseItem place) {
if (player.gamemode == GameMode.SPECTATOR || player.gamemode == GameMode.ADVENTURE) return;
ItemStack placedWith = player.getInventory().getHeldItem();
@@ -257,9 +255,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
}
// Check for interactable first (door, etc)
if (packet instanceof WrapperPlayClientPlayerBlockPlacement) {
WrapperPlayClientPlayerBlockPlacement place = (WrapperPlayClientPlayerBlockPlacement) packet;
if (packet instanceof WrapperPlayClientPlayerBlockPlacement place) {
ItemStack placedWith = player.getInventory().getHeldItem();
ItemStack offhand = player.getInventory().getOffHand();
@@ -295,8 +291,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
}
}
if (packet instanceof WrapperPlayClientPlayerBlockPlacement) {
WrapperPlayClientPlayerBlockPlacement place = (WrapperPlayClientPlayerBlockPlacement) packet;
if (packet instanceof WrapperPlayClientPlayerBlockPlacement place) {
Vector3i blockPosition = place.getBlockPosition();
BlockFace face = place.getFace();
@@ -375,6 +370,11 @@ public class CheckManagerListener extends PacketListenerAbstract {
return false;
}
// 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;
};
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getConnectionState() != ConnectionState.PLAY) return;
@@ -456,22 +456,24 @@ public class CheckManagerListener extends PacketListenerAbstract {
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING) {
WrapperPlayClientPlayerDigging dig = new WrapperPlayClientPlayerDigging(event);
WrappedBlockState block = player.compensatedWorld.getWrappedBlockStateAt(dig.getBlockPosition());
final Vector3i digPosition = dig.getBlockPosition();
WrappedBlockState block = player.compensatedWorld.getWrappedBlockStateAt(digPosition);
final StateType type = block.getType();
player.checkManager.getPacketCheck(BadPacketsX.class).handle(event, dig, block.getType());
player.checkManager.getPacketCheck(BadPacketsX.class).handle(event, dig, type);
player.checkManager.getPacketCheck(BadPacketsZ.class).handle(event, dig);
if (dig.getAction() == DiggingAction.FINISHED_DIGGING) {
// Not unbreakable
if (!block.getType().isAir() && block.getType().getHardness() != -1.0f && !event.isCancelled()) {
if (BREAKABLE.apply(type) && !event.isCancelled()) {
player.compensatedWorld.startPredicting();
player.compensatedWorld.updateBlock(dig.getBlockPosition().getX(), dig.getBlockPosition().getY(), dig.getBlockPosition().getZ(), 0);
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, dig.getBlockPosition());
double damage = BlockBreakSpeed.getBlockDamage(player, digPosition);
//Instant breaking, no damage means it is unbreakable by creative players (with swords)
if (damage >= 1) {
@@ -479,9 +481,9 @@ public class CheckManagerListener extends PacketListenerAbstract {
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(dig.getBlockPosition(), StateTypes.WATER.createBlockState(CompensatedWorld.blockVersion));
player.compensatedWorld.updateBlock(digPosition, StateTypes.WATER.createBlockState(CompensatedWorld.blockVersion));
} else {
player.compensatedWorld.updateBlock(dig.getBlockPosition().getX(), dig.getBlockPosition().getY(), dig.getBlockPosition().getZ(), 0);
player.compensatedWorld.updateBlock(digPosition.getX(), digPosition.getY(), digPosition.getZ(), 0);
}
player.compensatedWorld.stopPredicting(dig);
}
@@ -797,14 +799,14 @@ public class CheckManagerListener extends PacketListenerAbstract {
for (SimpleCollisionBox box : boxes) {
Pair<Vector, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
if (intercept.getFirst() == null) continue; // No intercept
if (intercept.first() == null) continue; // No intercept
Vector hitLoc = intercept.getFirst();
Vector hitLoc = intercept.first();
if (hitLoc.distanceSquared(startingVec) < bestHitResult) {
bestHitResult = hitLoc.distanceSquared(startingVec);
bestHitLoc = hitLoc;
bestFace = intercept.getSecond();
bestFace = intercept.second();
}
}
if (bestHitLoc != null) {
@@ -819,8 +821,8 @@ public class CheckManagerListener extends PacketListenerAbstract {
Pair<Vector, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
if (intercept.getFirst() != null) {
return new HitData(vector3i, intercept.getFirst(), intercept.getSecond(), block);
if (intercept.first() != null) {
return new HitData(vector3i, intercept.first(), intercept.second(), block);
}
}
@@ -1,9 +1,6 @@
package ac.grim.grimac.events.packets;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.checks.impl.movement.NoSlowC;
import ac.grim.grimac.checks.impl.movement.NoSlowD;
import ac.grim.grimac.checks.impl.movement.NoSlowE;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
import com.github.retrooper.packetevents.event.PacketListenerPriority;
@@ -98,6 +98,10 @@ public class PacketEntityReplication extends Check implements PacketCheck {
WrapperPlayServerSpawnPlayer packetOutEntity = new WrapperPlayServerSpawnPlayer(event);
addEntity(packetOutEntity.getEntityId(), packetOutEntity.getUUID(), EntityTypes.PLAYER, packetOutEntity.getPosition(), packetOutEntity.getYaw(), packetOutEntity.getPitch(), packetOutEntity.getEntityMetadata(), 0);
}
if (event.getPacketType() == PacketType.Play.Server.SPAWN_PAINTING) {
WrapperPlayServerSpawnPainting packetOutEntity = new WrapperPlayServerSpawnPainting(event);
addEntity(packetOutEntity.getEntityId(), packetOutEntity.getUUID(), EntityTypes.PAINTING, packetOutEntity.getPosition().toVector3d(), packetOutEntity.getDirection().getHorizontalIndex(), 0f, null, packetOutEntity.getType().isPresent() ? packetOutEntity.getType().get().getId() : -1);
}
if (event.getPacketType() == PacketType.Play.Server.ENTITY_RELATIVE_MOVE) {
WrapperPlayServerEntityRelativeMove move = new WrapperPlayServerEntityRelativeMove(event);
@@ -227,9 +231,8 @@ public class PacketEntityReplication extends Check implements PacketCheck {
if (status.getStatus() == 31) {
PacketEntity hook = player.compensatedEntities.getEntity(status.getEntityId());
if (!(hook instanceof PacketEntityHook)) return;
if (!(hook instanceof PacketEntityHook hookEntity)) return;
PacketEntityHook hookEntity = (PacketEntityHook) hook;
if (hookEntity.attached == player.entityID) {
player.sendTransaction();
// We don't transaction sandwich this, it's too rare to be a real problem.
@@ -454,8 +457,7 @@ public class PacketEntityReplication extends Check implements PacketCheck {
player.latencyUtils.addRealTimeTask(lastTrans, () -> {
PacketEntity entity = player.compensatedEntities.getEntity(entityId);
if (entity == null) return;
if (entity instanceof PacketEntityTrackXRot && yaw != null) {
PacketEntityTrackXRot xRotEntity = (PacketEntityTrackXRot) entity;
if (entity instanceof PacketEntityTrackXRot xRotEntity && yaw != null) {
xRotEntity.packetYaw = yaw;
xRotEntity.steps = entity.isBoat() ? 10 : 3;
}
@@ -8,6 +8,8 @@ import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
import com.github.retrooper.packetevents.event.PacketListenerPriority;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import com.github.retrooper.packetevents.protocol.item.enchantment.type.EnchantmentTypes;
@@ -72,6 +74,13 @@ public class PacketPlayerAttack extends PacketListenerAbstract {
player.maxPlayerAttackSlow = 1;
}
} else if (!isLegacyPlayer && player.isSprinting) {
// 1.9+ players who have attack speed cannot slow themselves twice in one tick because their attack cooldown gets reset on swing.
if (player.maxPlayerAttackSlow > 0
&& PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9)
&& player.compensatedEntities.getSelf().getAttributeValue(Attributes.GENERIC_ATTACK_SPEED) < 16) { // 16 is a reasonable limit
return;
}
// 1.9+ player who might have been slowed, but we can't be sure
player.maxPlayerAttackSlow += 1;
}
@@ -2,7 +2,6 @@ package ac.grim.grimac.events.packets;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.checks.impl.movement.NoSlowA;
import ac.grim.grimac.checks.impl.movement.NoSlowD;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
@@ -23,7 +22,6 @@ import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.protocol.player.InteractionHand;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.wrapper.play.client.*;
import org.bukkit.Bukkit;
public class PacketPlayerDigging extends PacketListenerAbstract {
@@ -172,10 +170,12 @@ public class PacketPlayerDigging extends PacketListenerAbstract {
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType())) {
final GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
if (player == null) return;
if (!player.packetStateData.lastPacketWasTeleport && !player.packetStateData.lastPacketWasOnePointSeventeenDuplicate) {
if (player.packetStateData.isSlowedByUsingItem() && player.packetStateData.eatingHand != InteractionHand.OFF_HAND && player.packetStateData.getSlowedByUsingItemSlot() != player.packetStateData.lastSlotSelected) {
if (player != null && player.packetStateData.isSlowedByUsingItem()
&& !player.packetStateData.lastPacketWasTeleport
&& !player.packetStateData.lastPacketWasOnePointSeventeenDuplicate) {
if (player.packetStateData.eatingHand != InteractionHand.OFF_HAND
&& player.packetStateData.getSlowedByUsingItemSlot() != player.packetStateData.lastSlotSelected
|| player.getInventory().getItemInHand(player.packetStateData.eatingHand).isEmpty()) {
player.packetStateData.setSlowedByUsingItem(false);
player.checkManager.getPostPredictionCheck(NoSlowA.class).didSlotChangeLastTick = true;
}
@@ -220,13 +220,7 @@ public class PacketPlayerDigging extends PacketListenerAbstract {
final ItemStack item = hand == InteractionHand.MAIN_HAND ?
player.getInventory().getHeldItem() : player.getInventory().getOffHand();
final boolean wasSlow = player.packetStateData.isSlowedByUsingItem();
handleUseItem(player, item, hand);
if (!wasSlow) {
player.checkManager.getPostPredictionCheck(NoSlowD.class).startedSprintingBeforeUse = player.packetStateData.isSlowedByUsingItem() && player.isSprinting;
}
}
}
}
@@ -10,7 +10,6 @@ import com.github.retrooper.packetevents.event.PacketListenerAbstract;
import com.github.retrooper.packetevents.event.PacketListenerPriority;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientSteerVehicle;
@@ -23,7 +22,7 @@ public class PacketPlayerSteer extends PacketListenerAbstract {
@Override
public void onPacketReceive(PacketReceiveEvent event) {
//TODO: add support for 1.21.2 with new vehicle move packet
if (event.getPacketType() == PacketType.Play.Client.STEER_VEHICLE) {
WrapperPlayClientSteerVehicle steer = new WrapperPlayClientSteerVehicle(event);
@@ -1,7 +1,6 @@
package ac.grim.grimac.events.packets;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.checks.impl.movement.NoSlowD;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.nmsutil.WatchableIndexUtil;
import com.github.retrooper.packetevents.PacketEvents;
@@ -209,8 +208,6 @@ public class PacketSelfMetadataListener extends PacketListenerAbstract {
// The above line is a hack to fake activate use item
player.packetStateData.setSlowedByUsingItem(isActive);
player.checkManager.getPostPredictionCheck(NoSlowD.class).startedSprintingBeforeUse = player.packetStateData.isSlowedByUsingItem() && player.isSprinting;
if (isActive) {
player.packetStateData.eatingHand = isOffhand ? InteractionHand.OFF_HAND : InteractionHand.MAIN_HAND;
}
@@ -6,14 +6,12 @@ import ac.grim.grimac.utils.data.Pair;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
import com.github.retrooper.packetevents.event.PacketListenerPriority;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.event.PacketSendEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.teleport.RelativeFlag;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientTeleportConfirm;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPlayerPositionAndLook;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerVehicleMove;
import org.bukkit.Location;
@@ -99,4 +99,4 @@ public class ProxyAlertMessenger extends PacketListenerAbstract {
if (!file.exists()) return false;
return YamlConfiguration.loadConfiguration(file).getBoolean(pathToValue);
}
}
}
@@ -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,18 +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.getEntityScheduler().execute(player.bukkitPlayer, GrimAPI.INSTANCE.getPlugin(), () -> {
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.
@@ -75,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
@@ -111,6 +114,6 @@ public class ResyncWorldUtil {
}
}
}
}, null, 0);
});
}
}
@@ -174,4 +174,4 @@ public class BasePacketWorldReader extends PacketListenerAbstract {
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(), () -> player.compensatedWorld.updateBlock(blockChange.getX(), blockChange.getY(), blockChange.getZ(), blockChange.getBlockId()));
}
}
}
}
@@ -15,7 +15,7 @@ import java.util.BitSet;
public class PacketWorldReaderEight extends BasePacketWorldReader {
@Override
public void handleMapChunkBulk(final GrimPlayer player, final PacketSendEvent event) {
PacketWrapper wrapper = new PacketWrapper(event);
PacketWrapper<?> wrapper = new PacketWrapper<>(event);
ByteBuf buffer = (ByteBuf) wrapper.getBuffer();
boolean skylight = wrapper.readBoolean();
@@ -47,7 +47,7 @@ public class PacketWorldReaderEight extends BasePacketWorldReader {
@Override
public void handleMapChunk(final GrimPlayer player, final PacketSendEvent event) {
PacketWrapper wrapper = new PacketWrapper(event);
PacketWrapper<?> wrapper = new PacketWrapper<>(event);
final int chunkX = wrapper.readInt();
final int chunkZ = wrapper.readInt();
@@ -5,10 +5,13 @@ import ac.grim.grimac.checks.impl.aim.AimDuplicateLook;
import ac.grim.grimac.checks.impl.aim.AimModulo360;
import ac.grim.grimac.checks.impl.aim.processor.AimProcessor;
import ac.grim.grimac.checks.impl.badpackets.*;
import ac.grim.grimac.checks.impl.combat.MultiInteractA;
import ac.grim.grimac.checks.impl.combat.MultiInteractB;
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;
@@ -32,7 +35,6 @@ import ac.grim.grimac.manager.init.start.SuperDebug;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.predictionengine.GhostBlockDetector;
import ac.grim.grimac.predictionengine.SneakingEstimator;
import ac.grim.grimac.utils.anticheat.LogUtil;
import ac.grim.grimac.utils.anticheat.update.*;
import ac.grim.grimac.utils.latency.CompensatedCooldown;
import ac.grim.grimac.utils.latency.CompensatedFireworks;
@@ -46,8 +48,11 @@ import org.bukkit.Bukkit;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
import java.util.concurrent.atomic.AtomicBoolean;
public class CheckManager {
private static boolean inited;
private static final AtomicBoolean initedAtomic = new AtomicBoolean(false);
ClassToInstanceMap<PacketCheck> packetChecks;
ClassToInstanceMap<PositionCheck> positionCheck;
@@ -134,6 +139,8 @@ public class CheckManager {
.put(NoSlowC.class, new NoSlowC(player))
.put(NoSlowD.class, new NoSlowD(player))
.put(NoSlowE.class, new NoSlowE(player))
.put(MultiInteractA.class, new MultiInteractA(player))
.put(MultiInteractB.class, new MultiInteractB(player))
.put(SetbackTeleportUtil.class, new SetbackTeleportUtil(player)) // Avoid teleporting to new position, update safe pos last
.put(CompensatedFireworks.class, player.compensatedFireworks)
.put(SneakingEstimator.class, new SneakingEstimator(player))
@@ -165,6 +172,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();
@@ -333,7 +341,12 @@ public class CheckManager {
}
private void init() {
// Fast non-thread safe check
if (inited) return;
// Slow thread safe check
if (!initedAtomic.compareAndSet(false, true)) return;
inited = true;
for (AbstractCheck check : allChecks.values()) {
if (check.getCheckName() != null) {
String permissionName = "grim.exempt." + check.getCheckName().toLowerCase();
@@ -346,7 +359,5 @@ public class CheckManager {
}
}
}
inited = true;
}
}
@@ -69,4 +69,4 @@ public class InitManager {
}
}
}
}
@@ -11,6 +11,7 @@ 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;
@@ -105,7 +106,7 @@ public class PunishmentManager implements ConfigReloadable {
original = MessageUtil.format(original
.replace("[alert]", alertString)
.replace("[proxy]", alertString)
.replace("%check_name%", check.getCheckName())
.replace("%check_name%", check.getDisplayName())
.replace("%experimental%", check.isExperimental() ? experimentalSymbol : "")
.replace("%vl%", vl)
.replace("%verbose%", verbose)
@@ -149,7 +150,7 @@ public class PunishmentManager implements ConfigReloadable {
if (command.command.equals("[webhook]")) {
String vl = group.violations.values().stream().filter((e) -> e == check).count() + "";
GrimAPI.INSTANCE.getDiscordManager().sendAlert(player, verbose, check.getCheckName(), vl);
GrimAPI.INSTANCE.getDiscordManager().sendAlert(player, verbose, check.getDisplayName(), vl);
} else if (command.command.equals("[proxy]")) {
ProxyAlertMessenger.sendPluginMessage(replaceAlertPlaceholders(command.getCommand(), group, check, proxyAlertString, verbose));
} else {
@@ -183,7 +184,7 @@ public class PunishmentManager implements ConfigReloadable {
group.violations.put(currentTime, check);
// Remove violations older than the defined time in the config
group.violations.entrySet().removeIf(time -> currentTime - time.getKey() > group.removeViolationsAfter);
group.violations.long2ObjectEntrySet().removeIf(time -> currentTime - time.getLongKey() > group.removeViolationsAfter);
}
}
}
@@ -195,7 +196,7 @@ class PunishGroup {
@Getter
List<ParsedCommand> commands;
@Getter
HashMap<Long, Check> violations = new HashMap<>();
Long2ObjectOpenHashMap<Check> violations = new Long2ObjectOpenHashMap<>();
@Getter
int removeViolationsAfter;
@@ -153,12 +153,12 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
VelocityData futureExplosion = player.checkManager.getExplosionHandler().getFutureExplosion();
// Velocity sets
if (futureKb.getFirst() != null) {
clientVel = futureKb.getSecond();
if (futureKb.first() != null) {
clientVel = futureKb.second();
}
// Explosion adds
if (futureExplosion != null && (futureKb.getFirst() == null || futureKb.getFirst().transaction < futureExplosion.transaction)) {
if (futureExplosion != null && (futureKb.first() == null || futureKb.first().transaction < futureExplosion.transaction)) {
clientVel.add(futureExplosion.vector);
}
@@ -328,16 +328,16 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
while (true) {
Pair<Integer, Vector3d> teleportPos = player.vehicleData.vehicleTeleports.peek();
if (teleportPos == null) break;
if (lastTransaction < teleportPos.getFirst()) {
if (lastTransaction < teleportPos.first()) {
break;
}
Vector3d position = teleportPos.getSecond();
Vector3d position = teleportPos.second();
if (position.getX() == x && position.getY() == y && position.getZ() == z) {
player.vehicleData.vehicleTeleports.poll();
return true;
} else if (lastTransaction > teleportPos.getFirst() + 1) {
} else if (lastTransaction > teleportPos.first() + 1) {
player.vehicleData.vehicleTeleports.poll();
// Vehicles have terrible netcode so just ignore it if the teleport wasn't from us setting the player back
@@ -71,14 +71,8 @@ public class SpectateManager implements Initable {
spectatingPlayers.remove(uuid);
}
private static class PreviousState {
public PreviousState(org.bukkit.GameMode gameMode, Location location) {
this.gameMode = gameMode;
this.location = location;
}
private record PreviousState(org.bukkit.GameMode gameMode, Location location) {
private final org.bukkit.GameMode gameMode;
private final Location location;
}
}
@@ -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);
}
}
@@ -12,6 +12,8 @@ import ac.grim.grimac.utils.lists.EvictingQueue;
import ac.grim.grimac.utils.math.GrimMath;
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;
@@ -20,7 +22,7 @@ import java.util.*;
public final class SuperDebug extends Check implements PostPredictionCheck {
private static final StringBuilder[] flags = new StringBuilder[256]; // 17 MB of logs in memory
Map<StringBuilder, Integer> continuedDebug = new HashMap<>();
Object2IntMap<StringBuilder> continuedDebug = new Object2IntOpenHashMap<>();
List<VectorData> predicted = new EvictingQueue<>(60);
List<Vector> actually = new EvictingQueue<>(60);
@@ -45,7 +47,7 @@ public final class SuperDebug extends Check implements PostPredictionCheck {
Location location = new Location(player.x, player.y, player.z, player.xRot, player.yRot, player.bukkitPlayer == null ? "null" : player.bukkitPlayer.getWorld().getName());
for (Iterator<Map.Entry<StringBuilder, Integer>> it = continuedDebug.entrySet().iterator(); it.hasNext(); ) {
for (Iterator<Object2IntMap.Entry<StringBuilder>> it = continuedDebug.object2IntEntrySet().iterator(); it.hasNext(); ) {
Map.Entry<StringBuilder, Integer> debug = it.next();
appendDebug(debug.getKey(), player.predictedVelocity, player.actualMovement, location, player.startTickClientVel, player.baseTickAddition, player.baseTickWaterPushing);
debug.setValue(debug.getValue() - 1);
@@ -287,4 +289,4 @@ public final class SuperDebug extends Check implements PostPredictionCheck {
return "x: " + x + " y: " + y + " z: " + z + " xRot: " + xRot + " yRot: " + yRot + " world: " + world;
}
}
}
}
@@ -42,7 +42,7 @@ public class TickEndEvent implements Initable {
// but whatever. At least plugins can't break it, I guess.
//
// Pledge injects into another list, so we should be safe injecting into this one
List<?> wrapper = Collections.synchronizedList(new HookedListWrapper<Object>(endOfTickObject) {
List<?> wrapper = Collections.synchronizedList(new HookedListWrapper<>(endOfTickObject) {
@Override
public void onIterator() {
hasTicked = true;
@@ -58,4 +58,4 @@ public class TickEndEvent implements Initable {
e.printStackTrace();
}
}
}
}
@@ -326,7 +326,7 @@ public class GrimPlayer implements GrimUser {
boolean hasID = false;
int skipped = 0;
for (Pair<Short, Long> iterator : transactionsSent) {
if (iterator.getFirst() == id) {
if (iterator.first() == id) {
hasID = true;
break;
}
@@ -347,9 +347,9 @@ public class GrimPlayer implements GrimUser {
lastTransactionReceived.incrementAndGet();
lastTransReceived = System.currentTimeMillis();
transactionPing = (System.nanoTime() - data.getSecond());
playerClockAtLeast = data.getSecond();
} while (data.getFirst() != id);
transactionPing = (System.nanoTime() - data.second());
playerClockAtLeast = data.second();
} while (data.first() != id);
// A transaction means a new tick, so apply any block places
CheckManagerListener.handleQueuedPlaces(this, false, 0, 0, System.currentTimeMillis());
@@ -357,7 +357,7 @@ public class GrimPlayer implements GrimUser {
}
// Were we the ones who sent the packet?
return data != null && data.getFirst() == id;
return data != null && data.first() == id;
}
public void baseTickAddWaterPushing(Vector vector) {
@@ -443,8 +443,7 @@ public class GrimPlayer implements GrimUser {
public void disconnect(Component reason) {
String textReason;
if (reason instanceof TranslatableComponent) {
TranslatableComponent translatableComponent = (TranslatableComponent) reason;
if (reason instanceof TranslatableComponent translatableComponent) {
textReason = translatableComponent.key();
} else {
textReason = LegacyComponentSerializer.legacySection().serialize(reason);
@@ -6,7 +6,6 @@ import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
public class GhostBlockDetector extends Check implements PostPredictionCheck {
@@ -258,8 +258,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
// When in control of the entity, the player sets the entity position to their current position
riding.setPositionRaw(GetBoundingBox.getPacketEntityBoundingBox(player, player.x, player.y, player.z, riding));
if (riding instanceof PacketEntityTrackXRot) {
PacketEntityTrackXRot boat = (PacketEntityTrackXRot) riding;
if (riding instanceof PacketEntityTrackXRot boat) {
boat.packetYaw = player.xRot;
boat.interpYaw = player.xRot;
boat.steps = 0;
@@ -339,7 +338,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
boolean clientClaimsRiptide = player.packetStateData.tryingToRiptide;
if (player.packetStateData.tryingToRiptide) {
long currentTime = System.currentTimeMillis();
boolean isInWater = player.compensatedWorld.isRaining || Collisions.hasMaterial(player, player.boundingBox.copy().expand(0.1f), (block) -> Materials.isWater(CompensatedWorld.blockVersion, block.getFirst()));
boolean isInWater = player.compensatedWorld.isRaining || Collisions.hasMaterial(player, player.boundingBox.copy().expand(0.1f), (block) -> Materials.isWater(CompensatedWorld.blockVersion, block.first()));
if (currentTime - player.packetStateData.lastRiptide < 450 || !isInWater) {
player.packetStateData.tryingToRiptide = false;
@@ -348,9 +347,9 @@ public class MovementCheckRunner extends Check implements PositionCheck {
player.packetStateData.lastRiptide = currentTime;
}
SimpleCollisionBox steppingOnBB = GetBoundingBox.getCollisionBoxForPlayer(player, player.x, player.y, player.z).expand(0.03).offset(0, -1, 0);
SimpleCollisionBox steppingOnBB = GetBoundingBox.getCollisionBoxForPlayer(player, player.x, player.y, player.z).expand(player.getMovementThreshold()).offset(0, -1, 0);
Collisions.hasMaterial(player, steppingOnBB, (pair) -> {
WrappedBlockState data = pair.getFirst();
WrappedBlockState data = pair.first();
if (data.getType() == StateTypes.SLIME_BLOCK && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_8)) {
player.uncertaintyHandler.isSteppingOnSlime = true;
player.uncertaintyHandler.isSteppingOnBouncyBlock = true;
@@ -396,8 +395,8 @@ public class MovementCheckRunner extends Check implements PositionCheck {
player.uncertaintyHandler.isNearGlitchyBlock = player.getClientVersion().isOlderThan(ClientVersion.V_1_9)
&& Collisions.hasMaterial(player, expandedBB.copy().expand(0.2),
checkData -> BlockTags.ANVIL.contains(checkData.getFirst().getType())
|| checkData.getFirst().getType() == StateTypes.CHEST || checkData.getFirst().getType() == StateTypes.TRAPPED_CHEST);
checkData -> BlockTags.ANVIL.contains(checkData.first().getType())
|| checkData.first().getType() == StateTypes.CHEST || checkData.first().getType() == StateTypes.TRAPPED_CHEST);
player.uncertaintyHandler.isOrWasNearGlitchyBlock = isGlitchy || player.uncertaintyHandler.isNearGlitchyBlock;
player.uncertaintyHandler.checkForHardCollision();
@@ -412,8 +411,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) {
@@ -1,6 +1,5 @@
package ac.grim.grimac.predictionengine;
import ac.grim.grimac.checks.impl.movement.NoSlowC;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.attribute.ValuedAttribute;
@@ -88,8 +87,6 @@ public class PlayerBaseTick {
if (player.compensatedEntities.getSelf().inVehicle()) player.isSlowMovement = false;
if (wasSlowMovement != player.isSlowMovement) player.checkManager.getPostPredictionCheck(NoSlowC.class).startedSprintingBeforeSlowMovement = player.isSlowMovement && player.isSprinting;
// Players in boats don't care about being in blocks
if (!player.compensatedEntities.getSelf().inVehicle()) {
this.moveTowardsClosestSpace(player.lastX - (player.boundingBox.maxX - player.boundingBox.minX) * 0.35, player.lastZ + (player.boundingBox.maxZ - player.boundingBox.minZ) * 0.35);
@@ -154,7 +151,7 @@ public class PlayerBaseTick {
// Might be null after respawn?
final Optional<WrapperPlayServerUpdateAttributes.Property> property = playerSpeed.property();
if (!property.isPresent()) return;
if (property.isEmpty()) return;
// The client first desync's this attribute
property.get().getModifiers().removeIf(modifier -> modifier.getUUID().equals(CompensatedEntities.SNOW_MODIFIER_UUID) || modifier.getName().getKey().equals("powder_snow"));
@@ -271,13 +268,14 @@ public class PlayerBaseTick {
private void moveTowardsClosestSpace(double xPosition, double zPosition) {
player.boundingBox = player.boundingBox.expand(0.03, 0, 0.03); // 0.03... thanks mojang!
double movementThreshold = player.getMovementThreshold();
player.boundingBox = player.boundingBox.expand(movementThreshold, 0, movementThreshold); // 0.03... thanks mojang!
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14)) {
moveTowardsClosestSpaceModern(xPosition, zPosition);
} else {
moveTowardsClosestSpaceLegacy(xPosition, zPosition);
}
player.boundingBox = player.boundingBox.expand(-0.03, 0, -0.03);
player.boundingBox = player.boundingBox.expand(-movementThreshold, 0, -movementThreshold);
}
// Mojang is incompetent and this will push the player out a lot when using elytras
@@ -363,22 +361,12 @@ public class PlayerBaseTick {
double d7 = direction2 == BlockFace.WEST || direction2 == BlockFace.EAST ? relativeXMovement : relativeZMovement;
d6 = direction2 == BlockFace.EAST || direction2 == BlockFace.SOUTH ? 1.0 - d7 : d7;
// d7 and d6 flip the movement direction based on desired movement direction
boolean doesSuffocate;
switch (direction2) {
case EAST:
doesSuffocate = this.suffocatesAt(blockX + 1, blockZ);
break;
case WEST:
doesSuffocate = this.suffocatesAt(blockX - 1, blockZ);
break;
case NORTH:
doesSuffocate = this.suffocatesAt(blockX, blockZ - 1);
break;
default:
case SOUTH:
doesSuffocate = this.suffocatesAt(blockX, blockZ + 1);
break;
}
boolean doesSuffocate = switch (direction2) {
case EAST -> this.suffocatesAt(blockX + 1, blockZ);
case WEST -> this.suffocatesAt(blockX - 1, blockZ);
case NORTH -> this.suffocatesAt(blockX, blockZ - 1);
default -> this.suffocatesAt(blockX, blockZ + 1);
};
if (d6 >= lowestValue || doesSuffocate) continue;
lowestValue = d6;
@@ -127,12 +127,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))) {
@@ -212,7 +213,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 +224,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;
@@ -248,21 +250,21 @@ public class PointThreeEstimator {
// Check for flowing water
Collisions.hasMaterial(player, pointThreeBox, (pair) -> {
final WrappedBlockState state = pair.getFirst();
final WrappedBlockState state = pair.first();
final StateType stateType = state.getType();
if (player.tagManager.block(SyncedTags.CLIMBABLE).contains(stateType) || (stateType == StateTypes.POWDER_SNOW && !player.compensatedEntities.getSelf().inVehicle() && player.getInventory().getBoots().getType() == ItemTypes.LEATHER_BOOTS)) {
isNearClimbable = true;
}
if (BlockTags.TRAPDOORS.contains(stateType)) {
isNearClimbable = isNearClimbable || Collisions.trapdoorUsableAsLadder(player, pair.getSecond().getX(), pair.getSecond().getY(), pair.getSecond().getZ(), state);
isNearClimbable = isNearClimbable || Collisions.trapdoorUsableAsLadder(player, pair.second().getX(), pair.second().getY(), pair.second().getZ(), state);
}
if (stateType == StateTypes.BUBBLE_COLUMN) {
isNearBubbleColumn = true;
}
if (Materials.isWater(player.getClientVersion(), pair.getFirst()) || pair.getFirst().getType() == StateTypes.LAVA) {
if (Materials.isWater(player.getClientVersion(), pair.first()) || pair.first().getType() == StateTypes.LAVA) {
isNearFluid = true;
}
@@ -3,7 +3,6 @@ package ac.grim.grimac.predictionengine;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.predictionengine.predictions.PredictionEngine;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.VectorData;
@@ -303,8 +303,7 @@ public class UncertaintyHandler {
// This is the end of that section.
// I can't figure out how the client exactly tracks boost time
if (player.compensatedEntities.getSelf().getRiding() instanceof PacketEntityRideable) {
PacketEntityRideable vehicle = (PacketEntityRideable) player.compensatedEntities.getSelf().getRiding();
if (player.compensatedEntities.getSelf().getRiding() instanceof PacketEntityRideable vehicle) {
if (vehicle.currentBoostTime < vehicle.boostTimeMax + 20)
offset -= 0.01;
}
@@ -339,10 +338,9 @@ public class UncertaintyHandler {
private boolean striderCollision(SimpleCollisionBox expandedBB) {
// Stiders can walk on top of other striders
if (player.compensatedEntities.getSelf().getRiding() instanceof PacketEntityStrider) {
for (Map.Entry<Integer, PacketEntity> entityPair : player.compensatedEntities.entityMap.int2ObjectEntrySet()) {
PacketEntity entity = entityPair.getValue();
if (entity.getType() == EntityTypes.STRIDER && entity != player.compensatedEntities.getSelf().getRiding() && !entity.hasPassenger(entityPair.getValue())
&& entity.getPossibleCollisionBoxes().isIntersected(expandedBB)) {
for (PacketEntity entity : player.compensatedEntities.entityMap.values()) {
if (entity.getType() == EntityTypes.STRIDER && entity != player.compensatedEntities.getSelf().getRiding()
&& !entity.hasPassenger(entity) && entity.getPossibleCollisionBoxes().isIntersected(expandedBB)) {
return true;
}
}
@@ -356,9 +354,9 @@ public class UncertaintyHandler {
final PacketEntity riding = player.compensatedEntities.getSelf().getRiding();
if (riding == null || !riding.isBoat()) return false;
for (Map.Entry<Integer, PacketEntity> entityPair : player.compensatedEntities.entityMap.int2ObjectEntrySet()) {
PacketEntity entity = entityPair.getValue();
if (entity != riding && entity.isPushable() && !riding.hasPassenger(entityPair.getValue()) && entity.getPossibleCollisionBoxes().isIntersected(expandedBB)) {
for (PacketEntity entity : player.compensatedEntities.entityMap.values()) {
if (entity != riding && entity.isPushable() && !riding.hasPassenger(entity)
&& entity.getPossibleCollisionBoxes().isIntersected(expandedBB)) {
return true;
}
}
@@ -22,7 +22,6 @@ import com.github.retrooper.packetevents.protocol.world.states.defaulttags.Block
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.util.Vector3d;
import org.bukkit.Bukkit;
import com.viaversion.viaversion.api.Via;
import io.github.retrooper.packetevents.util.viaversion.ViaVersionUtil;
import org.bukkit.util.Vector;
@@ -418,4 +417,4 @@ public class MovementTicker {
public boolean canStandOnLava() {
return false;
}
}
}
@@ -12,7 +12,6 @@ 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 com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import org.bukkit.util.Vector;
@@ -134,8 +133,8 @@ public class PredictionEngine {
// Returns pair of primary push movement, and then outputvel
Pair<Vector, Vector> output = doSeekingWallCollisions(player, primaryPushMovement, originalClientVel, clientVelAfterInput);
primaryPushMovement = output.getFirst();
Vector outputVel = clampMovementToHardBorder(player, output.getSecond());
primaryPushMovement = output.first();
Vector outputVel = clampMovementToHardBorder(player, output.second());
double resultAccuracy = outputVel.distanceSquared(player.actualMovement);
@@ -47,7 +47,7 @@ public class PredictionEngineNormal extends PredictionEngine {
// And 0.03 didn't affect onGround status
// The player cannot jump
final OptionalInt jumpBoost = player.compensatedEntities.getPotionLevelForPlayer(PotionTypes.JUMP_BOOST);
if (((!jumpBoost.isPresent() || jumpBoost.getAsInt() >= 0) && player.onGround) || !player.lastOnGround)
if (((jumpBoost.isEmpty() || jumpBoost.getAsInt() >= 0) && player.onGround) || !player.lastOnGround)
return;
JumpPower.jumpFromGround(player, jump);
@@ -17,9 +17,7 @@ import java.util.Set;
public class PredictionEngineRideableUtils {
public static Set<VectorData> handleJumps(GrimPlayer player, Set<VectorData> possibleVectors) {
if (!(player.compensatedEntities.getSelf().getRiding() instanceof PacketEntityHorse)) return possibleVectors;
PacketEntityHorse horse = (PacketEntityHorse) player.compensatedEntities.getSelf().getRiding();
if (!(player.compensatedEntities.getSelf().getRiding() instanceof PacketEntityHorse horse)) return possibleVectors;
// Setup player inputs
float f = player.vehicleData.vehicleHorizontal * 0.5F;
@@ -7,6 +7,7 @@ import ac.grim.grimac.utils.collisions.AxisUtil;
import ac.grim.grimac.utils.collisions.CollisionData;
import ac.grim.grimac.utils.collisions.blocks.DoorHandler;
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.ComplexCollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.HitData;
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
@@ -37,7 +38,6 @@ import lombok.Setter;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -72,6 +72,9 @@ public class BlockPlace {
@Getter private final boolean block;
// Allocated once instead of in functions to reduce new[] calls that need to be made. Since per-instance BlockPlace is always dealt with on the same thread we can use 1 buffer array
private final SimpleCollisionBox[] collisions = new SimpleCollisionBox[ComplexCollisionBox.DEFAULT_MAX_COLLISION_BOX_SIZE];
public BlockPlace(GrimPlayer player, InteractionHand hand, Vector3i blockPosition, int faceId, BlockFace face, ItemStack itemStack, HitData hitData) {
this.player = player;
this.hand = hand;
@@ -200,12 +203,12 @@ public class BlockPlace {
if (BlockTags.LEAVES.contains(data.getType())) return false;
if (BlockTags.FENCE_GATES.contains(data.getType())) return false;
List<SimpleCollisionBox> collisions = new ArrayList<>();
box.downCast(collisions);
int size = box.downCast(collisions);
AxisSelect axis = AxisUtil.getAxis(facing.getOppositeFace());
for (SimpleCollisionBox simpleBox : collisions) {
for (int i = 0; i < size; i++) {
SimpleCollisionBox simpleBox = collisions[i];
simpleBox = axis.modify(simpleBox);
if (simpleBox.minX <= 7 / 16d && simpleBox.maxX >= 7 / 16d
&& simpleBox.minY <= 0 && simpleBox.maxY >= 10 / 16d
@@ -225,12 +228,12 @@ public class BlockPlace {
if (isFullFace(facing)) return true;
if (BlockTags.LEAVES.contains(data.getType())) return false;
List<SimpleCollisionBox> collisions = new ArrayList<>();
box.downCast(collisions);
int size = box.downCast(collisions);
AxisSelect axis = AxisUtil.getAxis(facing.getOppositeFace());
for (SimpleCollisionBox simpleBox : collisions) {
for (int i = 0; i < size; i++) {
SimpleCollisionBox simpleBox = collisions[i];
simpleBox = axis.modify(simpleBox);
if (simpleBox.minX <= 2 / 16d && simpleBox.maxX >= 14 / 16d
&& simpleBox.minY <= 0 && simpleBox.maxY >= 1
@@ -289,10 +292,10 @@ public class BlockPlace {
}
}
List<SimpleCollisionBox> collisions = new ArrayList<>();
box.downCast(collisions);
int size = box.downCast(collisions);
for (SimpleCollisionBox simpleBox : collisions) {
for (int i = 0; i < size; i++) {
SimpleCollisionBox simpleBox = collisions[i];
if (axis.modify(simpleBox).isFullBlockNoCache()) return true;
}
@@ -318,12 +321,12 @@ public class BlockPlace {
if (isFullFace(facing)) return true;
if (BlockTags.LEAVES.contains(data.getType())) return false;
List<SimpleCollisionBox> collisions = new ArrayList<>();
box.downCast(collisions);
int size = box.downCast(collisions);
AxisSelect axis = AxisUtil.getAxis(facing.getOppositeFace());
for (SimpleCollisionBox simpleBox : collisions) {
for (int i = 0; i < size; i++) {
SimpleCollisionBox simpleBox = collisions[i];
simpleBox = axis.modify(simpleBox);
// If all sides to the box have width, there is collision.
switch (facing) {
@@ -537,21 +540,14 @@ public class BlockPlace {
}
public Vector3i getNormalBlockFace() {
switch (face) {
default:
case UP:
return new Vector3i(0, 1, 0);
case DOWN:
return new Vector3i(0, -1, 0);
case SOUTH:
return new Vector3i(0, 0, 1);
case NORTH:
return new Vector3i(0, 0, -1);
case WEST:
return new Vector3i(-1, 0, 0);
case EAST:
return new Vector3i(1, 0, 0);
}
return switch (face) {
case DOWN -> new Vector3i(0, -1, 0);
case SOUTH -> new Vector3i(0, 0, 1);
case NORTH -> new Vector3i(0, 0, -1);
case WEST -> new Vector3i(-1, 0, 0);
case EAST -> new Vector3i(1, 0, 0);
default -> new Vector3i(0, 1, 0);
};
}
public void set(StateType material) {
@@ -672,7 +668,7 @@ public class BlockPlace {
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).getFirst();
Vector intercept = ReachUtils.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!)
@@ -16,4 +16,4 @@ public final class PositionUpdate {
private final SetBackData setback;
private final TeleportData teleportData;
private boolean isTeleport;
}
}
@@ -107,17 +107,11 @@ public class ConsumesBlockPlace {
Attachment attachment = bell.getAttachment();
BlockFace dir2 = BlockFace.valueOf(direction.name());
switch (attachment) {
case FLOOR:
return AxisUtil.isSameAxis(dir, dir2);
case SINGLE_WALL:
case DOUBLE_WALL:
return !AxisUtil.isSameAxis(dir, dir2);
case CEILING:
return true;
default:
return false;
}
return switch (attachment) {
case FLOOR -> AxisUtil.isSameAxis(dir, dir2);
case SINGLE_WALL, DOUBLE_WALL -> !AxisUtil.isSameAxis(dir, dir2);
case CEILING -> true;
};
} else {
return false;
}
@@ -18,64 +18,42 @@ public class BlockFaceHelper {
*/
@Deprecated
public static BlockFace fromBukkitFace(org.bukkit.block.BlockFace face) {
switch (face) {
case NORTH:
return BlockFace.NORTH;
case SOUTH:
return BlockFace.SOUTH;
case WEST:
return BlockFace.WEST;
case EAST:
return BlockFace.EAST;
case UP:
return BlockFace.UP;
case DOWN:
return BlockFace.DOWN;
default:
return BlockFace.OTHER;
}
return switch (face) {
case NORTH -> BlockFace.NORTH;
case SOUTH -> BlockFace.SOUTH;
case WEST -> BlockFace.WEST;
case EAST -> BlockFace.EAST;
case UP -> BlockFace.UP;
case DOWN -> BlockFace.DOWN;
default -> BlockFace.OTHER;
};
}
public static BlockFace getClockWise(BlockFace face) {
switch (face) {
case NORTH:
return BlockFace.EAST;
case SOUTH:
return BlockFace.WEST;
case WEST:
return BlockFace.NORTH;
case EAST:
default:
return BlockFace.SOUTH;
}
return switch (face) {
case NORTH -> BlockFace.EAST;
case SOUTH -> BlockFace.WEST;
case WEST -> BlockFace.NORTH;
default -> BlockFace.SOUTH;
};
}
public static BlockFace getPEClockWise(BlockFace face) {
switch (face) {
case NORTH:
return BlockFace.EAST;
case SOUTH:
return BlockFace.WEST;
case WEST:
return BlockFace.NORTH;
case EAST:
default:
return BlockFace.SOUTH;
}
return switch (face) {
case NORTH -> BlockFace.EAST;
case SOUTH -> BlockFace.WEST;
case WEST -> BlockFace.NORTH;
default -> BlockFace.SOUTH;
};
}
public static BlockFace getCounterClockwise(BlockFace face) {
switch (face) {
case NORTH:
return BlockFace.WEST;
case SOUTH:
return BlockFace.EAST;
case WEST:
return BlockFace.SOUTH;
case EAST:
default:
return BlockFace.NORTH;
}
return switch (face) {
case NORTH -> BlockFace.WEST;
case SOUTH -> BlockFace.EAST;
case WEST -> BlockFace.SOUTH;
default -> BlockFace.NORTH;
};
}
public Vector offset(Vector toOffset, BlockFace face) {
@@ -4,4 +4,4 @@ import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
public interface AxisSelect {
SimpleCollisionBox modify(SimpleCollisionBox box);
}
}
@@ -54,36 +54,22 @@ public enum AxisUtil {
}
public static AxisSelect getAxis(BlockFace face) {
switch (face) {
case EAST:
return EAST.select;
case WEST:
return WEST.select;
case NORTH:
return NORTH.select;
case SOUTH:
return SOUTH.select;
case UP:
return UP.select;
case DOWN:
default:
return DOWN.select;
}
return switch (face) {
case EAST -> EAST.select;
case WEST -> WEST.select;
case NORTH -> NORTH.select;
case SOUTH -> SOUTH.select;
case UP -> UP.select;
default -> DOWN.select;
};
}
public static boolean isSameAxis(BlockFace one, BlockFace two) {
switch (one) {
case WEST:
case EAST:
return two == BlockFace.WEST || two == BlockFace.EAST;
case NORTH:
case SOUTH:
return two == BlockFace.NORTH || two == BlockFace.SOUTH;
case UP:
case DOWN:
return two == BlockFace.UP || two == BlockFace.DOWN;
default:
return false;
}
return switch (one) {
case WEST, EAST -> two == BlockFace.WEST || two == BlockFace.EAST;
case NORTH, SOUTH -> two == BlockFace.NORTH || two == BlockFace.SOUTH;
case UP, DOWN -> two == BlockFace.UP || two == BlockFace.DOWN;
default -> false;
};
}
}
File diff suppressed because it is too large. Load diff
@@ -1,8 +1,9 @@
package ac.grim.grimac.utils.collisions;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicFence;
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicWall;
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicHitboxFence;
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicHitboxPane;
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicHitboxWall;
import ac.grim.grimac.utils.collisions.datatypes.*;
import ac.grim.grimac.utils.nmsutil.Materials;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
@@ -17,55 +18,41 @@ import java.util.*;
// Expansion to the CollisionData class, which is different than regular ray tracing hitboxes
public enum HitboxData {
SCAFFOLDING((player, item, version, data, x, y, z) -> {
// If is holding scaffolding
if (item == StateTypes.SCAFFOLDING) {
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
RAILS((player, item, version, data, x, y, z) -> {
return switch (data.getShape()) {
case ASCENDING_NORTH, ASCENDING_SOUTH, ASCENDING_EAST, ASCENDING_WEST -> {
if (version.isOlderThan(ClientVersion.V_1_8)) {
StateType railType = data.getType();
// Activator rails always appear as flat detector rails in 1.7.10 because of ViaVersion
// Ascending power rails in 1.7 have flat rail hitbox https://bugs.mojang.com/browse/MC-9134
if (railType == StateTypes.ACTIVATOR_RAIL || (railType == StateTypes.POWERED_RAIL && data.isPowered())) {
yield new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F, false);
}
yield new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.625F, 1.0F, false);
} else if (version.isOlderThan(ClientVersion.V_1_9)) {
yield new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.625F, 1.0F, false);
} else if (version.isNewerThanOrEquals(ClientVersion.V_1_9) && version.isOlderThan(ClientVersion.V_1_10)) {
// https://bugs.mojang.com/browse/MC-89552 sloped rails in 1.9 - it is slightly taller than a regular rail
yield new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.1875F, 1.0F, false);
} else if (version.isOlderThan(ClientVersion.V_1_11)) {
// https://bugs.mojang.com/browse/MC-102638 All sloped rails are full blocks in 1.10
yield new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}
yield new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D);
}
default -> new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D);
};
}, BlockTags.RAILS.getStates().toArray(new StateType[0])),
END_PORTAL((player, item, version, data, x, y, z) -> {
if (version.isOlderThan(ClientVersion.V_1_9)) {
return new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 0.0625D, 1.0D);
} else if (version.isOlderThan(ClientVersion.V_1_17)) {
return new SimpleCollisionBox(0.0, 0.0D, 0.0D, 1.0D, 0.75D, 1.0D);
}
// STABLE_SHAPE for the scaffolding
ComplexCollisionBox box = new ComplexCollisionBox(
new HexCollisionBox(0.0D, 14.0D, 0.0D, 16.0D, 16.0D, 16.0D),
new HexCollisionBox(0.0D, 0.0D, 0.0D, 2.0D, 16.0D, 2.0D),
new HexCollisionBox(14.0D, 0.0D, 0.0D, 16.0D, 16.0D, 2.0D),
new HexCollisionBox(0.0D, 0.0D, 14.0D, 2.0D, 16.0D, 16.0D),
new HexCollisionBox(14.0D, 0.0D, 14.0D, 16.0D, 16.0D, 16.0D));
if (data.getHalf() == Half.LOWER) { // Add the unstable shape to the collision boxes
box.add(new HexCollisionBox(0.0D, 0.0D, 0.0D, 2.0D, 2.0D, 16.0D));
box.add(new HexCollisionBox(14.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D));
box.add(new HexCollisionBox(0.0D, 0.0D, 14.0D, 16.0D, 2.0D, 16.0D));
box.add(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 2.0D));
}
return box;
}, StateTypes.SCAFFOLDING),
DRIPLEAF((player, item, version, data, x, y, z) -> {
if (version.isOlderThanOrEquals(ClientVersion.V_1_16_4))
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
ComplexCollisionBox box = new ComplexCollisionBox();
if (data.getFacing() == BlockFace.NORTH) { // Stem
box.add(new HexCollisionBox(5.0D, 0.0D, 9.0D, 11.0D, 15.0D, 15.0D));
} else if (data.getFacing() == BlockFace.SOUTH) {
box.add(new HexCollisionBox(5.0D, 0.0D, 1.0D, 11.0D, 15.0D, 7.0D));
} else if (data.getFacing() == BlockFace.EAST) {
box.add(new HexCollisionBox(1.0D, 0.0D, 5.0D, 7.0D, 15.0D, 11.0D));
} else {
box.add(new HexCollisionBox(9.0D, 0.0D, 5.0D, 15.0D, 15.0D, 11.0D));
}
if (data.getTilt() == Tilt.NONE || data.getTilt() == Tilt.UNSTABLE) {
box.add(new HexCollisionBox(0.0, 11.0, 0.0, 16.0, 15.0, 16.0));
} else if (data.getTilt() == Tilt.PARTIAL) {
box.add(new HexCollisionBox(0.0, 11.0, 0.0, 16.0, 13.0, 16.0));
}
return box;
}, StateTypes.BIG_DRIPLEAF),
return new HexCollisionBox(0.0D, 6.0D, 0.0D, 16.0D, 12.0D, 16.0D);
}, StateTypes.END_PORTAL),
FENCE_GATE((player, item, version, data, x, y, z) -> {
// This technically should be taken from the block data/made multi-version/run block updates... but that's too far even for me
@@ -90,41 +77,214 @@ public enum HitboxData {
}, BlockTags.FENCE_GATES.getStates().toArray(new StateType[0])),
FENCE((player, item, version, data, x, y, z) -> {
WrappedBlockState state = player.compensatedWorld.getWrappedBlockStateAt(x, y, z);
FENCE(new DynamicHitboxFence(), BlockTags.FENCES.getStates().toArray(new StateType[0])),
if (version.isOlderThanOrEquals(ClientVersion.V_1_12_2)) {
int i = 0;
if (data.getSouth() == South.TRUE) {
i |= 0b1;
}
if (data.getWest() == West.TRUE) {
i |= 0b10;
}
if (data.getNorth() == North.TRUE) {
i |= 0b100;
}
if (data.getEast() == East.TRUE) {
i |= 0b1000;
}
PANE(new DynamicHitboxPane(), Materials.getPanes().toArray(new StateType[0])),
return DynamicFence.LEGACY_BOUNDING_BOXES[i].copy();
LEVER(((player, item, version, data, x, y, z) -> {
Face face = data.getFace();
BlockFace facing = data.getFacing();
if (version.isOlderThan(ClientVersion.V_1_13)) {
double f = 0.1875;
switch (face) {
case WALL:
switch (facing) {
case WEST:
return new SimpleCollisionBox(1.0 - f * 2.0, 0.2, 0.5 - f, 1.0, 0.8, 0.5 + f, false);
case EAST:
return new SimpleCollisionBox(0.0, 0.2, 0.5 - f, f * 2.0, 0.8, 0.5 + f, false);
case NORTH:
return new SimpleCollisionBox(0.5 - f, 0.2, 1.0 - f * 2.0, 0.5 + f, 0.8, 1.0, false);
case SOUTH:
return new SimpleCollisionBox(0.5 - f, 0.2, 0.0, 0.5 + f, 0.8, f * 2.0, false);
}
case CEILING:
return new SimpleCollisionBox(0.25, 0.4, 0.25, 0.75, 1.0, 0.75, false);
case FLOOR:
return new SimpleCollisionBox(0.25, 0.0, 0.25, 0.75, 0.6, 0.75, false);
}
}
List<SimpleCollisionBox> boxes = new ArrayList<>();
CollisionData.getData(state.getType()).getMovementCollisionBox(player, version, state).downCast(boxes);
return switch (face) {
case FLOOR -> {
// X-AXIS
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
yield new SimpleCollisionBox(0.25, 0.0, 0.3125, 0.75, 0.375, 0.6875, false);
}
// Z-AXIS
yield new SimpleCollisionBox(0.3125, 0.0, 0.25, 0.6875, 0.375, 0.75, false);
// Z-AXIS
}
case WALL -> switch (facing) {
case EAST -> new SimpleCollisionBox(0.0, 0.25, 0.3125, 0.375, 0.75, 0.6875, false);
case WEST -> new SimpleCollisionBox(0.625, 0.25, 0.3125, 1.0, 0.75, 0.6875, false);
case SOUTH -> new SimpleCollisionBox(0.3125, 0.25, 0.0, 0.6875, 0.75, 0.375, false);
default -> new SimpleCollisionBox(0.3125, 0.25, 0.625, 0.6875, 0.75, 1.0, false);
};
default -> {
// X-AXIS
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
yield new SimpleCollisionBox(0.25, 0.625, 0.3125, 0.75, 1.0, 0.6875, false);
}
// Z-Axis
yield new SimpleCollisionBox(0.3125, 0.625, 0.25, 0.6875, 1.0, 0.75, false);
}
};
}), StateTypes.LEVER),
for (SimpleCollisionBox box : boxes) {
box.maxY = 1;
BUTTON((player, item, version, data, x, y, z) -> {
final Face face = data.getFace();
final BlockFace facing = data.getFacing();
final boolean powered = data.isPowered();
if (version.isOlderThan(ClientVersion.V_1_13)) {
double f2 = (float) (data.isPowered() ? 1 : 2) / 16.0;
switch (face) {
case WALL:
switch (facing) {
case WEST:
return new SimpleCollisionBox(1.0 - f2, 0.375, 0.3125, 1.0, 0.625, 0.6875, false);
case EAST:
return new SimpleCollisionBox(0.0, 0.375, 0.3125, f2, 0.625, 0.6875, false);
case NORTH:
return new SimpleCollisionBox(0.3125, 0.375, 1.0 - f2, 0.6875, 0.625, 1.0, false);
case SOUTH:
return new SimpleCollisionBox(0.3125, 0.375, 0.0, 0.6875, 0.625, f2, false);
}
case CEILING:
return new SimpleCollisionBox(0.3125, 1.0 - f2, 0.375, 0.6875, 1.0, 0.625, false);
case FLOOR:
return new SimpleCollisionBox(0.3125, 0.0, 0.375, 0.6875, 0.0 + f2, 0.625, false);
}
}
return new ComplexCollisionBox(boxes.toArray(new SimpleCollisionBox[0]));
}, BlockTags.FENCES.getStates().toArray(new StateType[0])),
WALL((player, item, version, data, x, y, z) -> {
WrappedBlockState state = player.compensatedWorld.getWrappedBlockStateAt(x, y, z);
return new DynamicWall().fetchRegularBox(player, state, version, x, y, z);
}, BlockTags.WALLS.getStates().toArray(new StateType[0])),
switch (face) {
case WALL:
return switch (facing) {
case EAST ->
powered ? new HexCollisionBox(0.0, 6.0, 5.0, 1.0, 10.0, 11.0) : new HexCollisionBox(0.0, 6.0, 5.0, 2.0, 10.0, 11.0);
case WEST ->
powered ? new HexCollisionBox(15.0, 6.0, 5.0, 16.0, 10.0, 11.0) : new HexCollisionBox(14.0, 6.0, 5.0, 16.0, 10.0, 11.0);
case SOUTH ->
powered ? new HexCollisionBox(5.0, 6.0, 0.0, 11.0, 10.0, 1.0) : new HexCollisionBox(5.0, 6.0, 0.0, 11.0, 10.0, 2.0);
case NORTH, UP, DOWN ->
powered ? new HexCollisionBox(5.0, 6.0, 15.0, 11.0, 10.0, 16.0) : new HexCollisionBox(5.0, 6.0, 14.0, 11.0, 10.0, 16.0);
default -> NoCollisionBox.INSTANCE;
};
case CEILING:
// ViaVersion shows lever
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_8)) {
return LEVER.dynamic.fetch(player, item, version, data, x, y, z);
}
// x axis
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
return powered ? new HexCollisionBox(6.0, 15.0, 5.0, 10.0, 16.0, 11.0) : new HexCollisionBox(6.0, 14.0, 5.0, 10.0, 16.0, 11.0);
} else {
return powered ? new HexCollisionBox(5.0, 15.0, 6.0, 11.0, 16.0, 10.0) : new HexCollisionBox(5.0, 14.0, 6.0, 11.0, 16.0, 10.0);
}
case FLOOR:
// ViaVersion shows lever
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_8)) {
return LEVER.dynamic.fetch(player, item, version, data, x, y, z);
}
// x axis
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
return powered ? new HexCollisionBox(6.0, 0.0, 5.0, 10.0, 1.0, 11.0) : new HexCollisionBox(6.0, 0.0, 5.0, 10.0, 2.0, 11.0);
}
return powered ? new HexCollisionBox(5.0, 0.0, 6.0, 11.0, 1.0, 10.0) : new HexCollisionBox(5.0, 0.0, 6.0, 11.0, 2.0, 10.0);
default:
throw new IllegalStateException("Impossible Hitbox State");
}
}, BlockTags.BUTTONS.getStates().toArray(new StateType[0])),
WALL(new DynamicHitboxWall(), BlockTags.WALLS.getStates().toArray(new StateType[0])),
WALL_SIGN((player, item, version, data, x, y, z) -> {
return switch (data.getFacing()) {
case NORTH -> new HexCollisionBox(0.0, 4.5, 14.0, 16.0, 12.5, 16.0);
case SOUTH -> new HexCollisionBox(0.0, 4.5, 0.0, 16.0, 12.5, 2.0);
case EAST -> new HexCollisionBox(0.0, 4.5, 0.0, 2.0, 12.5, 16.0);
case WEST -> new HexCollisionBox(14.0, 4.5, 0.0, 16.0, 12.5, 16.0);
default -> NoCollisionBox.INSTANCE;
};
}, BlockTags.WALL_SIGNS.getStates().toArray(new StateType[0])),
WALL_HANGING_SIGN((player, item, version, data, x, y, z) -> {
return switch (data.getFacing()) {
case NORTH, SOUTH -> new ComplexCollisionBox(2,
new HexCollisionBox(0.0D, 14.0D, 6.0D, 16.0D, 16.0D, 10.0D),
new HexCollisionBox(1.0D, 0.0D, 7.0D, 15.0D, 10.0D, 9.0D));
default -> new ComplexCollisionBox(2,
new HexCollisionBox(6.0D, 14.0D, 0.0D, 10.0D, 16.0D, 16.0D),
new HexCollisionBox(7.0D, 0.0D, 1.0D, 9.0D, 10.0D, 15.0D));
};
}, BlockTags.WALL_HANGING_SIGNS.getStates().toArray(new StateType[0])),
STANDING_SIGN((player, item, version, data, x, y, z) ->
new HexCollisionBox(4.0, 0.0, 4.0, 12.0, 16.0, 12.0),
BlockTags.STANDING_SIGNS.getStates().toArray(new StateType[0])),
SAPLING(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 12.0D, 14.0D),
BlockTags.SAPLINGS.getStates().toArray(new StateType[0])),
ROOTS(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 13.0D, 14.0D),
StateTypes.WARPED_ROOTS, StateTypes.CRIMSON_ROOTS),
BANNER(new HexCollisionBox(4.0D, 0.0D, 4.0D, 12.0D, 16.0D, 12.0D),
StateTypes.WHITE_BANNER, StateTypes.ORANGE_BANNER, StateTypes.MAGENTA_BANNER, StateTypes.LIGHT_BLUE_BANNER,
StateTypes.YELLOW_BANNER, StateTypes.LIME_BANNER, StateTypes.PINK_BANNER, StateTypes.GRAY_BANNER,
StateTypes.LIGHT_GRAY_BANNER, StateTypes.CYAN_BANNER, StateTypes.PURPLE_BANNER, StateTypes.BLUE_BANNER,
StateTypes.BROWN_BANNER, StateTypes.GREEN_BANNER, StateTypes.RED_BANNER, StateTypes.BLACK_BANNER),
WALL_BANNER((player, item, version, data, x, y, z) -> {
// ViaVersion replacement block
if (version.isOlderThan(ClientVersion.V_1_8)) {
return WALL_SIGN.dynamic.fetch(player, item, version, data, x, y, z);
}
return switch (data.getFacing()) {
case NORTH -> new HexCollisionBox(0.0, 0.0, 14.0, 16.0, 12.5, 16.0);
case EAST -> new HexCollisionBox(0.0, 0.0, 0.0, 2.0, 12.5, 16.0);
case WEST -> new HexCollisionBox(14.0, 0.0, 0.0, 16.0, 12.5, 16.0);
case SOUTH -> new HexCollisionBox(0.0, 0.0, 0.0, 16.0, 12.5, 2.0);
default -> throw new IllegalStateException("Impossible Banner Facing State; Something very wrong is going on");
};
}, StateTypes.WHITE_WALL_BANNER, StateTypes.ORANGE_WALL_BANNER, StateTypes.MAGENTA_WALL_BANNER,
StateTypes.LIGHT_BLUE_WALL_BANNER, StateTypes.YELLOW_WALL_BANNER, StateTypes.LIME_WALL_BANNER,
StateTypes.PINK_WALL_BANNER, StateTypes.GRAY_WALL_BANNER, StateTypes.LIGHT_GRAY_WALL_BANNER,
StateTypes.CYAN_WALL_BANNER, StateTypes.PURPLE_WALL_BANNER, StateTypes.BLUE_WALL_BANNER,
StateTypes.BROWN_WALL_BANNER, StateTypes.GREEN_WALL_BANNER, StateTypes.RED_WALL_BANNER, StateTypes.BLACK_WALL_BANNER),
BREWING_STAND((player, item, version, block, x, y, z) -> {
// BEWARE OF https://bugs.mojang.com/browse/MC-85109 FOR 1.8 PLAYERS
if (version.isOlderThan(ClientVersion.V_1_13)) {
return new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F);
} else {
return new ComplexCollisionBox(2,
new HexCollisionBox(1.0, 0.0, 1.0, 15.0, 2.0, 15.0),
new SimpleCollisionBox(0.4375, 0.0, 0.4375, 0.5625, 0.875, 0.5625, false));
}
}, StateTypes.BREWING_STAND),
SMALL_FLOWER((player, item, version, data, x, y, z) -> player.getClientVersion().isOlderThan(ClientVersion.V_1_13)
? new SimpleCollisionBox(0.3125D, 0.0D, 0.3125D, 0.6875D, 0.625D, 0.6875D)
: new OffsetCollisionBox(data.getType(), 0.3125D, 0.0D, 0.3125D, 0.6875D, 0.625D, 0.6875D),
BlockTags.SMALL_FLOWERS.getStates().toArray(new StateType[0])),
TALL_FLOWERS(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), BlockTags.TALL_FLOWERS.getStates().toArray(new StateType[0])),
FIRE((player, item, version, data, x, y, z) -> {
// Since 1.16 fire has a small hitbox
if (version.isNewerThanOrEquals(ClientVersion.V_1_16)) {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 1.0D, 16.0D);
}
return NoCollisionBox.INSTANCE;
}, BlockTags.FIRE.getStates().toArray(new StateType[0])),
HONEY_BLOCK(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.HONEY_BLOCK),
@@ -132,14 +292,21 @@ public enum HitboxData {
SOUL_SAND(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.SOUL_SAND),
CACTUS(new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D), StateTypes.CACTUS),
CACTUS((player, item, version, data, x, y, z) -> {
if (version.isOlderThan(ClientVersion.V_1_13)) {
// https://bugs.mojang.com/browse/MC-59610
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}
return new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D);
}, StateTypes.CACTUS),
SNOW((player, item, version, data, x, y, z) -> {
return new SimpleCollisionBox(0, 0, 0, 1, data.getLayers() * 0.125, 1);
}, StateTypes.SNOW),
LECTERN_BLOCK((player, item, version, data, x, y, z) -> {
ComplexCollisionBox common = new ComplexCollisionBox(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D),
ComplexCollisionBox common = new ComplexCollisionBox(5,
new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D),
new HexCollisionBox(4.0D, 2.0D, 4.0D, 12.0D, 14.0D, 12.0D));
if (data.getFacing() == BlockFace.WEST) {
@@ -163,126 +330,261 @@ public enum HitboxData {
return common;
}, StateTypes.LECTERN),
WALL_HANGING_SIGNS((player, item, version, data, x, y, z) -> {
switch (data.getFacing()) {
case NORTH:
case SOUTH:
return new ComplexCollisionBox(new HexCollisionBox(0.0D, 14.0D, 6.0D, 16.0D, 16.0D, 10.0D),
new HexCollisionBox(1.0D, 0.0D, 7.0D, 15.0D, 10.0D, 9.0D));
case WEST:
case EAST:
return new ComplexCollisionBox(new HexCollisionBox(6.0D, 14.0D, 0.0D, 10.0D, 16.0D, 16.0D),
new HexCollisionBox(7.0D, 0.0D, 1.0D, 9.0D, 10.0D, 15.0D));
default:
return NoCollisionBox.INSTANCE;
GLOW_LICHEN_SCULK_VEIN((player, item, version, data, x, y, z) -> {
if (version.isNewerThan(ClientVersion.V_1_16_4)) {
ComplexCollisionBox box = new ComplexCollisionBox(6);
if (data.isUp()) {
box.add(new HexCollisionBox(0.0D, 15.0D, 0.0D, 16.0D, 16.0D, 16.0D));
}
if (data.isDown()) {
box.add(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 1.0D, 16.0D));
}
if (data.getWest() == West.TRUE) {
box.add(new HexCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 16.0D, 16.0D));
}
if (data.getEast() == East.TRUE) {
box.add(new HexCollisionBox(15.0D, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D));
}
if (data.getNorth() == North.TRUE) {
box.add(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, 1.0D));
}
if (data.getSouth() == South.TRUE) {
box.add(new HexCollisionBox(0.0D, 0.0D, 15.0D, 16.0D, 16.0D, 16.0D));
}
return box;
} else { // ViaVersion just replaces this with... nothing
return NoCollisionBox.INSTANCE;
}
}, BlockTags.WALL_HANGING_SIGNS.getStates().toArray(new StateType[0])),
}, StateTypes.GLOW_LICHEN, StateTypes.SCULK_CATALYST),
SPORE_BLOSSOM((player, item, version, data, x, y, z) -> {
if (version.isNewerThan(ClientVersion.V_1_16_4)) {
return new HexCollisionBox(2.0D, 13.0D, 2.0D, 14.0D, 16.0D, 14.0D);
} else { // ViaVersion replacement is a Peony
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}
}, StateTypes.SPORE_BLOSSOM),
PITCHER_CROP((player, item, version, data, x, y, z) -> {
final SimpleCollisionBox FULL_UPPER_SHAPE = new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 15.0D, 13.0D);
final SimpleCollisionBox FULL_LOWER_SHAPE = new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 16.0D, 13.0D);
final SimpleCollisionBox COLLISION_SHAPE_BULB = new HexCollisionBox(5.0D, -1.0D, 5.0D, 11.0D, 3.0D, 11.0D);
final SimpleCollisionBox COLLISION_SHAPE_CROP = new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 5.0D, 13.0D);
final SimpleCollisionBox[] UPPER_SHAPE_BY_AGE = new SimpleCollisionBox[]{new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 11.0D, 13.0D), FULL_UPPER_SHAPE};
final SimpleCollisionBox[] LOWER_SHAPE_BY_AGE = new SimpleCollisionBox[]{COLLISION_SHAPE_BULB, new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 14.0D, 13.0D), FULL_LOWER_SHAPE, FULL_LOWER_SHAPE, FULL_LOWER_SHAPE};
if (version.isNewerThan(ClientVersion.V_1_19_4)) {
final SimpleCollisionBox FULL_UPPER_SHAPE = new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 15.0D, 13.0D);
final SimpleCollisionBox FULL_LOWER_SHAPE = new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 16.0D, 13.0D);
final SimpleCollisionBox COLLISION_SHAPE_BULB = new HexCollisionBox(5.0D, -1.0D, 5.0D, 11.0D, 3.0D, 11.0D);
final SimpleCollisionBox COLLISION_SHAPE_CROP = new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 5.0D, 13.0D);
final SimpleCollisionBox[] UPPER_SHAPE_BY_AGE = new SimpleCollisionBox[]{new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 11.0D, 13.0D), FULL_UPPER_SHAPE};
final SimpleCollisionBox[] LOWER_SHAPE_BY_AGE = new SimpleCollisionBox[]{COLLISION_SHAPE_BULB, new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 14.0D, 13.0D), FULL_LOWER_SHAPE, FULL_LOWER_SHAPE, FULL_LOWER_SHAPE};
return data.getHalf() == Half.UPPER ? UPPER_SHAPE_BY_AGE[Math.min(Math.abs(4 - (data.getAge() + 1)), UPPER_SHAPE_BY_AGE.length - 1)] : LOWER_SHAPE_BY_AGE[data.getAge()];
return data.getHalf() == Half.UPPER ? UPPER_SHAPE_BY_AGE[Math.min(Math.abs(4 - (data.getAge() + 1)), UPPER_SHAPE_BY_AGE.length - 1)] : LOWER_SHAPE_BY_AGE[data.getAge()];
} else {
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}
}, StateTypes.PITCHER_CROP),
BUTTON((player, item, version, data, x, y, z) -> {
final BlockFace facing = data.getFacing();
final boolean powered = data.isPowered();
switch (data.getFace()) {
case FLOOR:
// x axis
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
return powered ? new HexCollisionBox(6.0, 0.0, 5.0, 10.0, 1.0, 11.0) : new HexCollisionBox(6.0, 0.0, 5.0, 10.0, 2.0, 11.0);
}
WHEAT_BEETROOTS((player, item, version, data, x, y, z) -> {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, (data.getAge() + 1) * 2, 16.0D);
}, StateTypes.WHEAT, StateTypes.BEETROOTS),
return powered ? new HexCollisionBox(5.0, 0.0, 6.0, 11.0, 1.0, 10.0) : new HexCollisionBox(5.0, 0.0, 6.0, 11.0, 2.0, 10.0);
case WALL:
CollisionBox shape;
switch (facing) {
case EAST:
shape = powered ? new HexCollisionBox(0.0, 6.0, 5.0, 1.0, 10.0, 11.0) : new HexCollisionBox(0.0, 6.0, 5.0, 2.0, 10.0, 11.0);
break;
case WEST:
shape = powered ? new HexCollisionBox(15.0, 6.0, 5.0, 16.0, 10.0, 11.0) : new HexCollisionBox(14.0, 6.0, 5.0, 16.0, 10.0, 11.0);
break;
case SOUTH:
shape = powered ? new HexCollisionBox(5.0, 6.0, 0.0, 11.0, 10.0, 1.0) : new HexCollisionBox(5.0, 6.0, 0.0, 11.0, 10.0, 2.0);
break;
case NORTH:
case UP:
case DOWN:
shape = powered ? new HexCollisionBox(5.0, 6.0, 15.0, 11.0, 10.0, 16.0) : new HexCollisionBox(5.0, 6.0, 14.0, 11.0, 10.0, 16.0);
break;
default:
shape = NoCollisionBox.INSTANCE;
}
CARROT_POTATOES((player, item, version, data, x, y, z) -> {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, data.getAge() + 2, 16.0D);
}, StateTypes.CARROTS, StateTypes.POTATOES),
return shape;
case CEILING:
default:
// x axis
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
return powered ? new HexCollisionBox(6.0, 15.0, 5.0, 10.0, 16.0, 11.0) : new HexCollisionBox(6.0, 14.0, 5.0, 10.0, 16.0, 11.0);
} else {
return powered ? new HexCollisionBox(5.0, 15.0, 6.0, 11.0, 16.0, 10.0) : new HexCollisionBox(5.0, 14.0, 6.0, 11.0, 16.0, 10.0);
}
NETHER_WART((player, item, version, data, x, y, z) -> {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0, 5 + (data.getAge() * 3), 16.0D);
}, StateTypes.NETHER_WART),
ATTACHED_PUMPKIN_STEM((player, item, version, data, x, y, z) -> {
if (version.isOlderThan(ClientVersion.V_1_13))
return new HexCollisionBox(7.0D, 0.0D, 7.0D, 9.0D, 16.0D, 9.0D);
return switch (data.getFacing()) {
case SOUTH -> new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 10.0D, 16.0D);
case WEST -> new HexCollisionBox(0.0D, 0.0D, 6.0D, 10.0D, 10.0D, 10.0D);
case NORTH -> new HexCollisionBox(6.0D, 0.0D, 0.0D, 10.0D, 10.0D, 10.0D);
default -> new HexCollisionBox(6.0D, 0.0D, 6.0D, 16.0D, 10.0D, 10.0D);
};
}, StateTypes.ATTACHED_MELON_STEM, StateTypes.ATTACHED_PUMPKIN_STEM),
PUMPKIN_STEM((player, item, version, data, x, y, z) -> {
return new HexCollisionBox(7, 0, 7, 9, 2 * (data.getAge() + 1), 9);
}, StateTypes.PUMPKIN_STEM, StateTypes.MELON_STEM),
// Hitbox/Outline is Same as Collision
COCOA_BEANS((player, item, version, data, x, y, z) -> {
return CollisionData.getCocoa(version, data.getAge(), data.getFacing());
}, StateTypes.COCOA),
// Easier to just use no collision box
// Redstone wire is very complex with its collision shapes and has many de-syncs
REDSTONE_WIRE(NoCollisionBox.INSTANCE, StateTypes.REDSTONE_WIRE),
SWEET_BERRY((player, item, version, data, x, y, z) -> {
if (data.getAge() == 0) {
return new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 8.0D, 13.0D);
} else if (data.getAge() < 3) {
return new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D);
}
}, BlockTags.BUTTONS.getStates().toArray(new StateType[0])),
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}, StateTypes.SWEET_BERRY_BUSH),
WALL_SIGN((player, item, version, data, x, y, z) -> {
switch (data.getFacing()) {
case NORTH:
return new HexCollisionBox(0.0, 4.5, 14.0, 16.0, 12.5, 16.0);
case SOUTH:
return new HexCollisionBox(0.0, 4.5, 0.0, 16.0, 12.5, 2.0);
case EAST:
return new HexCollisionBox(0.0, 4.5, 0.0, 2.0, 12.5, 16.0);
case WEST:
return new HexCollisionBox(14.0, 4.5, 0.0, 16.0, 12.5, 16.0);
default:
return NoCollisionBox.INSTANCE;
CORAL_FAN(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 4.0D, 14.0D), BlockTags.CORALS.getStates().toArray(new StateType[0])),
TORCHFLOWER_CROP((player, item, version, data, x, y, z) -> {
if (data.getAge() == 0) {
return new HexCollisionBox(5.0D, 0.0D, 5.0D, 11.0D, 6.0D, 11.0D);
}
}, BlockTags.WALL_SIGNS.getStates().toArray(new StateType[0])),
// age is 1
return new HexCollisionBox(5.0D, 0.0D, 5.0D, 11.0D, 10.0D, 11.0D);
}, StateTypes.TORCHFLOWER_CROP),
WALL_HANGING_SIGN((player, item, version, data, x, y, z) -> {
switch (data.getFacing()) {
case NORTH:
case SOUTH:
return new ComplexCollisionBox(new HexCollisionBox(0.0D, 14.0D, 6.0D, 16.0D, 16.0D, 10.0D),
new HexCollisionBox(1.0D, 0.0D, 7.0D, 15.0D, 10.0D, 9.0D));
default:
return new ComplexCollisionBox(new HexCollisionBox(6.0D, 14.0D, 0.0D, 10.0D, 16.0D, 16.0D),
new HexCollisionBox(7.0D, 0.0D, 1.0D, 9.0D, 10.0D, 15.0D));
DEAD_BUSH(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 13.0D, 14.0D), StateTypes.DEAD_BUSH),
SUGARCANE(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 16.0D, 14.0D), StateTypes.SUGAR_CANE),
NETHER_SPROUTS(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 3.0D, 14.0D), StateTypes.NETHER_SPROUTS),
HANGING_ROOTS(new HexCollisionBox(2.0D, 10.0D, 2.0D, 14.0D, 16.0D, 14.0D), StateTypes.HANGING_ROOTS),
GRASS_FERN((player, item, version, data, x ,y, z) -> {
if (version.isOlderThan(ClientVersion.V_1_13)) {
return new SimpleCollisionBox(0.1F, 0.0F, 0.1F, 0.9F, 0.8F, 0.9F);
}
}, BlockTags.WALL_HANGING_SIGNS.getStates().toArray(new StateType[0])),
return new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 13.0D, 14.0D);
}, StateTypes.SHORT_GRASS, StateTypes.FERN),
STANDING_SIGN((player, item, version, data, x, y, z) ->
new HexCollisionBox(4.0, 0.0, 4.0, 12.0, 16.0, 12.0),
BlockTags.STANDING_SIGNS.getStates().toArray(new StateType[0])),
SEA_GRASS(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 12.0D, 14.0D),
StateTypes.SEAGRASS),
REDSTONE_WIRE((player, item, version, data, x, y, z) ->
// Easier to just use no collision box
// Redstone wire is very complex with its collision shapes and has many de-syncs
NoCollisionBox.INSTANCE,
StateTypes.REDSTONE_WIRE),
TALL_SEAGRASS(new HexCollisionBox(2.0, 0.0, 2.0, 14.0, 16.0, 14.0),
StateTypes.TALL_SEAGRASS),
FIRE((player, item, version, data, x, y, z) -> {
// Since 1.16 fire has a small hitbox
if (version.isNewerThanOrEquals(ClientVersion.V_1_16)) {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 1.0D, 16.0D);
SMALL_DRIPLEAF(new HexCollisionBox(2.0, 0.0, 2.0, 14.0, 13.0, 14.0), StateTypes.SMALL_DRIPLEAF),
CAVE_VINES(new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D), StateTypes.CAVE_VINES, StateTypes.CAVE_VINES_PLANT),
// Then your enum entries become:
TWISTING_VINES_BLOCK((player, item, version, data, x, y, z) ->
getVineCollisionBox(version, false, true), StateTypes.TWISTING_VINES),
WEEPING_VINES_BLOCK((player, item, version, data, x, y, z) ->
getVineCollisionBox(version, true, true), StateTypes.WEEPING_VINES),
TWISTING_VINES((player, item, version, data, x, y, z) ->
getVineCollisionBox(version, false, false), StateTypes.TWISTING_VINES_PLANT),
WEEPING_VINES((player, item, version, data, x, y, z) ->
getVineCollisionBox(version, true, false), StateTypes.WEEPING_VINES_PLANT),
TALL_PLANT(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.TALL_GRASS, StateTypes.LARGE_FERN),
BAMBOO((player, item, version, data, x, y, z) -> data.getLeaves() == Leaves.LARGE
? new HexOffsetCollisionBox(data.getType(), 3.0, 0.0, 3.0, 13.0, 16.0, 13.0)
: new HexOffsetCollisionBox(data.getType(), 5.0, 0.0, 5.0, 11.0, 16.0, 11.0), StateTypes.BAMBOO),
BAMBOO_SAPLING((player, item, version, data, x, y, z) -> {
return new HexOffsetCollisionBox(data.getType(), 4.0D, 0.0D, 4.0D, 12.0D, 12.0D, 12.0D);
}, StateTypes.BAMBOO_SAPLING),
SCAFFOLDING((player, item, version, data, x, y, z) -> {
// If is holding scaffolding or Via replacement - hay bale
if (item == StateTypes.SCAFFOLDING || version.isOlderThan(ClientVersion.V_1_14)) {
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}
return NoCollisionBox.INSTANCE;
}, BlockTags.FIRE.getStates().toArray(new StateType[0])),
// STABLE_SHAPE for the scaffolding
ComplexCollisionBox box = new ComplexCollisionBox(9,
new HexCollisionBox(0.0D, 14.0D, 0.0D, 16.0D, 16.0D, 16.0D),
new HexCollisionBox(0.0D, 0.0D, 0.0D, 2.0D, 16.0D, 2.0D),
new HexCollisionBox(14.0D, 0.0D, 0.0D, 16.0D, 16.0D, 2.0D),
new HexCollisionBox(0.0D, 0.0D, 14.0D, 2.0D, 16.0D, 16.0D),
new HexCollisionBox(14.0D, 0.0D, 14.0D, 16.0D, 16.0D, 16.0D));
BANNER(((player, item, version, data, x, y, z) ->
new SimpleCollisionBox(4.0, 0.0, 4.0, 12.0, 16.0, 12.0)),
BlockTags.BANNERS.getStates().toArray(new StateType[0]));
if (data.getHalf() == Half.LOWER) { // Add the unstable shape to the collision boxes
box.add(new HexCollisionBox(0.0D, 0.0D, 0.0D, 2.0D, 2.0D, 16.0D));
box.add(new HexCollisionBox(14.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D));
box.add(new HexCollisionBox(0.0D, 0.0D, 14.0D, 16.0D, 2.0D, 16.0D));
box.add(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 2.0D));
}
return box;
}, StateTypes.SCAFFOLDING),
DRIPLEAF((player, item, version, data, x, y, z) -> {
if (version.isOlderThanOrEquals(ClientVersion.V_1_16_4))
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
ComplexCollisionBox box = new ComplexCollisionBox(2);
if (data.getFacing() == BlockFace.NORTH) { // Stem
box.add(new HexCollisionBox(5.0D, 0.0D, 9.0D, 11.0D, 15.0D, 15.0D));
} else if (data.getFacing() == BlockFace.SOUTH) {
box.add(new HexCollisionBox(5.0D, 0.0D, 1.0D, 11.0D, 15.0D, 7.0D));
} else if (data.getFacing() == BlockFace.EAST) {
box.add(new HexCollisionBox(1.0D, 0.0D, 5.0D, 7.0D, 15.0D, 11.0D));
} else {
box.add(new HexCollisionBox(9.0D, 0.0D, 5.0D, 15.0D, 15.0D, 11.0D));
}
if (data.getTilt() == Tilt.NONE || data.getTilt() == Tilt.UNSTABLE) {
box.add(new HexCollisionBox(0.0, 11.0, 0.0, 16.0, 15.0, 16.0));
} else if (data.getTilt() == Tilt.PARTIAL) {
box.add(new HexCollisionBox(0.0, 11.0, 0.0, 16.0, 13.0, 16.0));
}
return box;
}, StateTypes.BIG_DRIPLEAF),
PINK_PETALS_BLOCK((player, item, version, data, x, y, z) -> {
if (version.isNewerThan(ClientVersion.V_1_20_2)) {
int flowerAmount = data.getFlowerAmount();
int horizontalIndex = getHorizontalID(data.getFacing());
CollisionBox result = NoCollisionBox.INSTANCE;
// Pre-defined collision boxes for each quadrant
HexCollisionBox[] boxes = new HexCollisionBox[] {
new HexCollisionBox(8, 0, 8, 16, 3, 16), // SE
new HexCollisionBox(8, 0, 0, 16, 3, 8), // NE
new HexCollisionBox(0, 0, 0, 8, 3, 8), // NW
new HexCollisionBox(0, 0, 8, 8, 3, 16) // SW
};
// Add boxes based on flower amount and facing
for (int i = 0; i < flowerAmount; i++) {
int index = Math.floorMod(i - horizontalIndex, 4);
result = result.union(boxes[index]);
}
return result;
} else if (version.isNewerThan(ClientVersion.V_1_19_3)) {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 3.0D, 16.0D);
} else if (version.isNewerThan(ClientVersion.V_1_12_2)) {
return CORAL_FAN.box.copy();
}
return GRASS_FERN.dynamic.fetch(player, item, version, data, x, y, z);
}, StateTypes.PINK_PETALS),
MANGROVE_PROPAGULE(((player, item, version, data, x, y, z) -> {
if (data.isHanging()) {
return new HexOffsetCollisionBox(data.getType(), 7.0, 0.0, 7.0, 9.0, 16.0, 9.0);
} else {
return new HexOffsetCollisionBox(data.getType(), 7.0, getPropaguleMinHeight(data.getAge()), 7.0, 9.0, 16.0, 9.0);
}
}), StateTypes.MANGROVE_PROPAGULE),
FROGSPAWN((player, item, version, data, x, y, z) -> {
if (version.isNewerThan(ClientVersion.V_1_18_2)) {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 1.5D, 16.0D);
} else { // ViaVersion just replaces this with... nothing
return NoCollisionBox.INSTANCE;
}
}, StateTypes.FROGSPAWN),
// always a fullblock hitbox. Via replacement is obsidian
SCULK_SHRIKER(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.SCULK_SHRIEKER);
private static final Map<StateType, HitboxData> lookup = new HashMap<>();
@@ -329,6 +631,48 @@ public enum HitboxData {
return data.box.copy().offset(x, y, z);
// Allow this class to override collision boxes when they aren't the same as regular boxes
return HitboxData.getData(block.getType()).dynamic.fetch(player, heldItem, version, block, x, y, z).offset(x, y, z);
HitBoxFactory hitBoxFactory = data.dynamic;
CollisionBox collisionBox = hitBoxFactory.fetch(player, heldItem, version, block, x, y, z);
collisionBox.offset(x, y, z);
return collisionBox;
}
private static int getPropaguleMinHeight(int age) {
return switch (age) {
case 0, 1, 2 -> 13 - age * 3;
case 3, 4 -> (4 - age) * 3;
default -> throw new IllegalStateException("Impossible Propagule Height");
};
}
private static CollisionBox getVineCollisionBox(ClientVersion version, boolean isWeeping, boolean isBlock) {
if (version.isNewerThan(ClientVersion.V_1_15_2)) {
if (isWeeping) {
return isBlock
? new HexCollisionBox(4.0, 9.0, 4.0, 12.0, 16.0, 12.0)
: new HexCollisionBox(1.0, 0.0, 1.0, 15.0, 16.0, 15.0);
} else {
return new HexCollisionBox(4.0D, 0.0D, 4.0D, 12.0D, isBlock ? 15.0D : 16.0D, 12.0D);
}
} else {
// Via replacement - 4 sided vine
return new ComplexCollisionBox(4,
new HexCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 16.0D, 16.0D),
new HexCollisionBox(15.0D, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D),
new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, 1.0D),
new HexCollisionBox(0.0D, 0.0D, 15.0D, 16.0D, 16.0D, 16.0D)
);
}
}
private static int getHorizontalID(BlockFace facing) {
return switch (facing) {
case DOWN, UP -> -1;
case NORTH -> 2;
case SOUTH -> 0;
case WEST -> 1;
case EAST -> 3;
default -> throw new IllegalStateException("Impossible blockface for getHorizontalID");
};
}
}
@@ -21,18 +21,14 @@ public class DoorHandler implements CollisionFactory {
@Override
public CollisionBox fetch(GrimPlayer player, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
switch (fetchDirection(player, version, block, x, y, z)) {
case NORTH:
return NORTH_AABB.copy();
case SOUTH:
return SOUTH_AABB.copy();
case EAST:
return EAST_AABB.copy();
case WEST:
return WEST_AABB.copy();
}
return switch (fetchDirection(player, version, block, x, y, z)) {
case NORTH -> NORTH_AABB.copy();
case SOUTH -> SOUTH_AABB.copy();
case EAST -> EAST_AABB.copy();
case WEST -> WEST_AABB.copy();
default -> NoCollisionBox.INSTANCE;
};
return NoCollisionBox.INSTANCE;
}
public BlockFace fetchDirection(GrimPlayer player, ClientVersion version, WrappedBlockState door, int x, int y, int z) {
@@ -80,16 +76,11 @@ public class DoorHandler implements CollisionFactory {
isRightHinge = door.getHinge() == Hinge.RIGHT;
}
switch (facingDirection) {
case EAST:
default:
return isClosed ? BlockFace.EAST : (isRightHinge ? BlockFace.NORTH : BlockFace.SOUTH);
case SOUTH:
return isClosed ? BlockFace.SOUTH : (isRightHinge ? BlockFace.EAST : BlockFace.WEST);
case WEST:
return isClosed ? BlockFace.WEST : (isRightHinge ? BlockFace.SOUTH : BlockFace.NORTH);
case NORTH:
return isClosed ? BlockFace.NORTH : (isRightHinge ? BlockFace.WEST : BlockFace.EAST);
}
return switch (facingDirection) {
case SOUTH -> isClosed ? BlockFace.SOUTH : (isRightHinge ? BlockFace.EAST : BlockFace.WEST);
case WEST -> isClosed ? BlockFace.WEST : (isRightHinge ? BlockFace.SOUTH : BlockFace.NORTH);
case NORTH -> isClosed ? BlockFace.NORTH : (isRightHinge ? BlockFace.WEST : BlockFace.EAST);
default -> isClosed ? BlockFace.EAST : (isRightHinge ? BlockFace.NORTH : BlockFace.SOUTH);
};
}
}
@@ -31,7 +31,7 @@ public class DynamicChorusPlant implements CollisionFactory {
float f = 0.5F - (float) 0.3125;
float f1 = 0.5F + (float) 0.3125;
SimpleCollisionBox baseShape = new SimpleCollisionBox(f, f, f, f1, f1, f1, false);
CollisionBox[] avoxelshape = new CollisionBox[directions.length];
SimpleCollisionBox[] avoxelshape = new SimpleCollisionBox[directions.length];
for (int i = 0; i < directions.length; ++i) {
BlockFace direction = directions[i];
@@ -41,7 +41,7 @@ public class DynamicChorusPlant implements CollisionFactory {
CollisionBox[] avoxelshape1 = new CollisionBox[64];
for (int k = 0; k < 64; ++k) {
ComplexCollisionBox directionalShape = new ComplexCollisionBox(baseShape);
ComplexCollisionBox directionalShape = new ComplexCollisionBox(7, baseShape); // how big is this one??
for (int j = 0; j < directions.length; ++j) {
if ((k & 1 << j) != 0) {
@@ -1,10 +1,7 @@
package ac.grim.grimac.utils.collisions.blocks;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.CollisionFactory;
import ac.grim.grimac.utils.collisions.datatypes.ComplexCollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.HexCollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.*;
import ac.grim.grimac.utils.nmsutil.Materials;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
@@ -18,16 +15,16 @@ import com.github.retrooper.packetevents.protocol.world.states.enums.Shape;
import java.util.stream.IntStream;
public class DynamicStair implements CollisionFactory {
protected static final CollisionBox TOP_AABB = new HexCollisionBox(0, 8, 0, 16, 16, 16);
protected static final CollisionBox BOTTOM_AABB = new HexCollisionBox(0, 0, 0, 16, 8, 16);
protected static final CollisionBox OCTET_NNN = new HexCollisionBox(0.0D, 0.0D, 0.0D, 8.0D, 8.0D, 8.0D);
protected static final CollisionBox OCTET_NNP = new HexCollisionBox(0.0D, 0.0D, 8.0D, 8.0D, 8.0D, 16.0D);
protected static final CollisionBox OCTET_NPN = new HexCollisionBox(0.0D, 8.0D, 0.0D, 8.0D, 16.0D, 8.0D);
protected static final CollisionBox OCTET_NPP = new HexCollisionBox(0.0D, 8.0D, 8.0D, 8.0D, 16.0D, 16.0D);
protected static final CollisionBox OCTET_PNN = new HexCollisionBox(8.0D, 0.0D, 0.0D, 16.0D, 8.0D, 8.0D);
protected static final CollisionBox OCTET_PNP = new HexCollisionBox(8.0D, 0.0D, 8.0D, 16.0D, 8.0D, 16.0D);
protected static final CollisionBox OCTET_PPN = new HexCollisionBox(8.0D, 8.0D, 0.0D, 16.0D, 16.0D, 8.0D);
protected static final CollisionBox OCTET_PPP = new HexCollisionBox(8.0D, 8.0D, 8.0D, 16.0D, 16.0D, 16.0D);
protected static final SimpleCollisionBox TOP_AABB = new HexCollisionBox(0, 8, 0, 16, 16, 16);
protected static final SimpleCollisionBox BOTTOM_AABB = new HexCollisionBox(0, 0, 0, 16, 8, 16);
protected static final SimpleCollisionBox OCTET_NNN = new HexCollisionBox(0.0D, 0.0D, 0.0D, 8.0D, 8.0D, 8.0D);
protected static final SimpleCollisionBox OCTET_NNP = new HexCollisionBox(0.0D, 0.0D, 8.0D, 8.0D, 8.0D, 16.0D);
protected static final SimpleCollisionBox OCTET_NPN = new HexCollisionBox(0.0D, 8.0D, 0.0D, 8.0D, 16.0D, 8.0D);
protected static final SimpleCollisionBox OCTET_NPP = new HexCollisionBox(0.0D, 8.0D, 8.0D, 8.0D, 16.0D, 16.0D);
protected static final SimpleCollisionBox OCTET_PNN = new HexCollisionBox(8.0D, 0.0D, 0.0D, 16.0D, 8.0D, 8.0D);
protected static final SimpleCollisionBox OCTET_PNP = new HexCollisionBox(8.0D, 0.0D, 8.0D, 16.0D, 8.0D, 16.0D);
protected static final SimpleCollisionBox OCTET_PPN = new HexCollisionBox(8.0D, 8.0D, 0.0D, 16.0D, 16.0D, 8.0D);
protected static final SimpleCollisionBox OCTET_PPP = new HexCollisionBox(8.0D, 8.0D, 8.0D, 16.0D, 16.0D, 16.0D);
protected static final CollisionBox[] TOP_SHAPES = makeShapes(TOP_AABB, OCTET_NNN, OCTET_PNN, OCTET_NNP, OCTET_PNP);
protected static final CollisionBox[] BOTTOM_SHAPES = makeShapes(BOTTOM_AABB, OCTET_NPN, OCTET_PPN, OCTET_NPP, OCTET_PPP);
private static final int[] SHAPE_BY_STATE = new int[]{12, 5, 3, 10, 14, 13, 7, 11, 13, 7, 11, 14, 8, 4, 1, 2, 4, 1, 2, 8};
@@ -77,25 +74,20 @@ public class DynamicStair implements CollisionFactory {
}
private static BlockFace rotateYCCW(BlockFace face) {
switch (face) {
default:
case NORTH:
return BlockFace.WEST;
case EAST:
return BlockFace.NORTH;
case SOUTH:
return BlockFace.EAST;
case WEST:
return BlockFace.SOUTH;
}
return switch (face) {
case EAST -> BlockFace.NORTH;
case SOUTH -> BlockFace.EAST;
case WEST -> BlockFace.SOUTH;
default -> BlockFace.WEST;
};
}
private static CollisionBox[] makeShapes(CollisionBox p_199779_0_, CollisionBox p_199779_1_, CollisionBox p_199779_2_, CollisionBox p_199779_3_, CollisionBox p_199779_4_) {
private static CollisionBox[] makeShapes(SimpleCollisionBox p_199779_0_, SimpleCollisionBox p_199779_1_, SimpleCollisionBox p_199779_2_, SimpleCollisionBox p_199779_3_, SimpleCollisionBox p_199779_4_) {
return IntStream.range(0, 16).mapToObj((p_199780_5_) -> makeStairShape(p_199780_5_, p_199779_0_, p_199779_1_, p_199779_2_, p_199779_3_, p_199779_4_)).toArray(CollisionBox[]::new);
}
private static CollisionBox makeStairShape(int p_199781_0_, CollisionBox p_199781_1_, CollisionBox p_199781_2_, CollisionBox p_199781_3_, CollisionBox p_199781_4_, CollisionBox p_199781_5_) {
ComplexCollisionBox voxelshape = new ComplexCollisionBox(p_199781_1_);
private static CollisionBox makeStairShape(int p_199781_0_, SimpleCollisionBox p_199781_1_, SimpleCollisionBox p_199781_2_, SimpleCollisionBox p_199781_3_, SimpleCollisionBox p_199781_4_, SimpleCollisionBox p_199781_5_) {
ComplexCollisionBox voxelshape = new ComplexCollisionBox(5, p_199781_1_); // can we do better than 5?
if ((p_199781_0_ & 1) != 0) {
voxelshape.add(p_199781_2_);
}
@@ -134,36 +126,23 @@ public class DynamicStair implements CollisionFactory {
}
private int directionToValue(BlockFace face) {
switch (face) {
default:
case UP:
case DOWN:
return -1;
case NORTH:
return 2;
case SOUTH:
return 0;
case WEST:
return 1;
case EAST:
return 3;
}
return switch (face) {
case NORTH -> 2;
case SOUTH -> 0;
case WEST -> 1;
case EAST -> 3;
default -> -1;
};
}
private EnumShape toEnumShape(Shape shape) {
switch (shape) {
default:
case STRAIGHT:
return EnumShape.STRAIGHT;
case INNER_LEFT:
return EnumShape.INNER_LEFT;
case INNER_RIGHT:
return EnumShape.INNER_RIGHT;
case OUTER_LEFT:
return EnumShape.OUTER_LEFT;
case OUTER_RIGHT:
return EnumShape.OUTER_RIGHT;
}
return switch (shape) {
case INNER_LEFT -> EnumShape.INNER_LEFT;
case INNER_RIGHT -> EnumShape.INNER_RIGHT;
case OUTER_LEFT -> EnumShape.OUTER_LEFT;
case OUTER_RIGHT -> EnumShape.OUTER_RIGHT;
default -> EnumShape.STRAIGHT;
};
}
enum EnumShape {
@@ -14,20 +14,13 @@ public class PistonBaseCollision implements CollisionFactory {
public CollisionBox fetch(GrimPlayer player, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
if (!block.isExtended()) return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
switch (block.getFacing()) {
default:
case DOWN:
return new HexCollisionBox(0, 4, 0, 16, 16, 16);
case UP:
return new HexCollisionBox(0, 0, 0, 16, 12, 16);
case NORTH:
return new HexCollisionBox(0, 0, 4, 16, 16, 16);
case SOUTH:
return new HexCollisionBox(0, 0, 0, 16, 16, 12);
case WEST:
return new HexCollisionBox(4, 0, 0, 16, 16, 16);
case EAST:
return new HexCollisionBox(0, 0, 0, 12, 16, 16);
}
return switch (block.getFacing()) {
case UP -> new HexCollisionBox(0, 0, 0, 16, 12, 16);
case NORTH -> new HexCollisionBox(0, 0, 4, 16, 16, 16);
case SOUTH -> new HexCollisionBox(0, 0, 0, 16, 16, 12);
case WEST -> new HexCollisionBox(4, 0, 0, 16, 16, 16);
case EAST -> new HexCollisionBox(0, 0, 0, 12, 16, 16);
default -> new HexCollisionBox(0, 4, 0, 16, 16, 16);
};
}
}
@@ -38,38 +38,43 @@ public class PistonHeadCollision implements CollisionFactory {
longAmount = 0;
switch (block.getFacing()) {
case DOWN:
default:
return new ComplexCollisionBox(new HexCollisionBox(0, 0, 0, 16, 4, 16),
new HexCollisionBox(6, 4, 6, 10, 16 + longAmount, 10));
case UP:
return new ComplexCollisionBox(new HexCollisionBox(0, 12, 0, 16, 16, 16),
new HexCollisionBox(6, 0 - longAmount, 6, 10, 12, 10));
case NORTH:
return new ComplexCollisionBox(new HexCollisionBox(0, 0, 0, 16, 16, 4),
new HexCollisionBox(6, 6, 4, 10, 10, 16 + longAmount));
case SOUTH:
return switch (block.getFacing()) {
case UP -> new ComplexCollisionBox(2,
new HexCollisionBox(0, 12, 0, 16, 16, 16),
new HexCollisionBox(6, 0 - longAmount, 6, 10, 12, 10));
case NORTH -> new ComplexCollisionBox(2,
new HexCollisionBox(0, 0, 0, 16, 16, 4),
new HexCollisionBox(6, 6, 4, 10, 10, 16 + longAmount));
case SOUTH -> {
// SOUTH piston is glitched in 1.7 and 1.8, fixed in 1.9
// Don't bother with short piston boxes as 1.7/1.8 clients don't have them
if (version.isOlderThanOrEquals(ClientVersion.V_1_8))
return new ComplexCollisionBox(new HexCollisionBox(0, 0, 12, 16, 16, 16),
yield new ComplexCollisionBox(2,
new HexCollisionBox(0, 0, 12, 16, 16, 16),
new HexCollisionBox(4, 6, 0, 12, 10, 12));
return new ComplexCollisionBox(new HexCollisionBox(0, 0, 12, 16, 16, 16),
yield new ComplexCollisionBox(2,
new HexCollisionBox(0, 0, 12, 16, 16, 16),
new HexCollisionBox(6, 6, 0 - longAmount, 10, 10, 12));
case WEST:
}
case WEST -> {
// WEST piston is glitched in 1.7 and 1.8, fixed in 1.9
// Don't bother with short piston boxes as 1.7/1.8 clients don't have them
if (version.isOlderThanOrEquals(ClientVersion.V_1_8))
return new ComplexCollisionBox(new HexCollisionBox(0, 0, 0, 4, 16, 16),
yield new ComplexCollisionBox(2,
new HexCollisionBox(0, 0, 0, 4, 16, 16),
new HexCollisionBox(6, 4, 4, 10, 12, 16));
return new ComplexCollisionBox(new HexCollisionBox(0, 0, 0, 4, 16, 16),
yield new ComplexCollisionBox(2,
new HexCollisionBox(0, 0, 0, 4, 16, 16),
new HexCollisionBox(4, 6, 6, 16 + longAmount, 10, 10));
case EAST:
return new ComplexCollisionBox(new HexCollisionBox(12, 0, 0, 16, 16, 16),
new HexCollisionBox(0 - longAmount, 6, 4, 12, 10, 12));
}
}
case EAST -> new ComplexCollisionBox(2,
new HexCollisionBox(12, 0, 0, 16, 16, 16),
new HexCollisionBox(0 - longAmount, 6, 4, 12, 10, 12));
default -> new ComplexCollisionBox(2,
new HexCollisionBox(0, 0, 0, 16, 4, 16),
new HexCollisionBox(6, 4, 6, 10, 16 + longAmount, 10));
};
}
}
@@ -4,7 +4,6 @@ import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.CollisionData;
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.CollisionFactory;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
@@ -18,10 +17,8 @@ import com.github.retrooper.packetevents.protocol.world.states.enums.West;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
public class DynamicFence extends DynamicConnecting implements CollisionFactory {
private static final CollisionBox[] COLLISION_BOXES = makeShapes(2.0F, 2.0F, 24.0F, 0.0F, 24.0F, true);
public static SimpleCollisionBox[] LEGACY_BOUNDING_BOXES = new SimpleCollisionBox[] {new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D)};
public class DynamicCollisionFence extends DynamicConnecting implements CollisionFactory {
private static final CollisionBox[] COLLISION_BOXES = makeShapes(2.0F, 2.0F, 24.0F, 0.0F, 24.0F, true, 1);
@Override
public CollisionBox fetch(GrimPlayer player, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
@@ -19,9 +19,9 @@ import com.github.retrooper.packetevents.protocol.world.states.enums.West;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
public class DynamicPane extends DynamicConnecting implements CollisionFactory {
public class DynamicCollisionPane extends DynamicConnecting implements CollisionFactory {
private static final CollisionBox[] COLLISION_BOXES = makeShapes(1.0F, 1.0F, 16.0F, 0.0F, 16.0F, true);
private static final CollisionBox[] COLLISION_BOXES = makeShapes(1.0F, 1.0F, 16.0F, 0.0F, 16.0F, true, 1);
@Override
public CollisionBox fetch(GrimPlayer player, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
@@ -52,7 +52,7 @@ public class DynamicPane extends DynamicConnecting implements CollisionFactory {
if (version.isNewerThanOrEquals(ClientVersion.V_1_9)) {
return COLLISION_BOXES[getAABBIndex(north, east, south, west)].copy();
} else { // 1.8 and below clients have pane bounding boxes one pixel less
ComplexCollisionBox boxes = new ComplexCollisionBox();
ComplexCollisionBox boxes = new ComplexCollisionBox(2);
if ((!west || !east) && (west || east || north || south)) {
if (west) {
boxes.add(new SimpleCollisionBox(0.0F, 0.0F, 0.4375F, 0.5F, 1.0F, 0.5625F));
@@ -15,12 +15,15 @@ import com.github.retrooper.packetevents.protocol.world.states.enums.South;
import com.github.retrooper.packetevents.protocol.world.states.enums.West;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
public class DynamicWall extends DynamicConnecting implements CollisionFactory {
public static final CollisionBox[] BOXES = makeShapes(4.0F, 3.0F, 16.0F, 0.0F, 16.0F, false);
public class DynamicCollisionWall extends DynamicConnecting implements CollisionFactory {
// https://bugs.mojang.com/browse/MC-9565
// https://bugs.mojang.com/browse/MC-94016
private static final CollisionBox[] COLLISION_BOXES = makeShapes(4.0F, 3.0F, 24.0F, 0.0F, 24.0F, false);
private static final CollisionBox[] COLLISION_BOXES = makeShapes(4.0F, 3.0F, 24.0F, 0.0F, 24.0F, false, 1);
/**
* @deprecated use DynamicHitboxWall
*/
@Deprecated
public CollisionBox fetchRegularBox(GrimPlayer player, WrappedBlockState state, ClientVersion version, int x, int y, int z) {
int north, south, west, east, up;
north = south = west = east = up = 0;
@@ -49,12 +52,11 @@ public class DynamicWall extends DynamicConnecting implements CollisionFactory {
// On 1.13+ clients the bounding box is much more complicated
if (version.isNewerThanOrEquals(ClientVersion.V_1_13)) {
ComplexCollisionBox box = new ComplexCollisionBox();
ComplexCollisionBox box = new ComplexCollisionBox(5);
// Proper and faster way would be to compute all this beforehand
if (up == 1) {
box.add(new HexCollisionBox(4, 0, 4, 12, 16, 12));
return box;
}
if (north == 1) {
@@ -77,6 +79,7 @@ public class DynamicWall extends DynamicConnecting implements CollisionFactory {
} else if (east == 2) {
box.add(new HexCollisionBox(5, 0, 5, 16, 16, 11));
}
return box;
}
// Magic 1.8 code for walls that I copied over, 1.12 below uses this mess
@@ -139,9 +142,7 @@ public class DynamicWall extends DynamicConnecting implements CollisionFactory {
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13)) {
// Proper and faster way would be to compute all this beforehand
if (up) {
ComplexCollisionBox box = new ComplexCollisionBox(COLLISION_BOXES[getAABBIndex(north, east, south, west)].copy());
box.add(new HexCollisionBox(4, 0, 4, 12, 24, 12));
return box;
return COLLISION_BOXES[getAABBIndex(north, east, south, west)].copy().union(new HexCollisionBox(4, 0, 4, 12, 24, 12));
}
return COLLISION_BOXES[getAABBIndex(north, east, south, west)].copy();
@@ -18,7 +18,7 @@ import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
public class DynamicConnecting {
public static CollisionBox[] makeShapes(float p_196408_1_, float p_196408_2_, float p_196408_3_, float p_196408_4_, float p_196408_5_, boolean includeCenter) {
public static CollisionBox[] makeShapes(float p_196408_1_, float p_196408_2_, float p_196408_3_, float p_196408_4_, float p_196408_5_, boolean includeCenter, int additionalMaxIndex) {
float middleMin = 8.0F - p_196408_1_;
float middleMax = 8.0F + p_196408_1_;
float f2 = 8.0F - p_196408_2_;
@@ -29,14 +29,21 @@ public class DynamicConnecting {
SimpleCollisionBox voxelshape3 = new HexCollisionBox(0.0D, p_196408_4_, f2, f3, p_196408_5_, f3);
SimpleCollisionBox voxelshape4 = new HexCollisionBox(f2, p_196408_4_, f2, 16.0D, p_196408_5_, f3);
ComplexCollisionBox voxelshape5 = new ComplexCollisionBox(voxelshape1, voxelshape4);
ComplexCollisionBox voxelshape6 = new ComplexCollisionBox(voxelshape2, voxelshape3);
ComplexCollisionBox voxelshape5 = new ComplexCollisionBox(2 + additionalMaxIndex, voxelshape1, voxelshape4);
ComplexCollisionBox voxelshape6 = new ComplexCollisionBox(2 + additionalMaxIndex, voxelshape2, voxelshape3);
CollisionBox[] avoxelshape = new CollisionBox[]{NoCollisionBox.INSTANCE, voxelshape2, voxelshape3, voxelshape6, voxelshape1, new ComplexCollisionBox(voxelshape2, voxelshape1), new ComplexCollisionBox(voxelshape3, voxelshape1), new ComplexCollisionBox(voxelshape6, voxelshape1), voxelshape4, new ComplexCollisionBox(voxelshape2, voxelshape4), new ComplexCollisionBox(voxelshape3, voxelshape4), new ComplexCollisionBox(voxelshape6, voxelshape4), voxelshape5, new ComplexCollisionBox(voxelshape2, voxelshape5), new ComplexCollisionBox(voxelshape3, voxelshape5), new ComplexCollisionBox(voxelshape6, voxelshape5)};
CollisionBox[] avoxelshape = new CollisionBox[]{
NoCollisionBox.INSTANCE, voxelshape2, voxelshape3, voxelshape6, voxelshape1,
new ComplexCollisionBox(2 + additionalMaxIndex, voxelshape2, voxelshape1), new ComplexCollisionBox(2 + additionalMaxIndex, voxelshape3, voxelshape1),
new ComplexCollisionBox(3 + additionalMaxIndex, voxelshape2, voxelshape3, voxelshape1), voxelshape4,
new ComplexCollisionBox(2 + additionalMaxIndex, voxelshape2, voxelshape4), new ComplexCollisionBox(2 + additionalMaxIndex, voxelshape3, voxelshape4),
new ComplexCollisionBox(3 + additionalMaxIndex, voxelshape2, voxelshape3, voxelshape4), voxelshape5,
new ComplexCollisionBox(3 + additionalMaxIndex, voxelshape2, voxelshape1, voxelshape4), new ComplexCollisionBox(3 + additionalMaxIndex, voxelshape3, voxelshape1, voxelshape4),
new ComplexCollisionBox(4 + additionalMaxIndex, voxelshape1, voxelshape2, voxelshape3, voxelshape4)};
if (includeCenter) {
for (int i = 0; i < 16; ++i) {
avoxelshape[i] = new ComplexCollisionBox(up, avoxelshape[i]);
avoxelshape[i] = avoxelshape[i].union(up);
}
}
@@ -129,4 +136,4 @@ public class DynamicConnecting {
public boolean canConnectToGate(StateType fence) {
return !Materials.isGlassPane(fence);
}
}
}
@@ -0,0 +1,88 @@
package ac.grim.grimac.utils.collisions.blocks.connecting;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.CollisionData;
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.ComplexCollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.HitBoxFactory;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags;
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);
public static SimpleCollisionBox[] LEGACY_HITBOXES = new SimpleCollisionBox[] {new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D)};
// no ComplexCollisionBox produced by makeShapes is every larger than 5 SimpleCollisionBoxes
private static final int MAX_MODERN_HITBOX_COMPLEX_COLLISION_BOX_SIZE = 5;
static {
SimpleCollisionBox[] boxes = new SimpleCollisionBox[MAX_MODERN_HITBOX_COMPLEX_COLLISION_BOX_SIZE];
// we start from one because MODERN_HITBOXES[0] is a NoCollisionBox
for (int i = 1; i < MODERN_HITBOXES.length; i++) {
CollisionBox collisionBox = MODERN_HITBOXES[i];
int size = collisionBox.downCast(boxes);
for (int j = 0; j < size; j++) {
if (boxes[j].maxY > 1) {
boxes[j].maxY = 1;
}
}
MODERN_HITBOXES[i] = size == 1 ? boxes[0] : new ComplexCollisionBox(size, boxes);
}
}
@Override
public CollisionBox fetch(GrimPlayer player, StateType heldItem, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
boolean east;
boolean north;
boolean south;
boolean west;
// 1.13+ servers on 1.13+ clients send the full fence data
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13)
&& version.isNewerThanOrEquals(ClientVersion.V_1_13)) {
east = block.getEast() != East.FALSE;
north = block.getNorth() != North.FALSE;
south = block.getSouth() != South.FALSE;
west = block.getWest() != West.FALSE;
} else {
east = connectsTo(player, version, x, y, z, BlockFace.EAST);
north = connectsTo(player, version, x, y, z, BlockFace.NORTH);
south = connectsTo(player, version, x, y, z, BlockFace.SOUTH);
west = connectsTo(player, version, x, y, z, BlockFace.WEST);
}
return version.isNewerThanOrEquals(ClientVersion.V_1_12_2)
? getModernCollisionBox(north, east, south, west)
: getLegacyCollisionBox(north, east, south, west);
}
private CollisionBox getLegacyCollisionBox(boolean north, boolean east, boolean south, boolean west) {
return LEGACY_HITBOXES[getAABBIndex(north, east, south, west)].copy();
}
private CollisionBox getModernCollisionBox(boolean north, boolean east, boolean south, boolean west) {
return MODERN_HITBOXES[getAABBIndex(north, east, south, west)].copy();
}
@Override
public boolean checkCanConnect(GrimPlayer player, WrappedBlockState state, StateType one, StateType two, BlockFace direction) {
if (BlockTags.FENCES.contains(one))
return !(one == StateTypes.NETHER_BRICK_FENCE) && !(two == StateTypes.NETHER_BRICK_FENCE);
else
return BlockTags.FENCES.contains(one) || CollisionData.getData(one).getMovementCollisionBox(player, player.getClientVersion(), state, 0, 0, 0).isSideFullBlock(direction);
}
}
@@ -0,0 +1,109 @@
package ac.grim.grimac.utils.collisions.blocks.connecting;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.CollisionData;
import ac.grim.grimac.utils.collisions.datatypes.*;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags;
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 DynamicHitboxPane extends DynamicConnecting implements HitBoxFactory {
private static final CollisionBox[] COLLISION_BOXES = makeShapes(1.0F, 1.0F, 16.0F, 0.0F, 16.0F, true, 1);
@Override
public CollisionBox fetch(GrimPlayer player, StateType item, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
boolean east, north, south, west;
// 1.13+ servers on 1.13+ clients send the full fence data
if (isModernVersion(version)) {
east = block.getEast() != East.FALSE;
north = block.getNorth() != North.FALSE;
south = block.getSouth() != South.FALSE;
west = block.getWest() != West.FALSE;
} else {
east = connectsTo(player, version, x, y, z, BlockFace.EAST);
north = connectsTo(player, version, x, y, z, BlockFace.NORTH);
south = connectsTo(player, version, x, y, z, BlockFace.SOUTH);
west = connectsTo(player, version, x, y, z, BlockFace.WEST);
}
// On 1.7 and 1.8 clients, and 1.13+ clients on 1.7 and 1.8 servers, the glass pane is + instead of |
if (shouldUseOldPaneShape(version, north, south, east, west)) {
north = south = east = west = true;
}
return version.isNewerThanOrEquals(ClientVersion.V_1_9)
? getModernCollisionBox(north, east, south, west)
: getLegacyCollisionBox(north, east, south, west);
}
private boolean isModernVersion(ClientVersion version) {
return PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13)
&& version.isNewerThanOrEquals(ClientVersion.V_1_13);
}
private boolean shouldUseOldPaneShape(ClientVersion version, boolean north, boolean south, boolean east, boolean west) {
return (!north && !south && !east && !west) &&
(version.isOlderThanOrEquals(ClientVersion.V_1_8) ||
(PacketEvents.getAPI().getServerManager().getVersion().isOlderThanOrEquals(ServerVersion.V_1_8_8) &&
version.isNewerThanOrEquals(ClientVersion.V_1_13)));
}
private CollisionBox getModernCollisionBox(boolean north, boolean east, boolean south, boolean west) {
return COLLISION_BOXES[getAABBIndex(north, east, south, west)].copy();
}
private CollisionBox getLegacyCollisionBox(boolean north, boolean east, boolean south, boolean west) {
float minX = 0.4375F;
float maxX = 0.5625F;
float minZ = 0.4375F;
float maxZ = 0.5625F;
if ((!west || !east) && (west || east || north || south)) {
if (west) {
minX = 0.0F;
} else if (east) {
maxX = 1.0F;
}
} else {
minX = 0.0F;
maxX = 1.0F;
}
if ((!north || !south) && (west || east || north || south)) {
if (north) {
minZ = 0.0F;
} else if (south) {
maxZ = 1.0F;
}
} else {
minZ = 0.0F;
maxZ = 1.0F;
}
return new SimpleCollisionBox(minX, 0.0F, minZ, maxX, 1.0F, maxZ);
}
@Override
public boolean canConnectToGlassBlock() {
return true;
}
@Override
public boolean checkCanConnect(GrimPlayer player, WrappedBlockState state, StateType one, StateType two, BlockFace direction) {
if (BlockTags.GLASS_PANES.contains(one) || one == StateTypes.IRON_BARS) {
return true;
} else {
return CollisionData.getData(one)
.getMovementCollisionBox(player, player.getClientVersion(), state, 0, 0, 0)
.isSideFullBlock(direction);
}
}
}
@@ -0,0 +1,109 @@
package ac.grim.grimac.utils.collisions.blocks.connecting;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.CollisionData;
import ac.grim.grimac.utils.collisions.datatypes.*;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags;
import com.github.retrooper.packetevents.protocol.world.states.enums.*;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
public class DynamicHitboxWall extends DynamicConnecting implements HitBoxFactory {
@Override
public CollisionBox fetch(GrimPlayer player, StateType heldItem, ClientVersion version, WrappedBlockState state, int x, int y, int z) {
int[] connections = getConnections(player, version, state, x, y, z);
int north = connections[0], south = connections[1], west = connections[2], east = connections[3], up = connections[4];
if (version.isNewerThanOrEquals(ClientVersion.V_1_13)) {
return getModernHitBox(north, south, west, east, up);
} else {
return getLegacyHitBox(north, south, west, east);
}
}
private int[] getConnections(GrimPlayer player, ClientVersion version, WrappedBlockState state, int x, int y, int z) {
int north = 0, south = 0, west = 0, east = 0, up = 0;
if (isModernServer()) {
boolean sixteen = PacketEvents.getAPI().getServerManager().getVersion().isNewerThan(ServerVersion.V_1_16);
north = getConnectionValue(state.getNorth(), sixteen);
east = getConnectionValue(state.getEast(), sixteen);
south = getConnectionValue(state.getSouth(), sixteen);
west = getConnectionValue(state.getWest(), sixteen);
up = state.isUp() ? 1 : 0;
} else {
north = connectsTo(player, version, x, y, z, BlockFace.NORTH) ? 1 : 0;
south = connectsTo(player, version, x, y, z, BlockFace.SOUTH) ? 1 : 0;
west = connectsTo(player, version, x, y, z, BlockFace.WEST) ? 1 : 0;
east = connectsTo(player, version, x, y, z, BlockFace.EAST) ? 1 : 0;
up = 1;
}
return new int[]{north, south, west, east, up};
}
private boolean isModernServer() {
return PacketEvents.getAPI().getServerManager().getVersion().isNewerThan(ServerVersion.V_1_12_2);
}
private int getConnectionValue(Enum<?> direction, boolean sixteen) {
if (direction == North.NONE || direction == East.NONE || direction == South.NONE || direction == West.NONE) {
return 0;
}
return (direction == North.LOW || direction == East.LOW || direction == South.LOW || direction == West.LOW || sixteen) ? 1 : 2;
}
private CollisionBox getModernHitBox(int north, int south, int west, int east, int up) {
ComplexCollisionBox box = new ComplexCollisionBox(5);
if (up == 1) {
box.add(new HexCollisionBox(4, 0, 4, 12, 16, 12));
}
addDirectionalBox(box, north, 5, 0, 0.0D, 11, 14, 11);
addDirectionalBox(box, south, 5, 0, 5, 11, 14, 16);
addDirectionalBox(box, west, 0, 0, 5, 11, 14, 11);
addDirectionalBox(box, east, 5, 0, 5, 16, 14, 11);
return box;
}
private void addDirectionalBox(ComplexCollisionBox box, int direction, double x1, double y1, double z1, double x2, double y2, double z2) {
if (direction == 1) {
box.add(new HexCollisionBox(x1, y1, z1, x2, y2, z2));
} else if (direction == 2) {
box.add(new HexCollisionBox(x1, y1, z1, x2, 16, z2));
}
}
private CollisionBox getLegacyHitBox(int north, int south, int west, int east) {
float minX = 0.25F, maxX = 0.75F, minZ = 0.25F, maxZ = 0.75F;
float maxY = 1.0F;
if (north == 1) minZ = 0.0F;
if (south == 1) maxZ = 1.0F;
if (west == 1) minX = 0.0F;
if (east == 1) maxX = 1.0F;
if (north == 1 && south == 1 && west == 0 && east == 0) {
maxY = 0.8125F;
minX = 0.3125F;
maxX = 0.6875F;
} else if (west == 1 && east == 1 && north == 0 && south == 0) {
maxY = 0.8125F;
minZ = 0.3125F;
maxZ = 0.6875F;
}
return new SimpleCollisionBox(minX, 0.0F, minZ, maxX, maxY, maxZ);
}
@Override
public boolean checkCanConnect(GrimPlayer player, WrappedBlockState state, StateType one, StateType two, BlockFace direction) {
return BlockTags.WALLS.contains(one) ||
CollisionData.getData(one).getMovementCollisionBox(player, player.getClientVersion(), state, 0, 0, 0).isSideFullBlock(direction);
}
}
@@ -228,4 +228,4 @@ public class BoundingBox {
public String toString() {
return "[" + minX + ", " + minY + ", " + minZ + ", " + maxX + ", " + maxY + ", " + maxZ + "]";
}
}
}
@@ -5,6 +5,8 @@ import com.github.retrooper.packetevents.protocol.world.BlockFace;
import java.util.List;
public interface CollisionBox {
CollisionBox union(SimpleCollisionBox other);
boolean isCollided(SimpleCollisionBox other);
boolean isIntersected(SimpleCollisionBox other);
@@ -15,6 +17,13 @@ public interface CollisionBox {
void downCast(List<SimpleCollisionBox> list);
/**
* @param list - A list that will contain all of the SimpleCollisionBoxes representing the CollisionBox.
* @return - int representing the size of elements in the array that belong to this downcast.
* The contents of elements starting from list[size] and above are undefined and should not be iterated over.
*/
int downCast(SimpleCollisionBox[] list);
boolean isNull();
boolean isFullBlock();
@@ -22,4 +31,4 @@ public interface CollisionBox {
default boolean isSideFullBlock(BlockFace axis) {
return isFullBlock();
}
}
}
@@ -6,4 +6,4 @@ import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState
public interface CollisionFactory {
CollisionBox fetch(GrimPlayer player, ClientVersion version, WrappedBlockState block, int x, int y, int z);
}
}
@@ -1,64 +1,91 @@
package ac.grim.grimac.utils.collisions.datatypes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Arrays;
import java.util.List;
public class ComplexCollisionBox implements CollisionBox {
private final List<CollisionBox> boxes = new ArrayList<>();
// Most complex shape is the Modern MC Cauldron which is made up of 15 boxes
public static int DEFAULT_MAX_COLLISION_BOX_SIZE = 15; // increase if we somehow have a shape made of more than 15 parts.
private final SimpleCollisionBox[] boxes;
private int currentLength;
public ComplexCollisionBox(CollisionBox... boxes) {
Collections.addAll(this.boxes, boxes);
public ComplexCollisionBox(SimpleCollisionBox... boxes) {
this(DEFAULT_MAX_COLLISION_BOX_SIZE, boxes);
}
public boolean add(CollisionBox collisionBox) {
return boxes.add(collisionBox);
public ComplexCollisionBox(int maxIndex) {
this.boxes = new SimpleCollisionBox[maxIndex];
}
public ComplexCollisionBox(int maxIndex, SimpleCollisionBox... boxes) {
this.boxes = new SimpleCollisionBox[maxIndex];
currentLength = Math.min(maxIndex, boxes.length);
System.arraycopy(boxes, 0, this.boxes, 0, this.currentLength);
}
public boolean add(SimpleCollisionBox collisionBox) {
boxes[currentLength] = collisionBox;
currentLength++;
return currentLength <= boxes.length;
}
@Override
public CollisionBox union(SimpleCollisionBox other) {
add(other);
return this;
}
@Override
public boolean isCollided(SimpleCollisionBox other) {
for (CollisionBox box : boxes) {
if (box.isCollided(other)) return true;
for (int i = 0; i < currentLength; i++) {
if (boxes[i].isCollided(other)) return true;
}
return false;
}
@Override
public boolean isIntersected(SimpleCollisionBox other) {
for (CollisionBox box : boxes) {
if (box.isIntersected(other))
return true;
for (int i = 0; i < currentLength; i++) {
if (boxes[i].isIntersected(other)) return true;
}
return false;
}
@Override
public CollisionBox copy() {
ComplexCollisionBox cc = new ComplexCollisionBox();
for (CollisionBox b : boxes)
cc.boxes.add(b.copy());
return cc;
ComplexCollisionBox copy = new ComplexCollisionBox(boxes.length);
for (int i = 0; i < currentLength; i++) {
copy.boxes[i] = boxes[i].copy();
}
copy.currentLength = this.currentLength;
return copy;
}
@Override
public CollisionBox offset(double x, double y, double z) {
for (CollisionBox b : boxes)
b.offset(x, y, z);
for (int i = 0; i < currentLength; i++) {
boxes[i].offset(x, y ,z);
}
return this;
}
@Override
public void downCast(List<SimpleCollisionBox> list) {
for (CollisionBox box : boxes)
box.downCast(list);
list.addAll(Arrays.asList(boxes).subList(0, currentLength));
}
@Override
public int downCast(SimpleCollisionBox[] list) {
System.arraycopy(boxes, 0, list, 0, currentLength);
return currentLength;
}
@Override
public boolean isNull() {
for (CollisionBox box : boxes)
if (!box.isNull())
return false;
for (int i = 0; i < currentLength; i++) {
if (!boxes[i].isNull()) return false;
}
return true;
}
@@ -66,4 +93,4 @@ public class ComplexCollisionBox implements CollisionBox {
public boolean isFullBlock() {
return false;
}
}
}
@@ -21,6 +21,14 @@ public class DynamicCollisionBox implements CollisionBox {
this.block = block;
}
// Untested but currently unused
// *should* work because every single one of these eventually becomes a Complex, Simple, or NoCollision Box
@Override
public CollisionBox union(SimpleCollisionBox other) {
CollisionBox dynamicBox = box.fetch(player, version, block, x, y, z).offset(x, y, z);
return dynamicBox.union(other);
}
@Override
public boolean isCollided(SimpleCollisionBox other) {
return box.fetch(player, version, block, x, y, z).offset(x, y, z).isCollided(other);
@@ -44,6 +52,11 @@ public class DynamicCollisionBox implements CollisionBox {
return this;
}
@Override
public int downCast(SimpleCollisionBox[] list) {
return box.fetch(player, version, block, x, y, z).offset(x, y, z).downCast(list);
}
@Override
public void downCast(List<SimpleCollisionBox> list) {
box.fetch(player, version, block, x, y, z).offset(x, y, z).downCast(list);
@@ -66,4 +79,4 @@ public class DynamicCollisionBox implements CollisionBox {
public void setVersion(ClientVersion version) {
this.version = version;
}
}
}
@@ -2,6 +2,18 @@ package ac.grim.grimac.utils.collisions.datatypes;
public class HexCollisionBox extends SimpleCollisionBox {
/**
* Creates a box defined by two points in 3d space; used to represent hitboxes and collision boxes.
* Mojang's block hitbox values are all based on chunks, so they're stored in game as 16 * the actual size
* When copying block hitbox values, it may be easier to simple copy the multiplied values and use this class
* If your min/max values are < 1 you should probably check out {@link SimpleCollisionBox}
* @param minX 16 * x position of first corner
* @param minY 16 * y position of first corner
* @param minZ 16 * z position of first corner
* @param maxX 16 * x position of second corner
* @param maxY 16 * y position of second corner
* @param maxZ 16 * z position of second corner
*/
public HexCollisionBox(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
this.minX = minX / 16d;
this.minY = minY / 16d;
@@ -0,0 +1,10 @@
package ac.grim.grimac.utils.collisions.datatypes;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
// Exists for the same reason as HexCollisionBox but for offset blocks; read comments there if unsure
public class HexOffsetCollisionBox extends OffsetCollisionBox {
public HexOffsetCollisionBox(StateType block, double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
super(block, minX / 16d, minY / 16d, minZ / 16d, maxX / 16d, maxY / 16d, maxZ / 16d);
}
}
@@ -9,6 +9,11 @@ public class NoCollisionBox implements CollisionBox {
private NoCollisionBox() {
}
@Override
public CollisionBox union(SimpleCollisionBox other) {
return other;
}
@Override
public boolean isCollided(SimpleCollisionBox other) {
return false;
@@ -27,6 +32,9 @@ public class NoCollisionBox implements CollisionBox {
@Override
public void downCast(List<SimpleCollisionBox> list) { /**/ }
@Override
public int downCast(SimpleCollisionBox[] list) { return 0; }
@Override
public boolean isNull() {
return true;
@@ -41,4 +49,4 @@ public class NoCollisionBox implements CollisionBox {
public CollisionBox copy() {
return this;
}
}
}
Loaded 100 of 145 files, more files were not shown because too many files have changed in this diff. Show more