Compare commits

..
185 Commits
Author SHA1 Message Date
Axionize 727461e2ac Add new Reach config option to skip checks against non-player-entities 2024-11-15 02:25:24 -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 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
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
SamB440 15984b0a94 Merge branch '2.0' into optimize-iteration-order 2024-10-31 21:53:50 +00:00
AoElite 97de474b79 Merge pull request #1772 from ManInMyVan/fix/perm
fix permission default for already defined permissions
2024-10-30 19:53:03 -04:00
ManInMyVan cea13a719d fix permission default 2024-10-30 16:31:04 -06:00
AoElite faa471b197 catch permission error 2024-10-30 18:20:50 -04:00
BadPackage d0eb36c87a small doc fix 2024-10-26 13:04:13 +02:00
SamB440 60f8812efd Merge pull request #1737 from Rothes/2.0
Fix config for Timer
2024-10-24 18:18:46 +01:00
SamB440 7e82f19961 Correctly handle reEncodeByDefault = false by last commit 2024-10-22 18:15:10 +01:00
AoElite 27aae68f14 add website 2024-10-22 12:00:33 -04:00
AoElite a65f24424f improved custom config manager handling 2024-10-22 11:59:43 -04:00
AoElite bfd3433423 regex optimization 2024-10-22 11:55:38 -04:00
AoElite 09e6cec6ef fix older versions of via throwing an error due to PR 2024-10-22 10:56:36 -04:00
AoElite 4ffe6f4fc5 packetevent optimizations 2024-10-22 10:11:02 -04:00
AoElite 4aaeb579e7 Merge pull request #1759 from ManInMyVan/fix/perm
fix exempt permission default
2024-10-22 02:09:38 -04:00
AoElite 487e9c6590 when reloading player configuration reload their checks & punishment manager as well for better API ease of use 2024-10-21 23:37:30 -04:00
SamB440 bdf7eb7c06 Merge pull request #1754 from Cyramek/via-version-fix
warn about `ViaVersion rotation-place fix`
2024-10-16 12:29:30 +01:00
ManInMyVan c70e5b8fff fix exempt permission default 2024-10-15 17:32:47 -06:00
SamB440 9599b1c631 Reduce Optional allocs with attribute methods 2024-10-14 14:24:19 +01:00
SamB440 85b42fa90e Merge pull request #1756 from ManInMyVan/fix/fr-config-noslow
fix fr.yml noslow
2024-10-13 19:50:11 +01:00
ManInMyVan 0c1a6c7775 fix fr.yml noslow 2024-10-13 12:47:08 -06:00
SamB440 d4508fa36b Fix AimModulo360? 2024-10-13 17:53:02 +01:00
SamB440 3cb1bb3792 Update actions 2024-10-13 17:50:51 +01:00
SamB440 faf0c7d3d6 Gradle 8.10.1 2024-10-13 17:49:47 +01:00
Cyramek c8ff1109e1 reverse that patch 2024-10-10 14:31:50 +02:00
AoElite de0c28beab reloading rework, allows you to have per player configurations & other things, doesn't cover every option yet 2024-10-10 08:00:33 -04:00
Cyramek acdacffcc0 take care of viaversion rotation fix, and warn server admin about that option 2024-10-10 13:56:22 +02:00
AoElite 3cab3fe680 Merge pull request #1744 from ManInMyVan/fix/bpZ
fix a BadPacketsZ false
2024-10-07 16:09:44 -04:00
AoElite 880a7b5e74 Merge pull request #1751 from Axionize/optimize-attribute-iteration
Optimize looping over attribute modifier properties
2024-10-07 16:09:30 -04: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 c4848efc8a Optimize looping over attribute modifier properties 2024-10-05 19:32:14 -04:00
SamB440 0ddf0b5d22 Merge pull request #1749 from ManInMyVan/fix/mutliplace
fix multiplace false
2024-10-06 00:26:26 +01:00
SamB440 fde6e03b98 Merge pull request #1750 from Axionize/fix-attribute-clamp
Fix incorrect casting to float before clamping attribute values
2024-10-06 00:19:44 +01:00
Axionize 55c084216d Fix incorrect casting to float before clamping attribute values 2024-10-05 17:32:07 -04:00
ManInMyVan f3074e3934 fix multiplace false 2024-10-05 13:15:11 -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
ManInMyVan 748eb398b8 fix a BadPacketsZ false
also use MessageUtil
2024-09-28 17:59:06 -06:00
AoElite ea85282f52 fix lambda 2024-09-28 18:57:53 -04:00
AoElite d723dec820 Merge pull request #1706 from overkidding/perm-fix-pr
Permission Load Fix
2024-09-28 18:30:55 -04:00
AoElite de03945f42 Merge pull request #1742 from ManInMyVan/fix/chains
fix chains for pre-1.16 players
2024-09-28 18:27:29 -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 dc7f6dca93 Make result player.getPossibleEyeHeights() of final in checks and remove unused imports 2024-09-27 23:57:23 -04:00
ManInMyVan 03ea36ad28 fix chains for pre-1.16 players 2024-09-26 20:09:18 -06:00
Axionize 472415e0d4 Removing unneeded special case handling 2024-09-26 17:13:11 -04:00
SamB440 b27678c753 Merge pull request #1740 from ManInMyVan/fix/multiplace
fix MultiPlace
2024-09-25 19:54:42 +01:00
ManInMyVan 6eb99263a2 fix MultiPlace 2024-09-25 12:51:32 -06:00
SamB440 bde44f5136 Merge pull request #1736 from Axionize/optimize-iteration-order-lookdir
Reduce average iterations of RotationPlace
2024-09-25 10:12:46 +01:00
Rothes da61ff673a Fix constructor default value overriding configured value 2024-09-25 17:12:26 +08: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
Axionize d5927578a9 Reduce average iterations of rotation place 2024-09-25 01:01:16 -04:00
Axionize 01ca8c894e Readability/documentation improvements 2024-09-25 00:51:22 -04:00
Axionize f724d6d78e 1.8 Players 2024-09-25 00:32:07 -04:00
Axionize ee6183db90 Handle player joining on new version -> scale changing -> old version 2024-09-25 00:32:07 -04:00
Axionize e959ef37db Optimize returned array for crawling 2024-09-25 00:32:07 -04:00
Axionize f62d8602b8 fix conditionall check 2024-09-25 00:32:07 -04:00
Axionize bf44dd6855 Only rewrite player possibleEyeHeights when player scale changes 2024-09-25 00:32:07 -04:00
SamB440 cc9273d05d Duh, 1 2024-09-24 14:45:45 +01:00
SamB440 c354b4481d Max minimum max-transaction-time 1 2024-09-24 14:44:06 +01:00
SamB440 ba8c411ed4 Don't allow invalid max-transaction-time 2024-09-24 14:41:03 +01:00
Axionize 1beedd77c3 Don't bother updating scale attribute if no change is made 2024-09-23 21:14:30 -04:00
Axionize c34ad88a13 Fix intializing array 2024-09-23 21:02:33 -04:00
Axionize 619e7f1404 Simplify logic and handle caching even when scale changes 2024-09-23 21:02:33 -04:00
SamB440 da1468e858 Bump packetevents 2024-09-23 18:15:15 +01:00
Axionize 927b330c4b Cache eyeHeights where possible 2024-09-22 17:30:02 -04:00
Axionize bff6854189 make getPossibleEyeHeights return most likely heights 2024-09-22 17:18:06 -04:00
Axionize 290bf9032e Backport patch 2024-09-21 20:33:30 -04:00
Axionize 2629536ad7 Optimize getPossibleEyeHeights() 2024-09-21 20:24:31 -04:00
ManInMyVan 9708ebc821 Fix a BadPacketsZ false 2024-09-20 10:18:25 +01:00
ManInMyVan a38f341965 Add MultiInteract Checks 2024-09-19 16:11:51 -06:00
AoElite fad39af575 Merge pull request #1729 from Cyramek/fix-compilation
fix compilation issues
2024-09-18 23:55:07 -04:00
Cyramek 4e4492e417 fix compilation issues 2024-09-19 05:50:04 +02:00
AoElite 5374ee3c0d Merge pull request #1726 from ManInMyVan/multiplace
Add MultiPlace
2024-09-18 22:17:21 -04:00
ManInMyVan 262de24cbf allow setbacks in NegativeTimer 2024-09-18 20:14:11 -06:00
ManInMyVan 936662db2d Add MultiPlace 2024-09-18 17:45:20 -06:00
Axionize 16bb09aee0 All HitBox and CollisionBox Changes made for LineOfSightPlace 2024-09-17 04:08:23 -04:00
overkiddingandManInMyVan 7ac2319c07 fix spacing formatting 6
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-09-14 09:05:48 +02:00
overkiddingandManInMyVan 89a795a7e6 fix spacing formatting 1
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-09-14 07:09:48 +02:00
overkiddingandManInMyVan 510247f3b4 fix spacing formatting 4
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-09-14 07:09:35 +02:00
overkiddingandManInMyVan a61b710e81 fix spacing formatting 3
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-09-14 07:09:26 +02:00
overkiddingandManInMyVan 049c30f8b1 fix spacing formatting 2
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-09-14 07:09:04 +02:00
overkiddingandManInMyVan 21a7bdd395 fix spacing formatting 1
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-09-14 07:08:53 +02:00
AoElite 598caa9259 Merge pull request #1719 from ManInMyVan/fix/BadPacketsZ-false
fix a BadPacketsZ false
2024-09-13 22:11:21 -04:00
AoElite 0d28fa3a9d Merge pull request #1720 from ManInMyVan/fix/BadPacketsX-false
fix a BadPacketsX false
2024-09-13 22:11:05 -04:00
AoElite 87c5c1d6c7 Merge pull request #1721 from ManInMyVan/fix/vine-fastbreak-false
vines are mined faster by swords
2024-09-13 22:10:22 -04:00
ManInMyVan dde9cea757 vines are mined faster by swords 2024-09-13 16:05:51 -06:00
ManInMyVan 1fe77a1dd6 fix a BadPacketsX false 2024-09-13 15:31:35 -06:00
ManInMyVan 72a2dfdde4 fix BadPacketsZ false 2024-09-13 15:27:22 -06:00
ManInMyVan f6ef1df29d exempt 1.14.2+ clients in NoSlowC and NoSlowD 2024-09-13 13:02:40 -06:00
SamB440 4c039e612b Bump version 2024-09-12 15:32:27 +01:00
Cyramek b0cd32622e Fix fences hitbox (#1710) 2024-09-12 15:11:15 +01:00
SamB440 3445a0220f Merge pull request #1695 from ManInMyVan/custom-webhook-embed-title
Allow customizing webhook embed title
2024-09-12 15:09:17 +01:00
SamB440 f1a1e46edc Old servers can set horse.jump_strength 2024-09-11 16:08:43 +01:00
SamB440 343e9ef16d Fix horse jumping with viabackwards, fixes #1713 2024-09-11 15:30:16 +01:00
SamB440 c4c063418a Merge pull request #1709 from Cyramek/fix-main-supporting-block
fix searching for main supporting block
2024-09-11 13:43:57 +01:00
Cyramek 95955b81f6 typo 2024-09-11 05:21:47 +02:00
Cyramek f4cb19c113 fix searching for main supporting block 2024-09-11 05:00:06 +02:00
overkidding f313d0897d fix: permission load 2024-09-10 09:38:07 +02:00
Kayera 0293606dc9 GrimAntiCheat Turkish Translation (#1673)
* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload
2024-09-06 14:38:07 +01:00
gNewl 705f223a70 Translate updates 2024-09-06 14:36:53 +01:00
gNewl 409b1ae646 Make it clear and objective, fix some incorrect descriptions and improve the messages from the last commiter
Some messages are too long compared to other translations
"log" and "depuramento" can be more accurate as "registro"
2024-09-06 14:36:53 +01:00
gNewl 13bd66de7b Grammar fixes, coherence improvements, standardize and rework in updates. 2024-09-06 14:36:53 +01:00
gNewl b3d59788b3 Grammar fixes, coherence improvements, standardize and rework in updates. 2024-09-06 14:36:53 +01:00
Dg32z_ c58ae74a41 Update chinese translation (#1680) 2024-09-06 14:36:03 +01:00
SamB440 aa6d07f893 Merge pull request #1701 from ManInMyVan/fix/bed-bounce-version
fix bed bouncy version
2024-09-06 11:35:23 +01:00
ManInMyVan cef044c4fe beds became bouncy in 1.12, not 1.8 2024-09-05 19:01:11 -06:00
SamB440 e8c7b9066f Fix strider attribute desync 2024-09-05 21:20:37 +01:00
SamB440 7f0dbdbc28 Fix strider off ground speed 2024-09-05 16:56:44 +01:00
SamB440 3890d12105 Fix strider speed outside of lava 2024-09-05 16:45:29 +01:00
SamB440 d62755bc01 Merge pull request #1630 from ManInMyVan/fix/offhand-noslow
fix a noslow
2024-09-03 17:40:56 +01:00
ManInMyVan dfc8c988d1 Allow customizing embed title 2024-09-02 11:45:07 -06:00
SamB440 508daaa472 Merge pull request #1694 from ManInMyVan/fix/post/held-item-change-1.7
exempt 1.7 in post for HELD_ITEM_CHANGE packets
2024-09-01 19:17:31 +01:00
ManInMyVan a765065f98 exempt 1.7 in post for HELD_ITEM_CHANGE packets 2024-09-01 12:15:33 -06:00
AoElite 7add2b6658 Merge pull request #1684 from ManInMyVan/exempt-specific-checks
allow exempting for specific checks
2024-08-30 14:26:30 -04:00
AoElite b3d780b52b Merge pull request #1688 from ManInMyVan/post-slots
check slot changes in post
2024-08-29 16:53:08 -04:00
AoElite 1e46837fec Merge pull request #1693 from Axionize/debug-improvements
Allow Live Class Reloading for Fast Debugging During Development
2024-08-29 16:52:52 -04:00
Axionize ac516060c0 Clean up 2024-08-29 16:51:20 -04:00
Axionize a5c34a0829 MVP commit 2024-08-29 16:47:04 -04:00
ManInMyVan 06fa4bab17 check slot changes in post 2024-08-28 17:06:32 -06:00
SamB440 aabead19ec Fix some respawn issues 2024-08-25 17:49:20 +01:00
ManInMyVan af60e07327 allow exempting for specific checks 2024-08-24 17:23:06 -06:00
ManInMyVan 0c78830524 fix a noslow 2024-08-14 19:03:48 -06:00
186 changed files with 3496 additions and 2044 deletions

No files matched your search

+71
View File
@@ -0,0 +1,71 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '30 15 * * 2'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'java' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v3
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
+38
View File
@@ -0,0 +1,38 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will build a package using Gradle and then publish it to GitHub packages when a release is created
# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#Publishing-using-gradle
name: Gradle Package
on:
[push, workflow_dispatch]
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
- name: Build with Gradle
uses: gradle/actions/setup-gradle@v3
with:
arguments: build
- name: Upload a Build Artifact
uses: actions/upload-artifact@v4
with:
path: /home/runner/work/Grim/Grim/build/libs/**.jar
-95
View File
@@ -1,95 +0,0 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle
name: Java CI with Gradle
on:
push:
branches: [ "2.0" ]
pull_request:
branches: [ "2.0" ]
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
# Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies.
# See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md
- name: Setup Gradle
uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda
- name: Build with Gradle Wrapper
run: ./gradlew build
- name: Get current datetime
id: datetime
run: echo "datetime=$(date +'%Y-%m-%d-%H%M%S')" >> $GITHUB_OUTPUT
- name: Delete previous dev release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
previous_release_id=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/repos/${{ github.repository }}/releases/tags/dev" | jq -r .id)
if [ "$previous_release_id" != "null" ]; then
curl -X DELETE -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/repos/${{ github.repository }}/releases/$previous_release_id"
git push --delete origin dev || true
fi
- name: Create new dev release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: dev
release_name: Dev Build ${{ steps.datetime.outputs.datetime }}
body: |
This is an automated dev build of the master branch.
Build date: ${{ steps.datetime.outputs.datetime }}
draft: false
prerelease: true
- name: Find JAR file
id: find_jar
run: |
JAR_PATH=$(find ./build/libs -name "*.jar" | head -n 1)
echo "jar_path=$JAR_PATH" >> $GITHUB_OUTPUT
echo "jar_name=$(basename $JAR_PATH)" >> $GITHUB_OUTPUT
- name: Upload artifact
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ${{ steps.find_jar.outputs.jar_path }}
asset_name: GrimAC-${{ steps.datetime.outputs.datetime }}-dev.jar
asset_content_type: application/java-archive
dependency-submission:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
+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
+52 -24
View File
@@ -6,13 +6,36 @@ 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.67"
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_1_8
java.targetCompatibility = JavaVersion.VERSION_1_8
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;
repositories {
mavenLocal()
@@ -34,19 +57,20 @@ repositories {
}
dependencies {
implementation("com.github.retrooper:packetevents-spigot:2.5.0-SNAPSHOT")
implementation("com.github.retrooper:packetevents-spigot:2.5.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("github.scarsz:configuralize:1.4.0")
implementation("com.github.grimanticheat:grimapi:1193c4fa41")
// Used for local testing: implementation("ac.grim.grimac:grimapi:1.0")
//implementation("com.github.grimanticheat:grimapi:1193c4fa41")
// Used for local testing: implementation("ac.grim.grimac:GRIMAPI:1.0")
implementation("com.github.grimanticheat:grimapi:fc5634e444")
implementation("org.jetbrains:annotations:24.1.0")
compileOnly("org.geysermc.floodgate:api:2.0-SNAPSHOT")
compileOnly("org.spigotmc:spigot-api:1.18.2-R0.1-SNAPSHOT")
compileOnly("com.viaversion:viaversion-api:4.9.4-SNAPSHOT")
compileOnly("com.viaversion:viaversion-api:5.0.4-SNAPSHOT")
//
compileOnly("io.netty:netty-all:4.1.85.Final")
}
@@ -55,6 +79,7 @@ bukkit {
name = "GrimAC"
author = "GrimAC"
main = "ac.grim.grimac.GrimAC"
website = "https://grim.ac/"
apiVersion = "1.13"
foliaSupported = true
@@ -120,6 +145,7 @@ bukkit {
tasks.build {
dependsOn(tasks.shadowJar)
dependsOn(tasks.spotlessApply)
}
tasks.withType<JavaCompile> {
@@ -133,21 +159,23 @@ publishing.publications.create<MavenPublication>("maven") {
tasks.shadowJar {
minimize()
archiveFileName.set("${project.name}-${project.version}.jar")
relocate("io.github.retrooper.packetevents", "ac.grim.grimac.shaded.io.github.retrooper.packetevents")
relocate("com.github.retrooper.packetevents", "ac.grim.grimac.shaded.com.github.retrooper.packetevents")
relocate("co.aikar.commands", "ac.grim.grimac.shaded.acf")
relocate("co.aikar.locale", "ac.grim.grimac.shaded.locale")
relocate("club.minnced", "ac.grim.grimac.shaded.discord-webhooks")
relocate("github.scarsz.configuralize", "ac.grim.grimac.shaded.configuralize")
relocate("com.github.puregero", "ac.grim.grimac.shaded.com.github.puregero")
relocate("com.google.code.gson", "ac.grim.grimac.shaded.gson")
relocate("alexh", "ac.grim.grimac.shaded.maps")
relocate("it.unimi.dsi.fastutil", "ac.grim.grimac.shaded.fastutil")
relocate("net.kyori", "ac.grim.grimac.shaded.kyori")
relocate("okhttp3", "ac.grim.grimac.shaded.okhttp3")
relocate("okio", "ac.grim.grimac.shaded.okio")
relocate("org.yaml.snakeyaml", "ac.grim.grimac.shaded.snakeyaml")
relocate("org.json", "ac.grim.grimac.shaded.json")
relocate("org.intellij", "ac.grim.grimac.shaded.intellij")
relocate("org.jetbrains", "ac.grim.grimac.shaded.jetbrains")
if (relocate) {
relocate("io.github.retrooper.packetevents", "ac.grim.grimac.shaded.io.github.retrooper.packetevents")
relocate("com.github.retrooper.packetevents", "ac.grim.grimac.shaded.com.github.retrooper.packetevents")
relocate("co.aikar.commands", "ac.grim.grimac.shaded.acf")
relocate("co.aikar.locale", "ac.grim.grimac.shaded.locale")
relocate("club.minnced", "ac.grim.grimac.shaded.discord-webhooks")
relocate("github.scarsz.configuralize", "ac.grim.grimac.shaded.configuralize")
relocate("com.github.puregero", "ac.grim.grimac.shaded.com.github.puregero")
relocate("com.google.code.gson", "ac.grim.grimac.shaded.gson")
relocate("alexh", "ac.grim.grimac.shaded.maps")
relocate("it.unimi.dsi.fastutil", "ac.grim.grimac.shaded.fastutil")
relocate("net.kyori", "ac.grim.grimac.shaded.kyori")
relocate("okhttp3", "ac.grim.grimac.shaded.okhttp3")
relocate("okio", "ac.grim.grimac.shaded.okio")
relocate("org.yaml.snakeyaml", "ac.grim.grimac.shaded.snakeyaml")
relocate("org.json", "ac.grim.grimac.shaded.json")
relocate("org.intellij", "ac.grim.grimac.shaded.intellij")
relocate("org.jetbrains", "ac.grim.grimac.shaded.jetbrains")
}
}
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
+2 -6
View File
@@ -1,17 +1,16 @@
package ac.grim.grimac;
import ac.grim.grimac.api.GrimAbstractAPI;
import ac.grim.grimac.manager.*;
import ac.grim.grimac.manager.config.BaseConfigManager;
import ac.grim.grimac.utils.anticheat.PlayerDataManager;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.plugin.ServicePriority;
import org.bukkit.plugin.java.JavaPlugin;
@Getter
public enum GrimAPI {
INSTANCE;
private final BaseConfigManager configManager = new BaseConfigManager();
private final AlertManagerImpl alertManager = new AlertManagerImpl();
private final SpectateManager spectateManager = new SpectateManager();
private final DiscordManager discordManager = new DiscordManager();
@@ -19,12 +18,10 @@ public enum GrimAPI {
private final TickManager tickManager = new TickManager();
private final GrimExternalAPI externalAPI = new GrimExternalAPI(this);
private InitManager initManager;
private ConfigManager configManager;
private JavaPlugin plugin;
public void load(final JavaPlugin plugin) {
this.plugin = plugin;
this.configManager = new ConfigManager();
initManager = new InitManager();
initManager.load();
}
@@ -32,7 +29,6 @@ public enum GrimAPI {
public void start(final JavaPlugin plugin) {
this.plugin = plugin;
initManager.start();
Bukkit.getServicesManager().register(GrimAbstractAPI.class, externalAPI, plugin, ServicePriority.Normal);
}
public void stop(final JavaPlugin plugin) {
+132 -38
View File
@@ -1,25 +1,34 @@
package ac.grim.grimac;
import ac.grim.grimac.api.AbstractCheck;
import ac.grim.grimac.api.GrimAbstractAPI;
import ac.grim.grimac.api.GrimUser;
import ac.grim.grimac.api.alerts.AlertManager;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.api.events.GrimReloadEvent;
import ac.grim.grimac.manager.config.ConfigManagerFileImpl;
import ac.grim.grimac.manager.init.Initable;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.LogUtil;
import ac.grim.grimac.utils.common.ConfigReloadObserver;
import com.github.retrooper.packetevents.netty.channel.ChannelHelper;
import io.github.retrooper.packetevents.util.SpigotReflectionUtil;
import io.github.retrooper.packetevents.util.folia.FoliaScheduler;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.ServicePriority;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
//This is used for grim's external API. It has its own class just for organization.
public class GrimExternalAPI implements GrimAbstractAPI, Initable {
public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, Initable {
private final GrimAPI api;
@@ -41,7 +50,8 @@ public class GrimExternalAPI implements GrimAbstractAPI, Initable {
@Getter
private final Map<String, Function<GrimUser, String>> variableReplacements = new ConcurrentHashMap<>();
@Getter private final Map<String, String> staticReplacements = new ConcurrentHashMap<>();
@Getter
private final Map<String, String> staticReplacements = new ConcurrentHashMap<>();
public String replaceVariables(GrimUser user, String content, boolean colors) {
if (colors) content = ChatColor.translateAlternateColorCodes('&', content);
@@ -56,12 +66,20 @@ public class GrimExternalAPI implements GrimAbstractAPI, Initable {
@Override
public void registerVariable(String string, Function<GrimUser, String> replacement) {
variableReplacements.put(string, replacement);
if (replacement == null) {
variableReplacements.remove(string);
} else {
variableReplacements.put(string, replacement);
}
}
@Override
public void registerVariable(String variable, String replacement) {
staticReplacements.put(variable, replacement);
if (replacement == null) {
staticReplacements.remove(variable);
} else {
staticReplacements.put(variable, replacement);
}
}
@Override
@@ -70,34 +88,20 @@ public class GrimExternalAPI implements GrimAbstractAPI, Initable {
return description.getVersion();
}
private final Map<String, Function<Object, Object>> functions = new ConcurrentHashMap<>();
@Override
public void registerFunction(String key, Function<Object, Object> function) {
if (function == null) {
functions.remove(key);
} else {
functions.put(key, function);
}
}
@Override
public Function<Object, Object> getFunction(String key) {
return null;
}
@Override
public void reload() {
GrimAPI.INSTANCE.getConfigManager().reload();
//Reload checks for all players
for (GrimPlayer grimPlayer : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
ChannelHelper.runInEventLoop(grimPlayer.user.getChannel(), () -> {
grimPlayer.onReload();
grimPlayer.updatePermissions();
grimPlayer.punishmentManager.reload();
for (AbstractCheck value : grimPlayer.checkManager.allChecks.values()) {
value.reload();
}
});
}
//Restart
GrimAPI.INSTANCE.getDiscordManager().start();
GrimAPI.INSTANCE.getSpectateManager().start();
GrimAPI.INSTANCE.getExternalAPI().start();
return functions.get(key);
}
@Override
@@ -106,16 +110,106 @@ public class GrimExternalAPI implements GrimAbstractAPI, Initable {
}
@Override
public void start() {
variableReplacements.put("%player%", GrimUser::getName);
variableReplacements.put("%uuid%", user -> user.getUniqueId().toString());
variableReplacements.put("%ping%", user -> user.getTransactionPing() + "");
variableReplacements.put("%brand%", GrimUser::getBrand);
variableReplacements.put("%h_sensitivity%", user -> ((int) Math.round(user.getHorizontalSensitivity() * 200)) + "");
variableReplacements.put("%v_sensitivity%", user -> ((int) Math.round(user.getVerticalSensitivity() * 200)) + "");
variableReplacements.put("%fast_math%", user -> !user.isVanillaMath() + "");
variableReplacements.put("%tps%", user -> String.format("%.2f", SpigotReflectionUtil.getTPS()));
variableReplacements.put("%version%", GrimUser::getVersionName);
variableReplacements.put("%prefix%", user -> ChatColor.translateAlternateColorCodes('&', GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("prefix", "&bGrim &8»")));
public ConfigManager getConfigManager() {
return configManager;
}
private ConfigManager configManager = null;
private final ConfigManagerFileImpl configManagerFile = new ConfigManagerFileImpl();
private boolean started = false;
// on load, load the config & register the service
public void load() {
reload(configManagerFile);
Bukkit.getServicesManager().register(GrimAbstractAPI.class, this, GrimAPI.INSTANCE.getPlugin(), ServicePriority.Normal);
}
// handles any config loading that's needed to be done after load
@Override
public void start() {
started = true;
try {
GrimAPI.INSTANCE.getConfigManager().start();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void reload(ConfigManager config) {
if (config.isLoadedAsync() && started) {
FoliaScheduler.getAsyncScheduler().runNow(GrimAPI.INSTANCE.getPlugin(),
o -> successfulReload(config));
} else {
successfulReload(config);
}
}
@Override
public CompletableFuture<Boolean> reloadAsync(ConfigManager config) {
if (config.isLoadedAsync() && started) {
CompletableFuture<Boolean> future = new CompletableFuture<>();
FoliaScheduler.getAsyncScheduler().runNow(GrimAPI.INSTANCE.getPlugin(),
o -> future.complete(successfulReload(config)));
return future;
}
return CompletableFuture.completedFuture(successfulReload(config));
}
private boolean successfulReload(ConfigManager config) {
try {
config.reload();
GrimAPI.INSTANCE.getConfigManager().load(config);
if (started) GrimAPI.INSTANCE.getConfigManager().start();
onReload(config);
if (started) FoliaScheduler.getAsyncScheduler().runNow(GrimAPI.INSTANCE.getPlugin(),
o -> Bukkit.getPluginManager().callEvent(new GrimReloadEvent(true)));
return true;
} catch (Exception e) {
e.printStackTrace();
}
if (started) FoliaScheduler.getAsyncScheduler().runNow(GrimAPI.INSTANCE.getPlugin(),
o -> Bukkit.getPluginManager().callEvent(new GrimReloadEvent(false)));
return false;
}
@Override
public void onReload(ConfigManager newConfig) {
if (newConfig == null) {
LogUtil.warn("ConfigManager not set. Using default config file manager.");
configManager = configManagerFile;
} else {
configManager = newConfig;
}
// Update variables
updateVariables();
// Restart
GrimAPI.INSTANCE.getDiscordManager().start();
GrimAPI.INSTANCE.getSpectateManager().start();
// Don't reload players if the plugin hasn't started yet
if (!started) return;
// Reload checks for all players
for (GrimPlayer grimPlayer : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
ChannelHelper.runInEventLoop(grimPlayer.user.getChannel(), () -> {
grimPlayer.updatePermissions();
grimPlayer.reload(configManager);
});
}
}
private void updateVariables() {
variableReplacements.putIfAbsent("%player%", GrimUser::getName);
variableReplacements.putIfAbsent("%uuid%", user -> user.getUniqueId().toString());
variableReplacements.putIfAbsent("%ping%", user -> user.getTransactionPing() + "");
variableReplacements.putIfAbsent("%brand%", GrimUser::getBrand);
variableReplacements.putIfAbsent("%h_sensitivity%", user -> ((int) Math.round(user.getHorizontalSensitivity() * 200)) + "");
variableReplacements.putIfAbsent("%v_sensitivity%", user -> ((int) Math.round(user.getVerticalSensitivity() * 200)) + "");
variableReplacements.putIfAbsent("%fast_math%", user -> !user.isVanillaMath() + "");
variableReplacements.putIfAbsent("%tps%", user -> String.format("%.2f", SpigotReflectionUtil.getTPS()));
variableReplacements.putIfAbsent("%version%", GrimUser::getVersionName);
// static variables
staticReplacements.putIfAbsent("%prefix%", ChatColor.translateAlternateColorCodes('&', GrimAPI.INSTANCE.getConfigManager().getPrefix()));
staticReplacements.putIfAbsent("%grim_version%", getGrimVersion());
}
}
+34 -13
View File
@@ -2,18 +2,20 @@ package ac.grim.grimac.checks;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.api.AbstractCheck;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.api.events.FlagEvent;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.common.ConfigReloadObserver;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon;
import github.scarsz.configuralize.DynamicConfig;
import io.github.retrooper.packetevents.util.folia.FoliaScheduler;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.Bukkit;
// Class from https://github.com/Tecnio/AntiCheatBase/blob/master/src/main/java/me/tecnio/anticheat/check/Check.java
@Getter
public class Check implements AbstractCheck {
public class Check implements AbstractCheck, ConfigReloadObserver {
protected final GrimPlayer player;
public double violations;
@@ -23,11 +25,13 @@ public class Check implements AbstractCheck {
private String checkName;
private String configName;
private String alternativeName;
private String displayName;
private String description;
private boolean experimental;
@Setter
private boolean isEnabled;
private boolean exempted;
@Override
public boolean isExperimental() {
@@ -50,13 +54,21 @@ public class Check implements AbstractCheck {
this.alternativeName = checkData.alternativeName();
this.experimental = checkData.experimental();
this.description = checkData.description();
this.displayName = this.checkName;
}
//
reload();
}
public boolean shouldModifyPackets() {
return isEnabled && !player.disableGrim && !player.noModifyPacketPermission;
return isEnabled && !player.disableGrim && !player.noModifyPacketPermission && !exempted;
}
public void updateExempted() {
if (player.bukkitPlayer == null || checkName == null) return;
FoliaScheduler.getEntityScheduler().run(player.bukkitPlayer, GrimAPI.INSTANCE.getPlugin(),
t -> exempted = player.bukkitPlayer.hasPermission("grim.exempt." + checkName.toLowerCase()),
() -> {});
}
public final boolean flagAndAlert(String verbose) {
@@ -72,7 +84,7 @@ public class Check implements AbstractCheck {
}
public final boolean flag() {
if (player.disableGrim || (experimental && !GrimAPI.INSTANCE.getConfigManager().isExperimentalChecks()))
if (player.disableGrim || (experimental && !player.isExperimentalChecks()) || exempted)
return false; // Avoid calling event if disabled
FlagEvent event = new FlagEvent(player, this);
@@ -98,21 +110,26 @@ public class Check implements AbstractCheck {
violations = Math.max(0, violations - decay);
}
public void reload() {
decay = getConfig().getDoubleElse(configName + ".decay", decay);
setbackVL = getConfig().getDoubleElse(configName + ".setbackvl", setbackVL);
@Override
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);
}
@Override
public void onReload(ConfigManager config) {
}
public boolean alert(String verbose) {
return player.punishmentManager.handleAlert(player, verbose, this);
}
public DynamicConfig getConfig() {
return GrimAPI.INSTANCE.getConfigManager().getConfig();
}
public boolean setbackIfAboveSetbackVL() {
if (getViolations() > setbackVL) {
return player.getSetbackTeleportUtil().executeViolationSetback();
@@ -133,5 +150,9 @@ public class Check implements AbstractCheck {
packetType == PacketType.Play.Client.WINDOW_CONFIRMATION;
}
}
@Override
public void reload() {
reload(GrimAPI.INSTANCE.getConfigManager().getConfig());
}
}
@@ -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,12 +20,17 @@ public class AimModulo360 extends Check implements RotationCheck {
@Override
public void process(final RotationUpdate rotationUpdate) {
lastDeltaYaw = rotationUpdate.getDeltaXRot();
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();
} else {
reward();
}
lastDeltaYaw = rotationUpdate.getDeltaXRot();
}
}
@@ -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;
}
}
}
@@ -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);
}
}
}
@@ -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;
@@ -4,7 +4,6 @@ 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;
@@ -25,17 +24,16 @@ public class BadPacketsX extends Check implements PacketCheck {
if (dig.getAction() != DiggingAction.START_DIGGING && dig.getAction() != DiggingAction.FINISHED_DIGGING)
return;
final boolean invalid;
if (dig.getAction() == DiggingAction.FINISHED_DIGGING) {
invalid = BlockBreakSpeed.getBlockDamage(player, dig.getBlockPosition()) >= 1 || block.getHardness() == -1.0f;
} else invalid = (block == StateTypes.LIGHT && !(player.getInventory().getHeldItem().is(ItemTypes.LIGHT) || player.getInventory().getOffHand().is(ItemTypes.LIGHT)))
// 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()
|| block == StateTypes.WATER
|| block == StateTypes.LAVA
|| block == StateTypes.BUBBLE_COLUMN
|| block == StateTypes.MOVING_PISTON
|| (block == StateTypes.FIRE && noFireHitbox);
|| (block == StateTypes.FIRE && noFireHitbox)
// or the client claims to have broken an unbreakable block
|| block.getHardness() == -1.0f && dig.getAction() == DiggingAction.FINISHED_DIGGING;
if (invalid && flagAndAlert("block=" + block.getName() + ", type=" + dig.getAction()) && shouldModifyPackets()) {
event.setCancelled(true);
@@ -4,7 +4,10 @@ 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.anticheat.MessageUtil;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.util.Vector3i;
@@ -21,7 +24,7 @@ public class BadPacketsZ extends Check implements PacketCheck {
private boolean lastBlockWasInstantBreak = false;
private Vector3i lastBlock, lastCancelledBlock, lastLastBlock = null;
private final int exemptedY = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_8) ? 4095 : 255;
private final int exemptedY = player.getClientVersion().isOlderThan(ClientVersion.V_1_8) ? 255 : (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_14) ? -1 : 4095);
// The client sometimes sends a wierd cancel packet
private boolean shouldExempt(final Vector3i pos) {
@@ -33,18 +36,10 @@ public class BadPacketsZ extends Check implements PacketCheck {
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4) && pos.y != exemptedY)
return false;
// the client only sends this packet if the last block was an instant break
if (getBlockDamage(player, lastBlock) < 1)
return false;
// and if this block is not an instant break
return player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4) || getBlockDamage(player, pos) < 1;
}
private String formatted(Vector3i vec) {
return vec == null ? "null" : vec.x + ", " + vec.y + ", " + vec.z;
}
public void handle(PacketReceiveEvent event, WrapperPlayClientPlayerDigging dig) {
if (dig.getAction() == DiggingAction.START_DIGGING) {
final Vector3i pos = dig.getBlockPosition();
@@ -59,7 +54,7 @@ public class BadPacketsZ extends Check implements PacketCheck {
final Vector3i pos = dig.getBlockPosition();
if (shouldExempt(pos)) {
lastCancelledBlock = null;
lastCancelledBlock = pos;
lastLastBlock = null;
lastBlock = null;
return;
@@ -68,7 +63,7 @@ public class BadPacketsZ extends Check implements PacketCheck {
if (!pos.equals(lastBlock)) {
// https://github.com/GrimAnticheat/Grim/issues/1512
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4) || (!lastBlockWasInstantBreak && pos.equals(lastCancelledBlock))) {
if (flagAndAlert("action=CANCELLED_DIGGING" + ", last=" + formatted(lastBlock) + ", pos=" + formatted(pos))) {
if (flagAndAlert("action=CANCELLED_DIGGING" + ", last=" + MessageUtil.toUnlabledString(lastBlock) + ", pos=" + MessageUtil.toUnlabledString(pos))) {
if (shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
@@ -89,7 +84,7 @@ public class BadPacketsZ extends Check implements PacketCheck {
// when a player looks away from the mined block, they send a cancel, and if they look at it again, they don't send another start. (thanks mojang!)
if (!pos.equals(lastCancelledBlock) && (!lastBlockWasInstantBreak || player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4)) && !pos.equals(lastBlock)) {
if (flagAndAlert("action=FINISHED_DIGGING" + ", last=" + formatted(lastBlock) + ", pos=" + formatted(pos))) {
if (flagAndAlert("action=FINISHED_DIGGING" + ", last=" + MessageUtil.toUnlabledString(lastBlock) + ", pos=" + MessageUtil.toUnlabledString(pos))) {
if (shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
@@ -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.skippedTickInActualMovement && predictionComplete.isChecked()) {
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.skippedTickInActualMovement && predictionComplete.isChecked()) {
for (String verbose : flags) {
flagAndAlert(verbose);
}
}
flags.clear();
hasInteracted = false;
}
}
@@ -15,16 +15,13 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package ac.grim.grimac.checks.impl.combat;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.HitData;
import ac.grim.grimac.utils.data.Pair;
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
import ac.grim.grimac.utils.nmsutil.BlockRayTrace;
import ac.grim.grimac.utils.data.packetentity.dragon.PacketEntityEnderDragonPart;
import ac.grim.grimac.utils.nmsutil.ReachUtils;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
@@ -34,13 +31,11 @@ import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.util.Vector3i;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
@@ -57,14 +52,13 @@ 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<>();
// Used to prevent falses in the wall hit check
private final Set<Vector3i> blocksChangedThisTick = new HashSet<>();
private static final List<EntityType> blacklisted = Arrays.asList(
EntityTypes.BOAT,
EntityTypes.CHEST_BOAT,
EntityTypes.SHULKER);
private boolean ignoreNonPlayerTargets;
private boolean cancelImpossibleHits;
private double threshold;
private double cancelBuffer; // For the next 4 hits after using reach, we aggressively cancel reach
@@ -97,7 +91,11 @@ public class Reach extends Check implements PacketCheck {
}
return;
}
if (ignoreNonPlayerTargets && !entity.getType().equals(EntityTypes.PLAYER)) {
return;
}
// Dead entities cause false flags (https://github.com/GrimAnticheat/Grim/issues/546)
if (entity.isDead) return;
@@ -122,11 +120,10 @@ public class Reach extends Check implements PacketCheck {
}
// If the player set their look, or we know they have a new tick
final boolean isFlying = WrapperPlayClientPlayerFlying.isFlying(event.getPacketType());
if (isFlying ||
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) ||
event.getPacketType() == PacketType.Play.Client.PONG ||
event.getPacketType() == PacketType.Play.Client.WINDOW_CONFIRMATION) {
tickBetterReachCheckWithAngle(isFlying);
tickBetterReachCheckWithAngle();
}
}
@@ -158,7 +155,7 @@ public class Reach extends Check implements PacketCheck {
}
}
private void tickBetterReachCheckWithAngle(boolean isFlying) {
private void tickBetterReachCheckWithAngle() {
for (Map.Entry<Integer, Vector3d> attack : playerAttackQueue.entrySet()) {
PacketEntity reachEntity = player.compensatedEntities.entityMap.get(attack.getKey().intValue());
if (reachEntity != null) {
@@ -173,9 +170,6 @@ public class Reach extends Check implements PacketCheck {
}
}
playerAttackQueue.clear();
// We can't use transactions for this because of this problem:
// transaction -> block changed applied -> 2nd transaction -> list cleared -> attack packet -> flying -> reach block hit checked, falses
if (isFlying) blocksChangedThisTick.clear();
}
private String checkReach(PacketEntity reachEntity, Vector3d from, boolean isPrediction) {
@@ -222,12 +216,13 @@ public class Reach extends Check implements PacketCheck {
// +3 would be 3 + 3 = 6, which is the pre-1.20.5 behaviour, preventing "Missed Hitbox"
final double distance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_ENTITY_INTERACTION_RANGE) + 3;
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
for (Vector lookVec : possibleLookDirs) {
for (double eye : player.getPossibleEyeHeights()) {
for (double eye : possibleEyeHeights) {
Vector eyePos = new Vector(from.getX(), from.getY() + eye, from.getZ());
Vector endReachPos = eyePos.clone().add(new Vector(lookVec.getX() * distance, lookVec.getY() * distance, lookVec.getZ() * distance));
Vector intercept = ReachUtils.calculateIntercept(targetBox, eyePos, endReachPos).getFirst();
Vector intercept = ReachUtils.calculateIntercept(targetBox, eyePos, endReachPos).first();
if (ReachUtils.isVecInside(targetBox, eyePos)) {
minDistance = 0;
@@ -240,26 +235,9 @@ public class Reach extends Check implements PacketCheck {
}
}
final boolean experimentalChecks = true; //GrimAPI.INSTANCE.getConfigManager().isExperimentalChecks(); // TODO fix for undraft
HitData foundHitData = null;
// If the entity is within range of the player (we'll flag anyway if not, so no point checking blocks in this case)
// Ignore when could be hitting through a moving shulker, piston blocks. They are just too glitchy/uncertain to check.
if (experimentalChecks && minDistance <= 3 && !player.compensatedWorld.isNearHardEntity(player.boundingBox.copy().expand(4))) {
final @Nullable Pair<Double, HitData> targetBlock = getTargetBlock(player, possibleLookDirs, from, minDistance);
// And if the target block is closer to the player than the entity box, they should hit the block instead
// So, this hit is invalid.
if (targetBlock != null && targetBlock.getFirst() < (minDistance * minDistance)) { // targetBlock is squared
minDistance = Double.MIN_VALUE;
foundHitData = targetBlock.getSecond();
}
}
// if the entity is not exempt and the entity is alive
if ((!blacklisted.contains(reachEntity.getType()) && reachEntity.isLivingEntity()) || reachEntity.getType() == EntityTypes.END_CRYSTAL) {
if (minDistance == Double.MIN_VALUE && foundHitData != null) {
cancelBuffer = 1;
return "Hit block block=" + foundHitData.getState().getType().getName();
} else if (minDistance == Double.MAX_VALUE) {
if (minDistance == Double.MAX_VALUE) {
cancelBuffer = 1;
return "Missed hitbox";
} else if (minDistance > player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_ENTITY_INTERACTION_RANGE)) {
@@ -273,49 +251,10 @@ public class Reach extends Check implements PacketCheck {
return null;
}
public void handleBlockChange(Vector3i vector3i, WrappedBlockState state) {
if (blocksChangedThisTick.size() >= 40) return; // Don't let players freeze movement packets to grow this
// Only do this for nearby blocks
if (new Vector(vector3i.x, vector3i.y, vector3i.z).distanceSquared(new Vector(player.x, player.y, player.z)) > 6) return;
// Only do this if the state really had any world impact
if (state.equals(player.compensatedWorld.getWrappedBlockStateAt(vector3i))) return;
blocksChangedThisTick.add(vector3i);
}
// Returns a pair so we can check the block type in the flag
@Nullable
private Pair<Double, HitData> getTargetBlock(GrimPlayer player, List<Vector> possibleLookDirs, Vector3d from, double minDistance) {
// Check every possible look direction and every possible eye height
// IF *NONE* of them allow the player to hit the entity, this is an invalid hit
HitData bestHitData = null;
double min = Double.MAX_VALUE;
for (Vector lookVec : possibleLookDirs) {
for (double eye : player.getPossibleEyeHeights()) {
Vector eyes = new Vector(from.getX(), from.getY() + eye, from.getZ());
final double reach = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_BLOCK_INTERACTION_RANGE);
final HitData hitResult = BlockRayTrace.getNearestReachHitResult(player, eyes, lookVec, minDistance, reach);
if (hitResult == null) {
return null;
}
final double distance = eyes.distanceSquared(hitResult.getBlockHitLocation());
// Block changes are uncertain, can't check this tick
if (distance < (minDistance * minDistance) && blocksChangedThisTick.contains(hitResult.getPosition())) {
return null;
}
bestHitData = hitResult;
min = Math.min(min, distance);
}
}
return bestHitData == null ? null : Pair.of(min, bestHitData);
}
@Override
public void reload() {
super.reload();
this.cancelImpossibleHits = getConfig().getBooleanElse("Reach.block-impossible-hits", true);
this.threshold = getConfig().getDoubleElse("Reach.threshold", 0.0005);
public void onReload(ConfigManager config) {
this.ignoreNonPlayerTargets = config.getBooleanElse("Reach.ignore-non-player-targets", false);
this.cancelImpossibleHits = config.getBooleanElse("Reach.block-impossible-hits", true);
this.threshold = config.getDoubleElse("Reach.threshold", 0.0005);
}
}
@@ -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
@@ -43,7 +43,10 @@ public class NoFallA extends Check implements PacketCheck {
if (!isNearGround(wrapper.isOnGround())) { // If player isn't near ground
// 1.8 boats have a mind on their own... only flag if they're not near a boat or are on 1.9+
if (!GhostBlockDetector.isGhostBlock(player) && flagWithSetback()) alert("");
if (shouldModifyPackets()) wrapper.setOnGround(false);
if (shouldModifyPackets()) {
wrapper.setOnGround(false);
event.markForReEncode(true);
}
}
}
}
@@ -59,10 +62,16 @@ public class NoFallA extends Check implements PacketCheck {
// Also flip teleports because I don't trust vanilla's handling of teleports and ground
if (flipPlayerGroundStatus) {
flipPlayerGroundStatus = false;
if (shouldModifyPackets()) wrapper.setOnGround(!wrapper.isOnGround());
if (shouldModifyPackets()) {
wrapper.setOnGround(!wrapper.isOnGround());
event.markForReEncode(true);
}
}
if (player.packetStateData.lastPacketWasTeleport) {
if (shouldModifyPackets()) wrapper.setOnGround(false);
if (shouldModifyPackets()) {
wrapper.setOnGround(false);
event.markForReEncode(true);
}
}
}
}
@@ -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;
@@ -1,5 +1,6 @@
package ac.grim.grimac.checks.impl.misc;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.type.BlockPlaceCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
@@ -63,10 +64,9 @@ public class GhostBlockMitigation extends BlockPlaceCheck {
}
@Override
public void reload() {
super.reload();
allow = getConfig().getBooleanElse("exploit.allow-building-on-ghostblocks", true);
distance = getConfig().getIntElse("exploit.distance-to-check-for-ghostblocks", 2);
public void onReload(ConfigManager config) {
allow = config.getBooleanElse("exploit.allow-building-on-ghostblocks", true);
distance = config.getIntElse("exploit.distance-to-check-for-ghostblocks", 2);
if (distance < 2 || distance > 4) distance = 2;
}
@@ -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);
}
}
}
@@ -1,12 +1,13 @@
package ac.grim.grimac.checks.impl.movement;
import ac.grim.grimac.api.config.ConfigManager;
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;
@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) {
@@ -23,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;
}
}
@@ -35,8 +36,7 @@ public class NegativeTimerCheck extends TimerCheck implements PostPredictionChec
}
@Override
public void reload() {
super.reload();
clockDrift = (long) (getConfig().getDoubleElse(getConfigName() + ".drift", 1200.0) * 1e6);
public void onReload(ConfigManager config) {
clockDrift = (long) (config.getDoubleElse(getConfigName() + ".drift", 1200.0) * 1e6);
}
}
@@ -1,5 +1,6 @@
package ac.grim.grimac.checks.impl.movement;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
@@ -33,8 +34,7 @@ public class NoSlowA extends Check implements PostPredictionCheck {
}
if (bestOffset > offsetToFlag) {
if (flaggedLastTick) {
flagWithSetback();
if (flaggedLastTick && flagWithSetback()) {
alert("");
}
flaggedLastTick = true;
@@ -51,8 +51,7 @@ public class NoSlowA extends Check implements PostPredictionCheck {
}
@Override
public void reload() {
super.reload();
offsetToFlag = getConfig().getDoubleElse("NoSlowA.threshold", 0.001);
public void onReload(ConfigManager config) {
offsetToFlag = config.getDoubleElse("NoSlowA.threshold", 0.001);
}
}
@@ -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 {
@@ -1,5 +1,6 @@
package ac.grim.grimac.checks.impl.movement;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
@@ -19,9 +20,9 @@ public class TimerCheck extends Check implements PacketCheck {
// How long should the player be able to fall back behind their ping?
// Default: 120 milliseconds
long clockDrift = (long) 120e6;
long clockDrift;
long limitAbuseOverPing = 1000;
long limitAbuseOverPing;
boolean hasGottenMovementAfterTransaction = false;
@@ -115,9 +116,8 @@ public class TimerCheck extends Check implements PacketCheck {
}
@Override
public void reload() {
super.reload();
clockDrift = (long) (getConfig().getDoubleElse(getConfigName() + ".drift", 120.0) * 1e6);
limitAbuseOverPing = (long) (getConfig().getDoubleElse(getConfigName() + ".ping-abuse-limit-threshold", 1000));
public void onReload(ConfigManager config) {
clockDrift = (long) (config.getDoubleElse(getConfigName() + ".drift", 120.0) * 1e6);
limitAbuseOverPing = (long) (config.getDoubleElse(getConfigName() + ".ping-abuse-limit-threshold", 1000));
}
}
@@ -19,7 +19,6 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPl
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityAnimation;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@@ -87,6 +86,7 @@ public class PostCheck extends Check implements PacketCheck, PostPredictionCheck
post.clear();
sentFlying = false;
} else if (PLAYER_ABILITIES.equals(packetType)
|| (HELD_ITEM_CHANGE.equals(packetType) && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_8))
|| INTERACT_ENTITY.equals(packetType) || PLAYER_BLOCK_PLACEMENT.equals(packetType)
|| USE_ITEM.equals(packetType) || PLAYER_DIGGING.equals(packetType)) {
if (sentFlying) post.add(event.getPacketType());
@@ -1,5 +1,6 @@
package ac.grim.grimac.checks.impl.prediction;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.api.events.CompletePredictionEvent;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
@@ -99,14 +100,12 @@ public class OffsetHandler extends Check implements PostPredictionCheck {
}
@Override
public void reload() {
super.reload();
setbackDecayMultiplier = getConfig().getDoubleElse("Simulation.setback-decay-multiplier", 0.999);
threshold = getConfig().getDoubleElse("Simulation.threshold", 0.001);
immediateSetbackThreshold = getConfig().getDoubleElse("Simulation.immediate-setback-threshold", 0.1);
maxAdvantage = getConfig().getDoubleElse("Simulation.max-advantage", 1);
maxCeiling = getConfig().getDoubleElse("Simulation.max-ceiling", 4);
public void onReload(ConfigManager config) {
setbackDecayMultiplier = config.getDoubleElse("Simulation.setback-decay-multiplier", 0.999);
threshold = config.getDoubleElse("Simulation.threshold", 0.001);
immediateSetbackThreshold = config.getDoubleElse("Simulation.immediate-setback-threshold", 0.1);
maxAdvantage = config.getDoubleElse("Simulation.max-advantage", 1);
maxCeiling = config.getDoubleElse("Simulation.max-ceiling", 4);
if (maxAdvantage == -1) maxAdvantage = Double.MAX_VALUE;
if (immediateSetbackThreshold == -1) immediateSetbackThreshold = Double.MAX_VALUE;
}
@@ -1,5 +1,6 @@
package ac.grim.grimac.checks.impl.scaffolding;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockPlaceCheck;
import ac.grim.grimac.player.GrimPlayer;
@@ -29,8 +30,7 @@ public class AirLiquidPlace extends BlockPlaceCheck {
}
@Override
public void reload() {
super.reload();
this.cancelVL = getConfig().getIntElse(getConfigName() + ".cancelVL", 0);
public void onReload(ConfigManager config) {
this.cancelVL = config.getIntElse(getConfigName() + ".cancelVL", 0);
}
}
@@ -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;
@@ -26,7 +24,8 @@ public class FarPlace extends BlockPlaceCheck {
if (place.getMaterial() == StateTypes.SCAFFOLDING) return;
double min = Double.MAX_VALUE;
for (double d : player.getPossibleEyeHeights()) {
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
for (double d : possibleEyeHeights) {
SimpleCollisionBox box = new SimpleCollisionBox(blockPos);
Vector eyes = new Vector(player.x, player.y + d, player.z);
Vector best = VectorUtils.cutBoxToVector(eyes, box);
@@ -0,0 +1,74 @@
package ac.grim.grimac.checks.impl.scaffolding;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockPlaceCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.MessageUtil;
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.util.Vector3f;
import com.github.retrooper.packetevents.util.Vector3i;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
import java.util.ArrayList;
import java.util.List;
@CheckData(name = "MultiPlace", experimental = true)
public class MultiPlace extends BlockPlaceCheck {
public MultiPlace(GrimPlayer player) {
super(player);
}
private final List<String> flags = new ArrayList<>();
private boolean hasPlaced;
private BlockFace lastFace;
private Vector3f lastCursor;
private Vector3i lastPos;
@Override
public void onBlockPlace(final BlockPlace place) {
final BlockFace face = place.getDirection();
final Vector3f cursor = place.getCursor();
final Vector3i pos = place.getPlacedAgainstBlockLocation();
if (hasPlaced && (face != lastFace || !cursor.equals(lastCursor) || !pos.equals(lastPos))) {
final String verbose = "face=" + face + ", lastFace=" + lastFace
+ ", cursor=" + MessageUtil.toUnlabledString(cursor) + ", lastCursor=" + MessageUtil.toUnlabledString(lastCursor)
+ ", pos=" + MessageUtil.toUnlabledString(pos) + ", lastPos=" + MessageUtil.toUnlabledString(lastPos);
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert(verbose) && shouldModifyPackets() && shouldCancel()) {
place.resync();
}
} else {
flags.add(verbose);
}
}
lastFace = face;
lastCursor = cursor;
lastPos = pos;
hasPlaced = true;
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && !player.packetStateData.lastPacketWasTeleport && !player.packetStateData.lastPacketWasOnePointSeventeenDuplicate) {
hasPlaced = false;
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
if (player.getClientVersion().isNewerThan(ClientVersion.V_1_8) && !player.skippedTickInActualMovement && predictionComplete.isChecked()) {
for (String verbose : flags) {
flagAndAlert(verbose);
}
}
flags.clear();
}
}
@@ -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 {
@@ -28,8 +27,13 @@ public class PositionPlace extends BlockPlaceCheck {
// Each position represents the best case scenario to have clicked
//
// We will now calculate the most optimal position for the player's head to be in
double minEyeHeight = Collections.min(player.getPossibleEyeHeights());
double maxEyeHeight = Collections.max(player.getPossibleEyeHeights());
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
double minEyeHeight = Double.MAX_VALUE;
double maxEyeHeight = Double.MIN_VALUE;
for (double height : possibleEyeHeights) {
minEyeHeight = Math.min(minEyeHeight, height);
maxEyeHeight = Math.max(maxEyeHeight, height);
}
// I love the idle packet, why did you remove it mojang :(
// Don't give 0.03 lenience if the player is a 1.8 player and we know they couldn't have 0.03'd because idle packet
double movementThreshold = !player.packetStateData.didLastMovementIncludePosition || player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) ? player.getMovementThreshold() : 0;
@@ -44,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()) {
@@ -71,13 +71,19 @@ public class RotationPlace extends BlockPlaceCheck {
SimpleCollisionBox box = new SimpleCollisionBox(place.getPlacedAgainstBlockLocation());
List<Vector3f> possibleLookDirs = new ArrayList<>(Arrays.asList(
new Vector3f(player.lastXRot, player.yRot, 0),
new Vector3f(player.xRot, player.yRot, 0)
new Vector3f(player.xRot, player.yRot, 0),
new Vector3f(player.lastXRot, player.yRot, 0)
));
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
// Start checking if player is in the block
double minEyeHeight = Collections.min(player.getPossibleEyeHeights());
double maxEyeHeight = Collections.max(player.getPossibleEyeHeights());
double minEyeHeight = Double.MAX_VALUE;
double maxEyeHeight = Double.MIN_VALUE;
for (double height : possibleEyeHeights) {
minEyeHeight = Math.min(minEyeHeight, height);
maxEyeHeight = Math.max(maxEyeHeight, height);
}
SimpleCollisionBox eyePositions = new SimpleCollisionBox(player.x, player.y + minEyeHeight, player.z, player.x, player.y + maxEyeHeight, player.z);
eyePositions.expand(player.getMovementThreshold());
@@ -99,7 +105,7 @@ public class RotationPlace extends BlockPlaceCheck {
}
final double distance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_BLOCK_INTERACTION_RANGE);
for (double d : player.getPossibleEyeHeights()) {
for (double d : possibleEyeHeights) {
for (Vector3f lookDir : possibleLookDirs) {
// x, y, z are correct for the block placement even after post tick because of code elsewhere
Vector3d starting = new Vector3d(player.x, player.y + d, player.z);
@@ -107,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;
}
}
@@ -1,5 +1,6 @@
package ac.grim.grimac.checks.impl.velocity;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
@@ -18,7 +19,6 @@ import com.github.retrooper.packetevents.util.Vector3f;
import com.github.retrooper.packetevents.util.Vector3i;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerExplosion;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.Nullable;
@@ -256,12 +256,10 @@ public class ExplosionHandler extends Check implements PostPredictionCheck {
}
@Override
public void reload() {
super.reload();
offsetToFlag = getConfig().getDoubleElse("Explosion.threshold", 0.00001);
setbackVL = getConfig().getDoubleElse("Explosion.setbackvl", 10);
public void onReload(ConfigManager config) {
offsetToFlag = config.getDoubleElse("Explosion.threshold", 0.00001);
setbackVL = config.getDoubleElse("Explosion.setbackvl", 10);
if (setbackVL == -1) setbackVL = Double.MAX_VALUE;
}
}
@@ -1,6 +1,7 @@
package ac.grim.grimac.checks.impl.velocity;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
@@ -240,15 +241,14 @@ public class KnockbackHandler extends Check implements PostPredictionCheck {
}
@Override
public void reload() {
super.reload();
offsetToFlag = getConfig().getDoubleElse("Knockback.threshold", 0.001);
maxAdv = getConfig().getDoubleElse("Knockback.max-advantage", 1);
immediate = getConfig().getDoubleElse("Knockback.immediate-setback-threshold", 0.1);
multiplier = getConfig().getDoubleElse("Knockback.setback-decay-multiplier", 0.999);
ceiling = getConfig().getDoubleElse("Knockback.max-ceiling", 4);
public void onReload(ConfigManager config) {
offsetToFlag = config.getDoubleElse("Knockback.threshold", 0.001);
maxAdv = config.getDoubleElse("Knockback.max-advantage", 1);
immediate = config.getDoubleElse("Knockback.immediate-setback-threshold", 0.1);
multiplier = config.getDoubleElse("Knockback.setback-decay-multiplier", 0.999);
ceiling = config.getDoubleElse("Knockback.max-ceiling", 4);
if (maxAdv < 0) maxAdv = Double.MAX_VALUE;
if (immediate < 0) immediate = Double.MAX_VALUE;
}
}
@@ -1,5 +1,6 @@
package ac.grim.grimac.checks.type;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
@@ -14,7 +15,7 @@ import com.github.retrooper.packetevents.util.Vector3i;
import java.util.ArrayList;
import java.util.List;
public class BlockPlaceCheck extends Check implements RotationCheck {
public class BlockPlaceCheck extends Check implements RotationCheck, PostPredictionCheck {
private static final List<StateType> weirdBoxes = new ArrayList<>();
private static final List<StateType> buggyBoxes = new ArrayList<>();
@@ -33,9 +34,8 @@ public class BlockPlaceCheck extends Check implements RotationCheck {
}
@Override
public void reload() {
super.reload();
this.cancelVL = getConfig().getIntElse(getConfigName() + ".cancelVL", 5);
public void onReload(ConfigManager config) {
this.cancelVL = config.getIntElse(getConfigName() + ".cancelVL", 5);
}
protected boolean shouldCancel() {
@@ -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;
@@ -1,15 +1,11 @@
package ac.grim.grimac.commands;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.MessageUtil;
import co.aikar.commands.BaseCommand;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Subcommand;
import com.github.retrooper.packetevents.netty.channel.ChannelHelper;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
@CommandAlias("grim|grimac")
@@ -18,14 +14,15 @@ public class GrimReload extends BaseCommand {
@CommandPermission("grim.reload")
public void onReload(CommandSender sender) {
//reload config
try {
GrimAPI.INSTANCE.getExternalAPI().reload();
} catch (RuntimeException e) {
sender.sendMessage(ChatColor.RED + e.getMessage());
return;
}
sender.sendMessage(MessageUtil.format("%prefix% &fConfig has been reloaded."));
sender.sendMessage(MessageUtil.format("%prefix% &7Reloading config..."));
GrimAPI.INSTANCE.getExternalAPI().reloadAsync().exceptionally(throwable -> false)
.thenAccept(bool -> {
if (bool) {
sender.sendMessage(MessageUtil.format("%prefix% &fConfig has been reloaded."));
} else {
sender.sendMessage(MessageUtil.format("%prefix% &cFailed to reload config."));
}
});
}
}
@@ -20,7 +20,7 @@ public class GrimSendAlert extends BaseCommand {
bukkitPlayer.sendMessage(string);
}
if (GrimAPI.INSTANCE.getConfigManager().getConfig().getBooleanElse("alerts.print-to-console", true)) {
if (GrimAPI.INSTANCE.getConfigManager().isPrintAlertsToConsole()) {
LogUtil.console(string); // Print alert to console
}
}
@@ -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!");
@@ -17,8 +17,7 @@ public class GrimStopSpectating extends BaseCommand {
@CommandCompletion("here")
public void onStopSpectate(CommandSender sender, String[] args) {
String string = args.length > 0 ? args[0] : null;
if (!(sender instanceof Player)) return;
Player player = (Player) sender;
if (!(sender instanceof Player player)) return;
if (GrimAPI.INSTANCE.getSpectateManager().isSpectating(player.getUniqueId())) {
boolean teleportBack = string == null || !string.equalsIgnoreCase("here");
GrimAPI.INSTANCE.getSpectateManager().disable(player, teleportBack);
@@ -28,4 +27,3 @@ public class GrimStopSpectating extends BaseCommand {
}
}
}
@@ -14,6 +14,7 @@ import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.*;
import ac.grim.grimac.utils.inventory.Inventory;
import ac.grim.grimac.utils.latency.CompensatedWorld;
import ac.grim.grimac.utils.math.GrimMath;
import ac.grim.grimac.utils.math.VectorUtils;
import ac.grim.grimac.utils.nmsutil.*;
import com.github.retrooper.packetevents.PacketEvents;
@@ -46,9 +47,12 @@ import com.github.retrooper.packetevents.wrapper.play.client.*;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerAcknowledgeBlockChanges;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSetSlot;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import org.bukkit.util.Vector;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.util.Vector;
import java.util.function.BiFunction;
import java.util.function.Function;
public class CheckManagerListener extends PacketListenerAbstract {
@@ -56,8 +60,79 @@ public class CheckManagerListener extends PacketListenerAbstract {
super(PacketListenerPriority.LOW);
}
// Copied from MCP...
// Returns null if there isn't anything.
//
// I do have to admit that I'm starting to like bifunctions/new java 8 things more than I originally did.
// although I still don't understand Mojang's obsession with streams in some of the hottest methods... that kills performance
public static HitData traverseBlocks(GrimPlayer player, Vector3d start, Vector3d end, BiFunction<WrappedBlockState, Vector3i, HitData> predicate) {
// I guess go back by the collision epsilon?
double endX = GrimMath.lerp(-1.0E-7D, end.x, start.x);
double endY = GrimMath.lerp(-1.0E-7D, end.y, start.y);
double endZ = GrimMath.lerp(-1.0E-7D, end.z, start.z);
double startX = GrimMath.lerp(-1.0E-7D, start.x, end.x);
double startY = GrimMath.lerp(-1.0E-7D, start.y, end.y);
double startZ = GrimMath.lerp(-1.0E-7D, start.z, end.z);
int floorStartX = GrimMath.floor(startX);
int floorStartY = GrimMath.floor(startY);
int floorStartZ = GrimMath.floor(startZ);
if (start.equals(end)) return null;
WrappedBlockState state = player.compensatedWorld.getWrappedBlockStateAt(floorStartX, floorStartY, floorStartZ);
HitData apply = predicate.apply(state, new Vector3i(floorStartX, floorStartY, floorStartZ));
if (apply != null) {
return apply;
}
double xDiff = endX - startX;
double yDiff = endY - startY;
double zDiff = endZ - startZ;
double xSign = Math.signum(xDiff);
double ySign = Math.signum(yDiff);
double zSign = Math.signum(zDiff);
double posXInverse = xSign == 0 ? Double.MAX_VALUE : xSign / xDiff;
double posYInverse = ySign == 0 ? Double.MAX_VALUE : ySign / yDiff;
double posZInverse = zSign == 0 ? Double.MAX_VALUE : zSign / zDiff;
double d12 = posXInverse * (xSign > 0 ? 1.0D - GrimMath.frac(startX) : GrimMath.frac(startX));
double d13 = posYInverse * (ySign > 0 ? 1.0D - GrimMath.frac(startY) : GrimMath.frac(startY));
double d14 = posZInverse * (zSign > 0 ? 1.0D - GrimMath.frac(startZ) : GrimMath.frac(startZ));
// Can't figure out what this code does currently
while (d12 <= 1.0D || d13 <= 1.0D || d14 <= 1.0D) {
if (d12 < d13) {
if (d12 < d14) {
floorStartX += xSign;
d12 += posXInverse;
} else {
floorStartZ += zSign;
d14 += posZInverse;
}
} else if (d13 < d14) {
floorStartY += ySign;
d13 += posYInverse;
} else {
floorStartZ += zSign;
d14 += posZInverse;
}
state = player.compensatedWorld.getWrappedBlockStateAt(floorStartX, floorStartY, floorStartZ);
apply = predicate.apply(state, new Vector3i(floorStartX, floorStartY, floorStartZ));
if (apply != null) {
return apply;
}
}
return null;
}
private static void placeWaterLavaSnowBucket(GrimPlayer player, ItemStack held, StateType toPlace, InteractionHand hand) {
HitData data = BlockRayTrace.getNearestHitResult(player, StateTypes.AIR, false);
HitData data = getNearestHitResult(player, StateTypes.AIR, false);
if (data != null) {
BlockPlace blockPlace = new BlockPlace(player, hand, data.getPosition(), data.getClosestDirection().getFaceValue(), data.getClosestDirection(), held, data);
@@ -150,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;
@@ -169,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();
@@ -183,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();
@@ -194,7 +264,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
// The offhand is unable to interact with blocks like this... try to stop some desync points before they happen
if ((!player.isSneaking || onlyAir) && place.getHand() == InteractionHand.MAIN_HAND) {
Vector3i blockPosition = place.getBlockPosition();
BlockPlace blockPlace = new BlockPlace(player, place.getHand(), blockPosition, place.getFaceId(), place.getFace(), placedWith, BlockRayTrace.getNearestHitResult(player, null, true));
BlockPlace blockPlace = new BlockPlace(player, place.getHand(), blockPosition, place.getFaceId(), place.getFace(), placedWith, getNearestHitResult(player, null, true));
// Right-clicking a trapdoor/door/etc.
StateType placedAgainst = blockPlace.getPlacedAgainstMaterial();
@@ -221,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();
@@ -234,7 +303,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
placedWith = player.getInventory().getOffHand();
}
BlockPlace blockPlace = new BlockPlace(player, place.getHand(), blockPosition, place.getFaceId(), face, placedWith, BlockRayTrace.getNearestHitResult(player, null, true));
BlockPlace blockPlace = new BlockPlace(player, place.getHand(), blockPosition, place.getFaceId(), face, placedWith, getNearestHitResult(player, null, true));
// At this point, it is too late to cancel, so we can only flag, and cancel subsequent block places more aggressively
if (!player.compensatedEntities.getSelf().inVehicle()) {
player.checkManager.onPostFlyingBlockPlace(blockPlace);
@@ -274,7 +343,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
// Additionally, only yaw/pitch matters: https://github.com/GrimAnticheat/Grim/issues/1275#issuecomment-1872444018
// 1.9+ isn't impacted by this packet as much.
if (PacketEvents.getAPI().getServerManager().getVersion().isOlderThanOrEquals(ServerVersion.V_1_9)) {
if (GrimAPI.INSTANCE.getConfigManager().getConfig().getBooleanElse("cancel-duplicate-packet", true)) {
if (player.isCancelDuplicatePacket()) {
player.packetStateData.cancelDuplicatePacket = true;
}
} else {
@@ -284,7 +353,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
player.packetStateData.lastPacketWasOnePointSeventeenDuplicate = true;
if (!GrimAPI.INSTANCE.getConfigManager().isIgnoreDuplicatePacketRotation()) {
if (!player.isIgnoreDuplicatePacketRotation()) {
if (player.xRot != location.getYaw() || player.yRot != location.getPitch()) {
player.lastXRot = player.xRot;
player.lastYRot = player.yRot;
@@ -301,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;
@@ -354,7 +428,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType())) {
WrapperPlayClientPlayerFlying flying = new WrapperPlayClientPlayerFlying(event);
Location pos = flying.getLocation();
boolean ignoreRotation = player.packetStateData.lastPacketWasOnePointSeventeenDuplicate && GrimAPI.INSTANCE.getConfigManager().isIgnoreDuplicatePacketRotation();
boolean ignoreRotation = player.packetStateData.lastPacketWasOnePointSeventeenDuplicate && player.isIgnoreDuplicatePacketRotation();
handleFlying(player, pos.getX(), pos.getY(), pos.getZ(), ignoreRotation ? player.xRot : pos.getYaw(), ignoreRotation ? player.yRot : pos.getPitch(), flying.hasPositionChanged(), flying.hasRotationChanged(), flying.isOnGround(), teleportData, event);
}
@@ -382,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) {
@@ -405,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);
}
@@ -434,7 +510,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
player.placeUseItemPackets.add(new BlockPlaceSnapshot(packet, player.isSneaking));
} else {
// Anti-air place
BlockPlace blockPlace = new BlockPlace(player, packet.getHand(), packet.getBlockPosition(), packet.getFaceId(), packet.getFace(), placedWith, BlockRayTrace.getNearestHitResult(player, null, true));
BlockPlace blockPlace = new BlockPlace(player, packet.getHand(), packet.getBlockPosition(), packet.getFaceId(), packet.getFace(), placedWith, getNearestHitResult(player, null, true));
blockPlace.setCursor(packet.getCursorPosition());
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_11) && player.getClientVersion().isOlderThan(ClientVersion.V_1_11)) {
@@ -508,7 +584,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
}
private static void placeBucket(GrimPlayer player, InteractionHand hand) {
HitData data = BlockRayTrace.getNearestHitResult(player, null, true);
HitData data = getNearestHitResult(player, null, true);
if (data != null) {
BlockPlace blockPlace = new BlockPlace(player, hand, data.getPosition(), data.getClosestDirection().getFaceValue(), data.getClosestDirection(), ItemStack.EMPTY, data);
@@ -674,7 +750,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
}
private static void placeLilypad(GrimPlayer player, InteractionHand hand) {
HitData data = BlockRayTrace.getNearestHitResult(player, null, true);
HitData data = getNearestHitResult(player, null, true);
if (data != null) {
// A lilypad cannot replace a fluid
@@ -704,6 +780,56 @@ public class CheckManagerListener extends PacketListenerAbstract {
}
}
private static HitData getNearestHitResult(GrimPlayer player, StateType heldItem, boolean sourcesHaveHitbox) {
Vector3d startingPos = new Vector3d(player.x, player.y + player.getEyeHeight(), player.z);
Vector startingVec = new Vector(startingPos.getX(), startingPos.getY(), startingPos.getZ());
Ray trace = new Ray(player, startingPos.getX(), startingPos.getY(), startingPos.getZ(), player.xRot, player.yRot);
final double distance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_BLOCK_INTERACTION_RANGE);
Vector endVec = trace.getPointAtDistance(distance);
Vector3d endPos = new Vector3d(endVec.getX(), endVec.getY(), endVec.getZ());
return traverseBlocks(player, startingPos, endPos, (block, vector3i) -> {
CollisionBox data = HitboxData.getBlockHitbox(player, heldItem, player.getClientVersion(), block, vector3i.getX(), vector3i.getY(), vector3i.getZ());
List<SimpleCollisionBox> boxes = new ArrayList<>();
data.downCast(boxes);
double bestHitResult = Double.MAX_VALUE;
Vector bestHitLoc = null;
BlockFace bestFace = null;
for (SimpleCollisionBox box : boxes) {
Pair<Vector, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
if (intercept.first() == null) continue; // No intercept
Vector hitLoc = intercept.first();
if (hitLoc.distanceSquared(startingVec) < bestHitResult) {
bestHitResult = hitLoc.distanceSquared(startingVec);
bestHitLoc = hitLoc;
bestFace = intercept.second();
}
}
if (bestHitLoc != null) {
return new HitData(vector3i, bestHitLoc, bestFace, block);
}
if (sourcesHaveHitbox &&
(player.compensatedWorld.isWaterSourceBlock(vector3i.getX(), vector3i.getY(), vector3i.getZ())
|| player.compensatedWorld.getLavaFluidLevelAt(vector3i.getX(), vector3i.getY(), vector3i.getZ()) == (8 / 9f))) {
double waterHeight = player.compensatedWorld.getFluidLevelAt(vector3i.getX(), vector3i.getY(), vector3i.getZ());
SimpleCollisionBox box = new SimpleCollisionBox(vector3i.getX(), vector3i.getY(), vector3i.getZ(), vector3i.getX() + 1, vector3i.getY() + waterHeight, vector3i.getZ() + 1);
Pair<Vector, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
if (intercept.first() != null) {
return new HitData(vector3i, intercept.first(), intercept.second(), block);
}
}
return null;
});
}
@Override
public void onPacketSend(PacketSendEvent event) {
if (event.getConnectionState() != ConnectionState.PLAY) return;
@@ -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;
@@ -1,6 +1,6 @@
package ac.grim.grimac.events.packets;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
@@ -53,7 +53,6 @@ public class PacketEntityReplication extends Check implements PacketCheck {
private final List<Integer> despawnedEntitiesThisTransaction = new ArrayList<>();
// Maximum ping when a firework boost is removed from the player.
private final int maxFireworkBoostPing = GrimAPI.INSTANCE.getConfigManager().getConfig().getIntElse("max-ping-firework-boost", 1000);
public PacketEntityReplication(GrimPlayer player) {
super(player);
@@ -228,9 +227,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.
@@ -455,8 +453,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;
}
@@ -500,4 +497,12 @@ public class PacketEntityReplication extends Check implements PacketCheck {
public void tickStartTick() {
hasSentPreWavePacket = false;
}
private int maxFireworkBoostPing = 1000;
@Override
public void onReload(ConfigManager config) {
maxFireworkBoostPing = config.getIntElse("max-ping-firework-boost", 1000);
}
}
@@ -5,14 +5,11 @@ import ac.grim.grimac.player.GrimPlayer;
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.packettype.PacketTypeCommon;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityMetadata;
import com.github.retrooper.packetevents.protocol.player.UserProfile;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPlayerInfo;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPlayerInfoUpdate;
@@ -20,22 +17,15 @@ import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
public class PacketSetWrapperNull extends PacketListenerAbstract {
// It's faster (and less buggy) to simply not re-encode the wrapper unless we changed something
// The two packets we change are clientbound entity metadata (to fix a netcode issue)
// and the serverbound player flying packets (to patch NoFall)
public PacketSetWrapperNull() {
public class PacketHidePlayerInfo extends PacketListenerAbstract {
public PacketHidePlayerInfo() {
super(PacketListenerPriority.HIGHEST);
}
@Override
public void onPacketSend(PacketSendEvent event) {
if (event.getPacketType() == PacketType.Play.Server.ENTITY_METADATA) {
WrapperPlayServerEntityMetadata wrapper = new WrapperPlayServerEntityMetadata(event);
if (wrapper.getEntityId() != event.getUser().getEntityId()) {
event.setLastUsedWrapper(null);
}
} else if (event.getPacketType() == PacketType.Play.Server.PLAYER_INFO) {
if (event.getPacketType() == PacketType.Play.Server.PLAYER_INFO) {
//iterate through players and fake their game mode if they are spectating via grim spectate
if (PacketEvents.getAPI().getServerManager().getVersion().isOlderThanOrEquals(ServerVersion.V_1_12_2))
return;
@@ -62,8 +52,8 @@ public class PacketSetWrapperNull extends PacketListenerAbstract {
//if amount of hidden players is the amount of players updated & is an update game mode action just cancel it
if (hideCount == nmsPlayerInfoDataList.size() && info.getAction() == WrapperPlayServerPlayerInfo.Action.UPDATE_GAME_MODE) {
event.setCancelled(true);
} else if (hideCount <= 0) {
event.setLastUsedWrapper(null);
} else if (hideCount > 0) {
event.markForReEncode(true);
}
}
} else if (event.getPacketType() == PacketType.Play.Server.PLAYER_INFO_UPDATE) {
@@ -81,12 +71,13 @@ public class PacketSetWrapperNull extends PacketListenerAbstract {
for (WrapperPlayServerPlayerInfoUpdate.PlayerInfo entry : wrapper.getEntries()) {
//check if the player should be hidden
WrapperPlayServerPlayerInfoUpdate.PlayerInfo modifiedPacket = null;
if (GrimAPI.INSTANCE.getSpectateManager().shouldHidePlayer(receiver, entry.getProfileId())) {
final UserProfile gameProfile = entry.getGameProfile();
if (GrimAPI.INSTANCE.getSpectateManager().shouldHidePlayer(receiver, gameProfile.getUUID())) {
hideCount++;
//modify & create a new packet from pre-existing one if they are a spectator
if (entry.getGameMode() == GameMode.SPECTATOR) {
modifiedPacket = new WrapperPlayServerPlayerInfoUpdate.PlayerInfo(
entry.getGameProfile(),
gameProfile,
entry.isListed(),
entry.getLatency(),
GameMode.SURVIVAL,
@@ -104,30 +95,20 @@ public class PacketSetWrapperNull extends PacketListenerAbstract {
} //if only the game mode was updated and the packet was modified, don't send anything
}
//if no hidden players, don't modify packet
if (hideCount <= 0) {
event.setLastUsedWrapper(null);
} else if (hideCount == modified.size()) { //if the amount of hidden players & modified entries are the same
// if the amount of hidden players & modified entries are the same
if (hideCount == modified.size()) {
if (onlyGameMode) { // if only the game mode changed, cancel
event.setCancelled(true);
} else { //if more than the game mode changed, remove the action
wrapper.getActions().remove(WrapperPlayServerPlayerInfoUpdate.Action.UPDATE_GAME_MODE);
event.markForReEncode(true);
}
} else { //modify entries
wrapper.setEntries(modified);
event.markForReEncode(true);
}
}
} else if (event.getPacketType() != PacketType.Play.Server.PLAYER_POSITION_AND_LOOK) {
event.setLastUsedWrapper(null);
}
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
PacketTypeCommon packetType = event.getPacketType();
if (!WrapperPlayClientPlayerFlying.isFlying(packetType) && packetType != PacketType.Play.Client.CLIENT_SETTINGS && !event.isCancelled()) {
event.setLastUsedWrapper(null);
}
}
}
@@ -1,6 +1,6 @@
package ac.grim.grimac.events.packets;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
@@ -37,7 +37,6 @@ public class PacketPlayerAbilities extends Check implements PacketCheck {
if (lastSentPlayerCanFly && !abilities.isFlightAllowed()) {
int noFlying = player.lastTransactionSent.get();
int maxFlyingPing = GrimAPI.INSTANCE.getConfigManager().getConfig().getIntElse("max-ping-out-of-flying", 1000);
if (maxFlyingPing != -1) {
player.runNettyTaskInMs(() -> {
if (player.lastTransactionReceived.get() < noFlying) {
@@ -56,4 +55,12 @@ public class PacketPlayerAbilities extends Check implements PacketCheck {
}
}
int maxFlyingPing = 1000;
@Override
public void onReload(ConfigManager config) {
maxFlyingPing = config.getIntElse("max-ping-out-of-flying", 1000);
}
}
@@ -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.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;
}
@@ -196,7 +196,7 @@ public class PacketPlayerDigging extends PacketListenerAbstract {
if (player.packetStateData.lastSlotSelected != slot) {
// just assume they tick after this
if (!player.isTickingReliablyFor(3) && player.skippedTickInActualMovement) {
if (!player.isTickingReliablyFor(3) && player.skippedTickInActualMovement && player.packetStateData.eatingHand != InteractionHand.OFF_HAND) {
player.packetStateData.setSlowedByUsingItem(false);
}
}
@@ -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;
}
}
}
}
@@ -130,7 +130,6 @@ public class PacketPlayerRespawn extends PacketListenerAbstract {
player.compensatedEntities.serverPositionsMap.clear();
}
// TODO: What does keep all metadata do?
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get() + 1, () -> {
player.isSneaking = false;
player.lastOnGround = false;
@@ -141,8 +140,16 @@ public class PacketPlayerRespawn extends PacketListenerAbstract {
player.packetStateData.lastClaimedPosition = new Vector3d();
player.filterMojangStupidityOnMojangStupidity = new Vector3d();
final boolean keepTrackedData = this.hasFlag(respawn, KEEP_TRACKED_DATA);
if (!keepTrackedData) {
player.powderSnowFrozenTicks = 0;
player.compensatedEntities.getSelf().hasGravity = true;
player.playerEntityHasGravity = true;
}
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_19_4)) {
if (!this.hasFlag(respawn, KEEP_TRACKED_DATA)) {
if (!keepTrackedData) {
player.isSprinting = false;
}
} else {
@@ -184,7 +191,6 @@ public class PacketPlayerRespawn extends PacketListenerAbstract {
player.compensatedWorld.setDimension(respawn.getDimensionType(), event.getUser());
}
// TODO And there should probably be some attribute holder that we can just call reset() on.
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_16) && !this.hasFlag(respawn, KEEP_ATTRIBUTES)) {
// Reset attributes if not kept
player.compensatedEntities.getSelf().resetAttributes();
@@ -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;
@@ -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;
@@ -24,6 +23,7 @@ import java.util.List;
import java.util.Optional;
public class PacketSelfMetadataListener extends PacketListenerAbstract {
public PacketSelfMetadataListener() {
super(PacketListenerPriority.HIGH);
}
@@ -68,6 +68,7 @@ public class PacketSelfMetadataListener extends PacketListenerAbstract {
// Remove the pose metadata from the list
metadataStuff.removeIf(element -> element.getIndex() == 6);
entityMetadata.setEntityMetadata(metadataStuff);
event.markForReEncode(true);
}
EntityData watchable = WatchableIndexUtil.getIndex(entityMetadata.getEntityMetadata(), 0);
@@ -207,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);
}
}
}
@@ -37,7 +37,7 @@ public class ResyncWorldUtil {
// 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.getGlobalRegionScheduler().execute(GrimAPI.INSTANCE.getPlugin(), () -> {
boolean flat = PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13);
if (player.bukkitPlayer == null) return;
@@ -111,6 +111,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();
@@ -1,11 +1,12 @@
package ac.grim.grimac.manager;
import ac.grim.grimac.api.AbstractCheck;
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;
@@ -42,8 +43,13 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.event.PacketSendEvent;
import com.google.common.collect.ClassToInstanceMap;
import com.google.common.collect.ImmutableClassToInstanceMap;
import org.bukkit.Bukkit;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
public class CheckManager {
private static boolean inited;
ClassToInstanceMap<PacketCheck> packetChecks;
ClassToInstanceMap<PositionCheck> positionCheck;
ClassToInstanceMap<RotationCheck> rotationCheck;
@@ -129,6 +135,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))
@@ -139,6 +147,7 @@ public class CheckManager {
.put(InvalidPlaceA.class, new InvalidPlaceA(player))
.put(InvalidPlaceB.class, new InvalidPlaceB(player))
.put(AirLiquidPlace.class, new AirLiquidPlace(player))
.put(MultiPlace.class, new MultiPlace(player))
.put(FarPlace.class, new FarPlace(player))
.put(FabricatedPlace.class, new FabricatedPlace(player))
.put(PositionPlace.class, new PositionPlace(player))
@@ -171,6 +180,8 @@ public class CheckManager {
.putAll(blockPlaceCheck)
.putAll(prePredictionChecks)
.build();
init();
}
@SuppressWarnings("unchecked")
@@ -201,6 +212,9 @@ public class CheckManager {
for (PostPredictionCheck check : postPredictionCheck.values()) {
check.onPacketReceive(packet);
}
for (BlockPlaceCheck check : blockPlaceCheck.values()) {
check.onPacketReceive(packet);
}
}
public void onPacketSend(final PacketSendEvent packet) {
@@ -213,6 +227,9 @@ public class CheckManager {
for (PostPredictionCheck check : postPredictionCheck.values()) {
check.onPacketSend(packet);
}
for (BlockPlaceCheck check : blockPlaceCheck.values()) {
check.onPacketSend(packet);
}
}
public void onPositionUpdate(final PositionUpdate position) {
@@ -240,6 +257,9 @@ public class CheckManager {
for (PostPredictionCheck check : postPredictionCheck.values()) {
check.onPredictionComplete(complete);
}
for (BlockPlaceCheck check : blockPlaceCheck.values()) {
check.onPredictionComplete(complete);
}
}
public void onBlockPlace(final BlockPlace place) {
@@ -314,4 +334,22 @@ public class CheckManager {
public <T extends PostPredictionCheck> T getPostPredictionCheck(Class<T> check) {
return (T) postPredictionCheck.get(check);
}
private void init() {
if (inited) return;
for (AbstractCheck check : allChecks.values()) {
if (check.getCheckName() != null) {
String permissionName = "grim.exempt." + check.getCheckName().toLowerCase();
Permission permission = Bukkit.getPluginManager().getPermission(permissionName);
if (permission == null) {
Bukkit.getPluginManager().addPermission(new Permission(permissionName, PermissionDefault.FALSE));
} else {
permission.setDefault(PermissionDefault.FALSE);
}
}
}
inited = true;
}
}
@@ -19,6 +19,7 @@ public class DiscordManager implements Initable {
private static WebhookClient client;
private int embedColor;
private String staticContent = "";
private String embedTitle = "";
public static final Pattern WEBHOOK_PATTERN = Pattern.compile("(?:https?://)?(?:\\w+\\.)?\\w+\\.\\w+/api(?:/v\\d+)?/webhooks/(\\d+)/([\\w-]+)(?:/(?:\\w+)?)?");
@@ -40,6 +41,8 @@ public class DiscordManager implements Initable {
client = WebhookClient.withId(Long.parseUnsignedLong(matcher.group(1)), matcher.group(2));
client.setTimeout(15000); // Requests expire after 15 seconds
embedTitle = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("embed-title", "**Grim Alert**");
try {
embedColor = Color.decode(GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("embed-color", "#00FFFF")).getRGB();
} catch (NumberFormatException e) {
@@ -70,7 +73,7 @@ public class DiscordManager implements Initable {
public void sendAlert(GrimPlayer player, String verbose, String checkName, String violations) {
if (client != null) {
String content = staticContent + "";
String content = staticContent;
content = content.replace("%check%", checkName);
content = content.replace("%violations%", violations);
content = GrimAPI.INSTANCE.getExternalAPI().replaceVariables(player, content, false);
@@ -80,7 +83,7 @@ public class DiscordManager implements Initable {
.setImageUrl("https://i.stack.imgur.com/Fzh0w.png") // Constant width
.setThumbnailUrl("https://crafthead.net/helm/" + player.user.getProfile().getUUID())
.setColor(embedColor)
.setTitle(new WebhookEmbed.EmbedTitle("**Grim Alert**", null))
.setTitle(new WebhookEmbed.EmbedTitle(embedTitle, null))
.setDescription(content)
.setTimestamp(Instant.now())
.setFooter(new WebhookEmbed.EmbedFooter("", "https://grim.ac/images/grim.png"));
@@ -1,60 +1,72 @@
package ac.grim.grimac.manager;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.GrimExternalAPI;
import ac.grim.grimac.manager.init.Initable;
import ac.grim.grimac.manager.init.load.PacketEventsInit;
import ac.grim.grimac.manager.init.start.*;
import ac.grim.grimac.manager.init.stop.TerminatePacketEvents;
import com.google.common.collect.ClassToInstanceMap;
import com.google.common.collect.ImmutableClassToInstanceMap;
import com.google.common.collect.ImmutableList;
import lombok.Getter;
public class InitManager {
ClassToInstanceMap<Initable> initializersOnLoad;
ClassToInstanceMap<Initable> initializersOnStart;
ClassToInstanceMap<Initable> initializersOnStop;
private final ImmutableList<Initable> initializersOnLoad;
private final ImmutableList<Initable> initializersOnStart;
private final ImmutableList<Initable> initializersOnStop;
@Getter private boolean loaded = false;
@Getter private boolean started = false;
@Getter private boolean stopped = false;
public InitManager() {
initializersOnLoad = new ImmutableClassToInstanceMap.Builder<Initable>()
.put(PacketEventsInit.class, new PacketEventsInit())
initializersOnLoad = ImmutableList.<Initable>builder()
.add(new PacketEventsInit())
.add(() -> GrimAPI.INSTANCE.getExternalAPI().load())
.build();
initializersOnStart = new ImmutableClassToInstanceMap.Builder<Initable>()
.put(ExemptOnlinePlayers.class, new ExemptOnlinePlayers())
.put(EventManager.class, new EventManager())
.put(PacketManager.class, new PacketManager())
.put(ViaBackwardsManager.class, new ViaBackwardsManager())
.put(TickRunner.class, new TickRunner())
.put(TickEndEvent.class, new TickEndEvent())
.put(CommandRegister.class, new CommandRegister())
.put(BStats.class, new BStats())
.put(PacketLimiter.class, new PacketLimiter())
.put(DiscordManager.class, GrimAPI.INSTANCE.getDiscordManager())
.put(SpectateManager.class, GrimAPI.INSTANCE.getSpectateManager())
.put(GrimExternalAPI.class, GrimAPI.INSTANCE.getExternalAPI())
.put(JavaVersion.class, new JavaVersion())
initializersOnStart = ImmutableList.<Initable>builder()
.add(GrimAPI.INSTANCE.getExternalAPI())
.add(new ExemptOnlinePlayers())
.add(new EventManager())
.add(new PacketManager())
.add(new ViaBackwardsManager())
.add(new TickRunner())
.add(new TickEndEvent())
.add(new CommandRegister())
.add(new BStats())
.add(new PacketLimiter())
.add(GrimAPI.INSTANCE.getDiscordManager())
.add(GrimAPI.INSTANCE.getSpectateManager())
.add(new JavaVersion())
.add(new ViaVersion())
.build();
initializersOnStop = new ImmutableClassToInstanceMap.Builder<Initable>()
.put(TerminatePacketEvents.class, new TerminatePacketEvents())
initializersOnStop = ImmutableList.<Initable>builder()
.add(new TerminatePacketEvents())
.build();
}
public void load() {
for (Initable initable : initializersOnLoad.values()) {
initable.start();
}
for (Initable initable : initializersOnLoad) handle(initable);
loaded = true;
}
public void start() {
for (Initable initable : initializersOnStart.values()) {
initable.start();
}
for (Initable initable : initializersOnStart) handle(initable);
started = true;
}
public void stop() {
for (Initable initable : initializersOnStop.values()) {
for (Initable initable : initializersOnStop) handle(initable);
stopped = true;
}
private void handle(Initable initable) {
try {
initable.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
@@ -2,13 +2,14 @@ package ac.grim.grimac.manager;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.api.AbstractCheck;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.api.config.ConfigReloadable;
import ac.grim.grimac.api.events.CommandExecuteEvent;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.events.packets.ProxyAlertMessenger;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.LogUtil;
import ac.grim.grimac.utils.anticheat.MessageUtil;
import github.scarsz.configuralize.DynamicConfig;
import io.github.retrooper.packetevents.util.folia.FoliaScheduler;
import lombok.Getter;
import lombok.Setter;
@@ -17,21 +18,27 @@ import org.bukkit.entity.Player;
import java.util.*;
public class PunishmentManager {
public class PunishmentManager implements ConfigReloadable {
GrimPlayer player;
List<PunishGroup> groups = new ArrayList<>();
String experimentalSymbol = "*";
private String alertString;
private boolean testMode;
private boolean printToConsole;
private String proxyAlertString = "";
public PunishmentManager(GrimPlayer player) {
this.player = player;
reload();
}
public void reload() {
DynamicConfig config = GrimAPI.INSTANCE.getConfigManager().getConfig();
@Override
public void reload(ConfigManager config) {
List<String> punish = config.getStringListElse("Punishments", new ArrayList<>());
experimentalSymbol = config.getStringElse("experimental-symbol", "*");
alertString = config.getStringElse("alerts-format", "%prefix% &f%player% &bfailed &f%check_name% &f(x&c%vl%&f) &7%verbose%");
testMode = config.getBooleanElse("test-mode", false);
printToConsole = config.getBooleanElse("verbose.print-to-console", false);
proxyAlertString = config.getStringElse("alerts-format-proxy", "%prefix% &f[&cproxy&f] &f%player% &bfailed &f%check_name% &f(x&c%vl%&f) &7%verbose%");
try {
groups.clear();
@@ -98,7 +105,7 @@ public class PunishmentManager {
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)
@@ -111,8 +118,6 @@ public class PunishmentManager {
}
public boolean handleAlert(GrimPlayer player, String verbose, Check check) {
String alertString = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("alerts-format", "%prefix% &f%player% &bfailed &f%check_name% &f(x&c%vl%&f) &7%verbose%");
boolean testMode = GrimAPI.INSTANCE.getConfigManager().getConfig().getBooleanElse("test-mode", false);
boolean sentDebug = false;
// Check commands
@@ -128,7 +133,7 @@ public class PunishmentManager {
for (Player bukkitPlayer : GrimAPI.INSTANCE.getAlertManager().getEnabledVerbose()) {
bukkitPlayer.sendMessage(cmd);
}
if (GrimAPI.INSTANCE.getConfigManager().getConfig().getBooleanElse("verbose.print-to-console", false)) {
if (printToConsole) {
LogUtil.console(cmd); // Print verbose to console
}
}
@@ -144,11 +149,9 @@ public class PunishmentManager {
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]")) {
String proxyAlertString = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("alerts-format-proxy", "%prefix% &f[&cproxy&f] &f%player% &bfailed &f%check_name% &f(x&c%vl%&f) &7%verbose%");
proxyAlertString = replaceAlertPlaceholders(command.getCommand(), group, check, proxyAlertString, verbose);
ProxyAlertMessenger.sendPluginMessage(proxyAlertString);
ProxyAlertMessenger.sendPluginMessage(replaceAlertPlaceholders(command.getCommand(), group, check, proxyAlertString, verbose));
} else {
if (command.command.equals("[alert]")) {
sentDebug = true;
@@ -160,9 +163,8 @@ public class PunishmentManager {
}
String finalCmd = cmd;
FoliaScheduler.getGlobalRegionScheduler().run(GrimAPI.INSTANCE.getPlugin(), (dummy) -> {
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), finalCmd);
});
FoliaScheduler.getGlobalRegionScheduler().run(GrimAPI.INSTANCE.getPlugin(), (dummy) ->
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), finalCmd));
}
}
@@ -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;
}
}
@@ -0,0 +1,63 @@
package ac.grim.grimac.manager.config;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.utils.anticheat.LogUtil;
import lombok.Getter;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
public class BaseConfigManager {
/*
This is to hold whatever config manager was set via the reload method in the API
and any global variables that are the same between players.
*/
@Getter
private ConfigManager config = null;
// initialize the config
public void load(ConfigManager config) {
this.config = config;
//
int configuredMaxTransactionTime = config.getIntElse("max-transaction-time", 60);
if (configuredMaxTransactionTime > 180 || configuredMaxTransactionTime < 1) {
LogUtil.warn("Detected invalid max-transaction-time! This setting is clamped between 1 and 180 to prevent issues. Attempting to disable or set this too high can result in memory usage issues.");
}
ignoredClientPatterns.clear();
for (String string : config.getStringList("client-brand.ignored-clients")) {
try {
ignoredClientPatterns.add(Pattern.compile(string));
} catch (PatternSyntaxException e) {
throw new RuntimeException("Failed to compile client pattern", e);
}
}
printAlertsToConsole = config.getBooleanElse("alerts.print-to-console", true);
prefix = config.getStringElse("prefix", "&bGrim &8»");
}
// ran on start, can be used to handle things that can't be done while loading
public void start() {
}
@Getter private boolean printAlertsToConsole = false;
@Getter private String prefix = "&bGrim &8»";
private final List<Pattern> ignoredClientPatterns = new ArrayList<>();
public boolean isIgnoredClient(String brand) {
for (Pattern pattern : ignoredClientPatterns) {
if (pattern.matcher(brand).find()) return true;
}
return false;
}
}
@@ -1,56 +1,43 @@
package ac.grim.grimac.manager;
package ac.grim.grimac.manager.config;
import ac.grim.grimac.GrimAC;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.api.common.BasicReloadable;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.utils.anticheat.LogUtil;
import github.scarsz.configuralize.DynamicConfig;
import github.scarsz.configuralize.Language;
import lombok.Getter;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
public class ConfigManager {
@Getter
public class ConfigManagerFileImpl implements ConfigManager, BasicReloadable {
private final DynamicConfig config;
@Getter
private final File configFile = new File(GrimAPI.INSTANCE.getPlugin().getDataFolder(), "config.yml");
@Getter
private final File messagesFile = new File(GrimAPI.INSTANCE.getPlugin().getDataFolder(), "messages.yml");
@Getter
private final File discordFile = new File(GrimAPI.INSTANCE.getPlugin().getDataFolder(), "discord.yml");
@Getter
private final File punishFile = new File(GrimAPI.INSTANCE.getPlugin().getDataFolder(), "punishments.yml");
@Getter
private int maxPingTransaction = 60; // This is just a really hot variable so cache it.
@Getter
private boolean ignoreDuplicatePacketRotation = false;
@Getter
private boolean experimentalChecks = false;
private final List<Pattern> ignoredClientPatterns = new ArrayList<>();
public ConfigManager() {
upgrade();
// load config
GrimAPI.INSTANCE.getPlugin().getDataFolder().mkdirs();
config = new DynamicConfig();
config.addSource(GrimAC.class, "config", getConfigFile());
config.addSource(GrimAC.class, "messages", getMessagesFile());
config.addSource(GrimAC.class, "discord", getDiscordFile());
config.addSource(GrimAC.class, "punishments", getPunishFile());
reload();
private File getConfigFile(String path) {
return new File(GrimAPI.INSTANCE.getPlugin().getDataFolder(), path);
}
public ConfigManagerFileImpl() {
config = new DynamicConfig();
}
private boolean initialized = false;
@Override
public void reload() {
GrimAPI.INSTANCE.getPlugin().getDataFolder().mkdirs();
if (!initialized) {
initialized = true;
config.addSource(GrimAC.class, "config", getConfigFile("config.yml"));
config.addSource(GrimAC.class, "messages", getConfigFile("messages.yml"));
config.addSource(GrimAC.class, "discord", getConfigFile("discord.yml"));
config.addSource(GrimAC.class, "punishments", getConfigFile("punishments.yml"));
}
//
String languageCode = System.getProperty("user.language").toUpperCase();
try {
@@ -77,24 +64,6 @@ public class ConfigManager {
} catch (Exception e) {
throw new RuntimeException("Failed to load config", e);
}
maxPingTransaction = config.getIntElse("max-transaction-time", 60);
ignoredClientPatterns.clear();
for (String string : config.getStringList("client-brand.ignored-clients")) {
try {
ignoredClientPatterns.add(Pattern.compile(string));
} catch (PatternSyntaxException e) {
throw new RuntimeException("Failed to compile client pattern", e);
}
}
experimentalChecks = config.getBooleanElse("experimental-checks", false);
ignoreDuplicatePacketRotation = config.getBooleanElse("ignore-duplicate-packet-rotation", false);
}
public boolean isIgnoredClient(String brand) {
for (Pattern pattern : ignoredClientPatterns) {
if (pattern.matcher(brand).find()) return true;
}
return false;
}
private void upgrade() {
@@ -317,4 +286,45 @@ public class ConfigManager {
);
Files.write(config.toPath(), configString.getBytes());
}
@Override
public String getStringElse(String key, String otherwise) {
return config.getStringElse(key, otherwise);
}
@Override
public List<String> getStringList(String key) {
return config.getStringList(key);
}
@Override
public List<String> getStringListElse(String key, List<String> otherwise) {
return config.getStringListElse(key, otherwise);
}
@Override
public int getIntElse(String key, int other) {
return config.getIntElse(key, other);
}
@Override
public long getLongElse(String key, long otherwise) {
return config.getLongElse(key, otherwise);
}
@Override
public double getDoubleElse(String key, double otherwise) {
return config.getDoubleElse(key, otherwise);
}
@Override
public boolean getBooleanElse(String key, boolean otherwise) {
return config.getBooleanElse(key, otherwise);
}
@Override
public <T> T get(String key) {
return config.get(key);
}
}
@@ -4,8 +4,17 @@ import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.manager.init.Initable;
import ac.grim.grimac.utils.anticheat.LogUtil;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.protocol.chat.ChatTypes;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.item.enchantment.type.EnchantmentTypes;
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
import com.github.retrooper.packetevents.protocol.particle.type.ParticleTypes;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
import java.util.concurrent.Executors;
public class PacketEventsInit implements Initable {
@Override
public void start() {
@@ -16,7 +25,18 @@ public class PacketEventsInit implements Initable {
.fullStackTrace(true)
.kickOnPacketException(true)
.checkForUpdates(false)
.reEncodeByDefault(false)
.debug(false);
PacketEvents.getAPI().load();
// This may seem useless, but it causes java to start loading stuff async before we need it
Executors.defaultThreadFactory().newThread(() -> {
StateTypes.AIR.getName();
ItemTypes.AIR.getName();
EntityTypes.PLAYER.getParent();
EntityDataTypes.BOOLEAN.getName();
ChatTypes.CHAT.getName();
EnchantmentTypes.ALL_DAMAGE_PROTECTION.getName();
ParticleTypes.DUST.getName();
}).start();
}
}
@@ -43,7 +43,7 @@ public class PacketManager implements Initable {
}
PacketEvents.getAPI().getEventManager().registerListener(new ProxyAlertMessenger());
PacketEvents.getAPI().getEventManager().registerListener(new PacketSetWrapperNull());
PacketEvents.getAPI().getEventManager().registerListener(new PacketHidePlayerInfo());
PacketEvents.getAPI().init();
}
@@ -287,4 +287,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();
}
}
}
}
@@ -0,0 +1,18 @@
package ac.grim.grimac.manager.init.start;
import ac.grim.grimac.manager.init.Initable;
import ac.grim.grimac.utils.anticheat.LogUtil;
import com.viaversion.viaversion.api.Via;
import io.github.retrooper.packetevents.util.viaversion.ViaVersionUtil;
public class ViaVersion implements Initable {
@Override
public void start() {
if (!ViaVersionUtil.isAvailable()) return;
if (Via.getConfig().getValues().containsKey("fix-1_21-placement-rotation") && Via.getConfig().fix1_21PlacementRotation()) {
LogUtil.warn("GrimAC has detected that you are using ViaVersion with the `fix-1_21-placement-rotation` option enabled.");
LogUtil.warn("This option is known to cause issues with GrimAC and may result in false positives and bypasses.");
LogUtil.warn("Please disable this option in your ViaVersion configuration to prevent these issues.");
}
}
}
@@ -3,6 +3,8 @@ package ac.grim.grimac.player;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.api.AbstractCheck;
import ac.grim.grimac.api.GrimUser;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.impl.aim.processor.AimProcessor;
import ac.grim.grimac.checks.impl.misc.ClientBrand;
import ac.grim.grimac.checks.impl.misc.TransactionOrder;
@@ -35,7 +37,6 @@ import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.protocol.player.User;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.protocol.world.Dimension;
import com.github.retrooper.packetevents.protocol.world.dimension.DimensionType;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
@@ -47,6 +48,7 @@ import io.github.retrooper.packetevents.adventure.serializer.legacy.LegacyCompon
import io.github.retrooper.packetevents.util.folia.FoliaScheduler;
import io.github.retrooper.packetevents.util.viaversion.ViaVersionUtil;
import io.netty.channel.Channel;
import lombok.Getter;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TranslatableComponent;
import org.bukkit.Bukkit;
@@ -199,6 +201,8 @@ public class GrimPlayer implements GrimUser {
public long lastBlockPlaceUseItem = 0;
public AtomicInteger cancelledPackets = new AtomicInteger(0);
public MainSupportingBlockData mainSupportingBlockData = new MainSupportingBlockData(null, false);
// possibleEyeHeights[0] = Standing eye heights, [1] = Sneaking. [2] = Elytra, Swimming, and Riptide Trident which only exists in 1.9+
public double[][] possibleEyeHeights = new double[3][];
public void onPacketCancel() {
if (spamThreshold != -1 && cancelledPackets.incrementAndGet() > spamThreshold) {
@@ -217,7 +221,6 @@ public class GrimPlayer implements GrimUser {
public GrimPlayer(User user) {
this.user = user;
this.playerUUID = user.getUUID();
onReload();
boundingBox = GetBoundingBox.getBoundingBoxFromPosAndSizeRaw(x, y, z, 0.6f, 1.8f);
@@ -240,6 +243,24 @@ public class GrimPlayer implements GrimUser {
packetStateData = new PacketStateData();
uncertaintyHandler.collidingEntities.add(0);
if (getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14)) {
final float scale = (float) compensatedEntities.getSelf().getAttributeValue(Attributes.GENERIC_SCALE);
possibleEyeHeights[2] = new double[]{0.4 * scale, 1.62 * scale, 1.27 * scale}; // Elytra, standing, sneaking (1.14)
possibleEyeHeights[1] = new double[]{1.27 * scale, 1.62 * scale, 0.4 * scale}; // sneaking (1.14), standing, Elytra
possibleEyeHeights[0] = new double[]{1.62 * scale, 1.27 * scale, 0.4 * scale}; // standing, sneaking (1.14), Elytra
} else if (getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) { // standing, sneaking Elytra
possibleEyeHeights[2] = new double[]{0.4, 1.62, 1.54}; // Elytra, standing, sneaking (1.13)
possibleEyeHeights[1] = new double[]{1.54, 1.62, 0.4}; // sneaking (1.9-1.13), standing, Elytra
possibleEyeHeights[0] = new double[]{1.62, 1.54, 0.4}; // standing, sneaking (1.9-1.13), Elytra
} else {
possibleEyeHeights[1] = new double[]{(double) (1.62f - 0.08f), (double) (1.62f)}; // sneaking, standing
possibleEyeHeights[0] = new double[]{(double) (1.62f), (double) (1.62f - 0.08f)}; // standing, sneaking
}
// reload last
reload();
}
public Set<VectorData> getPossibleVelocities() {
@@ -305,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;
}
@@ -316,7 +337,8 @@ public class GrimPlayer implements GrimUser {
// Transactions that we send don't count towards total limit
if (packetTracker != null) packetTracker.setIntervalPackets(packetTracker.getIntervalPackets() - 1);
if (skipped > 0 && System.currentTimeMillis() - joinTime > 5000) checkManager.getPacketCheck(TransactionOrder.class).flagAndAlert("skipped: " + skipped);
if (skipped > 0 && System.currentTimeMillis() - joinTime > 5000)
checkManager.getPacketCheck(TransactionOrder.class).flagAndAlert("skipped: " + skipped);
do {
data = transactionsSent.poll();
@@ -325,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());
@@ -335,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) {
@@ -421,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);
@@ -450,7 +471,7 @@ public class GrimPlayer implements GrimUser {
if (lastTransSent != 0 && lastTransSent + 80 < System.currentTimeMillis()) {
sendTransaction(true); // send on netty thread
}
if ((System.nanoTime() - getPlayerClockAtLeast()) > GrimAPI.INSTANCE.getConfigManager().getMaxPingTransaction() * 1e9) {
if ((System.nanoTime() - getPlayerClockAtLeast()) > maxTransactionTime * 1e9) {
timedOut();
}
@@ -504,14 +525,17 @@ public class GrimPlayer implements GrimUser {
if (bukkitPlayer == null) return;
this.noModifyPacketPermission = bukkitPlayer.hasPermission("grim.nomodifypacket");
this.noSetbackPermission = bukkitPlayer.hasPermission("grim.nosetback");
FoliaScheduler.getAsyncScheduler().runNow(GrimAPI.INSTANCE.getPlugin(), t -> {
for (AbstractCheck check : checkManager.allChecks.values()) {
if (check instanceof Check) {
((Check) check).updateExempted();
}
}
});
}
private int spamThreshold = 100;
public void onReload() {
spamThreshold = GrimAPI.INSTANCE.getConfigManager().getConfig().getIntElse("packet-spam-threshold", 100);
}
public boolean isPointThree() {
return getClientVersion().isOlderThan(ClientVersion.V_1_18_2);
}
@@ -541,7 +565,7 @@ public class GrimPlayer implements GrimUser {
// - 3 ticks is a magic value, but it should buffer out incorrect predictions somewhat.
// 2. The player is in a vehicle
public boolean isTickingReliablyFor(int ticks) {
return (getClientVersion().isOlderThan(ClientVersion.V_1_9)
return (getClientVersion().isOlderThan(ClientVersion.V_1_9)
|| !uncertaintyHandler.lastPointThree.hasOccurredSince(ticks))
|| compensatedEntities.getSelf().inVehicle();
}
@@ -554,14 +578,23 @@ public class GrimPlayer implements GrimUser {
return checkManager.getInventory();
}
public List<Double> getPossibleEyeHeights() { // We don't return sleeping eye height
if (getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14)) { // Elytra, sneaking (1.14), standing
final float scale = (float) compensatedEntities.getSelf().getAttributeValue(Attributes.GENERIC_SCALE);
return Arrays.asList(0.4 * scale, 1.27 * scale, 1.62 * scale);
} else if (getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) { // Elytra, sneaking, standing
return Arrays.asList(0.4, 1.54, 1.62);
} else { // Only sneaking or standing
return Arrays.asList((double) (1.62f - 0.08f), (double) (1.62f));
public double[] getPossibleEyeHeights() { // We don't return sleeping eye height
// 1.8 Players once again ruin my clean switch-case
if (this.getClientVersion().isOlderThan(ClientVersion.V_1_9)) {
return this.isSneaking ? this.possibleEyeHeights[1] : this.possibleEyeHeights[0];
} else {
// 1.8 players just have their pose set to standing all the time
switch (pose) {
case FALL_FLYING: // Elytra gliding
case SPIN_ATTACK: // Riptide trident
case SWIMMING: // Swimming (includes crawling in 1.14+)
return this.possibleEyeHeights[2]; // [swimming/gliding/riptide height, standing height, sneaking height]
case NINE_CROUCHING:
case CROUCHING:
return this.possibleEyeHeights[1]; // [sneaking height, standing height, swimming/gliding/riptide height]
default:
return this.possibleEyeHeights[0]; // [standing height, sneaking height, swimming/gliding/riptide height]
}
}
}
@@ -709,10 +742,31 @@ public class GrimPlayer implements GrimUser {
return checkManager.allChecks.values();
}
public void runNettyTaskInMs(Runnable runnable, int ms) {
Channel channel = (Channel) user.getChannel();
channel.eventLoop().schedule(runnable, ms, TimeUnit.MILLISECONDS);
}
private int maxTransactionTime = 60;
@Getter private boolean ignoreDuplicatePacketRotation = false;
@Getter private boolean experimentalChecks = false;
@Getter private boolean cancelDuplicatePacket = true;
@Override
public void reload(ConfigManager config) {
spamThreshold = config.getIntElse("packet-spam-threshold", 100);
maxTransactionTime = (int) GrimMath.clamp(config.getIntElse("max-transaction-time", 60), 1, 180);
experimentalChecks = config.getBooleanElse("experimental-checks", false);
ignoreDuplicatePacketRotation = config.getBooleanElse("ignore-duplicate-packet-rotation", false);
cancelDuplicatePacket = config.getBooleanElse("cancel-duplicate-packet", true);
// reload all checks
for (AbstractCheck value : checkManager.allChecks.values()) value.reload(config);
// reload punishment manager
punishmentManager.reload(config);
}
@Override
public void reload() {
reload(GrimAPI.INSTANCE.getConfigManager().getConfig());
}
}
@@ -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 {
@@ -1,6 +1,6 @@
package ac.grim.grimac.predictionengine;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.impl.movement.EntityControl;
import ac.grim.grimac.checks.impl.prediction.Phase;
@@ -22,7 +22,6 @@ import ac.grim.grimac.utils.data.packetentity.PacketEntityHorse;
import ac.grim.grimac.utils.data.packetentity.PacketEntityRideable;
import ac.grim.grimac.utils.data.packetentity.PacketEntityTrackXRot;
import ac.grim.grimac.utils.enums.Pose;
import ac.grim.grimac.utils.inventory.EnchantmentHelper;
import ac.grim.grimac.utils.latency.CompensatedWorld;
import ac.grim.grimac.utils.math.GrimMath;
import ac.grim.grimac.utils.math.VectorUtils;
@@ -32,7 +31,6 @@ 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;
import com.github.retrooper.packetevents.protocol.item.type.ItemType;
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
@@ -260,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;
@@ -341,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;
@@ -352,7 +349,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
SimpleCollisionBox steppingOnBB = GetBoundingBox.getCollisionBoxForPlayer(player, player.x, player.y, player.z).expand(0.03).offset(0, -1, 0);
Collisions.hasMaterial(player, steppingOnBB, (pair) -> {
WrappedBlockState data = pair.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;
@@ -364,7 +361,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
}
player.uncertaintyHandler.isSteppingOnHoney = true;
}
if (BlockTags.BEDS.contains(data.getType()) && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_8)) {
if (BlockTags.BEDS.contains(data.getType()) && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_12)) {
player.uncertaintyHandler.isSteppingOnBouncyBlock = true;
}
if (BlockTags.ICE.contains(data.getType())) {
@@ -398,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();
@@ -544,8 +541,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
player.checkManager.onPredictionFinish(new PredictionComplete(offset, update, wasChecked));
// Patch sprint jumping with elytra exploit
if (player.bukkitPlayer != null && player.isGliding && player.predictedVelocity.isJump() && player.isSprinting
&& !GrimAPI.INSTANCE.getConfigManager().getConfig().getBooleanElse("exploit.allow-sprint-jumping-when-using-elytra", true)) {
if (player.bukkitPlayer != null && player.isGliding && player.predictedVelocity.isJump() && player.isSprinting && !allowSprintJumpingWithElytra) {
SetbackTeleportUtil.SetbackPosWithVector lastKnownGoodPosition = player.getSetbackTeleportUtil().lastKnownGoodPosition;
lastKnownGoodPosition.setVector(lastKnownGoodPosition.getVector().multiply(new Vector(0.6 * 0.91, 1, 0.6 * 0.91)));
player.getSetbackTeleportUtil().executeNonSimulatingSetback();
@@ -635,4 +631,12 @@ public class MovementCheckRunner extends Check implements PositionCheck {
// (Patches issues with slime and other desync's)
return riptideDiffToGround < riptideDiffToBase;
}
private boolean allowSprintJumpingWithElytra = true;
@Override
public void onReload(ConfigManager config) {
allowSprintJumpingWithElytra = config.getBooleanElse("exploit.allow-sprint-jumping-when-using-elytra", true);
}
}
@@ -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;
@@ -16,9 +15,10 @@ import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateAttributes;
import org.bukkit.World;
import org.bukkit.util.Vector;
import java.util.Optional;
public class PlayerBaseTick {
GrimPlayer player;
@@ -87,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);
@@ -149,9 +147,14 @@ public class PlayerBaseTick {
// Pre-1.17 clients don't have powder snow and therefore don't desync
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_16_4)) return;
// The client first desync's this attribute
final ValuedAttribute playerSpeed = player.compensatedEntities.getSelf().getAttribute(Attributes.GENERIC_MOVEMENT_SPEED).get();
playerSpeed.property().get().getModifiers().removeIf(modifier -> modifier.getUUID().equals(CompensatedEntities.SNOW_MODIFIER_UUID) || modifier.getName().getKey().equals("powder_snow"));
// Might be null after respawn?
final Optional<WrapperPlayServerUpdateAttributes.Property> property = playerSpeed.property();
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"));
playerSpeed.recalculate();
// And then re-adds it using purely what the server has sent it
@@ -165,7 +168,7 @@ public class PlayerBaseTick {
float percentFrozen = (float) Math.min(i, ticksToFreeze) / (float) ticksToFreeze;
float percentFrozenReducedToSpeed = -0.05F * percentFrozen;
playerSpeed.property().get().getModifiers().add(new WrapperPlayServerUpdateAttributes.PropertyModifier(CompensatedEntities.SNOW_MODIFIER_UUID, percentFrozenReducedToSpeed, WrapperPlayServerUpdateAttributes.PropertyModifier.Operation.ADDITION));
property.get().getModifiers().add(new WrapperPlayServerUpdateAttributes.PropertyModifier(CompensatedEntities.SNOW_MODIFIER_UUID, percentFrozenReducedToSpeed, WrapperPlayServerUpdateAttributes.PropertyModifier.Operation.ADDITION));
playerSpeed.recalculate();
}
}
@@ -357,22 +360,12 @@ public class PlayerBaseTick {
double d7 = direction2 == BlockFace.WEST || direction2 == BlockFace.EAST ? relativeXMovement : relativeZMovement;
d6 = direction2 == BlockFace.EAST || direction2 == BlockFace.SOUTH ? 1.0 - d7 : d7;
// d7 and d6 flip the movement direction based on desired movement direction
boolean doesSuffocate;
switch (direction2) {
case EAST:
doesSuffocate = this.suffocatesAt(blockX + 1, blockZ);
break;
case WEST:
doesSuffocate = this.suffocatesAt(blockX - 1, blockZ);
break;
case NORTH:
doesSuffocate = this.suffocatesAt(blockX, blockZ - 1);
break;
default:
case SOUTH:
doesSuffocate = this.suffocatesAt(blockX, blockZ + 1);
break;
}
boolean doesSuffocate = switch (direction2) {
case EAST -> this.suffocatesAt(blockX + 1, blockZ);
case WEST -> this.suffocatesAt(blockX - 1, blockZ);
case NORTH -> this.suffocatesAt(blockX, blockZ - 1);
default -> this.suffocatesAt(blockX, blockZ + 1);
};
if (d6 >= lowestValue || doesSuffocate) continue;
lowestValue = d6;
@@ -1,6 +1,5 @@
package ac.grim.grimac.predictionengine;
import ac.grim.grimac.checks.impl.combat.Reach;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.predictionengine.predictions.PredictionEngine;
import ac.grim.grimac.utils.collisions.CollisionData;
@@ -17,7 +16,6 @@ import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState
import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.util.Vector3i;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.util.Vector;
@@ -153,8 +151,6 @@ public class PointThreeEstimator {
isNearFluid = true;
}
player.checkManager.getPacketCheck(Reach.class).handleBlockChange(new Vector3i(x, y, z), state);
if (pointThreeBox.isIntersected(new SimpleCollisionBox(x, y, z))) {
// https://github.com/MWHunter/Grim/issues/613
int controllingEntityId = player.compensatedEntities.getSelf().inVehicle() ? player.getRidingVehicleId() : player.entityID;
@@ -252,21 +248,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;
}
@@ -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;
}
}
}
@@ -1,14 +1,20 @@
package ac.grim.grimac.predictionengine.movementtick;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.data.attribute.ValuedAttribute;
import ac.grim.grimac.utils.data.packetentity.PacketEntityStrider;
import ac.grim.grimac.utils.nmsutil.BlockProperties;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateAttributes;
import org.bukkit.util.Vector;
import java.util.ArrayList;
public class MovementTickerStrider extends MovementTickerRideable {
public MovementTickerStrider(GrimPlayer player) {
@@ -34,21 +40,45 @@ public class MovementTickerStrider extends MovementTickerRideable {
public void livingEntityAIStep() {
super.livingEntityAIStep();
((PacketEntityStrider) player.compensatedEntities.getSelf().getRiding()).isShaking = true;
StateType posMaterial = player.compensatedWorld.getStateTypeAt(player.x, player.y, player.z);
StateType belowMaterial = BlockProperties.getOnPos(player, player.mainSupportingBlockData, new Vector3d(player.x, player.y, player.z));
((PacketEntityStrider) player.compensatedEntities.getSelf().getRiding()).isShaking =
!BlockTags.STRIDER_WARM_BLOCKS.contains(posMaterial) &&
final PacketEntityStrider strider = (PacketEntityStrider) player.compensatedEntities.getSelf().getRiding();
strider.isShaking = !BlockTags.STRIDER_WARM_BLOCKS.contains(posMaterial) &&
!BlockTags.STRIDER_WARM_BLOCKS.contains(belowMaterial) &&
!player.wasTouchingLava;
}
private static final WrapperPlayServerUpdateAttributes.PropertyModifier SUFFOCATING_MODIFIER = new WrapperPlayServerUpdateAttributes.PropertyModifier(
ResourceLocation.minecraft("suffocating"), -0.34F, WrapperPlayServerUpdateAttributes.PropertyModifier.Operation.MULTIPLY_BASE);
@Override
public float getSteeringSpeed() {
PacketEntityStrider strider = (PacketEntityStrider) player.compensatedEntities.getSelf().getRiding();
return (float) strider.getAttributeValue(Attributes.GENERIC_MOVEMENT_SPEED) * (strider.isShaking ? 0.23F : 0.55F);
// Unsure which version the speed changed in
final boolean newSpeed = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_20);
final float coldSpeed = newSpeed ? 0.35F : 0.23F;
// Client desyncs the attribute
// Again I don't know when this was changed, or whether it always existed, so I will just put it behind 1.20+
final ValuedAttribute movementSpeedAttr = strider.getAttribute(Attributes.GENERIC_MOVEMENT_SPEED).get();
float updatedMovementSpeed = (float) movementSpeedAttr.get();
if (newSpeed) {
final WrapperPlayServerUpdateAttributes.Property lastProperty = movementSpeedAttr.property().orElse(null);
if (lastProperty != null && (!strider.isShaking || lastProperty.getModifiers().stream().noneMatch(mod -> mod.getName().getKey().equals("suffocating")))) {
WrapperPlayServerUpdateAttributes.Property newProperty = new WrapperPlayServerUpdateAttributes.Property(lastProperty.getAttribute(), lastProperty.getValue(), new ArrayList<>(lastProperty.getModifiers()));
if (!strider.isShaking) {
newProperty.getModifiers().removeIf(modifier -> modifier.getName().getKey().equals("suffocating"));
} else {
newProperty.getModifiers().add(SUFFOCATING_MODIFIER);
}
movementSpeedAttr.with(newProperty);
updatedMovementSpeed = (float) movementSpeedAttr.get();
movementSpeedAttr.with(lastProperty);
}
}
return updatedMovementSpeed * (strider.isShaking ? coldSpeed : 0.55F);
}
@Override
@@ -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;
@@ -3,6 +3,8 @@ package ac.grim.grimac.utils.anticheat;
import ac.grim.grimac.GrimAPI;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.util.Vector3f;
import com.github.retrooper.packetevents.util.Vector3i;
import lombok.experimental.UtilityClass;
import net.md_5.bungee.api.ChatColor;
@@ -11,6 +13,13 @@ import java.util.regex.Pattern;
@UtilityClass
public class MessageUtil {
public String toUnlabledString(Vector3i vec) {
return vec == null ? "null" : vec.x + ", " + vec.y + ", " + vec.z;
}
public String toUnlabledString(Vector3f vec) {
return vec == null ? "null" : vec.x + ", " + vec.y + ", " + vec.z;
}
public String format(String string) {
string = formatWithNoColor(string);
@@ -20,13 +29,14 @@ public class MessageUtil {
}
public String formatWithNoColor(String string) {
return string.replace("%prefix%", GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("prefix", "&bGrim &8»"));
return string.replace("%prefix%", GrimAPI.INSTANCE.getConfigManager().getPrefix());
}
private static final Pattern HEX_PATTERN = Pattern.compile("#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})");
private String translateHexCodes(String message) {
final String hexPattern = "#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})";
Matcher matcher = Pattern.compile(hexPattern).matcher(message);
StringBuffer sb = new StringBuffer(message.length());
Matcher matcher = HEX_PATTERN.matcher(message);
StringBuilder sb = new StringBuilder(message.length());
while (matcher.find()) {
String hex = matcher.group(1);
ChatColor color = ChatColor.of("#" + hex);
@@ -35,4 +45,5 @@ public class MessageUtil {
matcher.appendTail(sb);
return sb.toString();
}
}
@@ -537,21 +537,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 +665,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;
};
}
}
@@ -3,12 +3,11 @@ package ac.grim.grimac.utils.collisions;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.predictionengine.movementtick.MovementTickerStrider;
import ac.grim.grimac.utils.collisions.blocks.*;
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicFence;
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicPane;
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicWall;
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicCollisionFence;
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicCollisionPane;
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicCollisionWall;
import ac.grim.grimac.utils.collisions.datatypes.*;
import ac.grim.grimac.utils.data.packetentity.PacketEntityStrider;
import ac.grim.grimac.utils.math.GrimMath;
import ac.grim.grimac.utils.nmsutil.Materials;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
@@ -25,6 +24,7 @@ import com.viaversion.viaversion.api.Via;
import io.github.retrooper.packetevents.util.viaversion.ViaVersionUtil;
import java.util.*;
import java.util.stream.Stream;
// Warning for major game updates!
// Do not use an enum for stuff like Axis and other data types not in 1.7
@@ -71,9 +71,7 @@ public enum CollisionData {
return NoCollisionBox.INSTANCE;
}, StateTypes.LAVA),
WATER(NoCollisionBox.INSTANCE, StateTypes.WATER),
BREWINGSTAND((player, version, block, x, y, z) -> {
BREWING_STAND((player, version, block, x, y, z) -> {
int base = 0;
if (version.isNewerThanOrEquals(ClientVersion.V_1_13))
@@ -86,27 +84,13 @@ public enum CollisionData {
}, StateTypes.BREWING_STAND),
BAMBOO((player, version, block, x, y, z) -> {
// ViaVersion replacement block - sugarcane
if (version.isOlderThan(ClientVersion.V_1_13_2))
// ViaVersion replacement, sugarcane
if (version.isOlderThan(ClientVersion.V_1_14)) {
return NoCollisionBox.INSTANCE;
// Offset taken from NMS
long i = (x * 3129871L) ^ (long) z * 116129781L ^ (long) 0;
i = i * i * 42317861L + i * 11L;
i = i >> 16;
return new HexCollisionBox(6.5D, 0.0D, 6.5D, 9.5D, 16.0D, 9.5D).offset((((i & 15L) / 15.0F) - 0.5D) * 0.5D, 0, (((i >> 8 & 15L) / 15.0F) - 0.5D) * 0.5D);
}
return new HexOffsetCollisionBox(block.getType(), 6.5D, 0.0D, 6.5D, 9.5D, 16.0D, 9.5D);
}, StateTypes.BAMBOO),
BAMBOO_SAPLING((player, version, block, x, y, z) -> {
long i = (x * 3129871L) ^ (long) z * 116129781L ^ (long) 0;
i = i * i * 42317861L + i * 11L;
i = i >> 16;
return new HexCollisionBox(4.0D, 0.0D, 4.0D, 12.0D, 12.0D, 12.0D).offset((((i & 15L) / 15.0F) - 0.5D) * 0.5D, 0, (((i >> 8 & 15L) / 15.0F) - 0.5D) * 0.5D);
}, StateTypes.BAMBOO_SAPLING),
COMPOSTER((player, version, block, x, y, z) -> {
double height = 0.125;
@@ -159,7 +143,7 @@ public enum CollisionData {
}
}, BlockTags.ANVIL.getStates().toArray(new StateType[0])),
WALL(new DynamicWall(), BlockTags.WALLS.getStates().toArray(new StateType[0])),
WALL(new DynamicCollisionWall(), BlockTags.WALLS.getStates().toArray(new StateType[0])),
SLAB((player, version, data, x, y, z) -> {
Type slabType = data.getTypeData();
@@ -177,46 +161,29 @@ public enum CollisionData {
SKULL(new SimpleCollisionBox(0.25F, 0.0F, 0.25F, 0.75F, 0.5F, 0.75F, false),
StateTypes.CREEPER_HEAD, StateTypes.ZOMBIE_HEAD, StateTypes.DRAGON_HEAD, StateTypes.PLAYER_HEAD,
StateTypes.SKELETON_SKULL, StateTypes.WITHER_SKELETON_SKULL, StateTypes.HEAVY_CORE),
PIGLIN_HEAD(new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 8.0D, 13.0D), StateTypes.PIGLIN_HEAD),
// Overwrite previous SKULL enum for legacy, where head and wall skull isn't separate
WALL_SKULL((player, version, data, x, y, z) -> {
switch (data.getFacing()) {
default:
case NORTH:
return new SimpleCollisionBox(0.25F, 0.25F, 0.5F, 0.75F, 0.75F, 1.0F, false);
case SOUTH:
return new SimpleCollisionBox(0.25F, 0.25F, 0.0F, 0.75F, 0.75F, 0.5F, false);
case WEST:
return new SimpleCollisionBox(0.5F, 0.25F, 0.25F, 1.0F, 0.75F, 0.75F, false);
case EAST:
return new SimpleCollisionBox(0.0F, 0.25F, 0.25F, 0.5F, 0.75F, 0.75F, false);
}
return switch (data.getFacing()) {
case SOUTH -> new SimpleCollisionBox(0.25F, 0.25F, 0.0F, 0.75F, 0.75F, 0.5F, false);
case WEST -> new SimpleCollisionBox(0.5F, 0.25F, 0.25F, 1.0F, 0.75F, 0.75F, false);
case EAST -> new SimpleCollisionBox(0.0F, 0.25F, 0.25F, 0.5F, 0.75F, 0.75F, false);
default -> new SimpleCollisionBox(0.25F, 0.25F, 0.5F, 0.75F, 0.75F, 1.0F, false);
};
}, StateTypes.CREEPER_WALL_HEAD, StateTypes.DRAGON_WALL_HEAD, StateTypes.PLAYER_WALL_HEAD, StateTypes.ZOMBIE_WALL_HEAD,
StateTypes.SKELETON_WALL_SKULL, StateTypes.WITHER_SKELETON_WALL_SKULL),
PIGLIN_WALL_HEAD((player, version, data, x, y, z) -> {
switch (data.getFacing()) {
default:
case NORTH:
return new HexCollisionBox(3.0D, 4.0D, 8.0D, 13.0D, 12.0D, 16.0D);
case SOUTH:
return new HexCollisionBox(3.0D, 4.0D, 0.0D, 13.0D, 12.0D, 8.0D);
case EAST:
return new HexCollisionBox(0.0D, 4.0D, 3.0D, 8.0D, 12.0D, 13.0D);
case WEST:
return new HexCollisionBox(8.0D, 4.0D, 3.0D, 16.0D, 12.0D, 13.0D);
}
return switch (data.getFacing()) {
case SOUTH -> new HexCollisionBox(3.0D, 4.0D, 0.0D, 13.0D, 12.0D, 8.0D);
case EAST -> new HexCollisionBox(0.0D, 4.0D, 3.0D, 8.0D, 12.0D, 13.0D);
case WEST -> new HexCollisionBox(8.0D, 4.0D, 3.0D, 16.0D, 12.0D, 13.0D);
default -> new HexCollisionBox(3.0D, 4.0D, 8.0D, 13.0D, 12.0D, 16.0D);
};
}, StateTypes.PIGLIN_WALL_HEAD),
BANNER(new HexCollisionBox(4.0D, 0.0D, 4.0D, 12.0D, 16.0D, 12.0D),
BlockTags.BANNERS.getStates().toArray(new StateType[0])),
CORAL_FAN((player, version, data, x, y, z) -> {
return new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 4.0D, 14.0D);
}, BlockTags.CORALS.getStates().toArray(new StateType[0])),
DOOR(new DoorHandler(), BlockTags.DOORS.getStates().toArray(new StateType[0])),
HOPPER((player, version, data, x, y, z) -> {
@@ -281,43 +248,31 @@ public enum CollisionData {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 9.0D, 16.0D);
}, StateTypes.STONECUTTER),
SWEET_BERRY((player, 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);
}
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}, StateTypes.SWEET_BERRY_BUSH),
CORAL_FAN(NoCollisionBox.INSTANCE, BlockTags.CORALS.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])),
RAILS(NoCollisionBox.INSTANCE, BlockTags.RAILS.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(NoCollisionBox.INSTANCE, BlockTags.BANNERS.getStates().toArray(new StateType[0])),
FLOWER(new HexCollisionBox(5.0D, 0.0D, 5.0D, 11.0D, 10.0D, 11.0D),
BlockTags.SMALL_FLOWERS.getStates().toArray(new StateType[0])),
SMALL_FLOWER(NoCollisionBox.INSTANCE, BlockTags.SMALL_FLOWERS.getStates().toArray(new StateType[0])),
DEAD_BUSH(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 13.0D, 14.0D), StateTypes.DEAD_BUSH),
TALL_FLOWER(NoCollisionBox.INSTANCE, BlockTags.TALL_FLOWERS.getStates().toArray(new StateType[0])),
SUGARCANE(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 16.0D, 14.0D), StateTypes.SUGAR_CANE),
SAPLING(NoCollisionBox.INSTANCE, BlockTags.SAPLINGS.getStates().toArray(new StateType[0])),
NETHER_SPROUTS(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 3.0D, 14.0D), StateTypes.NETHER_SPROUTS),
BUTTON(NoCollisionBox.INSTANCE, BlockTags.BUTTONS.getStates().toArray(new StateType[0])),
GRASS_FERN(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 13.0D, 14.0D),
StateTypes.GRASS, StateTypes.FERN),
TALL_GRASS(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.TALL_GRASS),
SEA_GRASS(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 12.0D, 14.0D),
StateTypes.SEAGRASS),
CAVE_VINES(new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D), StateTypes.CAVE_VINES, StateTypes.CAVE_VINES_PLANT),
TWISTING_VINES_BLOCK(new HexCollisionBox(4.0D, 0.0D, 4.0D, 12.0D, 15.0D, 12.0D), StateTypes.TWISTING_VINES, StateTypes.WEEPING_VINES),
TWISTING_VINES(new HexCollisionBox(4.0D, 0.0D, 4.0D, 12.0D, 16.0D, 12.0D), StateTypes.TWISTING_VINES_PLANT, StateTypes.WEEPING_VINES_PLANT),
// All states that unconditionally have no collision and are not in a group where every member also has no collision.
NO_COLLISION(NoCollisionBox.INSTANCE, StateTypes.TWISTING_VINES_PLANT, StateTypes.WEEPING_VINES_PLANT,
StateTypes.TWISTING_VINES, StateTypes.WEEPING_VINES, StateTypes.CAVE_VINES, StateTypes.CAVE_VINES_PLANT,
StateTypes.TALL_SEAGRASS, StateTypes.SEAGRASS, StateTypes.SHORT_GRASS, StateTypes.FERN, StateTypes.NETHER_SPROUTS,
StateTypes.DEAD_BUSH, StateTypes.SUGAR_CANE, StateTypes.SWEET_BERRY_BUSH, StateTypes.WARPED_ROOTS,
StateTypes.CRIMSON_ROOTS, StateTypes.TORCHFLOWER_CROP, StateTypes.PINK_PETALS, StateTypes.TALL_GRASS,
StateTypes.LARGE_FERN, StateTypes.BAMBOO_SAPLING, StateTypes.HANGING_ROOTS,
StateTypes.SMALL_DRIPLEAF, StateTypes.END_PORTAL, StateTypes.LEVER, StateTypes.PUMPKIN_STEM, StateTypes.MELON_STEM,
StateTypes.ATTACHED_MELON_STEM, StateTypes.ATTACHED_PUMPKIN_STEM, StateTypes.BEETROOTS, StateTypes.POTATOES,
StateTypes.WHEAT, StateTypes.CARROTS, StateTypes.NETHER_WART, StateTypes.MOVING_PISTON, StateTypes.AIR, StateTypes.CAVE_AIR,
StateTypes.VOID_AIR, StateTypes.LIGHT, StateTypes.WATER),
KELP(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 9.0D, 16.0D), StateTypes.KELP),
// Kelp block is a full block, so it by default is correct
@@ -386,17 +341,12 @@ public enum CollisionData {
if (version.isOlderThanOrEquals(ClientVersion.V_1_8))
width = 2;
switch (data.getFacing()) {
case NORTH:
return new HexCollisionBox(0.0D, 0.0D, 16.0D - width, 16.0D, 16.0D, 16.0D);
case SOUTH:
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, width);
case WEST:
return new HexCollisionBox(16.0D - width, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D);
default:
case EAST:
return new HexCollisionBox(0.0D, 0.0D, 0.0D, width, 16.0D, 16.0D);
}
return switch (data.getFacing()) {
case NORTH -> new HexCollisionBox(0.0D, 0.0D, 16.0D - width, 16.0D, 16.0D, 16.0D);
case SOUTH -> new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, width);
case WEST -> new HexCollisionBox(16.0D - width, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D);
default -> new HexCollisionBox(0.0D, 0.0D, 0.0D, width, 16.0D, 16.0D);
};
}, StateTypes.LADDER),
CAMPFIRE((player, version, data, x, y, z) -> {
@@ -445,33 +395,6 @@ public enum CollisionData {
return new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 15.0D, 15.0D); // post
}, StateTypes.HONEY_BLOCK),
SPORE_BLOSSOM(new HexCollisionBox(2.0D, 13.0D, 2.0D, 14.0D, 16.0D, 14.0D), StateTypes.SPORE_BLOSSOM),
GLOW_LICHEN((player, version, data, x, y, z) -> {
ComplexCollisionBox box = new ComplexCollisionBox();
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;
}, StateTypes.GLOW_LICHEN),
DRAGON_EGG_BLOCK(new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D), StateTypes.DRAGON_EGG),
GRINDSTONE((player, version, data, x, y, z) -> {
@@ -567,7 +490,14 @@ public enum CollisionData {
}, StateTypes.GRINDSTONE),
PANE(new DynamicCollisionPane(), Materials.getPanes().toArray(new StateType[0])),
CHAIN_BLOCK((player, version, data, x, y, z) -> {
if (version.isOlderThan(ClientVersion.V_1_16)) {
// viaversion replacement - iron bars
return CollisionData.PANE.dynamic.fetch(player, version, data, x, y, z);
}
if (data.getAxis() == Axis.X) {
return new HexCollisionBox(0.0D, 6.5D, 6.5D, 16.0D, 9.5D, 9.5D);
} else if (data.getAxis() == Axis.Y) {
@@ -583,23 +513,15 @@ public enum CollisionData {
if (data.isOpen())
return NoCollisionBox.INSTANCE;
switch (data.getFacing()) {
case NORTH:
case SOUTH:
return new SimpleCollisionBox(0.0F, 0.0F, 0.375F, 1.0F, 1.5F, 0.625F, false);
case WEST:
case EAST:
return new SimpleCollisionBox(0.375F, 0.0F, 0.0F, 0.625F, 1.5F, 1.0F, false);
}
// This code is unreachable but the compiler does not know this
return NoCollisionBox.INSTANCE;
return switch (data.getFacing()) {
case NORTH, SOUTH -> new SimpleCollisionBox(0.0F, 0.0F, 0.375F, 1.0F, 1.5F, 0.625F, false);
case WEST, EAST -> new SimpleCollisionBox(0.375F, 0.0F, 0.0F, 0.625F, 1.5F, 1.0F, false);
default -> // This code is unreachable but the compiler does not know this
NoCollisionBox.INSTANCE;
};
}, BlockTags.FENCE_GATES.getStates().toArray(new StateType[0])),
FENCE(new DynamicFence(), BlockTags.FENCES.getStates().toArray(new StateType[0])),
PANE(new DynamicPane(), Materials.getPanes().toArray(new StateType[0])),
FENCE(new DynamicCollisionFence(), BlockTags.FENCES.getStates().toArray(new StateType[0])),
SNOW((player, version, data, x, y, z) -> {
if (data.getLayers() == 1 && version.isNewerThanOrEquals(ClientVersion.V_1_13)) {
@@ -677,8 +599,6 @@ public enum CollisionData {
}, StateTypes.FARMLAND),
HANGING_ROOTS(new HexCollisionBox(2.0D, 10.0D, 2.0D, 14.0D, 16.0D, 14.0D), StateTypes.HANGING_ROOTS),
GRASS_PATH((player, version, data, x, y, z) -> {
if (version.isNewerThanOrEquals(ClientVersion.V_1_9))
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 15.0D, 16.0D);
@@ -741,17 +661,36 @@ public enum CollisionData {
}, StateTypes.END_ROD, StateTypes.LIGHTNING_ROD),
CAULDRON((player, version, data, x, y, z) -> {
double height = 0.25;
if (version.isNewerThan(ClientVersion.getById(467))) { // changed in 19w13a, 1.14 Snapshot
return new ComplexCollisionBox(
new SimpleCollisionBox(0.0, 0.0, 0.0, 0.125, 1.0, 0.25, false),
new SimpleCollisionBox(0.0, 0.0, 0.75, 0.125, 1.0, 1.0, false),
new SimpleCollisionBox(0.125, 0.0, 0.0, 0.25, 1.0, 0.125, false),
new SimpleCollisionBox(0.125, 0.0, 0.875, 0.25, 1.0, 1.0, false),
new SimpleCollisionBox(0.75, 0.0, 0.0, 1.0, 1.0, 0.125, false),
new SimpleCollisionBox(0.75, 0.0, 0.875, 1.0, 1.0, 1.0, false),
new SimpleCollisionBox(0.875, 0.0, 0.125, 1.0, 1.0, 0.25, false),
new SimpleCollisionBox(0.875, 0.0, 0.75, 1.0, 1.0, 0.875, false),
new SimpleCollisionBox(0.0, 0.1875, 0.25, 1.0, 0.25, 0.75, false),
new SimpleCollisionBox(0.125, 0.1875, 0.125, 0.875, 0.25, 0.25, false),
new SimpleCollisionBox(0.125, 0.1875, 0.75, 0.875, 0.25, 0.875, false),
new SimpleCollisionBox(0.25, 0.1875, 0.0, 0.75, 1.0, 0.125, false),
new SimpleCollisionBox(0.25, 0.1875, 0.875, 0.75, 1.0, 1.0, false),
new SimpleCollisionBox(0.0, 0.25, 0.25, 0.125, 1.0, 0.75, false),
new SimpleCollisionBox(0.875, 0.25, 0.25, 1.0, 1.0, 0.75, false)
);
} else {
double height = 0.25;
if (version.isOlderThan(ClientVersion.V_1_13))
height = 0.3125;
if (version.isOlderThan(ClientVersion.V_1_13))
height = 0.3125;
return new ComplexCollisionBox(
new SimpleCollisionBox(0, 0, 0, 1, height, 1, false),
new SimpleCollisionBox(0, height, 0, 0.125, 1, 1, false),
new SimpleCollisionBox(1 - 0.125, height, 0, 1, 1, 1, false),
new SimpleCollisionBox(0, height, 0, 1, 1, 0.125, false),
new SimpleCollisionBox(0, height, 1 - 0.125, 1, 1, 1, false));
return new ComplexCollisionBox(
new SimpleCollisionBox(0, 0, 0, 1, height, 1, false),
new SimpleCollisionBox(0, height, 0, 0.125, 1, 1, false),
new SimpleCollisionBox(1 - 0.125, height, 0, 1, 1, 1, false),
new SimpleCollisionBox(0, height, 0, 1, 1, 0.125, false),
new SimpleCollisionBox(0, height, 1 - 0.125, 1, 1, 1, false));
}
}, BlockTags.CAULDRONS.getStates().toArray(new StateType[0])),
CACTUS(new SimpleCollisionBox(0.0625, 0, 0.0625,
@@ -794,101 +733,38 @@ public enum CollisionData {
BlockTags.FLOWER_POTS.getStates().toArray(new StateType[0])),
WALL_SIGN((player, version, data, x, y, z) -> {
switch (data.getFacing()) {
case NORTH:
return new HexCollisionBox(0.0D, 4.5D, 14.0D, 16.0D, 12.5D, 16.0D);
case SOUTH:
return new HexCollisionBox(0.0D, 4.5D, 0.0D, 16.0D, 12.5D, 2.0D);
case WEST:
return new HexCollisionBox(14.0D, 4.5D, 0.0D, 16.0D, 12.5D, 16.0D);
case EAST:
return new HexCollisionBox(0.0D, 4.5D, 0.0D, 2.0D, 12.5D, 16.0D);
default:
return NoCollisionBox.INSTANCE;
}
return switch (data.getFacing()) {
case NORTH -> new HexCollisionBox(0.0D, 4.5D, 14.0D, 16.0D, 12.5D, 16.0D);
case SOUTH -> new HexCollisionBox(0.0D, 4.5D, 0.0D, 16.0D, 12.5D, 2.0D);
case WEST -> new HexCollisionBox(14.0D, 4.5D, 0.0D, 16.0D, 12.5D, 16.0D);
case EAST -> new HexCollisionBox(0.0D, 4.5D, 0.0D, 2.0D, 12.5D, 16.0D);
default -> NoCollisionBox.INSTANCE;
};
}, BlockTags.WALL_SIGNS.getStates().toArray(new StateType[0])),
WALL_FAN((player, version, data, x, y, z) -> {
switch (data.getFacing()) {
case NORTH:
return new HexCollisionBox(0.0D, 4.0D, 5.0D, 16.0D, 12.0D, 16.0D);
case SOUTH:
return new HexCollisionBox(0.0D, 4.0D, 0.0D, 16.0D, 12.0D, 11.0D);
case WEST:
return new HexCollisionBox(5.0D, 4.0D, 0.0D, 16.0D, 12.0D, 16.0D);
case EAST:
default:
return new HexCollisionBox(0.0D, 4.0D, 0.0D, 11.0D, 12.0D, 16.0D);
}
return switch (data.getFacing()) {
case NORTH -> new HexCollisionBox(0.0D, 4.0D, 5.0D, 16.0D, 12.0D, 16.0D);
case SOUTH -> new HexCollisionBox(0.0D, 4.0D, 0.0D, 16.0D, 12.0D, 11.0D);
case WEST -> new HexCollisionBox(5.0D, 4.0D, 0.0D, 16.0D, 12.0D, 16.0D);
default -> new HexCollisionBox(0.0D, 4.0D, 0.0D, 11.0D, 12.0D, 16.0D);
};
}, BlockTags.WALL_CORALS.getStates().toArray(new StateType[0])),
CORAL_PLANT((player, version, data, x, y, z) -> {
return new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 15.0D, 14.0D);
}, BlockTags.CORAL_PLANTS.getStates().toArray(new StateType[0])),
}, Stream.concat(
Arrays.stream(BlockTags.CORAL_PLANTS.getStates().toArray(new StateType[0])),
Stream.of(StateTypes.DEAD_HORN_CORAL, StateTypes.DEAD_TUBE_CORAL, StateTypes.DEAD_BRAIN_CORAL,
StateTypes.DEAD_BUBBLE_CORAL, StateTypes.DEAD_FIRE_CORAL)
)
.distinct() // This will remove duplicates
.toArray(StateType[]::new)
),
SIGN(new SimpleCollisionBox(0.25, 0.0, 0.25, 0.75, 1.0, 0.75, false),
BlockTags.STANDING_SIGNS.getStates().toArray(new StateType[0])),
BEETROOT((player, version, data, x, y, z) -> {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, (data.getAge() + 1) * 2, 1.0D);
}, StateTypes.BEETROOTS),
WHEAT((player, version, data, x, y, z) -> {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, (data.getAge() + 1) * 2, 1.0D);
}, StateTypes.WHEAT),
CARROT_NETHERWART((player, version, data, x, y, z) -> {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, data.getAge() + 2, 1.0D);
}, StateTypes.CARROTS, StateTypes.NETHER_WART),
NETHER_WART((player, version, data, x, y, z) -> {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 5 + (data.getAge() * 3), 1.0D);
}, StateTypes.NETHER_WART),
BUTTON((player, version, data, x, y, z) -> {
double f2 = (float) (data.isPowered() ? 1 : 2) / 16.0;
switch (data.getFacing()) {
case WEST:
return new SimpleCollisionBox(0.0, 0.375, 0.3125, f2, 0.625, 0.6875, false);
case EAST:
return new SimpleCollisionBox(1.0 - f2, 0.375, 0.3125, 1.0, 0.625, 0.6875, false);
case NORTH:
return new SimpleCollisionBox(0.3125, 0.375, 0.0, 0.6875, 0.625, f2, false);
case SOUTH:
return new SimpleCollisionBox(0.3125, 0.375, 1.0 - f2, 0.6875, 0.625, 1.0, false);
case DOWN:
return new SimpleCollisionBox(0.3125, 0.0, 0.375, 0.6875, 0.0 + f2, 0.625, false);
case UP:
return new SimpleCollisionBox(0.3125, 1.0 - f2, 0.375, 0.6875, 1.0, 0.625, false);
}
return NoCollisionBox.INSTANCE;
}, BlockTags.BUTTONS.getStates().toArray(new StateType[0])),
LEVER((player, version, data, x, y, z) -> {
double f = 0.1875;
switch (data.getFacing()) {
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 DOWN:
return new SimpleCollisionBox(0.25, 0.4, 0.25, 0.75, 1.0, 0.75, false);
case UP:
return new SimpleCollisionBox(0.25, 0.0, 0.25, 0.75, 0.6, 0.75, false);
}
return NoCollisionBox.INSTANCE;
}, StateTypes.LEVER),
STONE_PRESSURE_PLATE((player, version, data, x, y, z) -> {
if (data.isPowered()) { // Pressed
return new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 0.5D, 15.0D);
@@ -920,89 +796,38 @@ public enum CollisionData {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D);
}, StateTypes.TRIPWIRE),
ATTACHED_PUMPKIN_STEM((player, 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);
switch (data.getFacing()) {
case SOUTH:
return new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 10.0D, 16.0D);
case WEST:
return new HexCollisionBox(0.0D, 0.0D, 6.0D, 10.0D, 10.0D, 10.0D);
case NORTH:
return new HexCollisionBox(6.0D, 0.0D, 0.0D, 10.0D, 10.0D, 10.0D);
case EAST:
default:
return new HexCollisionBox(6.0D, 0.0D, 6.0D, 16.0D, 10.0D, 10.0D);
}
}, StateTypes.ATTACHED_MELON_STEM, StateTypes.ATTACHED_PUMPKIN_STEM),
PUMPKIN_STEM((player, version, data, x, y, z) -> {
return new HexCollisionBox(7, 0, 7, 9, 2 * (data.getAge() + 1), 9);
}, StateTypes.PUMPKIN_STEM, StateTypes.MELON_STEM),
TRIPWIRE_HOOK((player, version, data, x, y, z) -> {
switch (data.getFacing()) {
case NORTH:
return new HexCollisionBox(5.0D, 0.0D, 10.0D, 11.0D, 10.0D, 16.0D);
case SOUTH:
return new HexCollisionBox(5.0D, 0.0D, 0.0D, 11.0D, 10.0D, 6.0D);
case WEST:
return new HexCollisionBox(10.0D, 0.0D, 5.0D, 16.0D, 10.0D, 11.0D);
case EAST:
default:
return new HexCollisionBox(0.0D, 0.0D, 5.0D, 6.0D, 10.0D, 11.0D);
}
return switch (data.getFacing()) {
case NORTH -> new HexCollisionBox(5.0D, 0.0D, 10.0D, 11.0D, 10.0D, 16.0D);
case SOUTH -> new HexCollisionBox(5.0D, 0.0D, 0.0D, 11.0D, 10.0D, 6.0D);
case WEST -> new HexCollisionBox(10.0D, 0.0D, 5.0D, 16.0D, 10.0D, 11.0D);
default -> new HexCollisionBox(0.0D, 0.0D, 5.0D, 6.0D, 10.0D, 11.0D);
};
}, StateTypes.TRIPWIRE_HOOK),
TORCH(new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 10.0D, 10.0D),
StateTypes.TORCH, StateTypes.REDSTONE_TORCH),
WALL_TORCH((player, version, data, x, y, z) -> {
switch (data.getFacing()) {
case NORTH:
return new HexCollisionBox(5.5D, 3.0D, 11.0D, 10.5D, 13.0D, 16.0D);
case SOUTH:
return new HexCollisionBox(5.5D, 3.0D, 0.0D, 10.5D, 13.0D, 5.0D);
case WEST:
return new HexCollisionBox(11.0D, 3.0D, 5.5D, 16.0D, 13.0D, 10.5D);
case EAST:
return new HexCollisionBox(0.0D, 3.0D, 5.5D, 5.0D, 13.0D, 10.5D);
default: // 1.13 separates wall and normal torches, 1.12 does not
case UP:
return new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 10.0D, 10.0D);
}
return switch (data.getFacing()) {
case NORTH -> new HexCollisionBox(5.5D, 3.0D, 11.0D, 10.5D, 13.0D, 16.0D);
case SOUTH -> new HexCollisionBox(5.5D, 3.0D, 0.0D, 10.5D, 13.0D, 5.0D);
case WEST -> new HexCollisionBox(11.0D, 3.0D, 5.5D, 16.0D, 13.0D, 10.5D);
case EAST -> new HexCollisionBox(0.0D, 3.0D, 5.5D, 5.0D, 13.0D, 10.5D);
// 1.13 separates wall and normal torches, 1.12 does not
default -> new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 10.0D, 10.0D);
};
}, StateTypes.WALL_TORCH, StateTypes.REDSTONE_WALL_TORCH),
RAILS((player, version, data, x, y, z) -> {
Shape shape = data.getShape();
if (shape == Shape.ASCENDING_EAST || shape == Shape.ASCENDING_WEST ||
shape == Shape.ASCENDING_NORTH || shape == Shape.ASCENDING_SOUTH) {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D);
}
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D);
}, BlockTags.RAILS.getStates().toArray(new StateType[0])),
// Known as block 36 - has no collision box
TECHNICAL_MOVING_PISTON(NoCollisionBox.INSTANCE, StateTypes.MOVING_PISTON),
// 1.17 blocks
CANDLE((player, version, data, x, y, z) -> {
if (version.isNewerThanOrEquals(ClientVersion.V_1_17)) {
switch (data.getCandles()) {
case 1:
return new HexCollisionBox(7.0, 0.0, 7.0, 9.0, 6.0, 9.0);
case 2:
return new HexCollisionBox(5.0, 0.0, 6.0, 11.0, 6.0, 9.0);
case 3:
return new HexCollisionBox(5.0, 0.0, 6.0, 10.0, 6.0, 11.0);
default:
case 4:
return new HexCollisionBox(5.0, 0.0, 5.0, 11.0, 6.0, 10.0);
}
return switch (data.getCandles()) {
case 1 -> new HexCollisionBox(7.0, 0.0, 7.0, 9.0, 6.0, 9.0);
case 2 -> new HexCollisionBox(5.0, 0.0, 6.0, 11.0, 6.0, 9.0);
case 3 -> new HexCollisionBox(5.0, 0.0, 6.0, 10.0, 6.0, 11.0);
default -> new HexCollisionBox(5.0, 0.0, 5.0, 11.0, 6.0, 10.0);
};
}
return getPicklesBox(version, data.getCandles());
@@ -1017,7 +842,13 @@ public enum CollisionData {
SCULK_SENSOR(new HexCollisionBox(0.0, 0.0, 0.0, 16.0, 8.0, 16.0), StateTypes.SCULK_SENSOR, StateTypes.CALIBRATED_SCULK_SENSOR),
DECORATED_POT(new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0), StateTypes.DECORATED_POT),
DECORATED_POT((player, version, data, x, y, z) -> {
if (version.isNewerThan(ClientVersion.V_1_19_3)) {
return new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0);
} else { // ViaVersion replacement is a Brick
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}
}, StateTypes.DECORATED_POT),
BIG_DRIPLEAF((player, version, data, x, y, z) -> {
if (version.isOlderThanOrEquals(ClientVersion.V_1_16_4))
@@ -1033,34 +864,29 @@ public enum CollisionData {
}, StateTypes.BIG_DRIPLEAF),
DRIPSTONE((player, version, data, x, y, z) -> {
POINTED_DRIPSTONE((player, version, data, x, y, z) -> {
if (version.isOlderThan(ClientVersion.V_1_17))
return getEndRod(version, BlockFace.UP);
HexCollisionBox box;
SimpleCollisionBox box;
if (data.getThickness() == Thickness.TIP_MERGE) {
box = new HexCollisionBox(5.0, 0.0, 5.0, 11.0, 16.0, 11.0);
box = new HexOffsetCollisionBox(data.getType(), 5.0, 0.0, 5.0, 11.0, 16.0, 11.0);
} else if (data.getThickness() == Thickness.TIP) {
if (data.getVerticalDirection() == VerticalDirection.DOWN) {
box = new HexCollisionBox(5.0, 5.0, 5.0, 11.0, 16.0, 11.0);
box = new HexOffsetCollisionBox(data.getType(), 5.0, 5.0, 5.0, 11.0, 16.0, 11.0);
} else {
box = new HexCollisionBox(5.0, 0.0, 5.0, 11.0, 11.0, 11.0);
box = new HexOffsetCollisionBox(data.getType(), 5.0, 0.0, 5.0, 11.0, 11.0, 11.0);
}
} else if (data.getThickness() == Thickness.FRUSTUM) {
box = new HexCollisionBox(4.0, 0.0, 4.0, 12.0, 16.0, 12.0);
box = new HexOffsetCollisionBox(data.getType(), 4.0, 0.0, 4.0, 12.0, 16.0, 12.0);
} else if (data.getThickness() == Thickness.MIDDLE) {
box = new HexCollisionBox(3.0, 0.0, 3.0, 13.0, 16.0, 13.0);
box = new HexOffsetCollisionBox(data.getType(), 3.0, 0.0, 3.0, 13.0, 16.0, 13.0);
} else {
box = new HexCollisionBox(2.0, 0.0, 2.0, 14.0, 16.0, 14.0);
box = new HexOffsetCollisionBox(data.getType(), 2.0, 0.0, 2.0, 14.0, 16.0, 14.0);
}
// Copied from NMS and it works! That's all you need to know.
long i = (x * 3129871L) ^ (long) z * 116129781L ^ (long) 0;
i = i * i * 42317861L + i * 11L;
i = i >> 16;
return box.offset(GrimMath.clamp((((i & 15L) / 15.0F) - 0.5D) * 0.5D, -0.125f, 0.125f), 0, GrimMath.clamp((((i >> 8 & 15L) / 15.0F) - 0.5D) * 0.5D, -0.125f, 0.125f));
return box;
}, StateTypes.POINTED_DRIPSTONE),
POWDER_SNOW((player, version, data, x, y, z) -> {
@@ -1087,8 +913,6 @@ public enum CollisionData {
return new HexCollisionBox(6.0D, 0.0D, 0.0D, 10.0D, 16.0D, 16.0D);
}, StateTypes.NETHER_PORTAL),
END_PORTAL(new HexCollisionBox(0.0D, 6.0D, 0.0D, 16.0D, 12.0D, 16.0D), StateTypes.END_PORTAL),
AZALEA((player, version, data, x, y, z) -> {
return new ComplexCollisionBox(new HexCollisionBox(0.0, 8.0, 0.0, 16.0, 16.0, 16.0),
new HexCollisionBox(6.0, 0.0, 6.0, 10.0, 8.0, 10.0));
@@ -1121,63 +945,54 @@ public enum CollisionData {
if (!data.isHanging()) {
return new HexCollisionBox(7.0D, 0.0D, 7.0D, 9.0D, 16.0D, 9.0D);
}
switch (data.getAge()) {
case 0:
return new HexCollisionBox(7.0D, 13.0D, 7.0D, 9.0D, 16.0D, 9.0D);
case 1:
return new HexCollisionBox(7.0D, 10.0D, 7.0D, 9.0D, 16.0D, 9.0D);
case 2:
return new HexCollisionBox(7.0D, 7.0D, 7.0D, 9.0D, 16.0D, 9.0D);
case 3:
return new HexCollisionBox(7.0D, 3.0D, 7.0D, 9.0D, 16.0D, 9.0D);
case 4:
default:
return new HexCollisionBox(7.0D, 0.0D, 7.0D, 9.0D, 16.0D, 9.0D);
}
return switch (data.getAge()) {
case 0 -> new HexCollisionBox(7.0D, 13.0D, 7.0D, 9.0D, 16.0D, 9.0D);
case 1 -> new HexCollisionBox(7.0D, 10.0D, 7.0D, 9.0D, 16.0D, 9.0D);
case 2 -> new HexCollisionBox(7.0D, 7.0D, 7.0D, 9.0D, 16.0D, 9.0D);
case 3 -> new HexCollisionBox(7.0D, 3.0D, 7.0D, 9.0D, 16.0D, 9.0D);
default -> new HexCollisionBox(7.0D, 0.0D, 7.0D, 9.0D, 16.0D, 9.0D);
};
}, StateTypes.MANGROVE_PROPAGULE),
SCULK_SHRIKER(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D), StateTypes.SCULK_SHRIEKER),
FROGSPAWN(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 1.5D, 16.0D), StateTypes.FROGSPAWN),
SNIFFER_EGG(new HexCollisionBox(1.0D, 0.0D, 2.0D, 15.0D, 16.0D, 14.0D), StateTypes.SNIFFER_EGG),
PINK_PETALS_BLOCK(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 3.0D, 16.0D), StateTypes.PINK_PETALS),
TORCHFLOWER_CROP((player, version, data, x, y, z) -> {
if (data.getAge() == 0) {
return new HexCollisionBox(5.0D, 0.0D, 5.0D, 11.0D, 6.0D, 11.0D);
SCULK_SHRIKER((player, version, data, x, y, z) -> {
if (version.isNewerThan(ClientVersion.V_1_18_2)) {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D);
} else {
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}
// age is 1
return new HexCollisionBox(5.0D, 0.0D, 5.0D, 11.0D, 10.0D, 11.0D);
}, StateTypes.TORCHFLOWER_CROP),
}, StateTypes.SCULK_SHRIEKER),
SNIFFER_EGG((player, version, data, x, y, z) -> {
if (version.isNewerThan(ClientVersion.V_1_19_4)) {
return new HexCollisionBox(1.0D, 0.0D, 2.0D, 15.0D, 16.0D, 14.0D);
} else {
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}
}, StateTypes.SNIFFER_EGG),
PITCHER_CROP((player, version, data, x, y, z) -> {
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);
if (version.isNewerThan(ClientVersion.V_1_19_4)) {
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);
if (data.getAge() == 0) {
return COLLISION_SHAPE_BULB;
if (data.getAge() == 0) {
return COLLISION_SHAPE_BULB;
} else {
return data.getHalf() == Half.LOWER ? COLLISION_SHAPE_CROP : NoCollisionBox.INSTANCE;
}
} else {
return data.getHalf() == Half.LOWER ? COLLISION_SHAPE_CROP : NoCollisionBox.INSTANCE;
return NoCollisionBox.INSTANCE;
}
}, StateTypes.PITCHER_CROP),
WALL_HANGING_SIGNS((player, version, data, x, y, z) -> {
switch (data.getFacing()) {
case NORTH:
case SOUTH:
return new HexCollisionBox(0.0, 14.0, 6.0, 16.0, 16.0, 10.0);
case WEST:
case EAST:
return new HexCollisionBox(6.0, 14.0, 0.0, 10.0, 16.0, 16.0);
default:
return NoCollisionBox.INSTANCE;
}
return switch (data.getFacing()) {
case NORTH, SOUTH -> new HexCollisionBox(0.0, 14.0, 6.0, 16.0, 16.0, 10.0);
case WEST, EAST -> new HexCollisionBox(6.0, 14.0, 0.0, 10.0, 16.0, 16.0);
default -> NoCollisionBox.INSTANCE;
};
}, BlockTags.WALL_HANGING_SIGNS.getStates().toArray(new StateType[0])),
NONE(NoCollisionBox.INSTANCE, StateTypes.AIR, StateTypes.CAVE_AIR, StateTypes.VOID_AIR, StateTypes.LIGHT),
DEFAULT(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.STONE);
// This should be an array... but a hashmap will do for now...
@@ -1213,21 +1028,14 @@ public enum CollisionData {
if (version.isOlderThanOrEquals(ClientVersion.V_1_16_4))
return NoCollisionBox.INSTANCE;
switch (facing) {
default:
case UP:
return new HexCollisionBox(param_1, 0.0, param_1, 16 - param_1, param_0, 16 - param_1);
case DOWN:
return new HexCollisionBox(param_1, 16 - param_0, param_1, 16 - param_1, 16.0, 16 - param_1);
case NORTH:
return new HexCollisionBox(param_1, param_1, 16 - param_0, 16 - param_1, 16 - param_1, 16.0);
case SOUTH:
return new HexCollisionBox(param_1, param_1, 0.0, 16 - param_1, 16 - param_1, param_0);
case EAST:
return new HexCollisionBox(0.0, param_1, param_1, param_0, 16 - param_1, 16 - param_1);
case WEST:
return new HexCollisionBox(16 - param_0, param_1, param_1, 16.0, 16 - param_1, 16 - param_1);
}
return switch (facing) {
case DOWN -> new HexCollisionBox(param_1, 16 - param_0, param_1, 16 - param_1, 16.0, 16 - param_1);
case NORTH -> new HexCollisionBox(param_1, param_1, 16 - param_0, 16 - param_1, 16 - param_1, 16.0);
case SOUTH -> new HexCollisionBox(param_1, param_1, 0.0, 16 - param_1, 16 - param_1, param_0);
case EAST -> new HexCollisionBox(0.0, param_1, param_1, param_0, 16 - param_1, 16 - param_1);
case WEST -> new HexCollisionBox(16 - param_0, param_1, param_1, 16.0, 16 - param_1, 16 - param_1);
default -> new HexCollisionBox(param_1, 0.0, param_1, 16 - param_1, param_0, 16 - param_1);
};
}
private static CollisionBox getPicklesBox(ClientVersion version, int pickles) {
@@ -1236,20 +1044,16 @@ public enum CollisionData {
return getCocoa(version, pickles, BlockFace.WEST);
}
switch (pickles) {
case 1:
return new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 6.0D, 10.0D);
case 2:
return new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 6.0D, 13.0D);
case 3:
return new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 6.0D, 14.0D);
case 4:
return new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 7.0D, 14.0D);
}
return NoCollisionBox.INSTANCE;
return switch (pickles) {
case 1 -> new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 6.0D, 10.0D);
case 2 -> new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 6.0D, 13.0D);
case 3 -> new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 6.0D, 14.0D);
case 4 -> new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 7.0D, 14.0D);
default -> NoCollisionBox.INSTANCE;
};
}
private static CollisionBox getCocoa(ClientVersion version, int age, BlockFace direction) {
public static CollisionBox getCocoa(ClientVersion version, int age, BlockFace direction) {
// From 1.9 - 1.10, the large cocoa block is the same as the medium one
// https://bugs.mojang.com/browse/MC-94274
if (version.isNewerThanOrEquals(ClientVersion.V_1_9_1) && version.isOlderThan(ClientVersion.V_1_11))
@@ -1301,24 +1105,17 @@ public enum CollisionData {
if (version.isOlderThan(ClientVersion.V_1_9))
return NoCollisionBox.INSTANCE;
switch (face) {
case UP:
case DOWN:
default:
return new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 16.0D, 10.0);
case NORTH:
case SOUTH:
return new HexCollisionBox(6.0D, 6.0D, 0.0D, 10.0D, 10.0D, 16.0D);
case EAST:
case WEST:
return new HexCollisionBox(0.0D, 6.0D, 6.0D, 16.0D, 10.0D, 10.0D);
}
return switch (face) {
case NORTH, SOUTH -> new HexCollisionBox(6.0D, 6.0D, 0.0D, 10.0D, 10.0D, 16.0D);
case EAST, WEST -> new HexCollisionBox(0.0D, 6.0D, 6.0D, 16.0D, 10.0D, 10.0D);
default -> new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 16.0D, 10.0);
};
}
// Would pre-computing all states be worth the memory cost? I doubt it
public static CollisionData getData(StateType state) { // TODO: Find a better hack for lava and scaffolding
// What the fuck mojang, why put noCollision() and then give PITCHER_CROP collision?
return state.isSolid() || state == StateTypes.LAVA || state == StateTypes.SCAFFOLDING || state == StateTypes.PITCHER_CROP || state == StateTypes.HEAVY_CORE || BlockTags.WALL_HANGING_SIGNS.contains(state) ? rawLookupMap.getOrDefault(state, DEFAULT) : NONE;
return state.isSolid() || state == StateTypes.LAVA || state == StateTypes.SCAFFOLDING || state == StateTypes.PITCHER_CROP || state == StateTypes.HEAVY_CORE || BlockTags.WALL_HANGING_SIGNS.contains(state) ? rawLookupMap.getOrDefault(state, DEFAULT) : NO_COLLISION;
}
// TODO: This is wrong if a block doesn't have any hitbox and isn't specified, light block?
Loaded 100 of 186 files, more files were not shown because too many files have changed in this diff. Show more