Compare commits

...
238 Commits
Author SHA1 Message Date
Axionize e4afe412a5 Revert "Remove unncessary NO_COLLISION entry for torchcrop"
This reverts commit e149ca6be8.
2024-11-03 19:30:08 -05:00
Axionize 45f5b5060c Revert "Remove unncessary NO_COLLISION entries"
This reverts commit 1761d853de.
2024-11-03 19:30:08 -05:00
Axionize a6af4cbbd9 Fix scaffolding hitbox for pre-1.14 clients 2024-11-03 07:06:50 -05:00
Axionize e76a59f14a Enable checks against scaffolding 2024-11-03 07:06:50 -05:00
Axionize ea2673541c Fix Weeping Vines and Vine Plant for all versions; Fix Weeping + Twisted for pre 1.16 players 2024-11-03 06:39:15 -05:00
Axionize 0193d342e0 Fix pitcher crop for pre-1.20 players 2024-11-03 06:39:13 -05:00
Axionize 0a1881592a Fix sniffer egg for pre 1.20 players 2024-11-03 06:39:13 -05:00
Axionize fcfb5d0cbb Fix frogspawn for pre-1.19 players 2024-11-03 06:39:12 -05:00
Axionize a8fc80dd50 Fix Sculk Shrieker hit box for all versions and collision box for pre 1.18.2 2024-11-03 06:39:12 -05:00
Axionize 8805899f39 Fix Spore Blossom hitbox on < 1.17 2024-11-03 06:39:12 -05:00
Axionize 5dc6d47dd0 Fix glow lichen and sculk vein boxes 2024-11-03 06:39:12 -05:00
Axionize 4450bd5c5d Fix Cauldron Hit/Collision Box 2024-11-03 03:52:52 -05:00
Axionize 45faec8290 Remove unncessary NO_COLLISION entries 2024-11-03 02:58:45 -05:00
Axionize a19a70813c Fix Pink Petals Boxes 2024-11-03 02:52:03 -05:00
Axionize 34582ffe50 Add new union() function to collision boxes 2024-11-03 02:52:03 -05:00
Axionize 0d4c1ed81e Fix Coral Fan Collision box 2024-11-03 02:52:03 -05:00
Axionize e1d739be1b Remove unncessary NO_COLLISION entry for torchcrop 2024-11-03 01:18:02 -04:00
Axionize 2c72efd3ea Cleanup and documnet why 2nd rayTrace in getNearestReachHitResult is intentionally non-optimal 2024-11-03 00:49:39 -04:00
Axionize 270f160dd2 Remove unsued getFacing() from vector code 2024-11-02 22:37:58 -04:00
Axionize b026f494d8 Fix falses on blocks with 'insides/internal walls' (different Raycast block placement shape then HitBox shape) like Cauldrons, Hoppers, and Composters 2024-11-02 22:37:57 -04:00
Axionize 8a690e2423 Annotate LOSP functions 2024-11-02 22:37:57 -04:00
Axionize 08678e4252 Fix falses on dead corals 2024-11-01 21:25:40 -04:00
Axionize 980226fab9 Update LOSP to work with new grim config 2024-10-10 23:54:19 -04:00
Axionize 0f726a0f34 Merge remote-tracking branch 'origin/2.0' into los-containers 2024-10-10 23:53:39 -04: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
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
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 0697c425ca Document SimpleCollisionBox and HexCollisionBox with Javadoc 2024-10-03 03:40:32 -04:00
Axionize 428f213e15 Migrate reach-related functions to new BlockRayTrace util class 2024-10-03 01:23:39 -04:00
AxionizeandManInMyVan 593d48a6e6 Button Styling Change
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-10-03 00:10:15 -04:00
AxionizeandManInMyVan 3dcf3c931a Sapling Styling Change
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-10-03 00:10:15 -04:00
AxionizeandManInMyVan 8cc45ddba3 TALL_FLOWER Styling Change
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-10-03 00:10:14 -04:00
AxionizeandManInMyVan 6f23122657 Redstone Wire Comment Styling
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-10-03 00:10:14 -04:00
AxionizeandManInMyVan c0b0759ed0 Inline Hitbox Calculation
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-10-03 00:10:14 -04:00
AxionizeandManInMyVan 00252f3b97 getPropaguleMinHeight Spacing
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-10-03 00:10:13 -04:00
AxionizeandManInMyVan 531c49011b Small Flower Line Spacing/Styling
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-10-03 00:10:13 -04:00
AxionizeandManInMyVan dea22454f7 Coral Fan Line Spacing/Styling
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-10-03 00:10:13 -04:00
AxionizeandManInMyVan 12bda63785 Banner Line Spacing/Styling
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-10-03 00:10:12 -04:00
AxionizeandManInMyVan c65e17cf0c Simplify TALL_FLOWERS handling
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-10-03 00:10:12 -04:00
AxionizeandManInMyVan 753ca0e6ad Rails Styling
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-10-03 00:10:12 -04:00
AxionizeandManInMyVan 8b85e1711d Mark function as Deprecated
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-10-03 00:10:12 -04:00
Axionize 12b0dfd19d Fix spelling mistake 2024-10-03 00:07:51 -04:00
Axionize 8c05d41423 Replace RuntimeException in HitBox.java with IllegalStateException 2024-10-03 00:07:26 -04:00
Axionize c5ab0f5440 Merge remote-tracking branch 'origin/2.0' into los-containers
# Conflicts:
#	src/main/java/ac/grim/grimac/utils/collisions/CollisionData.java
2024-10-02 21:01:12 -04:00
ManInMyVan d92f5ef375 fix chains for pre-1.16 players 2024-10-02 21:00:18 -04:00
Axionize c313d6203a Compact all NO_COLLISION states together 2024-10-02 20:51:10 -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 070f92aea2 Fix 1.11 Ascending rails hitboxes 2024-09-28 01:53:38 -04:00
Axionize 4e3c5894b5 Fix HitBox of ascending powered rail on 1.9 2024-09-28 01:37:43 -04:00
Axionize 8cfb9f0f17 Fix HitBox of ascending powered rails on 1.7.10 2024-09-28 01:14:49 -04:00
Axionize 4e135e3f92 Make result player.getPossibleEyeHeights() of final in checks and remove unused imports 2024-09-28 00:21:33 -04:00
ManInMyVan 03ea36ad28 fix chains for pre-1.16 players 2024-09-26 20:09:18 -06:00
Axionize 938522974b Replace RotationPlace with LOSP in Turkish config 2024-09-26 21:23:08 -04:00
Axionize 6994975387 Merge remote-tracking branch 'origin/2.0' into los-containers
# Conflicts:
#	src/main/java/ac/grim/grimac/checks/impl/scaffolding/RotationPlace.java
2024-09-26 21:20:07 -04:00
Axionize 6d3a172466 Removing unneeded special case handling 2024-09-26 17:12:07 -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
Axionize d5927578a9 Reduce average iterations of rotation place 2024-09-25 01:01:16 -04:00
Axionize 3aaa185617 Readability/documentation improvements 2024-09-25 00:51:50 -04:00
Axionize 980cd92333 1.8 Players 2024-09-25 00:28:59 -04:00
Axionize 89da2942c7 Handle player joining on new version -> scale changing -> old version 2024-09-25 00:01:52 -04:00
Axionize 8ea905e053 Optimize returned array for crawling 2024-09-25 00:01:09 -04:00
Axionize 885e959cdb fix conditionall check 2024-09-24 23:22:22 -04:00
Axionize 02904a3dce Only rewrite player possibleEyeHeights when player scale changes 2024-09-24 23:03:10 -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 e8d4bb2376 Don't bother updating scale attribute if no change is made 2024-09-23 21:16:24 -04:00
Axionize 7695167578 Fix intializing array 2024-09-23 20:34:50 -04:00
Axionize 94e5f63a65 Simplify logic and handle caching even when scale changes 2024-09-23 20:19:05 -04:00
SamB440 da1468e858 Bump packetevents 2024-09-23 18:15:15 +01:00
Axionize 9fa0f773d5 Cache eyeHeights where possible 2024-09-22 17:41:54 -04:00
Axionize 88111e6f7e make getPossibleEyeHeights return most likely heights 2024-09-22 17:17:25 -04:00
Axionize 9a5c16fe6a Merge branch '2.0' into los-containers 2024-09-21 23:04:16 -04:00
Axionize 4efaf8d497 Fix Array matching error and block
The lower level ArraySupport.mismatch() function doesn't work on Java 8 and requires access modification in modern Java. Repalced with Arrays.equals()

The removal of checking if hitdata.getSecond() == expectedBlockFace during iteration (added in Fix 1.8 Brewing Stand bug - 4a9d8627d3) eliminates cases where a block inbetween the target block has a different blockface intercept.

Ideally we should only check the blockface once we've reached the target block as future optimization.
2024-09-21 22:46:48 -04:00
Axionize 07881da451 More minor optimizations 2024-09-21 21:40:40 -04:00
Axionize bd6c2524e7 Optimize iteration order 2024-09-21 20:19:19 -04:00
Axionize b096e02b9b Optimize getPossibleEyeHeights() 2024-09-21 20:18:55 -04:00
ManInMyVan 9708ebc821 Fix a BadPacketsZ false 2024-09-20 10:18:25 +01:00
Axionize ad6b24345b Minor Dynamic Fence cleanup 2024-09-20 03:43:41 -04:00
Axionize 9694b756b5 Inline lever hitboxes 2024-09-20 03:35:47 -04:00
Axionize 6b5786be9d Fix legacy hitboxes for versions up to 1.12.2 and make fence hitbox generation static 2024-09-20 03:29:00 -04:00
AxionizeandCyramek 1f49ab2551 Update src/main/java/ac/grim/grimac/player/GrimPlayer.java
Co-authored-by: Cyramek <xcyramek@gmail.com>
2024-09-20 03:21:28 -04:00
Axionize b18a5bec8e Fix fences on legacy servers 2024-09-20 03:20:01 -04: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 936662db2d Add MultiPlace 2024-09-18 17:45:20 -06:00
Axionize d741459e2f Fix levers and buttons for < 1.13 2024-09-17 23:01:42 -04:00
Axionize 7007722aca Fix cactuses for pre-1.13 2024-09-17 22:48:53 -04:00
Axionize 05e7b3c1e5 Exit conditions early and remove unnccessary if statement 2024-09-17 22:27:43 -04:00
Axionize 13554546eb Deobfuscate and document purpose of misunderstood code in traverseBlocks() 2024-09-17 19:38:38 -04:00
Axionize e2f3722b52 Minor cleanup 2024-09-17 19:21:40 -04:00
AxionizeandManInMyVan 1b75734046 Update src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-09-16 21:55:53 -04:00
Axionize 61d9609328 More minor optimizations 2024-09-16 01:55:13 -04:00
Axionize 4cff51c89f More minor optimizations 2024-09-16 01:32:27 -04:00
Axionize ace5178e99 Minor optimizations 2024-09-16 01:10:44 -04:00
Axionize 4a9d8627d3 Fix 1.8 Brewing Stand bug 2024-09-16 00:26:25 -04:00
Axionize fc89b932dd Cleanup exit conditions 2024-09-15 22:38:42 -04:00
Axionize 0852ac8326 Fix ascending rails on 1.8.9 2024-09-15 22:23:15 -04:00
Axionize 87194d7f1b Fix legacy walls 2024-09-15 22:19:07 -04:00
Axionize 31fa689a98 Fix modern walls 2024-09-15 22:12:40 -04:00
Axionize 5d366e742c Fix rails 2024-09-15 18:44:07 -04:00
Axionize 7cc3c8f5ef Fix blockface false flags 2024-09-15 17:58:14 -04:00
Axionize 7215dfcf60 Add scoring system for most likely blockface 2024-09-15 17:49:15 -04:00
Axionize 5c709c1c54 Check 0.03/0.002 and non-expanded hitbox (for blockfaces) 2024-09-15 17:20:55 -04:00
Axionize 1e5ad65c9f Fix null exception 2024-09-15 14:06:48 -04:00
Axionize f6aeffb264 Quick and dirty button patch 2024-09-15 04:33:14 -04:00
Axionize 4593f3077c Quick and dirty lever fix 2024-09-15 04:29:17 -04:00
Axionize b92c8726de End portals 2024-09-15 04:16:11 -04:00
Axionize c560539a16 Brewing stands (excluding 1.8-1.8.9) 2024-09-15 04:02:53 -04:00
Axionize 9cba7a95f7 Fix cactus 2024-09-15 03:54:00 -04:00
Axionize d6d86ef04d Fix rails 2024-09-15 03:38:03 -04:00
Axionize ed643f3481 legacy pane hitbox faces, still falses because different face? 2024-09-15 03:23:04 -04:00
Axionize d8c2f77a0b Pane fix outline 2024-09-15 02:23:41 -04:00
Axionize 6cfda647f7 Fix flower and short grass offsets for older versions 2024-09-15 01:43:07 -04:00
Axionize fc58fcc721 Fix Wall Banners and Banner Simulation falses 2024-09-15 00:31:30 -04:00
Axionize 5f0707956a Also check blockfaces 2024-09-14 21:37:21 -04:00
Axionize 528712e931 Fix Wall Banners and Banner Simulation falses 2024-09-14 21:13:03 -04:00
Axionize 8fe609452b Bypass check when player near moving shulkers, pistons 2024-09-14 18:52:20 -04:00
Axionize 3afd2c4ac9 Skip post on pre 2024-09-14 18:27:51 -04:00
Axionize 5793d7b678 Merge branch '2.0' into los-containers 2024-09-14 05:13:00 -04:00
Axionize 569a8b4c4d 'Fix' 0.03 for 1.8-1.18.1 2024-09-14 05:11:17 -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
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
Axionize 9cab8f7469 Fix flower offset being visual in 1.8 + bamboo sim 2024-09-09 01:42:07 -04:00
Axionize 1ac0b2efbe Fix Bamboo HitBox & Collision Box 2024-09-08 23:19:34 -04:00
Axionize b78a36f1c2 accidentally added bamboo to No Collision 2024-09-08 22:58:35 -04:00
Axionize e23d191cb8 Merge branch '2.0' into los-containers 2024-09-08 22:50:26 -04: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
Axionize 8854b00b46 Support for buttons in 1.7 2024-09-05 21:03:12 -04:00
ManInMyVan cef044c4fe beds became bouncy in 1.12, not 1.8 2024-09-05 19:01:11 -06:00
Axionize 4be835de07 Skip redstone wires 2024-09-05 20:38:05 -04: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
Axionize b8f59fa99c Fix Pointed Dripstone 2024-09-02 14:40:28 -04:00
Axionize 2236efea4f Fix Small Dripleaf 2024-09-02 14:34:17 -04:00
Axionize 344ba4138d Fix Hanging Roots hit/collision box 2024-09-02 14:23:31 -04:00
Axionize ba0fa7a869 Refactor Bamboo to use OffsetCollisionBox; Fix Bamboo Sapling Hit & Collision Box 2024-09-02 14:16:47 -04:00
Axionize 21668a63fd Fix Tall Grass & Large Ferns 2024-09-02 14:10:45 -04:00
ManInMyVan dfc8c988d1 Allow customizing embed title 2024-09-02 11:45:07 -06:00
Axionize 2f6f9bcbc5 Fix Tall Seagrass hitbox 2024-09-02 03:25:02 -04:00
Axionize 86d19de452 Reorder HitboxData - Groups Before Individual StateTypes so individual statetypes can override group settings (mangrove and saplings) 2024-09-02 03:18:44 -04:00
Axionize d4986449f1 Fix Mangrove Propagule, add HexOffsetCollisionBox 2024-09-02 03:09:38 -04:00
Axionize 3fc366bf1a Fix Tall Flower Hit and Collision boxes 2024-09-02 02:00:32 -04:00
Axionize 9d192b215e Add OffsetCollisionBox; Document all OffSetBlocks and quirks 2024-09-02 01:54:03 -04:00
AxionizeandCyramek b4ea4517e9 Update src/main/java/ac/grim/grimac/utils/collisions/HitboxData.java
Co-authored-by: Cyramek <xcyramek@gmail.com>
2024-09-01 23:25:07 -04:00
AxionizeandManInMyVan 07ad12a16f Update src/main/java/ac/grim/grimac/utils/collisions/HitboxData.java
Whitespace

Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-09-01 22:46:16 -04:00
Axionize f7529a18df Remove Fence Hack; Fix Lever South Wall Hitbox; Initial Work for Flower Hitbox 2024-09-01 22:34:11 -04:00
Axionize c66522cb47 Move Blocks with NoCollision but a Outline/Hitbox into HitboxData 2024-09-01 21:36:34 -04:00
Axionize 94a31f77ff Fix Sweet Berry Collision + HitBox 2024-09-01 19:57:51 -04: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
Axionize c0630f9f6d Crop Collision Box Overhaul
Add Crop Hitboxes
- NetherWart
- Carrot
- Wheat
- Beetroot
- Potatoes
- COCOA

Replace CollisionBoxes with NoCollisionBox
- NetherWart
- Carrot
- Wheat
- Beatroot
- Pumpkin Stem
- Melon Stem

Added new CollisionBox and set to NoCollisionBox for
- Potatoes
2024-08-31 21:05:24 -04:00
Axionize 3a145cb615 Fix banners 2024-08-31 20:03:34 -04:00
Axionize 837516ea86 Fix syntax error 2024-08-31 14:57:07 -04:00
Axionize 66228f59be Add lever hitboxes and fix banner hitboxes 2024-08-31 14:55:33 -04:00
Axionize 800c79a898 Document Hex vs Simple CollisionBox purpose 2024-08-31 14:55:16 -04:00
Axionize 2177b12bcf Print out target block when flagged - help find falses from constant flagging on non-standard blocks 2024-08-30 14:44:22 -04: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
Axionize 050c22ce27 Clean up 2024-08-29 17:02:29 -04:00
Axionize ded614f30f MVP commit 2024-08-29 17:02:28 -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
Axionize 88e439ebfd Improve temporary fix - delete unncessary code 2024-08-29 02:55:40 -04:00
AxionizeandManInMyVan a027355c7d Update src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-08-29 02:53:48 -04:00
AxionizeandManInMyVan 8a351ec928 Update src/main/java/ac/grim/grimac/utils/collisions/datatypes/ComplexCollisionBox.java
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-08-29 02:52:53 -04:00
AxionizeandManInMyVan b2fa4974ef Update src/main/java/ac/grim/grimac/utils/collisions/HitboxData.java
Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2024-08-29 02:52:29 -04:00
Axionize 9c2a034b43 Temporary Hacky Fix to demonstrate cause of fence false flag 2024-08-29 02:35:29 -04:00
ManInMyVan 06fa4bab17 check slot changes in post 2024-08-28 17:06:32 -06:00
Axionize 45ba746300 Comment cleanup 2024-08-26 01:22:07 -04:00
Axionize 483f317f83 Remove unneeded entry in punishments.yml 2024-08-26 01:16:26 -04:00
Axionize dee3268bce Make variables private 2024-08-26 01:14:30 -04:00
Axionize 2a88d3eef4 Account for player being inside of target block 2024-08-26 00:39:00 -04:00
Axionize 2898fed15a 1.9+ tick behind and 1.7 ticking behaviour compensation 2024-08-26 00:20:40 -04:00
Axionize d2721077c9 Remove print statements and cleanup comments 2024-08-26 00:13:54 -04:00
Axionize dd7bb81b46 Rework whitelist to use HashSet 2024-08-26 00:13:04 -04:00
Axionize 4515d21023 Seperate LOS-block-place checks from Sam's LOS reach checks 2024-08-26 00:01:14 -04:00
Axionize 3315e3c148 Revert "Merge reach wall hit check"
This reverts commit b5990080
2024-08-25 23:58:36 -04:00
Axionize 2b552680a7 Fix styling 2024-08-25 13:44:26 -04:00
SamB440 aabead19ec Fix some respawn issues 2024-08-25 17:49:20 +01:00
Axionize f78c80f04d Temporarily comment out system.out.println 2024-08-25 03:18:17 -04:00
Axionize bc239a7994 Fix block whitelist 2024-08-25 03:15:00 -04:00
Axionize a508ec2279 0.03/0.0002 changes, cleanup code, add comments 2024-08-24 23:15:46 -04:00
Axionize cccecbacbc Remove RotationPlace, replacec with LineOfSightPlace 2024-08-24 23:09:06 -04:00
Axionize 177a8b0527 Add config options for check and enable it by adding to punishments 2024-08-24 23:08:41 -04:00
Axionize d869ff959f Add config options for check and enable it by adding to punishments 2024-08-24 23:05:04 -04:00
Axionize 772ba562e6 Rename check 2024-08-24 23:04:50 -04:00
Axionize 1786360d7c Clean up Code and add code for blockTypeWhitelist to BlockLOS checks for performance. 2024-08-24 22:24:23 -04:00
ManInMyVan af60e07327 allow exempting for specific checks 2024-08-24 17:23:06 -06:00
Axionize 328596f01f Expand hitbox of target block by 0.03/0.0002 to account for known MC bug 2024-08-24 19:18:43 -04:00
Axionize 6a13c9f822 Expand hitbox of target block by 0.03/0.0002 to account for known MC bug 2024-08-24 19:18:41 -04:00
Axionize 3c78a63719 Workable check 2024-08-24 19:18:41 -04:00
Axionize 8b645b75df Stash commits - loop alternate possible scenarios 2024-08-24 19:18:41 -04:00
Axionize ae3ea30c92 Stash commits - compensated everything 2024-08-24 19:18:40 -04:00
Axionize 01bd2c0c11 Stash commits - compensated world 2024-08-24 19:18:40 -04:00
Axionize e91721299a Container LOS initial commit 2024-08-24 19:18:39 -04:00
Axionize 4e08ec9fca Deobfuscate and document purpose of misunderstood code in traverseBlocks() 2024-08-24 19:18:39 -04:00
Axionize b599008040 Merge reach wall hit check
(cherry picked from commit 335c33b109)
2024-08-24 19:18:39 -04:00
ManInMyVan 0c78830524 fix a noslow 2024-08-14 19:03:48 -06:00
92 changed files with 3618 additions and 1081 deletions

No files matched your search

+31 -24
View File
@@ -9,10 +9,14 @@ plugins {
}
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.sourceCompatibility = JavaVersion.VERSION_17
java.targetCompatibility = JavaVersion.VERSION_17
// Set to false for debug builds
// You cannot live reload classes if the jar relocates dependencies
var relocate = true;
repositories {
mavenLocal()
@@ -34,14 +38,15 @@ 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")
@@ -133,21 +138,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")
}
}
+2 -2
View File
@@ -19,12 +19,12 @@ public enum GrimAPI {
private final TickManager tickManager = new TickManager();
private final GrimExternalAPI externalAPI = new GrimExternalAPI(this);
private InitManager initManager;
private ConfigManager configManager;
private ConfigManagerImpl configManager;
private JavaPlugin plugin;
public void load(final JavaPlugin plugin) {
this.plugin = plugin;
this.configManager = new ConfigManager();
this.configManager = new ConfigManagerImpl();
initManager = new InitManager();
initManager.load();
}
@@ -4,22 +4,29 @@ 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.init.Initable;
import ac.grim.grimac.player.GrimPlayer;
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.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 +48,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 +64,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 +86,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
@@ -105,8 +107,16 @@ public class GrimExternalAPI implements GrimAbstractAPI, Initable {
return GrimAPI.INSTANCE.getAlertManager();
}
@Override
public ConfigManager getConfigManager() {
return configManager;
}
private ConfigManager configManager = null;
@Override
public void start() {
if (configManager == null) configManager = GrimAPI.INSTANCE.getConfigManager();
variableReplacements.put("%player%", GrimUser::getName);
variableReplacements.put("%uuid%", user -> user.getUniqueId().toString());
variableReplacements.put("%ping%", user -> user.getTransactionPing() + "");
@@ -118,4 +128,61 @@ public class GrimExternalAPI implements GrimAbstractAPI, Initable {
variableReplacements.put("%version%", GrimUser::getVersionName);
variableReplacements.put("%prefix%", user -> ChatColor.translateAlternateColorCodes('&', GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("prefix", "&bGrim &8»")));
}
@Override
public void reload(ConfigManager config) {
if (config.isLoadedAsync()) {
FoliaScheduler.getAsyncScheduler().runNow(GrimAPI.INSTANCE.getPlugin(),
o -> successfulReload(config));
} else {
successfulReload(config);
}
}
@Override
public CompletableFuture<Boolean> reloadAsync(ConfigManager config) {
if (config.isLoadedAsync()) {
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();
onReload(config);
FoliaScheduler.getAsyncScheduler().runNow(GrimAPI.INSTANCE.getPlugin(),
o -> Bukkit.getPluginManager().callEvent(new GrimReloadEvent(true)));
return true;
} catch (Exception e) {
e.printStackTrace();
}
FoliaScheduler.getAsyncScheduler().runNow(GrimAPI.INSTANCE.getPlugin(),
o -> Bukkit.getPluginManager().callEvent(new GrimReloadEvent(false)));
return false;
}
@Override
public void onReload(ConfigManager newConfig) {
configManager = newConfig != null ? newConfig : configManager;
//Reload checks for all players
for (GrimPlayer grimPlayer : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
ChannelHelper.runInEventLoop(grimPlayer.user.getChannel(), () -> {
grimPlayer.reload(configManager);
grimPlayer.updatePermissions();
grimPlayer.punishmentManager.reload(configManager);
for (AbstractCheck value : grimPlayer.checkManager.allChecks.values()) {
value.reload(configManager);
}
});
}
//Restart
GrimAPI.INSTANCE.getDiscordManager().start();
GrimAPI.INSTANCE.getSpectateManager().start();
GrimAPI.INSTANCE.getExternalAPI().start();
}
}
+32 -14
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;
@@ -28,6 +30,7 @@ public class Check implements AbstractCheck {
private boolean experimental;
@Setter
private boolean isEnabled;
private boolean exempted;
@Override
public boolean isExperimental() {
@@ -51,12 +54,19 @@ public class Check implements AbstractCheck {
this.experimental = checkData.experimental();
this.description = checkData.description();
}
reload();
//
reload(GrimAPI.INSTANCE.getConfigManager().getConfig());
}
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 +82,7 @@ public class Check implements AbstractCheck {
}
public final boolean flag() {
if (player.disableGrim || (experimental && !GrimAPI.INSTANCE.getConfigManager().isExperimentalChecks()))
if (player.disableGrim || (experimental && !GrimAPI.INSTANCE.getConfigManager().isExperimentalChecks()) || exempted)
return false; // Avoid calling event if disabled
FlagEvent event = new FlagEvent(player, this);
@@ -98,21 +108,24 @@ 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);
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 +146,10 @@ public class Check implements AbstractCheck {
packetType == PacketType.Play.Client.WINDOW_CONFIRMATION;
}
@Override
public void reload() {
reload(GrimAPI.INSTANCE.getConfigManager().getConfig());
}
}
@@ -25,17 +25,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();
@@ -15,6 +15,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package ac.grim.grimac.checks.impl.combat;
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;
@@ -201,8 +202,9 @@ 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));
@@ -236,9 +238,8 @@ public class Reach extends Check implements PacketCheck {
}
@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.cancelImpossibleHits = config.getBooleanElse("Reach.block-impossible-hits", true);
this.threshold = config.getDoubleElse("Reach.threshold", 0.0005);
}
}
@@ -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;
}
@@ -1,5 +1,6 @@
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;
@@ -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;
@@ -51,8 +52,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);
}
}
@@ -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;
@@ -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);
}
}
@@ -26,7 +26,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,196 @@
package ac.grim.grimac.checks.impl.scaffolding;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockPlaceCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
import ac.grim.grimac.utils.collisions.HitboxData;
import ac.grim.grimac.utils.collisions.RaycastData;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.HitData;
import ac.grim.grimac.utils.nmsutil.BlockRayTrace;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.util.Vector3i;
import org.bukkit.util.Vector;
import java.util.HashSet;
import java.util.List;
@CheckData(name = "LineOfSightPlace")
public class LineOfSightPlace extends BlockPlaceCheck {
private double flagBuffer = 0; // If the player flags once, force them to play legit, or we will cancel the tick before.
private boolean ignorePost = false;
private boolean useBlockWhitelist;
private HashSet<StateType> blockWhitelist;
public LineOfSightPlace(GrimPlayer player) {
super(player);
}
@Override
public void onBlockPlace(final BlockPlace place) {
if (checkIfShouldSkip(place)) return;
if (flagBuffer > 0 && !didRayTraceHit(place)) {
ignorePost = true;
// If the player hit and has flagged this check recently
if (flagAndAlert("pre-flying: " + player.compensatedWorld.getWrappedBlockStateAt(place.getPlacedAgainstBlockLocation()).getType()) && shouldModifyPackets() && shouldCancel()) {
place.resync(); // Deny the block placement.
}
}
}
// Use post flying because it has the correct rotation, and can't false easily.
@Override
public void onPostFlyingBlockPlace(BlockPlace place) {
if (checkIfShouldSkip(place)) return;
// Don't flag twice
if (ignorePost) {
ignorePost = false;
return;
}
// Ray trace to try and hit the target block.
boolean hit = didRayTraceHit(place);
// This can false with rapidly moving yaw in 1.8+ clients
if (!hit) {
flagBuffer = 1;
flagAndAlert("post-flying: " + player.compensatedWorld.getWrappedBlockStateAt(place.getPlacedAgainstBlockLocation()).getType());
} else {
flagBuffer = Math.max(0, flagBuffer - 0.1);
}
}
private boolean checkIfShouldSkip(BlockPlace place) {
StateType targetBlockStateType = player.compensatedWorld.getWrappedBlockStateAt(place.getPlacedAgainstBlockLocation()).getType();
if (player.gamemode == GameMode.SPECTATOR) return true; // A waste to check creative mode players
if (targetBlockStateType == StateTypes.REDSTONE_WIRE) return true; // Redstone too buggy
if (player.compensatedWorld.isNearHardEntity(player.boundingBox.copy().expand(4))) return true; // Shulkers and Pistons are too buggy
if (useBlockWhitelist) {
return !isBlockTypeWhitelisted(targetBlockStateType);
}
return false;
}
private boolean didRayTraceHit(BlockPlace place) {
double[] possibleEyeHeights = player.getPossibleEyeHeights();
// Start checking if player is in the block
double minEyeHeight = Double.MAX_VALUE;
double maxEyeHeight = Double.MIN_VALUE;
for (double height : possibleEyeHeights) {
minEyeHeight = Math.min(minEyeHeight, height);
maxEyeHeight = Math.max(maxEyeHeight, height);
}
double movementThreshold = player.getMovementThreshold();
SimpleCollisionBox eyePositions = new SimpleCollisionBox(player.x, player.y + minEyeHeight, player.z, player.x, player.y + maxEyeHeight, player.z);
eyePositions.expand(movementThreshold);
int[] interactBlockVec = new int[]{place.blockPosition.x, place.blockPosition.y, place.blockPosition.z};
BlockFace expectedBlockFace = place.getDirection();
// If the player is inside a block, then they can ray trace through the block and hit the other side of the block
// This may potentially be exploitable as a minor bypass
if (eyePositions.isIntersected(new SimpleCollisionBox(interactBlockVec[0], interactBlockVec[1], interactBlockVec[2]))) {
return true;
}
// End checking if the player is in the block
double[][] possibleLookDirs;
// 1.9+ players could be a tick behind because we don't get skipped ticks
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
possibleLookDirs = new double[][]{
{player.xRot, player.yRot},
{player.lastXRot, player.lastYRot},
{player.lastXRot, player.yRot}
};
} else if (player.getClientVersion().isOlderThan(ClientVersion.V_1_8)) {
// 1.7 players do not have any of these issues! They are always on the latest look vector
possibleLookDirs = new double[][]{{player.xRot, player.yRot}};
} else {
possibleLookDirs = new double[][]{
{player.xRot, player.yRot},
{player.lastXRot, player.yRot}
};
}
// We do not need to add 0.03/0.0002 to maxDistance to ensure our raytrace hits blocks
// Since we expand the hitboxes of the expectedTargetBlock by 0.03/0.002 already later
double maxDistance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_BLOCK_INTERACTION_RANGE);
// Define possible offsets
// TODO, vectorize this with SIMD or AVX for performance
double[][] offsets = {
{0, 0, 0},
{movementThreshold, 0, 0},
{-movementThreshold, 0, 0},
{0, movementThreshold, 0},
{0, -movementThreshold, 0},
{0, 0, movementThreshold},
{0, 0, -movementThreshold}
};
double[] eyePosition = new double[3];
double[] eyeLookDir = new double[3];
for (double eyeHeight : possibleEyeHeights) {
for (double[] lookDir : possibleLookDirs) {
for (double[] offset : offsets) {
eyePosition[0] = player.x + offset[0];
eyePosition[1] = player.y + eyeHeight + offset[1];
eyePosition[2] = player.z + offset[2];
calculateDirection(eyeLookDir, lookDir[0], lookDir[1]);
if (getTargetBlock(eyePosition, eyeLookDir, maxDistance, interactBlockVec, expectedBlockFace)) {
return true; // If any possible face matches the client-side placement, assume it's legitimate
}
}
}
}
return false; // No matching face found
}
// Helper method to calculate direction (replace the Ray class method)
private void calculateDirection(double[] result, double xRot, double yRot) {
float rotX = (float) Math.toRadians(xRot);
float rotY = (float) Math.toRadians(yRot);
result[1] = -player.trigHandler.sin(rotY);
double xz = player.trigHandler.cos(rotY);
result[0] = -xz * player.trigHandler.sin(rotX);
result[2] = xz * player.trigHandler.cos(rotX);
}
private boolean getTargetBlock(double[] eyePos, double[] eyeDir, double maxDistance, int[] targetBlockVec, BlockFace expectedBlockFace) {
HitData hitData = BlockRayTrace.getNearestReachHitResult(player, eyePos, eyeDir, maxDistance, maxDistance, targetBlockVec, false);
// we check for hitdata != null because of being in expanded hitbox, or there was no result, do we still need this?
return hitData != null && new Vector3i(targetBlockVec[0], targetBlockVec[1], targetBlockVec[2]).equals(hitData.getPosition()) && hitData.getClosestDirection() == expectedBlockFace;
}
private boolean isBlockTypeWhitelisted(StateType type) {
return blockWhitelist.contains(type);
}
@Override
public void onReload(ConfigManager config) {
useBlockWhitelist = config.getBooleanElse("LineOfSightPlace.use-block-whitelist", false);
blockWhitelist = new HashSet<>();
List<String> blocks = config.getStringList("LineOfSightPlace.block-whitelist");
for (String block : blocks) {
blockWhitelist.add(StateTypes.getByName(block));
}
}
}
@@ -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();
}
}
@@ -28,8 +28,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;
@@ -1,116 +0,0 @@
package ac.grim.grimac.checks.impl.scaffolding;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockPlaceCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.Pair;
import ac.grim.grimac.utils.nmsutil.Ray;
import ac.grim.grimac.utils.nmsutil.ReachUtils;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.util.Vector3f;
import org.bukkit.util.Vector;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@CheckData(name = "RotationPlace")
public class RotationPlace extends BlockPlaceCheck {
double flagBuffer = 0; // If the player flags once, force them to play legit, or we will cancel the tick before.
boolean ignorePost = false;
public RotationPlace(GrimPlayer player) {
super(player);
}
@Override
public void onBlockPlace(final BlockPlace place) {
if (place.getMaterial() == StateTypes.SCAFFOLDING) return;
if (player.gamemode == GameMode.SPECTATOR) return; // you don't send flying packets when spectating entities
if (flagBuffer > 0 && !didRayTraceHit(place)) {
ignorePost = true;
// If the player hit and has flagged this check recently
if (flagAndAlert("pre-flying") && shouldModifyPackets() && shouldCancel()) {
place.resync(); // Deny the block placement.
}
}
}
// Use post flying because it has the correct rotation, and can't false easily.
@Override
public void onPostFlyingBlockPlace(BlockPlace place) {
if (place.getMaterial() == StateTypes.SCAFFOLDING) return;
if (player.gamemode == GameMode.SPECTATOR) return; // you don't send flying packets when spectating entities
// Don't flag twice
if (ignorePost) {
ignorePost = false;
return;
}
// Ray trace to try and hit the target block.
boolean hit = didRayTraceHit(place);
// This can false with rapidly moving yaw in 1.8+ clients
if (!hit) {
flagBuffer = 1;
flagAndAlert("post-flying");
} else {
flagBuffer = Math.max(0, flagBuffer - 0.1);
}
}
private boolean didRayTraceHit(BlockPlace place) {
SimpleCollisionBox box = new SimpleCollisionBox(place.getPlacedAgainstBlockLocation());
List<Vector3f> possibleLookDirs = new ArrayList<>(Arrays.asList(
new Vector3f(player.lastXRot, player.yRot, 0),
new Vector3f(player.xRot, player.yRot, 0)
));
// Start checking if player is in the block
double minEyeHeight = Collections.min(player.getPossibleEyeHeights());
double maxEyeHeight = Collections.max(player.getPossibleEyeHeights());
SimpleCollisionBox eyePositions = new SimpleCollisionBox(player.x, player.y + minEyeHeight, player.z, player.x, player.y + maxEyeHeight, player.z);
eyePositions.expand(player.getMovementThreshold());
// If the player is inside a block, then they can ray trace through the block and hit the other side of the block
if (eyePositions.isIntersected(box)) {
return true;
}
// End checking if the player is in the block
// 1.9+ players could be a tick behind because we don't get skipped ticks
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
possibleLookDirs.add(new Vector3f(player.lastXRot, player.lastYRot, 0));
}
// 1.7 players do not have any of these issues! They are always on the latest look vector
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_8)) {
possibleLookDirs = Collections.singletonList(new Vector3f(player.xRot, player.yRot, 0));
}
final double distance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_BLOCK_INTERACTION_RANGE);
for (double d : player.getPossibleEyeHeights()) {
for (Vector3f lookDir : possibleLookDirs) {
// x, y, z are correct for the block placement even after post tick because of code elsewhere
Vector3d starting = new Vector3d(player.x, player.y + d, player.z);
// xRot and yRot are a tick behind
Ray trace = new Ray(player, starting.getX(), starting.getY(), starting.getZ(), lookDir.getX(), lookDir.getY());
Pair<Vector, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
if (intercept.getFirst() != null) return true;
}
}
return false;
}
}
@@ -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,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."));
}
});
}
}
@@ -8,13 +8,9 @@ import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.*;
import ac.grim.grimac.utils.blockplace.BlockPlaceResult;
import ac.grim.grimac.utils.blockplace.ConsumesBlockPlace;
import ac.grim.grimac.utils.collisions.HitboxData;
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.*;
import ac.grim.grimac.utils.inventory.Inventory;
import ac.grim.grimac.utils.latency.CompensatedWorld;
import ac.grim.grimac.utils.math.GrimMath;
import ac.grim.grimac.utils.math.VectorUtils;
import ac.grim.grimac.utils.nmsutil.*;
import com.github.retrooper.packetevents.PacketEvents;
@@ -24,7 +20,6 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.event.PacketSendEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.ConnectionState;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import com.github.retrooper.packetevents.protocol.item.type.ItemType;
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
@@ -36,6 +31,7 @@ import com.github.retrooper.packetevents.protocol.player.InteractionHand;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.protocol.world.Location;
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.protocol.world.states.type.StateValue;
@@ -47,11 +43,6 @@ import com.github.retrooper.packetevents.wrapper.play.client.*;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerAcknowledgeBlockChanges;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSetSlot;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import org.bukkit.util.Vector;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
public class CheckManagerListener extends PacketListenerAbstract {
@@ -59,79 +50,8 @@ public class CheckManagerListener extends PacketListenerAbstract {
super(PacketListenerPriority.LOW);
}
// Copied from MCP...
// Returns null if there isn't anything.
//
// I do have to admit that I'm starting to like bifunctions/new java 8 things more than I originally did.
// although I still don't understand Mojang's obsession with streams in some of the hottest methods... that kills performance
public static HitData traverseBlocks(GrimPlayer player, Vector3d start, Vector3d end, BiFunction<WrappedBlockState, Vector3i, HitData> predicate) {
// I guess go back by the collision epsilon?
double endX = GrimMath.lerp(-1.0E-7D, end.x, start.x);
double endY = GrimMath.lerp(-1.0E-7D, end.y, start.y);
double endZ = GrimMath.lerp(-1.0E-7D, end.z, start.z);
double startX = GrimMath.lerp(-1.0E-7D, start.x, end.x);
double startY = GrimMath.lerp(-1.0E-7D, start.y, end.y);
double startZ = GrimMath.lerp(-1.0E-7D, start.z, end.z);
int floorStartX = GrimMath.floor(startX);
int floorStartY = GrimMath.floor(startY);
int floorStartZ = GrimMath.floor(startZ);
if (start.equals(end)) return null;
WrappedBlockState state = player.compensatedWorld.getWrappedBlockStateAt(floorStartX, floorStartY, floorStartZ);
HitData apply = predicate.apply(state, new Vector3i(floorStartX, floorStartY, floorStartZ));
if (apply != null) {
return apply;
}
double xDiff = endX - startX;
double yDiff = endY - startY;
double zDiff = endZ - startZ;
double xSign = Math.signum(xDiff);
double ySign = Math.signum(yDiff);
double zSign = Math.signum(zDiff);
double posXInverse = xSign == 0 ? Double.MAX_VALUE : xSign / xDiff;
double posYInverse = ySign == 0 ? Double.MAX_VALUE : ySign / yDiff;
double posZInverse = zSign == 0 ? Double.MAX_VALUE : zSign / zDiff;
double d12 = posXInverse * (xSign > 0 ? 1.0D - GrimMath.frac(startX) : GrimMath.frac(startX));
double d13 = posYInverse * (ySign > 0 ? 1.0D - GrimMath.frac(startY) : GrimMath.frac(startY));
double d14 = posZInverse * (zSign > 0 ? 1.0D - GrimMath.frac(startZ) : GrimMath.frac(startZ));
// Can't figure out what this code does currently
while (d12 <= 1.0D || d13 <= 1.0D || d14 <= 1.0D) {
if (d12 < d13) {
if (d12 < d14) {
floorStartX += xSign;
d12 += posXInverse;
} else {
floorStartZ += zSign;
d14 += posZInverse;
}
} else if (d13 < d14) {
floorStartY += ySign;
d13 += posYInverse;
} else {
floorStartZ += zSign;
d14 += posZInverse;
}
state = player.compensatedWorld.getWrappedBlockStateAt(floorStartX, floorStartY, floorStartZ);
apply = predicate.apply(state, new Vector3i(floorStartX, floorStartY, floorStartZ));
if (apply != null) {
return apply;
}
}
return null;
}
private static void placeWaterLavaSnowBucket(GrimPlayer player, ItemStack held, StateType toPlace, InteractionHand hand) {
HitData data = getNearestHitResult(player, StateTypes.AIR, false);
HitData data = BlockRayTrace.getNearestHitResult(player, StateTypes.AIR, false);
if (data != null) {
BlockPlace blockPlace = new BlockPlace(player, hand, data.getPosition(), data.getClosestDirection().getFaceValue(), data.getClosestDirection(), held, data);
@@ -268,7 +188,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
// The offhand is unable to interact with blocks like this... try to stop some desync points before they happen
if ((!player.isSneaking || onlyAir) && place.getHand() == InteractionHand.MAIN_HAND) {
Vector3i blockPosition = place.getBlockPosition();
BlockPlace blockPlace = new BlockPlace(player, place.getHand(), blockPosition, place.getFaceId(), place.getFace(), placedWith, getNearestHitResult(player, null, true));
BlockPlace blockPlace = new BlockPlace(player, place.getHand(), blockPosition, place.getFaceId(), place.getFace(), placedWith, BlockRayTrace.getNearestHitResult(player, null, true));
// Right-clicking a trapdoor/door/etc.
StateType placedAgainst = blockPlace.getPlacedAgainstMaterial();
@@ -308,7 +228,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
placedWith = player.getInventory().getOffHand();
}
BlockPlace blockPlace = new BlockPlace(player, place.getHand(), blockPosition, place.getFaceId(), face, placedWith, getNearestHitResult(player, null, true));
BlockPlace blockPlace = new BlockPlace(player, place.getHand(), blockPosition, place.getFaceId(), face, placedWith, BlockRayTrace.getNearestHitResult(player, null, true));
// At this point, it is too late to cancel, so we can only flag, and cancel subsequent block places more aggressively
if (!player.compensatedEntities.getSelf().inVehicle()) {
player.checkManager.onPostFlyingBlockPlace(blockPlace);
@@ -508,7 +428,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
player.placeUseItemPackets.add(new BlockPlaceSnapshot(packet, player.isSneaking));
} else {
// Anti-air place
BlockPlace blockPlace = new BlockPlace(player, packet.getHand(), packet.getBlockPosition(), packet.getFaceId(), packet.getFace(), placedWith, getNearestHitResult(player, null, true));
BlockPlace blockPlace = new BlockPlace(player, packet.getHand(), packet.getBlockPosition(), packet.getFaceId(), packet.getFace(), placedWith, BlockRayTrace.getNearestHitResult(player, null, true));
blockPlace.setCursor(packet.getCursorPosition());
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_11) && player.getClientVersion().isOlderThan(ClientVersion.V_1_11)) {
@@ -582,7 +502,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
}
private static void placeBucket(GrimPlayer player, InteractionHand hand) {
HitData data = getNearestHitResult(player, null, true);
HitData data = BlockRayTrace.getNearestHitResult(player, null, true);
if (data != null) {
BlockPlace blockPlace = new BlockPlace(player, hand, data.getPosition(), data.getClosestDirection().getFaceValue(), data.getClosestDirection(), ItemStack.EMPTY, data);
@@ -748,7 +668,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
}
private static void placeLilypad(GrimPlayer player, InteractionHand hand) {
HitData data = getNearestHitResult(player, null, true);
HitData data = BlockRayTrace.getNearestHitResult(player, null, true);
if (data != null) {
// A lilypad cannot replace a fluid
@@ -778,56 +698,6 @@ public class CheckManagerListener extends PacketListenerAbstract {
}
}
private static HitData getNearestHitResult(GrimPlayer player, StateType heldItem, boolean sourcesHaveHitbox) {
Vector3d startingPos = new Vector3d(player.x, player.y + player.getEyeHeight(), player.z);
Vector startingVec = new Vector(startingPos.getX(), startingPos.getY(), startingPos.getZ());
Ray trace = new Ray(player, startingPos.getX(), startingPos.getY(), startingPos.getZ(), player.xRot, player.yRot);
final double distance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_BLOCK_INTERACTION_RANGE);
Vector endVec = trace.getPointAtDistance(distance);
Vector3d endPos = new Vector3d(endVec.getX(), endVec.getY(), endVec.getZ());
return traverseBlocks(player, startingPos, endPos, (block, vector3i) -> {
CollisionBox data = HitboxData.getBlockHitbox(player, heldItem, player.getClientVersion(), block, vector3i.getX(), vector3i.getY(), vector3i.getZ());
List<SimpleCollisionBox> boxes = new ArrayList<>();
data.downCast(boxes);
double bestHitResult = Double.MAX_VALUE;
Vector bestHitLoc = null;
BlockFace bestFace = null;
for (SimpleCollisionBox box : boxes) {
Pair<Vector, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
if (intercept.getFirst() == null) continue; // No intercept
Vector hitLoc = intercept.getFirst();
if (hitLoc.distanceSquared(startingVec) < bestHitResult) {
bestHitResult = hitLoc.distanceSquared(startingVec);
bestHitLoc = hitLoc;
bestFace = intercept.getSecond();
}
}
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.getFirst() != null) {
return new HitData(vector3i, intercept.getFirst(), intercept.getSecond(), block);
}
}
return null;
});
}
@Override
public void onPacketSend(PacketSendEvent event) {
if (event.getConnectionState() != ConnectionState.PLAY) return;
@@ -836,4 +706,8 @@ public class CheckManagerListener extends PacketListenerAbstract {
player.checkManager.onPacketSend(event);
}
private static boolean isFence(StateType state) {
return BlockTags.FENCES.contains(state);
}
}
@@ -175,7 +175,7 @@ public class PacketPlayerDigging extends PacketListenerAbstract {
if (player == null) return;
if (!player.packetStateData.lastPacketWasTeleport && !player.packetStateData.lastPacketWasOnePointSeventeenDuplicate) {
if (player.packetStateData.isSlowedByUsingItem() && player.packetStateData.getSlowedByUsingItemSlot() != player.packetStateData.lastSlotSelected) {
if (player.packetStateData.isSlowedByUsingItem() && player.packetStateData.eatingHand != InteractionHand.OFF_HAND && player.packetStateData.getSlowedByUsingItemSlot() != player.packetStateData.lastSlotSelected) {
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);
}
}
@@ -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();
@@ -139,11 +139,12 @@ 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))
.put(RotationPlace.class, new RotationPlace(player))
.put(DuplicateRotPlace.class, new DuplicateRotPlace(player))
.put(LineOfSightPlace.class, new LineOfSightPlace(player))
.put(GhostBlockMitigation.class, new GhostBlockMitigation(player))
.build();
@@ -201,6 +202,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 +217,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 +247,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) {
@@ -2,6 +2,8 @@ package ac.grim.grimac.manager;
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;
@@ -15,8 +17,13 @@ import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
public class ConfigManager {
@Getter
public class ConfigManagerImpl implements ConfigManager, BasicReloadable {
public ConfigManager getConfig() {
if (GrimAPI.INSTANCE.getInitManager().isStarted()) return GrimAPI.INSTANCE.getExternalAPI().getConfigManager();
return this;
}
private final DynamicConfig config;
@Getter
private final File configFile = new File(GrimAPI.INSTANCE.getPlugin().getDataFolder(), "config.yml");
@@ -27,8 +34,6 @@ public class ConfigManager {
@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
@@ -36,9 +41,8 @@ public class ConfigManager {
private final List<Pattern> ignoredClientPatterns = new ArrayList<>();
public ConfigManager() {
public ConfigManagerImpl() {
upgrade();
// load config
GrimAPI.INSTANCE.getPlugin().getDataFolder().mkdirs();
config = new DynamicConfig();
@@ -50,6 +54,7 @@ public class ConfigManager {
reload();
}
@Override
public void reload() {
String languageCode = System.getProperty("user.language").toUpperCase();
@@ -77,7 +82,12 @@ public class ConfigManager {
} catch (Exception e) {
throw new RuntimeException("Failed to load config", e);
}
maxPingTransaction = config.getIntElse("max-transaction-time", 60);
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 {
@@ -317,4 +327,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);
}
}
@@ -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"));
@@ -8,11 +8,16 @@ 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 lombok.Getter;
public class InitManager {
ClassToInstanceMap<Initable> initializersOnLoad;
ClassToInstanceMap<Initable> initializersOnStart;
ClassToInstanceMap<Initable> initializersOnStop;
private final ClassToInstanceMap<Initable> initializersOnLoad;
private final ClassToInstanceMap<Initable> initializersOnStart;
private final ClassToInstanceMap<Initable> initializersOnStop;
@Getter private boolean loaded = false;
@Getter private boolean started = false;
@Getter private boolean stopped = false;
public InitManager() {
initializersOnLoad = new ImmutableClassToInstanceMap.Builder<Initable>()
@@ -20,6 +25,7 @@ public class InitManager {
.build();
initializersOnStart = new ImmutableClassToInstanceMap.Builder<Initable>()
.put(GrimExternalAPI.class, GrimAPI.INSTANCE.getExternalAPI())
.put(ExemptOnlinePlayers.class, new ExemptOnlinePlayers())
.put(EventManager.class, new EventManager())
.put(PacketManager.class, new PacketManager())
@@ -31,7 +37,6 @@ public class InitManager {
.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())
.build();
@@ -44,17 +49,20 @@ public class InitManager {
for (Initable initable : initializersOnLoad.values()) {
initable.start();
}
loaded = true;
}
public void start() {
for (Initable initable : initializersOnStart.values()) {
initable.start();
}
started = true;
}
public void stop() {
for (Initable initable : initializersOnStop.values()) {
initable.start();
}
stopped = true;
}
}
@@ -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,18 +18,18 @@ 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 = "*";
public PunishmentManager(GrimPlayer player) {
this.player = player;
reload();
reload(GrimAPI.INSTANCE.getConfigManager().getConfig());
}
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", "*");
@@ -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;
@@ -52,6 +53,7 @@ import net.kyori.adventure.text.TranslatableComponent;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.Nullable;
@@ -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,7 @@ public class GrimPlayer implements GrimUser {
public GrimPlayer(User user) {
this.user = user;
this.playerUUID = user.getUUID();
onReload();
reload(GrimAPI.INSTANCE.getConfigManager().getConfig());
boundingBox = GetBoundingBox.getBoundingBoxFromPosAndSizeRaw(x, y, z, 0.6f, 1.8f);
@@ -240,6 +244,21 @@ 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
}
}
public Set<VectorData> getPossibleVelocities() {
@@ -316,7 +335,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();
@@ -450,7 +470,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 +524,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 +564,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 +577,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 +741,21 @@ 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;
@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);
}
@Override
public void reload() {
reload(GrimAPI.INSTANCE.getConfigManager().getConfig());
}
}
@@ -364,7 +364,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())) {
@@ -16,9 +16,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;
@@ -149,9 +150,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.isPresent()) return;
// The client first desync's this attribute
property.get().getModifiers().removeIf(modifier -> modifier.getUUID().equals(CompensatedEntities.SNOW_MODIFIER_UUID) || modifier.getName().getKey().equals("powder_snow"));
playerSpeed.recalculate();
// And then re-adds it using purely what the server has sent it
@@ -165,7 +171,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();
}
}
@@ -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
@@ -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);
@@ -46,7 +46,7 @@ public class BlockPlace {
private static final BlockFace[] BY_2D = new BlockFace[]{BlockFace.SOUTH, BlockFace.WEST, BlockFace.NORTH, BlockFace.EAST};
static final BlockFace[] BY_3D = new BlockFace[]{BlockFace.DOWN, BlockFace.UP, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.WEST, BlockFace.EAST};
@Setter
Vector3i blockPosition;
public Vector3i blockPosition;
@Getter
InteractionHand hand;
@Getter
@@ -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,7 +161,7 @@ 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
@@ -210,13 +194,6 @@ public enum CollisionData {
}
}, 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 +258,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
@@ -445,33 +410,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 +505,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) {
@@ -597,9 +542,7 @@ public enum CollisionData {
}, 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 +620,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 +682,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,
@@ -824,71 +784,18 @@ public enum CollisionData {
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,27 +827,6 @@ 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:
@@ -975,20 +861,6 @@ public enum CollisionData {
}, 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)) {
@@ -1017,7 +889,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 +911,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 +960,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));
@@ -1136,30 +1007,34 @@ public enum CollisionData {
}
}, 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),
@@ -1176,8 +1051,6 @@ public enum CollisionData {
}
}, 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...
@@ -1249,7 +1122,7 @@ public enum CollisionData {
return 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))
@@ -1318,7 +1191,7 @@ public enum CollisionData {
// 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?
@@ -1,8 +1,9 @@
package ac.grim.grimac.utils.collisions;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicFence;
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicWall;
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicHitboxFence;
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicHitboxPane;
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicHitboxWall;
import ac.grim.grimac.utils.collisions.datatypes.*;
import ac.grim.grimac.utils.nmsutil.Materials;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
@@ -17,9 +18,510 @@ import java.util.*;
// Expansion to the CollisionData class, which is different than regular ray tracing hitboxes
public enum HitboxData {
RAILS((player, item, version, data, x, y, z) -> {
switch (data.getShape()) {
case ASCENDING_NORTH:
case ASCENDING_SOUTH:
case ASCENDING_EAST:
case ASCENDING_WEST:
if (version.isOlderThan(ClientVersion.V_1_8)) {
StateType railType = data.getType();
// Activator rails always appear as flat detector rails in 1.7.10 because of ViaVersion
// Ascending power rails in 1.7 have flat rail hitbox https://bugs.mojang.com/browse/MC-9134
if (railType == StateTypes.ACTIVATOR_RAIL || (railType == StateTypes.POWERED_RAIL && data.isPowered())) {
return new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F, false);
}
return new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.625F, 1.0F, false);
} else if (version.isOlderThan(ClientVersion.V_1_9)) {
return new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.625F, 1.0F, false);
} else if (version.isNewerThanOrEquals(ClientVersion.V_1_9) && version.isOlderThan(ClientVersion.V_1_10)) {
// https://bugs.mojang.com/browse/MC-89552 sloped rails in 1.9 - it is slightly taller than a regular rail
return new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.1875F, 1.0F, false);
} else if (version.isOlderThan(ClientVersion.V_1_11)) {
// https://bugs.mojang.com/browse/MC-102638 All sloped rails are full blocks in 1.10
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D);
default:
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D);
}
}, BlockTags.RAILS.getStates().toArray(new StateType[0])),
END_PORTAL((player, item, version, data, x, y, z) -> {
if (version.isOlderThan(ClientVersion.V_1_9)) {
return new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 0.0625D, 1.0D);
} else if (version.isOlderThan(ClientVersion.V_1_17)) {
return new SimpleCollisionBox(0.0, 0.0D, 0.0D, 1.0D, 0.75D, 1.0D);
}
return new HexCollisionBox(0.0D, 6.0D, 0.0D, 16.0D, 12.0D, 16.0D);
}, StateTypes.END_PORTAL),
FENCE_GATE((player, item, version, data, x, y, z) -> {
// This technically should be taken from the block data/made multi-version/run block updates... but that's too far even for me
// This way is so much easier and works unless the magic stick wand is used
boolean isInWall;
boolean isXAxis = data.getFacing() == BlockFace.WEST || data.getFacing() == BlockFace.EAST;
if (isXAxis) {
boolean zPosWall = Materials.isWall(player.compensatedWorld.getStateTypeAt(x, y, z + 1));
boolean zNegWall = Materials.isWall(player.compensatedWorld.getStateTypeAt(x, y, z - 1));
isInWall = zPosWall || zNegWall;
} else {
boolean xPosWall = Materials.isWall(player.compensatedWorld.getStateTypeAt(x + 1, y, z));
boolean xNegWall = Materials.isWall(player.compensatedWorld.getStateTypeAt(x - 1, y, z));
isInWall = xPosWall || xNegWall;
}
if (isInWall) {
return isXAxis ? new HexCollisionBox(6.0D, 0.0D, 0.0D, 10.0D, 13.0D, 16.0D) : new HexCollisionBox(0.0D, 0.0D, 6.0D, 16.0D, 13.0D, 10.0D);
}
return isXAxis ? new HexCollisionBox(6.0D, 0.0D, 0.0D, 10.0D, 16.0D, 16.0D) : new HexCollisionBox(0.0D, 0.0D, 6.0D, 16.0D, 16.0D, 10.0D);
}, BlockTags.FENCE_GATES.getStates().toArray(new StateType[0])),
FENCE(new DynamicHitboxFence(), BlockTags.FENCES.getStates().toArray(new StateType[0])),
PANE(new DynamicHitboxPane(), Materials.getPanes().toArray(new StateType[0])),
LEVER(((player, item, version, data, x, y, z) -> {
Face face = data.getFace();
BlockFace facing = data.getFacing();
if (version.isOlderThan(ClientVersion.V_1_13)) {
double f = 0.1875;
switch (face) {
case WALL:
switch (facing) {
case WEST:
return new SimpleCollisionBox(1.0 - f * 2.0, 0.2, 0.5 - f, 1.0, 0.8, 0.5 + f, false);
case EAST:
return new SimpleCollisionBox(0.0, 0.2, 0.5 - f, f * 2.0, 0.8, 0.5 + f, false);
case NORTH:
return new SimpleCollisionBox(0.5 - f, 0.2, 1.0 - f * 2.0, 0.5 + f, 0.8, 1.0, false);
case SOUTH:
return new SimpleCollisionBox(0.5 - f, 0.2, 0.0, 0.5 + f, 0.8, f * 2.0, false);
}
case CEILING:
return new SimpleCollisionBox(0.25, 0.4, 0.25, 0.75, 1.0, 0.75, false);
case FLOOR:
return new SimpleCollisionBox(0.25, 0.0, 0.25, 0.75, 0.6, 0.75, false);
}
}
switch (face) {
case FLOOR:
// X-AXIS
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
return new SimpleCollisionBox(0.25, 0.0, 0.3125, 0.75, 0.375, 0.6875, false);
}
// Z-AXIS
return new SimpleCollisionBox(0.3125, 0.0, 0.25, 0.6875, 0.375, 0.75, false);
case WALL:
switch (facing) {
case EAST:
return new SimpleCollisionBox(0.0, 0.25, 0.3125, 0.375, 0.75, 0.6875, false);
case WEST:
return new SimpleCollisionBox(0.625, 0.25, 0.3125, 1.0, 0.75, 0.6875, false);
case SOUTH:
return new SimpleCollisionBox(0.3125, 0.25, 0.0, 0.6875, 0.75, 0.375, false);
case NORTH:
default:
return new SimpleCollisionBox(0.3125, 0.25, 0.625, 0.6875, 0.75, 1.0, false);
}
case CEILING:
default:
// X-AXIS
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
return new SimpleCollisionBox(0.25, 0.625, 0.3125, 0.75, 1.0, 0.6875, false);
}
// Z-Axis
return new SimpleCollisionBox(0.3125, 0.625, 0.25, 0.6875, 1.0, 0.75, false);
}
}), StateTypes.LEVER),
BUTTON((player, item, version, data, x, y, z) -> {
final Face face = data.getFace();
final BlockFace facing = data.getFacing();
final boolean powered = data.isPowered();
if (version.isOlderThan(ClientVersion.V_1_13)) {
double f2 = (float) (data.isPowered() ? 1 : 2) / 16.0;
switch (face) {
case WALL:
switch (facing) {
case WEST:
return new SimpleCollisionBox(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 CEILING:
return new SimpleCollisionBox(0.3125, 1.0 - f2, 0.375, 0.6875, 1.0, 0.625, false);
case FLOOR:
return new SimpleCollisionBox(0.3125, 0.0, 0.375, 0.6875, 0.0 + f2, 0.625, false);
}
}
switch (face) {
case WALL:
CollisionBox shape;
switch (facing) {
case EAST:
shape = powered ? new HexCollisionBox(0.0, 6.0, 5.0, 1.0, 10.0, 11.0) : new HexCollisionBox(0.0, 6.0, 5.0, 2.0, 10.0, 11.0);
break;
case WEST:
shape = powered ? new HexCollisionBox(15.0, 6.0, 5.0, 16.0, 10.0, 11.0) : new HexCollisionBox(14.0, 6.0, 5.0, 16.0, 10.0, 11.0);
break;
case SOUTH:
shape = powered ? new HexCollisionBox(5.0, 6.0, 0.0, 11.0, 10.0, 1.0) : new HexCollisionBox(5.0, 6.0, 0.0, 11.0, 10.0, 2.0);
break;
case NORTH:
case UP:
case DOWN:
shape = powered ? new HexCollisionBox(5.0, 6.0, 15.0, 11.0, 10.0, 16.0) : new HexCollisionBox(5.0, 6.0, 14.0, 11.0, 10.0, 16.0);
break;
default:
shape = NoCollisionBox.INSTANCE;
}
return shape;
case CEILING:
// ViaVersion shows lever
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_8)) {
return LEVER.dynamic.fetch(player, item, version, data, x, y, z);
}
// x axis
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
return powered ? new HexCollisionBox(6.0, 15.0, 5.0, 10.0, 16.0, 11.0) : new HexCollisionBox(6.0, 14.0, 5.0, 10.0, 16.0, 11.0);
} else {
return powered ? new HexCollisionBox(5.0, 15.0, 6.0, 11.0, 16.0, 10.0) : new HexCollisionBox(5.0, 14.0, 6.0, 11.0, 16.0, 10.0);
}
case FLOOR:
// ViaVersion shows lever
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_8)) {
return LEVER.dynamic.fetch(player, item, version, data, x, y, z);
}
// x axis
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
return powered ? new HexCollisionBox(6.0, 0.0, 5.0, 10.0, 1.0, 11.0) : new HexCollisionBox(6.0, 0.0, 5.0, 10.0, 2.0, 11.0);
}
return powered ? new HexCollisionBox(5.0, 0.0, 6.0, 11.0, 1.0, 10.0) : new HexCollisionBox(5.0, 0.0, 6.0, 11.0, 2.0, 10.0);
default:
throw new IllegalStateException("Impossible Hitbox State");
}
}, BlockTags.BUTTONS.getStates().toArray(new StateType[0])),
WALL(new DynamicHitboxWall(), BlockTags.WALLS.getStates().toArray(new StateType[0])),
WALL_SIGN((player, item, version, data, x, y, z) -> {
switch (data.getFacing()) {
case NORTH:
return new HexCollisionBox(0.0, 4.5, 14.0, 16.0, 12.5, 16.0);
case SOUTH:
return new HexCollisionBox(0.0, 4.5, 0.0, 16.0, 12.5, 2.0);
case EAST:
return new HexCollisionBox(0.0, 4.5, 0.0, 2.0, 12.5, 16.0);
case WEST:
return new HexCollisionBox(14.0, 4.5, 0.0, 16.0, 12.5, 16.0);
default:
return NoCollisionBox.INSTANCE;
}
}, BlockTags.WALL_SIGNS.getStates().toArray(new StateType[0])),
WALL_HANGING_SIGN((player, item, version, data, x, y, z) -> {
switch (data.getFacing()) {
case NORTH:
case SOUTH:
return new ComplexCollisionBox(new HexCollisionBox(0.0D, 14.0D, 6.0D, 16.0D, 16.0D, 10.0D),
new HexCollisionBox(1.0D, 0.0D, 7.0D, 15.0D, 10.0D, 9.0D));
default:
return new ComplexCollisionBox(new HexCollisionBox(6.0D, 14.0D, 0.0D, 10.0D, 16.0D, 16.0D),
new HexCollisionBox(7.0D, 0.0D, 1.0D, 9.0D, 10.0D, 15.0D));
}
}, BlockTags.WALL_HANGING_SIGNS.getStates().toArray(new StateType[0])),
STANDING_SIGN((player, item, version, data, x, y, z) ->
new HexCollisionBox(4.0, 0.0, 4.0, 12.0, 16.0, 12.0),
BlockTags.STANDING_SIGNS.getStates().toArray(new StateType[0])),
SAPLING(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 12.0D, 14.0D),
BlockTags.SAPLINGS.getStates().toArray(new StateType[0])),
ROOTS(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 13.0D, 14.0D),
StateTypes.WARPED_ROOTS, StateTypes.CRIMSON_ROOTS),
BANNER(new HexCollisionBox(4.0D, 0.0D, 4.0D, 12.0D, 16.0D, 12.0D),
StateTypes.WHITE_BANNER, StateTypes.ORANGE_BANNER, StateTypes.MAGENTA_BANNER, StateTypes.LIGHT_BLUE_BANNER,
StateTypes.YELLOW_BANNER, StateTypes.LIME_BANNER, StateTypes.PINK_BANNER, StateTypes.GRAY_BANNER,
StateTypes.LIGHT_GRAY_BANNER, StateTypes.CYAN_BANNER, StateTypes.PURPLE_BANNER, StateTypes.BLUE_BANNER,
StateTypes.BROWN_BANNER, StateTypes.GREEN_BANNER, StateTypes.RED_BANNER, StateTypes.BLACK_BANNER),
WALL_BANNER((player, item, version, data, x, y, z) -> {
// ViaVersion replacement block
if (version.isOlderThan(ClientVersion.V_1_8)) {
return WALL_SIGN.dynamic.fetch(player, item, version, data, x, y, z);
}
switch (data.getFacing()) {
case NORTH:
return new HexCollisionBox(0.0, 0.0, 14.0, 16.0, 12.5, 16.0);
case EAST:
return new HexCollisionBox(0.0, 0.0, 0.0, 2.0, 12.5, 16.0);
case WEST:
return new HexCollisionBox(14.0, 0.0, 0.0, 16.0, 12.5, 16.0);
case SOUTH:
return new HexCollisionBox(0.0, 0.0, 0.0, 16.0, 12.5, 2.0);
default:
throw new IllegalStateException("Impossible Banner Facing State; Something very wrong is going on");
}
}, StateTypes.WHITE_WALL_BANNER, StateTypes.ORANGE_WALL_BANNER, StateTypes.MAGENTA_WALL_BANNER,
StateTypes.LIGHT_BLUE_WALL_BANNER, StateTypes.YELLOW_WALL_BANNER, StateTypes.LIME_WALL_BANNER,
StateTypes.PINK_WALL_BANNER, StateTypes.GRAY_WALL_BANNER, StateTypes.LIGHT_GRAY_WALL_BANNER,
StateTypes.CYAN_WALL_BANNER, StateTypes.PURPLE_WALL_BANNER, StateTypes.BLUE_WALL_BANNER,
StateTypes.BROWN_WALL_BANNER, StateTypes.GREEN_WALL_BANNER, StateTypes.RED_WALL_BANNER, StateTypes.BLACK_WALL_BANNER),
BREWING_STAND((player, item, version, block, x, y, z) -> {
// BEWARE OF https://bugs.mojang.com/browse/MC-85109 FOR 1.8 PLAYERS
if (version.isOlderThan(ClientVersion.V_1_13)) {
return new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F);
} else {
return new ComplexCollisionBox(
new HexCollisionBox(1.0, 0.0, 1.0, 15.0, 2.0, 15.0),
new SimpleCollisionBox(0.4375, 0.0, 0.4375, 0.5625, 0.875, 0.5625, false));
}
}, StateTypes.BREWING_STAND),
SMALL_FLOWER((player, item, version, data, x, y, z) -> player.getClientVersion().isOlderThan(ClientVersion.V_1_13)
? new SimpleCollisionBox(0.3125D, 0.0D, 0.3125D, 0.6875D, 0.625D, 0.6875D)
: new OffsetCollisionBox(data.getType(), 0.3125D, 0.0D, 0.3125D, 0.6875D, 0.625D, 0.6875D),
BlockTags.SMALL_FLOWERS.getStates().toArray(new StateType[0])),
TALL_FLOWERS(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), BlockTags.TALL_FLOWERS.getStates().toArray(new StateType[0])),
FIRE((player, item, version, data, x, y, z) -> {
// Since 1.16 fire has a small hitbox
if (version.isNewerThanOrEquals(ClientVersion.V_1_16)) {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 1.0D, 16.0D);
}
return NoCollisionBox.INSTANCE;
}, BlockTags.FIRE.getStates().toArray(new StateType[0])),
HONEY_BLOCK(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.HONEY_BLOCK),
POWDER_SNOW(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.POWDER_SNOW),
SOUL_SAND(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.SOUL_SAND),
CACTUS((player, item, version, data, x, y, z) -> {
if (version.isOlderThan(ClientVersion.V_1_13)) {
// https://bugs.mojang.com/browse/MC-59610
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}
return new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D);
}, StateTypes.CACTUS),
SNOW((player, item, version, data, x, y, z) -> {
return new SimpleCollisionBox(0, 0, 0, 1, data.getLayers() * 0.125, 1);
}, StateTypes.SNOW),
LECTERN_BLOCK((player, item, version, data, x, y, z) -> {
ComplexCollisionBox common = new ComplexCollisionBox(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D),
new HexCollisionBox(4.0D, 2.0D, 4.0D, 12.0D, 14.0D, 12.0D));
if (data.getFacing() == BlockFace.WEST) {
common.add(new HexCollisionBox(1.0D, 10.0D, 0.0D, 5.333333D, 14.0D, 16.0D));
common.add(new HexCollisionBox(5.333333D, 12.0D, 0.0D, 9.666667D, 16.0D, 16.0D));
common.add(new HexCollisionBox(9.666667D, 14.0D, 0.0D, 14.0D, 18.0D, 16.0D));
} else if (data.getFacing() == BlockFace.NORTH) {
common.add(new HexCollisionBox(0.0D, 10.0D, 1.0D, 16.0D, 14.0D, 5.333333D));
common.add(new HexCollisionBox(0.0D, 12.0D, 5.333333D, 16.0D, 16.0D, 9.666667D));
common.add(new HexCollisionBox(0.0D, 14.0D, 9.666667D, 16.0D, 18.0D, 14.0D));
} else if (data.getFacing() == BlockFace.EAST) {
common.add(new HexCollisionBox(10.666667D, 10.0D, 0.0D, 15.0D, 14.0D, 16.0D));
common.add(new HexCollisionBox(6.333333D, 12.0D, 0.0D, 10.666667D, 16.0D, 16.0D));
common.add(new HexCollisionBox(2.0D, 14.0D, 0.0D, 6.333333D, 18.0D, 16.0D));
} else { // SOUTH
common.add(new HexCollisionBox(0.0D, 10.0D, 10.666667D, 16.0D, 14.0D, 15.0D));
common.add(new HexCollisionBox(0.0D, 12.0D, 6.333333D, 16.0D, 16.0D, 10.666667D));
common.add(new HexCollisionBox(0.0D, 14.0D, 2.0D, 16.0D, 18.0D, 6.333333D));
}
return common;
}, StateTypes.LECTERN),
GLOW_LICHEN_SCULK_VEIN((player, item, version, data, x, y, z) -> {
if (version.isNewerThan(ClientVersion.V_1_16_4)) {
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;
} else { // ViaVersion just replaces this with... nothing
return NoCollisionBox.INSTANCE;
}
}, StateTypes.GLOW_LICHEN, StateTypes.SCULK_CATALYST),
SPORE_BLOSSOM((player, item, version, data, x, y, z) -> {
if (version.isNewerThan(ClientVersion.V_1_16_4)) {
return new HexCollisionBox(2.0D, 13.0D, 2.0D, 14.0D, 16.0D, 14.0D);
} else { // ViaVersion replacement is a Peony
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}
}, StateTypes.SPORE_BLOSSOM),
PITCHER_CROP((player, item, version, data, x, y, z) -> {
if (version.isNewerThan(ClientVersion.V_1_19_4)) {
final SimpleCollisionBox FULL_UPPER_SHAPE = new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 15.0D, 13.0D);
final SimpleCollisionBox FULL_LOWER_SHAPE = new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 16.0D, 13.0D);
final SimpleCollisionBox COLLISION_SHAPE_BULB = new HexCollisionBox(5.0D, -1.0D, 5.0D, 11.0D, 3.0D, 11.0D);
final SimpleCollisionBox COLLISION_SHAPE_CROP = new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 5.0D, 13.0D);
final SimpleCollisionBox[] UPPER_SHAPE_BY_AGE = new SimpleCollisionBox[]{new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 11.0D, 13.0D), FULL_UPPER_SHAPE};
final SimpleCollisionBox[] LOWER_SHAPE_BY_AGE = new SimpleCollisionBox[]{COLLISION_SHAPE_BULB, new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 14.0D, 13.0D), FULL_LOWER_SHAPE, FULL_LOWER_SHAPE, FULL_LOWER_SHAPE};
return data.getHalf() == Half.UPPER ? UPPER_SHAPE_BY_AGE[Math.min(Math.abs(4 - (data.getAge() + 1)), UPPER_SHAPE_BY_AGE.length - 1)] : LOWER_SHAPE_BY_AGE[data.getAge()];
} else {
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}
}, StateTypes.PITCHER_CROP),
WHEAT_BEETROOTS((player, item, version, data, x, y, z) -> {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, (data.getAge() + 1) * 2, 16.0D);
}, StateTypes.WHEAT, StateTypes.BEETROOTS),
CARROT_POTATOES((player, item, version, data, x, y, z) -> {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, data.getAge() + 2, 16.0D);
}, StateTypes.CARROTS, StateTypes.POTATOES),
NETHER_WART((player, item, version, data, x, y, z) -> {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0, 5 + (data.getAge() * 3), 16.0D);
}, StateTypes.NETHER_WART),
ATTACHED_PUMPKIN_STEM((player, item, version, data, x, y, z) -> {
if (version.isOlderThan(ClientVersion.V_1_13))
return new HexCollisionBox(7.0D, 0.0D, 7.0D, 9.0D, 16.0D, 9.0D);
switch (data.getFacing()) {
case SOUTH:
return new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 10.0D, 16.0D);
case WEST:
return new HexCollisionBox(0.0D, 0.0D, 6.0D, 10.0D, 10.0D, 10.0D);
case NORTH:
return new HexCollisionBox(6.0D, 0.0D, 0.0D, 10.0D, 10.0D, 10.0D);
case EAST:
default:
return new HexCollisionBox(6.0D, 0.0D, 6.0D, 16.0D, 10.0D, 10.0D);
}
}, StateTypes.ATTACHED_MELON_STEM, StateTypes.ATTACHED_PUMPKIN_STEM),
PUMPKIN_STEM((player, item, version, data, x, y, z) -> {
return new HexCollisionBox(7, 0, 7, 9, 2 * (data.getAge() + 1), 9);
}, StateTypes.PUMPKIN_STEM, StateTypes.MELON_STEM),
// Hitbox/Outline is Same as Collision
COCOA_BEANS((player, item, version, data, x, y, z) -> {
return CollisionData.getCocoa(version, data.getAge(), data.getFacing());
}, StateTypes.COCOA),
// Easier to just use no collision box
// Redstone wire is very complex with its collision shapes and has many de-syncs
REDSTONE_WIRE(NoCollisionBox.INSTANCE, StateTypes.REDSTONE_WIRE),
SWEET_BERRY((player, item, version, data, x, y, z) -> {
if (data.getAge() == 0) {
return new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 8.0D, 13.0D);
} else if (data.getAge() < 3) {
return new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D);
}
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}, StateTypes.SWEET_BERRY_BUSH),
CORAL_FAN(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 4.0D, 14.0D), BlockTags.CORALS.getStates().toArray(new StateType[0])),
TORCHFLOWER_CROP((player, item, version, data, x, y, z) -> {
if (data.getAge() == 0) {
return new HexCollisionBox(5.0D, 0.0D, 5.0D, 11.0D, 6.0D, 11.0D);
}
// age is 1
return new HexCollisionBox(5.0D, 0.0D, 5.0D, 11.0D, 10.0D, 11.0D);
}, StateTypes.TORCHFLOWER_CROP),
DEAD_BUSH(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 13.0D, 14.0D), StateTypes.DEAD_BUSH),
SUGARCANE(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 16.0D, 14.0D), StateTypes.SUGAR_CANE),
NETHER_SPROUTS(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 3.0D, 14.0D), StateTypes.NETHER_SPROUTS),
HANGING_ROOTS(new HexCollisionBox(2.0D, 10.0D, 2.0D, 14.0D, 16.0D, 14.0D), StateTypes.HANGING_ROOTS),
GRASS_FERN((player, item, version, data, x ,y, z) -> {
if (version.isOlderThan(ClientVersion.V_1_13)) {
return new SimpleCollisionBox(0.1F, 0.0F, 0.1F, 0.9F, 0.8F, 0.9F);
}
return new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 13.0D, 14.0D);
}, StateTypes.SHORT_GRASS, StateTypes.FERN),
SEA_GRASS(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 12.0D, 14.0D),
StateTypes.SEAGRASS),
TALL_SEAGRASS(new HexCollisionBox(2.0, 0.0, 2.0, 14.0, 16.0, 14.0),
StateTypes.TALL_SEAGRASS),
SMALL_DRIPLEAF(new HexCollisionBox(2.0, 0.0, 2.0, 14.0, 13.0, 14.0), StateTypes.SMALL_DRIPLEAF),
CAVE_VINES(new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D), StateTypes.CAVE_VINES, StateTypes.CAVE_VINES_PLANT),
// Then your enum entries become:
TWISTING_VINES_BLOCK((player, item, version, data, x, y, z) ->
getVineCollisionBox(version, false, true), StateTypes.TWISTING_VINES),
WEEPING_VINES_BLOCK((player, item, version, data, x, y, z) ->
getVineCollisionBox(version, true, true), StateTypes.WEEPING_VINES),
TWISTING_VINES((player, item, version, data, x, y, z) ->
getVineCollisionBox(version, false, false), StateTypes.TWISTING_VINES_PLANT),
WEEPING_VINES((player, item, version, data, x, y, z) ->
getVineCollisionBox(version, true, false), StateTypes.WEEPING_VINES_PLANT),
TALL_PLANT(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.TALL_GRASS, StateTypes.LARGE_FERN),
BAMBOO((player, item, version, data, x, y, z) -> data.getLeaves() == Leaves.LARGE
? new HexOffsetCollisionBox(data.getType(), 3.0, 0.0, 3.0, 13.0, 16.0, 13.0)
: new HexOffsetCollisionBox(data.getType(), 5.0, 0.0, 5.0, 11.0, 16.0, 11.0), StateTypes.BAMBOO),
BAMBOO_SAPLING((player, item, version, data, x, y, z) -> {
return new HexOffsetCollisionBox(data.getType(), 4.0D, 0.0D, 4.0D, 12.0D, 12.0D, 12.0D);
}, StateTypes.BAMBOO_SAPLING),
SCAFFOLDING((player, item, version, data, x, y, z) -> {
// If is holding scaffolding
if (item == StateTypes.SCAFFOLDING) {
// If is holding scaffolding or Via replacement - hay bale
if (item == StateTypes.SCAFFOLDING || version.isOlderThan(ClientVersion.V_1_14)) {
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}
@@ -67,222 +569,54 @@ public enum HitboxData {
}, StateTypes.BIG_DRIPLEAF),
FENCE_GATE((player, item, version, data, x, y, z) -> {
// This technically should be taken from the block data/made multi-version/run block updates... but that's too far even for me
// This way is so much easier and works unless the magic stick wand is used
boolean isInWall;
boolean isXAxis = data.getFacing() == BlockFace.WEST || data.getFacing() == BlockFace.EAST;
if (isXAxis) {
boolean zPosWall = Materials.isWall(player.compensatedWorld.getStateTypeAt(x, y, z + 1));
boolean zNegWall = Materials.isWall(player.compensatedWorld.getStateTypeAt(x, y, z - 1));
isInWall = zPosWall || zNegWall;
PINK_PETALS_BLOCK((player, item, version, data, x, y, z) -> {
if (version.isNewerThan(ClientVersion.V_1_20_2)) {
int flowerAmount = data.getFlowerAmount();
int horizontalIndex = getHorizontalID(data.getFacing());
CollisionBox result = NoCollisionBox.INSTANCE;
// Pre-defined collision boxes for each quadrant
HexCollisionBox[] boxes = new HexCollisionBox[] {
new HexCollisionBox(8, 0, 8, 16, 3, 16), // SE
new HexCollisionBox(8, 0, 0, 16, 3, 8), // NE
new HexCollisionBox(0, 0, 0, 8, 3, 8), // NW
new HexCollisionBox(0, 0, 8, 8, 3, 16) // SW
};
// Add boxes based on flower amount and facing
for (int i = 0; i < flowerAmount; i++) {
int index = Math.floorMod(i - horizontalIndex, 4);
result = result.union(boxes[index]);
}
return result;
} else if (version.isNewerThan(ClientVersion.V_1_19_3)) {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 3.0D, 16.0D);
} else if (version.isNewerThan(ClientVersion.V_1_12_2)) {
return CORAL_FAN.dynamic.fetch(player, item, version, data, x, y, z);
}
return GRASS_FERN.dynamic.fetch(player, item, version, data, x, y, z);
}, StateTypes.PINK_PETALS),
MANGROVE_PROPAGULE(((player, item, version, data, x, y, z) -> {
if (data.isHanging()) {
return new HexOffsetCollisionBox(data.getType(), 7.0, 0.0, 7.0, 9.0, 16.0, 9.0);
} else {
boolean xPosWall = Materials.isWall(player.compensatedWorld.getStateTypeAt(x + 1, y, z));
boolean xNegWall = Materials.isWall(player.compensatedWorld.getStateTypeAt(x - 1, y, z));
isInWall = xPosWall || xNegWall;
return new HexOffsetCollisionBox(data.getType(), 7.0, getPropaguleMinHeight(data.getAge()), 7.0, 9.0, 16.0, 9.0);
}
}), StateTypes.MANGROVE_PROPAGULE),
if (isInWall) {
return isXAxis ? new HexCollisionBox(6.0D, 0.0D, 0.0D, 10.0D, 13.0D, 16.0D) : new HexCollisionBox(0.0D, 0.0D, 6.0D, 16.0D, 13.0D, 10.0D);
FROGSPAWN((player, item, version, data, x, y, z) -> {
if (version.isNewerThan(ClientVersion.V_1_18_2)) {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 1.5D, 16.0D);
} else { // ViaVersion just replaces this with... nothing
return NoCollisionBox.INSTANCE;
}
}, StateTypes.FROGSPAWN),
return isXAxis ? new HexCollisionBox(6.0D, 0.0D, 0.0D, 10.0D, 16.0D, 16.0D) : new HexCollisionBox(0.0D, 0.0D, 6.0D, 16.0D, 16.0D, 10.0D);
}, BlockTags.FENCE_GATES.getStates().toArray(new StateType[0])),
FENCE((player, item, version, data, x, y, z) -> {
WrappedBlockState state = player.compensatedWorld.getWrappedBlockStateAt(x, y, z);
if (version.isOlderThanOrEquals(ClientVersion.V_1_12_2)) {
int i = 0;
if (data.getSouth() == South.TRUE) {
i |= 0b1;
}
if (data.getWest() == West.TRUE) {
i |= 0b10;
}
if (data.getNorth() == North.TRUE) {
i |= 0b100;
}
if (data.getEast() == East.TRUE) {
i |= 0b1000;
}
return DynamicFence.LEGACY_BOUNDING_BOXES[i].copy();
}
List<SimpleCollisionBox> boxes = new ArrayList<>();
CollisionData.getData(state.getType()).getMovementCollisionBox(player, version, state, x, y, z).downCast(boxes);
for (SimpleCollisionBox box : boxes) {
box.maxY = 1;
}
return new ComplexCollisionBox(boxes.toArray(new SimpleCollisionBox[0]));
}, BlockTags.FENCES.getStates().toArray(new StateType[0])),
WALL((player, item, version, data, x, y, z) -> {
WrappedBlockState state = player.compensatedWorld.getWrappedBlockStateAt(x, y, z);
return new DynamicWall().fetchRegularBox(player, state, version, x, y, z);
}, BlockTags.WALLS.getStates().toArray(new StateType[0])),
HONEY_BLOCK(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.HONEY_BLOCK),
POWDER_SNOW(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.POWDER_SNOW),
SOUL_SAND(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.SOUL_SAND),
CACTUS(new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D), StateTypes.CACTUS),
SNOW((player, item, version, data, x, y, z) -> {
return new SimpleCollisionBox(0, 0, 0, 1, data.getLayers() * 0.125, 1);
}, StateTypes.SNOW),
LECTERN_BLOCK((player, item, version, data, x, y, z) -> {
ComplexCollisionBox common = new ComplexCollisionBox(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D),
new HexCollisionBox(4.0D, 2.0D, 4.0D, 12.0D, 14.0D, 12.0D));
if (data.getFacing() == BlockFace.WEST) {
common.add(new HexCollisionBox(1.0D, 10.0D, 0.0D, 5.333333D, 14.0D, 16.0D));
common.add(new HexCollisionBox(5.333333D, 12.0D, 0.0D, 9.666667D, 16.0D, 16.0D));
common.add(new HexCollisionBox(9.666667D, 14.0D, 0.0D, 14.0D, 18.0D, 16.0D));
} else if (data.getFacing() == BlockFace.NORTH) {
common.add(new HexCollisionBox(0.0D, 10.0D, 1.0D, 16.0D, 14.0D, 5.333333D));
common.add(new HexCollisionBox(0.0D, 12.0D, 5.333333D, 16.0D, 16.0D, 9.666667D));
common.add(new HexCollisionBox(0.0D, 14.0D, 9.666667D, 16.0D, 18.0D, 14.0D));
} else if (data.getFacing() == BlockFace.EAST) {
common.add(new HexCollisionBox(10.666667D, 10.0D, 0.0D, 15.0D, 14.0D, 16.0D));
common.add(new HexCollisionBox(6.333333D, 12.0D, 0.0D, 10.666667D, 16.0D, 16.0D));
common.add(new HexCollisionBox(2.0D, 14.0D, 0.0D, 6.333333D, 18.0D, 16.0D));
} else { // SOUTH
common.add(new HexCollisionBox(0.0D, 10.0D, 10.666667D, 16.0D, 14.0D, 15.0D));
common.add(new HexCollisionBox(0.0D, 12.0D, 6.333333D, 16.0D, 16.0D, 10.666667D));
common.add(new HexCollisionBox(0.0D, 14.0D, 2.0D, 16.0D, 18.0D, 6.333333D));
}
return common;
}, StateTypes.LECTERN),
WALL_HANGING_SIGNS((player, item, version, data, x, y, z) -> {
switch (data.getFacing()) {
case NORTH:
case SOUTH:
return new ComplexCollisionBox(new HexCollisionBox(0.0D, 14.0D, 6.0D, 16.0D, 16.0D, 10.0D),
new HexCollisionBox(1.0D, 0.0D, 7.0D, 15.0D, 10.0D, 9.0D));
case WEST:
case EAST:
return new ComplexCollisionBox(new HexCollisionBox(6.0D, 14.0D, 0.0D, 10.0D, 16.0D, 16.0D),
new HexCollisionBox(7.0D, 0.0D, 1.0D, 9.0D, 10.0D, 15.0D));
default:
return NoCollisionBox.INSTANCE;
}
}, BlockTags.WALL_HANGING_SIGNS.getStates().toArray(new StateType[0])),
PITCHER_CROP((player, item, version, data, x, y, z) -> {
final SimpleCollisionBox FULL_UPPER_SHAPE = new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 15.0D, 13.0D);
final SimpleCollisionBox FULL_LOWER_SHAPE = new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 16.0D, 13.0D);
final SimpleCollisionBox COLLISION_SHAPE_BULB = new HexCollisionBox(5.0D, -1.0D, 5.0D, 11.0D, 3.0D, 11.0D);
final SimpleCollisionBox COLLISION_SHAPE_CROP = new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 5.0D, 13.0D);
final SimpleCollisionBox[] UPPER_SHAPE_BY_AGE = new SimpleCollisionBox[]{new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 11.0D, 13.0D), FULL_UPPER_SHAPE};
final SimpleCollisionBox[] LOWER_SHAPE_BY_AGE = new SimpleCollisionBox[]{COLLISION_SHAPE_BULB, new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 14.0D, 13.0D), FULL_LOWER_SHAPE, FULL_LOWER_SHAPE, FULL_LOWER_SHAPE};
return data.getHalf() == Half.UPPER ? UPPER_SHAPE_BY_AGE[Math.min(Math.abs(4 - (data.getAge() + 1)), UPPER_SHAPE_BY_AGE.length - 1)] : LOWER_SHAPE_BY_AGE[data.getAge()];
}, StateTypes.PITCHER_CROP),
BUTTON((player, item, version, data, x, y, z) -> {
final BlockFace facing = data.getFacing();
final boolean powered = data.isPowered();
switch (data.getFace()) {
case FLOOR:
// x axis
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
return powered ? new HexCollisionBox(6.0, 0.0, 5.0, 10.0, 1.0, 11.0) : new HexCollisionBox(6.0, 0.0, 5.0, 10.0, 2.0, 11.0);
}
return powered ? new HexCollisionBox(5.0, 0.0, 6.0, 11.0, 1.0, 10.0) : new HexCollisionBox(5.0, 0.0, 6.0, 11.0, 2.0, 10.0);
case WALL:
CollisionBox shape;
switch (facing) {
case EAST:
shape = powered ? new HexCollisionBox(0.0, 6.0, 5.0, 1.0, 10.0, 11.0) : new HexCollisionBox(0.0, 6.0, 5.0, 2.0, 10.0, 11.0);
break;
case WEST:
shape = powered ? new HexCollisionBox(15.0, 6.0, 5.0, 16.0, 10.0, 11.0) : new HexCollisionBox(14.0, 6.0, 5.0, 16.0, 10.0, 11.0);
break;
case SOUTH:
shape = powered ? new HexCollisionBox(5.0, 6.0, 0.0, 11.0, 10.0, 1.0) : new HexCollisionBox(5.0, 6.0, 0.0, 11.0, 10.0, 2.0);
break;
case NORTH:
case UP:
case DOWN:
shape = powered ? new HexCollisionBox(5.0, 6.0, 15.0, 11.0, 10.0, 16.0) : new HexCollisionBox(5.0, 6.0, 14.0, 11.0, 10.0, 16.0);
break;
default:
shape = NoCollisionBox.INSTANCE;
}
return shape;
case CEILING:
default:
// x axis
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
return powered ? new HexCollisionBox(6.0, 15.0, 5.0, 10.0, 16.0, 11.0) : new HexCollisionBox(6.0, 14.0, 5.0, 10.0, 16.0, 11.0);
} else {
return powered ? new HexCollisionBox(5.0, 15.0, 6.0, 11.0, 16.0, 10.0) : new HexCollisionBox(5.0, 14.0, 6.0, 11.0, 16.0, 10.0);
}
}
}, BlockTags.BUTTONS.getStates().toArray(new StateType[0])),
WALL_SIGN((player, item, version, data, x, y, z) -> {
switch (data.getFacing()) {
case NORTH:
return new HexCollisionBox(0.0, 4.5, 14.0, 16.0, 12.5, 16.0);
case SOUTH:
return new HexCollisionBox(0.0, 4.5, 0.0, 16.0, 12.5, 2.0);
case EAST:
return new HexCollisionBox(0.0, 4.5, 0.0, 2.0, 12.5, 16.0);
case WEST:
return new HexCollisionBox(14.0, 4.5, 0.0, 16.0, 12.5, 16.0);
default:
return NoCollisionBox.INSTANCE;
}
}, BlockTags.WALL_SIGNS.getStates().toArray(new StateType[0])),
WALL_HANGING_SIGN((player, item, version, data, x, y, z) -> {
switch (data.getFacing()) {
case NORTH:
case SOUTH:
return new ComplexCollisionBox(new HexCollisionBox(0.0D, 14.0D, 6.0D, 16.0D, 16.0D, 10.0D),
new HexCollisionBox(1.0D, 0.0D, 7.0D, 15.0D, 10.0D, 9.0D));
default:
return new ComplexCollisionBox(new HexCollisionBox(6.0D, 14.0D, 0.0D, 10.0D, 16.0D, 16.0D),
new HexCollisionBox(7.0D, 0.0D, 1.0D, 9.0D, 10.0D, 15.0D));
}
}, BlockTags.WALL_HANGING_SIGNS.getStates().toArray(new StateType[0])),
STANDING_SIGN((player, item, version, data, x, y, z) ->
new HexCollisionBox(4.0, 0.0, 4.0, 12.0, 16.0, 12.0),
BlockTags.STANDING_SIGNS.getStates().toArray(new StateType[0])),
REDSTONE_WIRE((player, item, version, data, x, y, z) ->
// Easier to just use no collision box
// Redstone wire is very complex with its collision shapes and has many de-syncs
NoCollisionBox.INSTANCE,
StateTypes.REDSTONE_WIRE),
FIRE((player, item, version, data, x, y, z) -> {
// Since 1.16 fire has a small hitbox
if (version.isNewerThanOrEquals(ClientVersion.V_1_16)) {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 1.0D, 16.0D);
}
return NoCollisionBox.INSTANCE;
}, BlockTags.FIRE.getStates().toArray(new StateType[0])),
BANNER(((player, item, version, data, x, y, z) ->
new SimpleCollisionBox(4.0, 0.0, 4.0, 12.0, 16.0, 12.0)),
BlockTags.BANNERS.getStates().toArray(new StateType[0]));
// always a fullblock hitbox. Via replacement is obsidian
SCULK_SHRIKER(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.SCULK_SHRIEKER);
private static final Map<StateType, HitboxData> lookup = new HashMap<>();
@@ -329,6 +663,59 @@ public enum HitboxData {
return data.box.copy().offset(x, y, z);
// Allow this class to override collision boxes when they aren't the same as regular boxes
return HitboxData.getData(block.getType()).dynamic.fetch(player, heldItem, version, block, x, y, z).offset(x, y, z);
HitBoxFactory hitBoxFactory = data.dynamic;
CollisionBox collisionBox = hitBoxFactory.fetch(player, heldItem, version, block, x, y, z);
collisionBox.offset(x, y, z);
return collisionBox;
}
private static int getPropaguleMinHeight(int age) {
switch (age) {
case 0:
case 1:
case 2:
return 13 - age * 3;
case 3:
case 4:
return (4 - age) * 3;
}
throw new IllegalStateException("Impossible Propagule Height");
}
private static CollisionBox getVineCollisionBox(ClientVersion version, boolean isWeeping, boolean isBlock) {
if (version.isNewerThan(ClientVersion.V_1_15_2)) {
if (isWeeping) {
return isBlock
? new HexCollisionBox(4.0, 9.0, 4.0, 12.0, 16.0, 12.0)
: new HexCollisionBox(1.0, 0.0, 1.0, 15.0, 16.0, 15.0);
} else {
return new HexCollisionBox(4.0D, 0.0D, 4.0D, 12.0D, isBlock ? 15.0D : 16.0D, 12.0D);
}
} else {
// Via replacement - 4 sided vine
return new ComplexCollisionBox(
new HexCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 16.0D, 16.0D),
new HexCollisionBox(15.0D, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D),
new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, 1.0D),
new HexCollisionBox(0.0D, 0.0D, 15.0D, 16.0D, 16.0D, 16.0D)
);
}
}
private static int getHorizontalID(BlockFace facing) {
switch (facing) {
case DOWN:
case UP:
return -1;
case NORTH:
return 2;
case SOUTH:
return 0;
case WEST:
return 1;
case EAST:
return 3;
}
throw new IllegalStateException("Impossible blockface for getHorizontalID");
}
}
@@ -0,0 +1,98 @@
package ac.grim.grimac.utils.collisions;
/*
*/
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.datatypes.*;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import org.jetbrains.annotations.Nullable;
import java.util.*;
// Expansion to the CollisionData class, which is different than regular ray tracing hitboxes
public enum RaycastData {
HOPPER((player, item, version, data, x, y, z) -> {
CollisionBox insideShape = new HexCollisionBox(2.0, 11.0, 2.0, 14.0, 16.0, 14.0);
switch (data.getFacing()) {
case NORTH:
new ComplexCollisionBox(insideShape, new HexCollisionBox(6.0, 8.0, 0.0, 10.0, 10.0, 4.0));
case SOUTH:
new ComplexCollisionBox(insideShape, new HexCollisionBox(6.0, 8.0, 12.0, 10.0, 10.0, 16.0));
case WEST:
new ComplexCollisionBox(insideShape, new HexCollisionBox(0.0, 8.0, 6.0, 4.0, 10.0, 10.0));
case EAST:
new ComplexCollisionBox(insideShape, new HexCollisionBox(12.0, 8.0, 6.0, 16.0, 10.0, 10.0));
default:
return insideShape;
}
}, StateTypes.HOPPER),
CAULDRON((player, item, version, data, x, y, z) -> {
return new HexCollisionBox(2.0, 4.0, 2.0, 14.0, 16.0, 14.0);
}, BlockTags.CAULDRONS.getStates().toArray(new StateType[0])),
FULL_BLOCK((player, item, version, data, x, y, z) -> {
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}, StateTypes.COMPOSTER, StateTypes.SCAFFOLDING);
private static final Map<StateType, RaycastData> lookup = new HashMap<>();
static {
for (RaycastData data : RaycastData.values()) {
for (StateType type : data.materials) {
lookup.put(type, data);
}
}
}
private final StateType[] materials;
private CollisionBox box;
private HitBoxFactory dynamic;
RaycastData(CollisionBox box, StateType... materials) {
this.box = box;
Set<StateType> mList = new HashSet<>(Arrays.asList(materials));
mList.remove(null); // Sets can contain one null
this.materials = mList.toArray(new StateType[0]);
}
RaycastData(HitBoxFactory dynamic, StateType... materials) {
this.dynamic = dynamic;
Set<StateType> mList = new HashSet<>(Arrays.asList(materials));
mList.remove(null); // Sets can contain one null
this.materials = mList.toArray(new StateType[0]);
}
public static RaycastData getData(StateType material) {
return lookup.get(material);
}
@Nullable
public static CollisionBox getBlockHitbox(GrimPlayer player, StateType heldItem, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
RaycastData data = getData(block.getType());
if (data == null) {
// We explicitly do not want to fallback on HitBoxData or CollisionBox
// Because we only ever use this as a 2nd ray trace
return NoCollisionBox.INSTANCE;
}
// Simple collision box to override
if (data.box != null)
return data.box.copy().offset(x, y, z);
// Allow this class to override collision boxes when they aren't the same as regular boxes
HitBoxFactory hitBoxFactory = data.dynamic;
CollisionBox collisionBox = hitBoxFactory.fetch(player, heldItem, version, block, x, y, z);
collisionBox.offset(x, y, z);
return collisionBox;
}
}
@@ -18,11 +18,9 @@ import com.github.retrooper.packetevents.protocol.world.states.enums.West;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
public class DynamicFence extends DynamicConnecting implements CollisionFactory {
public class DynamicCollisionFence extends DynamicConnecting implements CollisionFactory {
private static final CollisionBox[] COLLISION_BOXES = makeShapes(2.0F, 2.0F, 24.0F, 0.0F, 24.0F, true);
public static SimpleCollisionBox[] LEGACY_BOUNDING_BOXES = new SimpleCollisionBox[] {new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D)};
@Override
public CollisionBox fetch(GrimPlayer player, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
boolean east;
@@ -19,7 +19,7 @@ import com.github.retrooper.packetevents.protocol.world.states.enums.West;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
public class DynamicPane extends DynamicConnecting implements CollisionFactory {
public class DynamicCollisionPane extends DynamicConnecting implements CollisionFactory {
private static final CollisionBox[] COLLISION_BOXES = makeShapes(1.0F, 1.0F, 16.0F, 0.0F, 16.0F, true);
@@ -84,7 +84,7 @@ public class DynamicPane extends DynamicConnecting implements CollisionFactory {
@Override
public boolean checkCanConnect(GrimPlayer player, WrappedBlockState state, StateType one, StateType two, BlockFace direction) {
if (BlockTags.GLASS_PANES.contains(one) || one == StateTypes.IRON_BARS)
if (BlockTags.GLASS_PANES.contains(one) || one == StateTypes.IRON_BARS || one == StateTypes.CHAIN && player.getClientVersion().isOlderThan(ClientVersion.V_1_16))
return true;
else
return CollisionData.getData(one).getMovementCollisionBox(player, player.getClientVersion(), state, 0, 0, 0).isSideFullBlock(direction);
@@ -15,12 +15,16 @@ import com.github.retrooper.packetevents.protocol.world.states.enums.South;
import com.github.retrooper.packetevents.protocol.world.states.enums.West;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
public class DynamicWall extends DynamicConnecting implements CollisionFactory {
public class DynamicCollisionWall extends DynamicConnecting implements CollisionFactory {
public static final CollisionBox[] BOXES = makeShapes(4.0F, 3.0F, 16.0F, 0.0F, 16.0F, false);
// https://bugs.mojang.com/browse/MC-9565
// https://bugs.mojang.com/browse/MC-94016
private static final CollisionBox[] COLLISION_BOXES = makeShapes(4.0F, 3.0F, 24.0F, 0.0F, 24.0F, false);
/**
* @deprecated use DynamicHitboxWall
*/
@Deprecated
public CollisionBox fetchRegularBox(GrimPlayer player, WrappedBlockState state, ClientVersion version, int x, int y, int z) {
int north, south, west, east, up;
north = south = west = east = up = 0;
@@ -0,0 +1,86 @@
package ac.grim.grimac.utils.collisions.blocks.connecting;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.CollisionData;
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.ComplexCollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.HitBoxFactory;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags;
import com.github.retrooper.packetevents.protocol.world.states.enums.*;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import java.util.ArrayList;
import java.util.List;
public class DynamicHitboxFence extends DynamicConnecting implements HitBoxFactory {
private static final CollisionBox[] MODERN_HITBOXES = makeShapes(2.0F, 2.0F, 24.0F, 0.0F, 24.0F, true);
public static SimpleCollisionBox[] LEGACY_HITBOXES = new SimpleCollisionBox[] {new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D)};
static {
for (int i = 0; i < MODERN_HITBOXES.length; i++) {
CollisionBox collisionBox = MODERN_HITBOXES[i];
List<SimpleCollisionBox> boxes = new ArrayList<>();
collisionBox.downCast(boxes);
for (SimpleCollisionBox box : boxes) {
if (box.maxY > 1) {
box.maxY = 1;
}
}
MODERN_HITBOXES[i] = new ComplexCollisionBox(boxes.toArray(new SimpleCollisionBox[0]));
}
}
@Override
public CollisionBox fetch(GrimPlayer player, StateType heldItem, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
boolean east;
boolean north;
boolean south;
boolean west;
// 1.13+ servers on 1.13+ clients send the full fence data
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13)
&& version.isNewerThanOrEquals(ClientVersion.V_1_13)) {
east = block.getEast() != East.FALSE;
north = block.getNorth() != North.FALSE;
south = block.getSouth() != South.FALSE;
west = block.getWest() != West.FALSE;
} else {
east = connectsTo(player, version, x, y, z, BlockFace.EAST);
north = connectsTo(player, version, x, y, z, BlockFace.NORTH);
south = connectsTo(player, version, x, y, z, BlockFace.SOUTH);
west = connectsTo(player, version, x, y, z, BlockFace.WEST);
}
return version.isNewerThanOrEquals(ClientVersion.V_1_12_2)
? getModernCollisionBox(north, east, south, west)
: getLegacyCollisionBox(north, east, south, west);
}
private CollisionBox getLegacyCollisionBox(boolean north, boolean east, boolean south, boolean west) {
return LEGACY_HITBOXES[getAABBIndex(north, east, south, west)].copy();
}
private CollisionBox getModernCollisionBox(boolean north, boolean east, boolean south, boolean west) {
return MODERN_HITBOXES[getAABBIndex(north, east, south, west)].copy();
}
@Override
public boolean checkCanConnect(GrimPlayer player, WrappedBlockState state, StateType one, StateType two, BlockFace direction) {
if (BlockTags.FENCES.contains(one))
return !(one == StateTypes.NETHER_BRICK_FENCE) && !(two == StateTypes.NETHER_BRICK_FENCE);
else
return BlockTags.FENCES.contains(one) || CollisionData.getData(one).getMovementCollisionBox(player, player.getClientVersion(), state, 0, 0, 0).isSideFullBlock(direction);
}
}
@@ -0,0 +1,109 @@
package ac.grim.grimac.utils.collisions.blocks.connecting;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.CollisionData;
import ac.grim.grimac.utils.collisions.datatypes.*;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags;
import com.github.retrooper.packetevents.protocol.world.states.enums.*;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
public class DynamicHitboxPane extends DynamicConnecting implements HitBoxFactory {
private static final CollisionBox[] COLLISION_BOXES = makeShapes(1.0F, 1.0F, 16.0F, 0.0F, 16.0F, true);
@Override
public CollisionBox fetch(GrimPlayer player, StateType item, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
boolean east, north, south, west;
// 1.13+ servers on 1.13+ clients send the full fence data
if (isModernVersion(version)) {
east = block.getEast() != East.FALSE;
north = block.getNorth() != North.FALSE;
south = block.getSouth() != South.FALSE;
west = block.getWest() != West.FALSE;
} else {
east = connectsTo(player, version, x, y, z, BlockFace.EAST);
north = connectsTo(player, version, x, y, z, BlockFace.NORTH);
south = connectsTo(player, version, x, y, z, BlockFace.SOUTH);
west = connectsTo(player, version, x, y, z, BlockFace.WEST);
}
// On 1.7 and 1.8 clients, and 1.13+ clients on 1.7 and 1.8 servers, the glass pane is + instead of |
if (shouldUseOldPaneShape(version, north, south, east, west)) {
north = south = east = west = true;
}
return version.isNewerThanOrEquals(ClientVersion.V_1_9)
? getModernCollisionBox(north, east, south, west)
: getLegacyCollisionBox(north, east, south, west);
}
private boolean isModernVersion(ClientVersion version) {
return PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13)
&& version.isNewerThanOrEquals(ClientVersion.V_1_13);
}
private boolean shouldUseOldPaneShape(ClientVersion version, boolean north, boolean south, boolean east, boolean west) {
return (!north && !south && !east && !west) &&
(version.isOlderThanOrEquals(ClientVersion.V_1_8) ||
(PacketEvents.getAPI().getServerManager().getVersion().isOlderThanOrEquals(ServerVersion.V_1_8_8) &&
version.isNewerThanOrEquals(ClientVersion.V_1_13)));
}
private CollisionBox getModernCollisionBox(boolean north, boolean east, boolean south, boolean west) {
return COLLISION_BOXES[getAABBIndex(north, east, south, west)].copy();
}
private CollisionBox getLegacyCollisionBox(boolean north, boolean east, boolean south, boolean west) {
float minX = 0.4375F;
float maxX = 0.5625F;
float minZ = 0.4375F;
float maxZ = 0.5625F;
if ((!west || !east) && (west || east || north || south)) {
if (west) {
minX = 0.0F;
} else if (east) {
maxX = 1.0F;
}
} else {
minX = 0.0F;
maxX = 1.0F;
}
if ((!north || !south) && (west || east || north || south)) {
if (north) {
minZ = 0.0F;
} else if (south) {
maxZ = 1.0F;
}
} else {
minZ = 0.0F;
maxZ = 1.0F;
}
return new SimpleCollisionBox(minX, 0.0F, minZ, maxX, 1.0F, maxZ);
}
@Override
public boolean canConnectToGlassBlock() {
return true;
}
@Override
public boolean checkCanConnect(GrimPlayer player, WrappedBlockState state, StateType one, StateType two, BlockFace direction) {
if (BlockTags.GLASS_PANES.contains(one) || one == StateTypes.IRON_BARS) {
return true;
} else {
return CollisionData.getData(one)
.getMovementCollisionBox(player, player.getClientVersion(), state, 0, 0, 0)
.isSideFullBlock(direction);
}
}
}
@@ -0,0 +1,111 @@
package ac.grim.grimac.utils.collisions.blocks.connecting;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.CollisionData;
import ac.grim.grimac.utils.collisions.datatypes.*;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags;
import com.github.retrooper.packetevents.protocol.world.states.enums.*;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
public class DynamicHitboxWall extends DynamicConnecting implements HitBoxFactory {
private static final CollisionBox[] HIT_BOXES = makeShapes(4.0F, 3.0F, 16.0F, 0.0F, 16.0F, false);
@Override
public CollisionBox fetch(GrimPlayer player, StateType heldItem, ClientVersion version, WrappedBlockState state, int x, int y, int z) {
int[] connections = getConnections(player, version, state, x, y, z);
int north = connections[0], south = connections[1], west = connections[2], east = connections[3], up = connections[4];
if (version.isNewerThanOrEquals(ClientVersion.V_1_13)) {
return getModernHitBox(north, south, west, east, up);
} else {
return getLegacyHitBox(north, south, west, east);
}
}
private int[] getConnections(GrimPlayer player, ClientVersion version, WrappedBlockState state, int x, int y, int z) {
int north = 0, south = 0, west = 0, east = 0, up = 0;
if (isModernServer()) {
boolean sixteen = PacketEvents.getAPI().getServerManager().getVersion().isNewerThan(ServerVersion.V_1_16);
north = getConnectionValue(state.getNorth(), sixteen);
east = getConnectionValue(state.getEast(), sixteen);
south = getConnectionValue(state.getSouth(), sixteen);
west = getConnectionValue(state.getWest(), sixteen);
up = state.isUp() ? 1 : 0;
} else {
north = connectsTo(player, version, x, y, z, BlockFace.NORTH) ? 1 : 0;
south = connectsTo(player, version, x, y, z, BlockFace.SOUTH) ? 1 : 0;
west = connectsTo(player, version, x, y, z, BlockFace.WEST) ? 1 : 0;
east = connectsTo(player, version, x, y, z, BlockFace.EAST) ? 1 : 0;
up = 1;
}
return new int[]{north, south, west, east, up};
}
private boolean isModernServer() {
return PacketEvents.getAPI().getServerManager().getVersion().isNewerThan(ServerVersion.V_1_12_2);
}
private int getConnectionValue(Enum<?> direction, boolean sixteen) {
if (direction == North.NONE || direction == East.NONE || direction == South.NONE || direction == West.NONE) {
return 0;
}
return (direction == North.LOW || direction == East.LOW || direction == South.LOW || direction == West.LOW || sixteen) ? 1 : 2;
}
private CollisionBox getModernHitBox(int north, int south, int west, int east, int up) {
ComplexCollisionBox box = new ComplexCollisionBox();
if (up == 1) {
box.add(new HexCollisionBox(4, 0, 4, 12, 16, 12));
}
addDirectionalBox(box, north, 5, 0, 0.0D, 11, 14, 11);
addDirectionalBox(box, south, 5, 0, 5, 11, 14, 16);
addDirectionalBox(box, west, 0, 0, 5, 11, 14, 11);
addDirectionalBox(box, east, 5, 0, 5, 16, 14, 11);
return box;
}
private void addDirectionalBox(ComplexCollisionBox box, int direction, double x1, double y1, double z1, double x2, double y2, double z2) {
if (direction == 1) {
box.add(new HexCollisionBox(x1, y1, z1, x2, y2, z2));
} else if (direction == 2) {
box.add(new HexCollisionBox(x1, y1, z1, x2, 16, z2));
}
}
private CollisionBox getLegacyHitBox(int north, int south, int west, int east) {
float minX = 0.25F, maxX = 0.75F, minZ = 0.25F, maxZ = 0.75F;
float maxY = 1.0F;
if (north == 1) minZ = 0.0F;
if (south == 1) maxZ = 1.0F;
if (west == 1) minX = 0.0F;
if (east == 1) maxX = 1.0F;
if (north == 1 && south == 1 && west == 0 && east == 0) {
maxY = 0.8125F;
minX = 0.3125F;
maxX = 0.6875F;
} else if (west == 1 && east == 1 && north == 0 && south == 0) {
maxY = 0.8125F;
minZ = 0.3125F;
maxZ = 0.6875F;
}
return new SimpleCollisionBox(minX, 0.0F, minZ, maxX, maxY, maxZ);
}
@Override
public boolean checkCanConnect(GrimPlayer player, WrappedBlockState state, StateType one, StateType two, BlockFace direction) {
return BlockTags.WALLS.contains(one) ||
CollisionData.getData(one).getMovementCollisionBox(player, player.getClientVersion(), state, 0, 0, 0).isSideFullBlock(direction);
}
}
@@ -5,6 +5,8 @@ import com.github.retrooper.packetevents.protocol.world.BlockFace;
import java.util.List;
public interface CollisionBox {
CollisionBox union(SimpleCollisionBox other);
boolean isCollided(SimpleCollisionBox other);
boolean isIntersected(SimpleCollisionBox other);
@@ -16,6 +16,12 @@ public class ComplexCollisionBox implements CollisionBox {
return boxes.add(collisionBox);
}
@Override
public CollisionBox union(SimpleCollisionBox other) {
add(other);
return this;
}
@Override
public boolean isCollided(SimpleCollisionBox other) {
for (CollisionBox box : boxes) {
@@ -21,6 +21,14 @@ public class DynamicCollisionBox implements CollisionBox {
this.block = block;
}
// Untested but currently unused
// *should* work because every single one of these eventually becomes a Complex, Simple, or NoCollision Box
@Override
public CollisionBox union(SimpleCollisionBox other) {
CollisionBox dynamicBox = box.fetch(player, version, block, x, y, z).offset(x, y, z);
return dynamicBox.union(other);
}
@Override
public boolean isCollided(SimpleCollisionBox other) {
return box.fetch(player, version, block, x, y, z).offset(x, y, z).isCollided(other);
@@ -2,6 +2,18 @@ package ac.grim.grimac.utils.collisions.datatypes;
public class HexCollisionBox extends SimpleCollisionBox {
/**
* Creates a box defined by two points in 3d space; used to represent hitboxes and collision boxes.
* Mojang's block hitbox values are all based on chunks, so they're stored in game as 16 * the actual size
* When copying block hitbox values, it may be easier to simple copy the multiplied values and use this class
* If your min/max values are < 1 you should probably check out {@link SimpleCollisionBox}
* @param minX 16 * x position of first corner
* @param minY 16 * y position of first corner
* @param minZ 16 * z position of first corner
* @param maxX 16 * x position of second corner
* @param maxY 16 * y position of second corner
* @param maxZ 16 * z position of second corner
*/
public HexCollisionBox(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
this.minX = minX / 16d;
this.minY = minY / 16d;
@@ -0,0 +1,10 @@
package ac.grim.grimac.utils.collisions.datatypes;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
// Exists for the same reason as HexCollisionBox but for offset blocks; read comments there if unsure
public class HexOffsetCollisionBox extends OffsetCollisionBox {
public HexOffsetCollisionBox(StateType block, double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
super(block, minX / 16d, minY / 16d, minZ / 16d, maxX / 16d, maxY / 16d, maxZ / 16d);
}
}
@@ -9,6 +9,11 @@ public class NoCollisionBox implements CollisionBox {
private NoCollisionBox() {
}
@Override
public CollisionBox union(SimpleCollisionBox other) {
return other;
}
@Override
public boolean isCollided(SimpleCollisionBox other) {
return false;
@@ -0,0 +1,105 @@
package ac.grim.grimac.utils.collisions.datatypes;
import ac.grim.grimac.utils.math.GrimMath;
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 java.util.HashSet;
public class OffsetCollisionBox extends SimpleCollisionBox {
public static enum OffsetType {
NONE,
XZ,
XYZ,
}
float maxHorizontalModelOffset = 0.25F;
float maxVerticalModelOffset = 0.2F;
double offsetX = 0;
double offsetY = 0;
double offsetZ = 0;
OffsetType offsetType;
private static final HashSet<StateType> XZ_OFFSET_BLOCKSTATES = new HashSet<>();
private static final HashSet<StateType> XYZ_OFFSET_BLOCKSTATES = new HashSet<>();
static {
// Can we add a hasOffSet to StateType() ?
// Or a new BlockTag for XZ and XYZ Offset ?
XZ_OFFSET_BLOCKSTATES.add(StateTypes.MANGROVE_PROPAGULE);
XZ_OFFSET_BLOCKSTATES.addAll(BlockTags.SMALL_FLOWERS.getStates());
XZ_OFFSET_BLOCKSTATES.add(StateTypes.BAMBOO_SAPLING);
XZ_OFFSET_BLOCKSTATES.add(StateTypes.BAMBOO);
XZ_OFFSET_BLOCKSTATES.add(StateTypes.POINTED_DRIPSTONE);
// Only offsets rendering HitBox on XZ // we should document this somewhere for future reference
// XZ_OFFSET_BLOCKSTATES.addAll(BlockTags.TALL_FLOWERS.getStates());
// XZ_OFFSET_BLOCKSTATES.add(StateTypes.TALL_SEAGRASS);
// XZ_OFFSET_BLOCKSTATES.add(StateTypes.TALL_GRASS);
// XZ_OFFSET_BLOCKSTATES.add(StateTypes.LARGE_FERN);
// XZ_OFFSET_BLOCKSTATES.add(StateTypes.WARPED_ROOTS);
// XZ_OFFSET_BLOCKSTATES.add(StateTypes.NETHER_SPROUTS);
// XZ_OFFSET_BLOCKSTATES.add(StateTypes.CRIMSON_ROOTS);
// XZ_OFFSET_BLOCKSTATES.add(StateTypes.HANGING_ROOTS);
// Only offsets rendering on XYZ, not HitBox
// XYZ_OFFSET_BLOCKSTATES.add(StateTypes.SHORT_GRASS);
// XYZ_OFFSET_BLOCKSTATES.add(StateTypes.FERN);
// XYZ_OFFSET_BLOCKSTATES.add(StateTypes.SMALL_DRIPLEAF);
}
public OffsetCollisionBox(StateType block, double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
super(minX, minY, minZ, maxX, maxY, maxZ);
if (block.equals(StateTypes.POINTED_DRIPSTONE)) {
maxHorizontalModelOffset = 0.125F;
}
// else if (block.equals(StateTypes.SMALL_DRIPLEAF)) {
// maxVerticalModelOffset = 0.1F;
// }
if (XZ_OFFSET_BLOCKSTATES.contains(block)) {
offsetType = OffsetType.XZ;
return;
} else if (XYZ_OFFSET_BLOCKSTATES.contains(block)) {
offsetType = OffsetType.XYZ;
return;
}
throw new RuntimeException("Invalid State Type for OffSetCollisionBox: " + block);
}
@Override
public SimpleCollisionBox offset(double x, double y, double z) {
// In case you want to call .offset() again or get the box values without offset.
resetBlockStateOffSet();
long l;
switch (offsetType) {
case NONE:
return super.offset(x, y, z);
case XZ:
l = GrimMath.hashCode(x, 0, z);
offsetX = GrimMath.clamp(((double)((float)(l & 15L) / 15.0F) - 0.5) * 0.5, (double)(-maxHorizontalModelOffset), (double)maxHorizontalModelOffset);
offsetZ = GrimMath.clamp(((double)((float)(l >> 8 & 15L) / 15.0F) - 0.5) * 0.5, (double)(-maxHorizontalModelOffset), (double)maxHorizontalModelOffset);
return super.offset(x + offsetX, y, z + offsetZ);
case XYZ:
l = GrimMath.hashCode(x, 0, z);
offsetY = ((double)((float)(l >> 4 & 15L) / 15.0F) - 1.0) * (double) maxVerticalModelOffset;
offsetX = GrimMath.clamp(((double)((float)(l & 15L) / 15.0F) - 0.5) * 0.5, (double)(-maxHorizontalModelOffset), (double)maxHorizontalModelOffset);
offsetZ = GrimMath.clamp(((double)((float)(l >> 8 & 15L) / 15.0F) - 0.5) * 0.5, (double)(-maxHorizontalModelOffset), (double)maxHorizontalModelOffset);
return super.offset(x + offsetX, offsetY, z + offsetZ);
}
// You *really* shouldn't be using this class if offsetType = null
return null;
}
public void resetBlockStateOffSet() {
this.minX += offsetX;
this.minY += offsetY;
this.minZ += offsetZ;
this.maxX += offsetX;
this.maxY += offsetY;
this.maxZ += offsetZ;
}
}
@@ -24,6 +24,17 @@ public class SimpleCollisionBox implements CollisionBox {
this(0, 0, 0, 0, 0, 0, false);
}
/**
* Creates a box defined by two points in 3d space; used to represent hitboxes and collision boxes.
* If your min/max values are > 1 you should probably check out {@link HexCollisionBox}
* @param minX x position of first corner
* @param minY y position of first corner
* @param minZ z position of first corner
* @param maxX x position of second corner
* @param maxY y position of second corner
* @param maxZ z position of second corner
* @param fullBlock - whether on not the box is a perfect 1x1x1 sized block
*/
public SimpleCollisionBox(double minX, double minY, double minZ, double maxX, double maxY, double maxZ, boolean fullBlock) {
this.minX = minX;
this.maxX = maxX;
@@ -47,7 +58,17 @@ public class SimpleCollisionBox implements CollisionBox {
this(minX, minY, minZ, minX + 1, minY + 1, minZ + 1, true);
}
// Use only if you don't know the fullBlock status, which is rare
/**
* Creates a box defined by two points in 3d space; used to represent hitboxes and collision boxes.
* If your min/max values are > 1 you should probably check out {@link HexCollisionBox}
* Use only if you don't know the fullBlock status, which is rare
* @param minX x position of first corner
* @param minY y position of first corner
* @param minZ z position of first corner
* @param maxX x position of second corner
* @param maxY y position of second corner
* @param maxZ z position of second corner
*/
public SimpleCollisionBox(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
this.minX = minX;
this.maxX = maxX;
@@ -142,7 +163,8 @@ public class SimpleCollisionBox implements CollisionBox {
return vectors;
}
public SimpleCollisionBox union(SimpleCollisionBox other) {
@Override
public CollisionBox union(SimpleCollisionBox other) {
this.minX = Math.min(this.minX, other.minX);
this.minY = Math.min(this.minY, other.minY);
this.minZ = Math.min(this.minZ, other.minZ);
@@ -0,0 +1,10 @@
package ac.grim.grimac.utils.common;
import ac.grim.grimac.api.config.ConfigManager;
public interface ConfigReloadObserver {
void onReload(ConfigManager config);
}
@@ -80,6 +80,7 @@ public final class ValuedAttribute {
public void reset() {
this.value = defaultValue;
this.lastProperty = null; // Remove the old property with its modifiers so we don't accidentally use it again, messing up the calculation.
}
public double get() {
@@ -100,29 +101,29 @@ public final class ValuedAttribute {
}
public double with(WrapperPlayServerUpdateAttributes.Property property) {
double d0 = property.getValue();
double baseValue = property.getValue();
double additionSum = 0;
double multiplyBaseSum = 0;
double multiplyTotalProduct = 1.0;
List<WrapperPlayServerUpdateAttributes.PropertyModifier> modifiers = property.getModifiers();
modifiers.removeIf(modifier -> modifier.getUUID().equals(SPRINTING_MODIFIER_UUID) || modifier.getName().getKey().equals("sprinting"));
for (WrapperPlayServerUpdateAttributes.PropertyModifier attributemodifier : modifiers) {
if (attributemodifier.getOperation() == WrapperPlayServerUpdateAttributes.PropertyModifier.Operation.ADDITION)
d0 += attributemodifier.getAmount();
for (WrapperPlayServerUpdateAttributes.PropertyModifier modifier : modifiers) {
switch (modifier.getOperation()) {
case ADDITION:
additionSum += modifier.getAmount();
break;
case MULTIPLY_BASE:
multiplyBaseSum += modifier.getAmount();
break;
case MULTIPLY_TOTAL:
multiplyTotalProduct *= (1.0 + modifier.getAmount());
break;
}
}
double d1 = d0;
for (WrapperPlayServerUpdateAttributes.PropertyModifier attributemodifier : modifiers) {
if (attributemodifier.getOperation() == WrapperPlayServerUpdateAttributes.PropertyModifier.Operation.MULTIPLY_BASE)
d1 += d0 * attributemodifier.getAmount();
}
for (WrapperPlayServerUpdateAttributes.PropertyModifier attributemodifier : modifiers) {
if (attributemodifier.getOperation() == WrapperPlayServerUpdateAttributes.PropertyModifier.Operation.MULTIPLY_TOTAL)
d1 *= 1.0D + attributemodifier.getAmount();
}
double newValue = GrimMath.clampFloat((float) d1, (float) min, (float) max);
double newValue = GrimMath.clamp((baseValue + additionSum) * (1 + multiplyBaseSum) * multiplyTotalProduct, min, max);
if (setRewriter != null) {
newValue = setRewriter.apply(this.value, newValue);
}
@@ -2,6 +2,8 @@ package ac.grim.grimac.utils.data.packetentity;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.data.attribute.ValuedAttribute;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
@@ -20,7 +22,16 @@ public class PacketEntityHorse extends PacketEntityTrackXRot {
setAttribute(Attributes.GENERIC_STEP_HEIGHT, 1.0f);
final boolean preAttribute = player.getClientVersion().isOlderThan(ClientVersion.V_1_20_5);
trackAttribute(ValuedAttribute.ranged(Attributes.GENERIC_JUMP_STRENGTH, 0.7, 0, preAttribute ? 2 : 32));
// This was horse.jump_strength pre-attribute
trackAttribute(ValuedAttribute.ranged(Attributes.GENERIC_JUMP_STRENGTH, 0.7, 0, preAttribute ? 2 : 32)
.withSetRewriter((oldValue, newValue) -> {
// Seems viabackwards doesn't rewrite this (?)
if (preAttribute && PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_20_5)) {
return oldValue;
}
// Modern player OR an old server setting legacy horse.jump_strength attribute
return newValue;
}));
trackAttribute(ValuedAttribute.ranged(Attributes.GENERIC_MOVEMENT_SPEED, 0.225f, 0, 1024));
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.CHESTED_HORSE)) {
@@ -47,6 +47,28 @@ public class PacketEntitySelf extends PacketEntity {
setAttribute(Attributes.GENERIC_STEP_HEIGHT, 0.5f);
}
getAttribute(Attributes.GENERIC_SCALE).get().withSetRewriter((oldValue, newValue) -> {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_20_5) || (newValue).equals(oldValue)) {
return oldValue;
} else {
// Elytra, standing, sneaking (1.14)
player.possibleEyeHeights[2][0] = 0.4 * newValue;
player.possibleEyeHeights[2][1] = 1.62 * newValue;
player.possibleEyeHeights[2][2] = 1.27 * newValue;
// sneaking (1.14), standing, Elytra
player.possibleEyeHeights[1][0] = 1.27 * newValue;
player.possibleEyeHeights[1][1] = 1.62 * newValue;
player.possibleEyeHeights[1][2] = 0.4 * newValue;
// standing, sneaking (1.14), Elytra
player.possibleEyeHeights[0][0] = 1.62 * newValue;
player.possibleEyeHeights[0][1] = 1.27 * newValue;
player.possibleEyeHeights[0][2] = 0.4 * newValue;
return newValue;
}
});
final ValuedAttribute movementSpeed = ValuedAttribute.ranged(Attributes.GENERIC_MOVEMENT_SPEED, 0.1f, 0, 1024);
movementSpeed.with(new WrapperPlayServerUpdateAttributes.Property("MOVEMENT_SPEED", 0.1f, new ArrayList<>()));
trackAttribute(movementSpeed);
@@ -1,5 +1,7 @@
package ac.grim.grimac.utils.math;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.util.Vector3d;
import lombok.experimental.UtilityClass;
import java.util.List;
@@ -105,4 +107,10 @@ public class GrimMath {
public static boolean isNearlySame(double a, double b, double epoch) {
return Math.abs(a-b) < epoch;
}
public static long hashCode(double x, int y, double z) {
long l = (long)(x * 3129871) ^ (long)z * 116129781L ^ (long)y;
l = l * l * 42317861L + l * 11L;
return l >> 16;
}
}
@@ -110,10 +110,11 @@ public class BlockBreakSpeed {
if (tool.getType().hasAttribute(ItemTypes.ItemAttribute.SWORD)) {
if (block.getType() == StateTypes.COBWEB) {
speedMultiplier = 15.0f;
} else if (block.getType().getMaterialType() == MaterialType.PLANT ||
BlockTags.LEAVES.contains(block.getType()) ||
block.getType() == StateTypes.PUMPKIN ||
block.getType() == StateTypes.MELON) {
} else if (block.getType().getMaterialType() == MaterialType.PLANT
|| BlockTags.LEAVES.contains(block.getType())
|| block.getType() == StateTypes.VINE
|| block.getType() == StateTypes.PUMPKIN
|| block.getType() == StateTypes.MELON) {
speedMultiplier = 1.5f;
}
@@ -30,6 +30,11 @@ public class BlockProperties {
}
if (player.compensatedEntities.getSelf().getRiding() instanceof PacketEntityStrider) {
// Unsure which version the speed changed in
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_20)) {
return (float) player.speed * 0.1f;
}
PacketEntityStrider strider = (PacketEntityStrider) player.compensatedEntities.getSelf().getRiding();
// Vanilla multiplies by 0.1 to calculate speed
return (float) strider.getAttributeValue(Attributes.GENERIC_MOVEMENT_SPEED) * (strider.isShaking ? 0.66F : 1.0F) * 0.1f;
@@ -0,0 +1,309 @@
package ac.grim.grimac.utils.nmsutil;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.HitboxData;
import ac.grim.grimac.utils.collisions.RaycastData;
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.NoCollisionBox;
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.math.GrimMath;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.util.Vector3i;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.BiFunction;
public class BlockRayTrace {
// 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
@Nullable
public static Pair<int[], BlockFace> traverseBlocksLOSP(GrimPlayer player, double[] start, double[] end, BiFunction<WrappedBlockState, int[], Pair<int[], BlockFace>> predicate) {
// I guess go back by the collision epsilon?
double endX = GrimMath.lerp(-1.0E-7D, end[0], start[0]);
double endY = GrimMath.lerp(-1.0E-7D, end[1], start[1]);
double endZ = GrimMath.lerp(-1.0E-7D, end[2], start[2]);
double startX = GrimMath.lerp(-1.0E-7D, start[0], end[0]);
double startY = GrimMath.lerp(-1.0E-7D, start[1], end[1]);
double startZ = GrimMath.lerp(-1.0E-7D, start[2], end[2]);
int[] floorStart = new int[]{GrimMath.floor(startX), GrimMath.floor(startY), GrimMath.floor(startZ)};
if (start[0] == end[0] && start[1] == end[1] && start[2] == end[2]) return null;
WrappedBlockState state = player.compensatedWorld.getWrappedBlockStateAt(floorStart[0], floorStart[1], floorStart[2]);
Pair<int[], BlockFace> apply = predicate.apply(state, floorStart);
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 tMaxX = posXInverse * (xSign > 0 ? 1.0D - GrimMath.frac(startX) : GrimMath.frac(startX));
double tMaxY = posYInverse * (ySign > 0 ? 1.0D - GrimMath.frac(startY) : GrimMath.frac(startY));
double tMaxZ = posZInverse * (zSign > 0 ? 1.0D - GrimMath.frac(startZ) : GrimMath.frac(startZ));
// tMax represents the maximum distance along each axis before crossing a block boundary
// The loop continues as long as the ray hasn't reached its end point along at least one axis.
// In each iteration, it moves to the next block boundary along the axis with the smallest tMax value,
// updates the corresponding coordinate, and checks for a hit in the new block, Google "3D DDA" for more info
while (tMaxX <= 1.0D || tMaxY <= 1.0D || tMaxZ <= 1.0D) {
if (tMaxX < tMaxY) {
if (tMaxX < tMaxZ) {
floorStart[0] += xSign;
tMaxX += posXInverse;
} else {
floorStart[2] += zSign;
tMaxZ += posZInverse;
}
} else if (tMaxY < tMaxZ) {
floorStart[1] += ySign;
tMaxY += posYInverse;
} else {
floorStart[2] += zSign;
tMaxZ += posZInverse;
}
state = player.compensatedWorld.getWrappedBlockStateAt(floorStart[0], floorStart[1], floorStart[2]);
apply = predicate.apply(state, floorStart);
if (apply != null) {
return apply;
}
}
return null;
}
public static HitData traverseBlocks(GrimPlayer player, double[] start, double[] end, BiFunction<WrappedBlockState, Vector3i, HitData> predicate) {
// I guess go back by the collision epsilon?
double endX = GrimMath.lerp(-1.0E-7D, end[0], start[0]);
double endY = GrimMath.lerp(-1.0E-7D, end[1], start[1]);
double endZ = GrimMath.lerp(-1.0E-7D, end[2], start[2]);
double startX = GrimMath.lerp(-1.0E-7D, start[0], end[0]);
double startY = GrimMath.lerp(-1.0E-7D, start[1], end[1]);
double startZ = GrimMath.lerp(-1.0E-7D, start[2], end[2]);
int floorStartX = GrimMath.floor(startX);
int floorStartY = GrimMath.floor(startY);
int floorStartZ = GrimMath.floor(startZ);
if (start[0] == end[0] && start[1] == end[1] && start[2] == end[2]) 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 tMaxX = posXInverse * (xSign > 0 ? 1.0D - GrimMath.frac(startX) : GrimMath.frac(startX));
double tMaxY = posYInverse * (ySign > 0 ? 1.0D - GrimMath.frac(startY) : GrimMath.frac(startY));
double tMaxZ = posZInverse * (zSign > 0 ? 1.0D - GrimMath.frac(startZ) : GrimMath.frac(startZ));
// tMax represents the maximum distance along each axis before crossing a block boundary
// The loop continues as long as the ray hasn't reached its end point along at least one axis.
// In each iteration, it moves to the next block boundary along the axis with the smallest tMax value,
// updates the corresponding coordinate, and checks for a hit in the new block, Google "3D DDA" for more info
while (tMaxX <= 1.0D || tMaxY <= 1.0D || tMaxZ <= 1.0D) {
if (tMaxX < tMaxY) {
if (tMaxX < tMaxZ) {
floorStartX += xSign;
tMaxX += posXInverse;
} else {
floorStartZ += zSign;
tMaxZ += posZInverse;
}
} else if (tMaxY < tMaxZ) {
floorStartY += ySign;
tMaxY += posYInverse;
} else {
floorStartZ += zSign;
tMaxZ += posZInverse;
}
state = player.compensatedWorld.getWrappedBlockStateAt(floorStartX, floorStartY, floorStartZ);
apply = predicate.apply(state, new Vector3i(floorStartX, floorStartY, floorStartZ));
if (apply != null) {
return apply;
}
}
return null;
}
public static HitData traverseBlocks(GrimPlayer player, Vector3d start, Vector3d end, BiFunction<WrappedBlockState, Vector3i, HitData> predicate) {
return traverseBlocks(player, new double[]{start.x, start.y, start.z}, new double[]{end.x, end.y, end.z}, predicate);
}
@Nullable
public static HitData getNearestReachHitResult(GrimPlayer player, double[] startPos, double[] lookVec, double currentDistance, double maxDistance, int[] targetBlockVec, boolean raycastContext) {
double[] endPos = new double[]{
startPos[0] + lookVec[0] * maxDistance,
startPos[1] + lookVec[1] * maxDistance,
startPos[2] + lookVec[2] * maxDistance
};
return traverseBlocks(player, startPos, endPos, (block, vector3i) -> {
CollisionBox data;
if (!raycastContext) {
data = HitboxData.getBlockHitbox(player, player.getInventory().getHeldItem().getType().getPlacedType(), player.getClientVersion(), block, vector3i.x, vector3i.y, vector3i.z);
} else {
data = RaycastData.getBlockHitbox(player, null, player.getClientVersion(), block, vector3i.x, vector3i.y, vector3i.z);
}
if (data == NoCollisionBox.INSTANCE) return null;
List<SimpleCollisionBox> boxes = new ArrayList<>();
data.downCast(boxes);
double bestHitResult = Double.MAX_VALUE;
double[] bestHitLoc = null;
BlockFace bestFace = null;
// BEWARE OF https://bugs.mojang.com/browse/MC-85109 FOR 1.8 PLAYERS
// 1.8 Brewing Stand hitbox is a fullblock until it is hit sometimes, can be caused be restarting client and joining server
if (block.getType() == StateTypes.BREWING_STAND && player.getClientVersion().equals(ClientVersion.V_1_8) && Arrays.equals(new int[]{vector3i.x, vector3i.y, vector3i.z}, targetBlockVec)) {
boxes.add(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true));
}
double[] currentEnd = new double[]{
startPos[0] + lookVec[0] * currentDistance,
startPos[1] + lookVec[1] * currentDistance,
startPos[2] + lookVec[2] * currentDistance
};
for (SimpleCollisionBox box : boxes) {
Pair<double[], BlockFace> intercept = ReachUtilsPrimitives.calculateIntercept(box, startPos, currentEnd);
if (intercept.getFirst() == null) continue; // No intercept or wrong blockFace
double[] hitLoc = intercept.getFirst();
double distSq = distanceSquared(hitLoc, startPos);
if (distSq < bestHitResult) {
bestHitResult = distSq;
bestHitLoc = hitLoc;
bestFace = intercept.getSecond();
}
}
// Yes, this is not the most optimal algorithm for handling Cauldrons, Hoppers, Composters, and Scaffolding
// that is to say, blocks that have a different outline/hitbox shape from the box used to calculate placement blockfaces
// but it is the one vanilla uses
// No we will not
// 1. Calculate the ray trace from a new closer startPos to reduce iterations
// because it adds a lot of code complexity for very little performance gain
// 2. Run a switch case on the target block and check if the index of the SimpleCollisionBox corresponds to a wall with an inside face
// and hardcode in blockface fixes for placements against those compnents above a certain relative y level
// because that is version-specific, will break if the implementation of the returned ComplexCollisionBox changes
// and again, lots of code complexity for little performance gain
if (bestHitLoc != null) {
HitData hitData = new HitData(vector3i, new Vector(bestHitLoc[0], bestHitLoc[1], bestHitLoc[2]), bestFace, block);
if (!raycastContext) {
HitData hitData2 = BlockRayTrace.getNearestReachHitResult(player, startPos, lookVec, maxDistance, maxDistance, targetBlockVec, true);
if (hitData2 != null) {
Vector startVector = new Vector(startPos[0], startPos[1], startPos[2]);
if (hitData2.getBlockHitLocation().subtract(startVector).lengthSquared() <
hitData.getBlockHitLocation().subtract(startVector).lengthSquared()) {
return new HitData(vector3i, hitData.getBlockHitLocation(), hitData2.getClosestDirection(), block);
}
}
}
return hitData;
}
return null;
});
}
private static double distanceSquared(double[] vec1, double[] vec2) {
double dx = vec1[0] - vec2[0];
double dy = vec1[1] - vec2[1];
double dz = vec1[2] - vec2[2];
return dx * dx + dy * dy + dz * dz;
}
public 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.getFirst() == null) continue; // No intercept
Vector hitLoc = intercept.getFirst();
if (hitLoc.distanceSquared(startingVec) < bestHitResult) {
bestHitResult = hitLoc.distanceSquared(startingVec);
bestHitLoc = hitLoc;
bestFace = intercept.getSecond();
}
}
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.getFirst() != null) {
return new HitData(vector3i, intercept.getFirst(), intercept.getSecond(), block);
}
}
return null;
});
}
}
@@ -31,6 +31,7 @@ import org.bukkit.util.Vector;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
public class Collisions {
@@ -699,7 +700,7 @@ public class Collisions {
int x = currX | chunkXGlobalPos;
int z = currZ | chunkZGlobalPos;
WrappedBlockState data = section.get(CompensatedWorld.blockVersion, x & 0xF, y & 0xF, z & 0xF);
WrappedBlockState data = section.get(CompensatedWorld.blockVersion, x & 0xF, y & 0xF, z & 0xF, false);
if (searchingFor.test(new Pair<>(data, new Vector3d(x, y, z)))) return true;
}
@@ -710,6 +711,85 @@ public class Collisions {
return false;
}
// Thanks Tuinity
public static void forEachCollisionBox(GrimPlayer player, SimpleCollisionBox checkBox, Consumer<Vector3d> searchingFor) {
int minBlockX = (int) Math.floor(checkBox.minX - COLLISION_EPSILON) - 1;
int maxBlockX = (int) Math.floor(checkBox.maxX + COLLISION_EPSILON) + 1;
int minBlockY = (int) Math.floor(checkBox.minY - COLLISION_EPSILON) - 1;
int maxBlockY = (int) Math.floor(checkBox.maxY + COLLISION_EPSILON) + 1;
int minBlockZ = (int) Math.floor(checkBox.minZ - COLLISION_EPSILON) - 1;
int maxBlockZ = (int) Math.floor(checkBox.maxZ + COLLISION_EPSILON) + 1;
final int minSection = player.compensatedWorld.getMinHeight() >> 4;
final int minBlock = minSection << 4;
final int maxBlock = player.compensatedWorld.getMaxHeight() - 1;
int minChunkX = minBlockX >> 4;
int maxChunkX = maxBlockX >> 4;
int minChunkZ = minBlockZ >> 4;
int maxChunkZ = maxBlockZ >> 4;
int minYIterate = Math.max(minBlock, minBlockY);
int maxYIterate = Math.min(maxBlock, maxBlockY);
for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
int minZ = currChunkZ == minChunkZ ? minBlockZ & 15 : 0; // coordinate in chunk
int maxZ = currChunkZ == maxChunkZ ? maxBlockZ & 15 : 15; // coordinate in chunk
for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
int minX = currChunkX == minChunkX ? minBlockX & 15 : 0; // coordinate in chunk
int maxX = currChunkX == maxChunkX ? maxBlockX & 15 : 15; // coordinate in chunk
int chunkXGlobalPos = currChunkX << 4;
int chunkZGlobalPos = currChunkZ << 4;
Column chunk = player.compensatedWorld.getChunk(currChunkX, currChunkZ);
if (chunk == null) continue;
BaseChunk[] sections = chunk.getChunks();
for (int y = minYIterate; y <= maxYIterate; ++y) {
BaseChunk section = sections[(y >> 4) - minSection];
if (section == null || (IS_FOURTEEN && section.isEmpty())) { // Check for empty on 1.13+ servers
// empty
// skip to next section
y = (y & ~(15)) + 15; // increment by 15: iterator loop increments by the extra one
continue;
}
for (int currZ = minZ; currZ <= maxZ; ++currZ) {
for (int currX = minX; currX <= maxX; ++currX) {
int x = currX | chunkXGlobalPos;
int z = currZ | chunkZGlobalPos;
WrappedBlockState data = section.get(CompensatedWorld.blockVersion, x & 0xF, y & 0xF, z & 0xF, false);
// Works on both legacy and modern! Faster than checking for material types, most common case
if (data.getGlobalId() == 0) continue;
// Thanks SpottedLeaf for this optimization, I took edgeCount from Tuinity
int edgeCount = ((x == minBlockX || x == maxBlockX) ? 1 : 0) +
((y == minBlockY || y == maxBlockY) ? 1 : 0) +
((z == minBlockZ || z == maxBlockZ) ? 1 : 0);
final StateType type = data.getType();
if (edgeCount != 3 && (edgeCount != 1 || Materials.isShapeExceedsCube(type))
&& (edgeCount != 2 || type == StateTypes.PISTON_HEAD)) {
final CollisionBox collisionBox = CollisionData.getData(type).getMovementCollisionBox(player, player.getClientVersion(), data, x, y, z);
if (collisionBox.isIntersected(checkBox)) {
searchingFor.accept(new Vector3d(x, y, z));
}
}
}
}
}
}
}
}
public static boolean onClimbable(GrimPlayer player, double x, double y, double z) {
WrappedBlockState blockState = player.compensatedWorld.getWrappedBlockStateAt(x, y, z);
StateType blockMaterial = blockState.getType();
@@ -1,8 +1,6 @@
package ac.grim.grimac.utils.nmsutil;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.CollisionData;
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.MainSupportingBlockData;
import com.github.retrooper.packetevents.util.Vector3d;
@@ -42,12 +40,8 @@ public class MainSupportingBlockPosFinder {
AtomicReference<Vector3i> bestBlockPos = new AtomicReference<>();
AtomicDouble blockPosDistance = new AtomicDouble(Double.MAX_VALUE);
Collisions.hasMaterial(player, searchBox, (thing) -> {
Vector3i blockPos = thing.getSecond().toVector3i();
CollisionBox collision = CollisionData.getData(thing.getFirst().getType()).getMovementCollisionBox(player, player.getClientVersion(), thing.getFirst(), blockPos.getX(), blockPos.getY(), blockPos.getZ());
if (!collision.isIntersected(searchBox)) return false;
Collisions.forEachCollisionBox(player, searchBox, (pos) -> {
Vector3i blockPos = pos.toVector3i();
Vector3d blockPosAsVector3d = new Vector3d(blockPos.getX() + 0.5, blockPos.getY() + 0.5, blockPos.getZ() + 0.5);
double distance = playerPos.distanceSquared(blockPosAsVector3d);
@@ -55,8 +49,6 @@ public class MainSupportingBlockPosFinder {
bestBlockPos.set(blockPos);
blockPosDistance.set(distance);
}
return false;
});
@@ -181,7 +181,8 @@ public class ReachUtils {
double lowest = Double.MAX_VALUE;
for (double eyes : player.getPossibleEyeHeights()) {
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
for (double eyes : possibleEyeHeights) {
if (giveMovementThresholdLenience) targetBox.expand(player.getMovementThreshold());
Vector from = new Vector(player.x, player.y + eyes, player.z);
Vector closestPoint = VectorUtils.cutBoxToVector(from, targetBox);
@@ -0,0 +1,184 @@
package ac.grim.grimac.utils.nmsutil;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.Pair;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jetbrains.annotations.NotNull;
public class ReachUtilsPrimitives {
public static @NotNull Pair<@Nullable double[], @Nullable BlockFace> calculateIntercept(SimpleCollisionBox self, double[] origin, double[] end) {
double[] minX = getIntermediateWithXValue(origin, end, self.minX);
double[] maxX = getIntermediateWithXValue(origin, end, self.maxX);
double[] minY = getIntermediateWithYValue(origin, end, self.minY);
double[] maxY = getIntermediateWithYValue(origin, end, self.maxY);
double[] minZ = getIntermediateWithZValue(origin, end, self.minZ);
double[] maxZ = getIntermediateWithZValue(origin, end, self.maxZ);
BlockFace bestFace = null;
if (!isVecInYZ(self, minX)) minX = null;
if (!isVecInYZ(self, maxX)) maxX = null;
if (!isVecInXZ(self, minY)) minY = null;
if (!isVecInXZ(self, maxY)) maxY = null;
if (!isVecInXY(self, minZ)) minZ = null;
if (!isVecInXY(self, maxZ)) maxZ = null;
double[] best = null;
double bestDistSq = Double.MAX_VALUE;
if (minX != null) {
best = minX;
bestDistSq = distanceSquared(origin, minX);
bestFace = BlockFace.WEST;
}
if (maxX != null) {
double distSq = distanceSquared(origin, maxX);
if (best == null || distSq < bestDistSq) {
best = maxX;
bestDistSq = distSq;
bestFace = BlockFace.EAST;
}
}
if (minY != null) {
double distSq = distanceSquared(origin, minY);
if (best == null || distSq < bestDistSq) {
best = minY;
bestDistSq = distSq;
bestFace = BlockFace.DOWN;
}
}
if (maxY != null) {
double distSq = distanceSquared(origin, maxY);
if (best == null || distSq < bestDistSq) {
best = maxY;
bestDistSq = distSq;
bestFace = BlockFace.UP;
}
}
if (minZ != null) {
double distSq = distanceSquared(origin, minZ);
if (best == null || distSq < bestDistSq) {
best = minZ;
bestDistSq = distSq;
bestFace = BlockFace.NORTH;
}
}
if (maxZ != null) {
double distSq = distanceSquared(origin, maxZ);
if (best == null || distSq < bestDistSq) {
best = maxZ;
bestFace = BlockFace.SOUTH;
}
}
return new Pair<>(best, bestFace);
}
private static double[] getIntermediateWithXValue(double[] self, double[] other, double x) {
double d0 = other[0] - self[0];
double d1 = other[1] - self[1];
double d2 = other[2] - self[2];
if (d0 * d0 < 1.0000000116860974E-7D) {
return null;
} else {
double d3 = (x - self[0]) / d0;
return d3 >= 0.0D && d3 <= 1.0D ? new double[]{self[0] + d0 * d3, self[1] + d1 * d3, self[2] + d2 * d3} : null;
}
}
private static double[] getIntermediateWithYValue(double[] self, double[] other, double y) {
double d0 = other[0] - self[0];
double d1 = other[1] - self[1];
double d2 = other[2] - self[2];
if (d1 * d1 < 1.0000000116860974E-7D) {
return null;
} else {
double d3 = (y - self[1]) / d1;
return d3 >= 0.0D && d3 <= 1.0D ? new double[]{self[0] + d0 * d3, self[1] + d1 * d3, self[2] + d2 * d3} : null;
}
}
private static double[] getIntermediateWithZValue(double[] self, double[] other, double z) {
double d0 = other[0] - self[0];
double d1 = other[1] - self[1];
double d2 = other[2] - self[2];
if (d2 * d2 < 1.0000000116860974E-7D) {
return null;
} else {
double d3 = (z - self[2]) / d2;
return d3 >= 0.0D && d3 <= 1.0D ? new double[]{self[0] + d0 * d3, self[1] + d1 * d3, self[2] + d2 * d3} : null;
}
}
private static boolean isVecInYZ(SimpleCollisionBox self, double[] vec) {
return vec != null && vec[1] >= self.minY && vec[1] <= self.maxY && vec[2] >= self.minZ && vec[2] <= self.maxZ;
}
private static boolean isVecInXZ(SimpleCollisionBox self, double[] vec) {
return vec != null && vec[0] >= self.minX && vec[0] <= self.maxX && vec[2] >= self.minZ && vec[2] <= self.maxZ;
}
private static boolean isVecInXY(SimpleCollisionBox self, double[] vec) {
return vec != null && vec[0] >= self.minX && vec[0] <= self.maxX && vec[1] >= self.minY && vec[1] <= self.maxY;
}
public static boolean isVecInside(SimpleCollisionBox self, double[] vec) {
return vec[0] > self.minX && vec[0] < self.maxX &&
vec[1] > self.minY && vec[1] < self.maxY &&
vec[2] > self.minZ && vec[2] < self.maxZ;
}
private static double distanceSquared(double[] vec1, double[] vec2) {
double dx = vec1[0] - vec2[0];
double dy = vec1[1] - vec2[1];
double dz = vec1[2] - vec2[2];
return dx * dx + dy * dy + dz * dz;
}
// The following methods remain unchanged but are included for completeness
public static double[] getLook(GrimPlayer player, float yaw, float pitch) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_12_2)) {
float f = player.trigHandler.cos(-yaw * 0.017453292F - (float)Math.PI);
float f1 = player.trigHandler.sin(-yaw * 0.017453292F - (float)Math.PI);
float f2 = -player.trigHandler.cos(-pitch * 0.017453292F);
float f3 = player.trigHandler.sin(-pitch * 0.017453292F);
return new double[]{f1 * f2, f3, f * f2};
} else {
float f = pitch * ((float) Math.PI / 180F);
float f1 = -yaw * ((float) Math.PI / 180F);
float f2 = player.trigHandler.cos(f1);
float f3 = player.trigHandler.sin(f1);
float f4 = player.trigHandler.cos(f);
float f5 = player.trigHandler.sin(f);
return new double[]{f3 * f4, -f5, f2 * f4};
}
}
// public static double getMinReachToBox(GrimPlayer player, SimpleCollisionBox targetBox) {
// boolean giveMovementThresholdLenience = player.packetStateData.didLastMovementIncludePosition || player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9);
// if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) targetBox.expand(0.1);
//
// double lowest = Double.MAX_VALUE;
//
// for (double eyes : player.getPossibleEyeHeights()) {
// if (giveMovementThresholdLenience) targetBox.expand(player.getMovementThreshold());
// double[] from = new double[]{player.x, player.y + eyes, player.z};
// double[] closestPoint = VectorUtils.cutBoxToVector(from, targetBox);
// lowest = Math.min(lowest, Math.sqrt(distanceSquared(from, closestPoint)));
// }
//
// return lowest;
// }
}
+46 -2
View File
@@ -81,9 +81,53 @@ FarPlace:
PositionPlace:
cancelvl: 5
RotationPlace:
cancelvl: 5
LineOfSightPlace:
cancelvl: 1
# Determines whether to only run the LineOfSightPlace check on specific blocks
# Increases performance, mainly intended for mini-game servers which already prevent block placement
# And want to stop container or workstation access through walls
use-block-whitelist: false
block-whitelist:
- ANVIL
- BARREL
- BEACON
- BEEHIVE
- BEE_NEST
- BLAST_FURNACE
- BREWING_STAND
- CAMPFIRE
- CARTOGRAPHY_TABLE
- CHEST
- CHEST_MINECART
- CHIPPED_ANVIL
- COMPOSTER
- CRAFTING_TABLE
- DAMAGED_ANVIL
- DISPENSER
- DROPPER
- ENCHANTING_TABLE
- ENDER_CHEST
- FURNACE
- FURNACE_MINECART
- FLETCHING_TABLE
- GRINDSTONE
- HOPPER
- HOPPER_MINECART
- ITEM_FRAME
- JUKEBOX
- LECTERN
- LOOM
- RESPAWN_ANCHOR
- SHULKER_BOX
- SMITHING_TABLE
- SMOKER
- SOUL_CAMPFIRE
- STONECUTTER
- TRAPPED_CHEST
- COMMAND_BLOCK
- STRUCTURE_BLOCK
- JIGSAW
# Prediction-based no slow check
# Grim ist verantwortlich für den fehlerhaften Netcode hier... schwer zu falsen, selbst wenn man Rechtsklick und Offhand-Taste spammt.
# Viel stabiler als andere Anticheats, aber bitte melde alle Falses... Ich habe hier eine Menge Netcode-Probleme behoben.
+46 -2
View File
@@ -81,8 +81,52 @@ FarPlace:
PositionPlace:
cancelvl: 5
RotationPlace:
cancelvl: 5
LineOfSightPlace:
cancelvl: 1
# Determines whether to only run the LineOfSightPlace check on specific blocks
# Increases performance, mainly intended for mini-game servers which already prevent block placement
# And want to stop container or workstation access through walls
use-block-whitelist: false
block-whitelist:
- ANVIL
- BARREL
- BEACON
- BEEHIVE
- BEE_NEST
- BLAST_FURNACE
- BREWING_STAND
- CAMPFIRE
- CARTOGRAPHY_TABLE
- CHEST
- CHEST_MINECART
- CHIPPED_ANVIL
- COMPOSTER
- CRAFTING_TABLE
- DAMAGED_ANVIL
- DISPENSER
- DROPPER
- ENCHANTING_TABLE
- ENDER_CHEST
- FURNACE
- FURNACE_MINECART
- FLETCHING_TABLE
- GRINDSTONE
- HOPPER
- HOPPER_MINECART
- ITEM_FRAME
- JUKEBOX
- LECTERN
- LOOM
- RESPAWN_ANCHOR
- SHULKER_BOX
- SMITHING_TABLE
- SMOKER
- SOUL_CAMPFIRE
- STONECUTTER
- TRAPPED_CHEST
- COMMAND_BLOCK
- STRUCTURE_BLOCK
- JIGSAW
# Prediction-based no slow check
# Grim accounts for buggy netcode here... hard to false even when spamming right click and offhand button
+46 -3
View File
@@ -82,9 +82,52 @@ FarPlace:
PositionPlace:
cancelvl: 5
RotationPlace:
cancelvl: 5
LineOfSightPlace:
cancelvl: 1
# Determines whether to only run the LineOfSightPlace check on specific blocks
# Increases performance, mainly intended for mini-game servers which already prevent block placement
# And want to stop container or workstation access through walls
use-block-whitelist: false
block-whitelist:
- ANVIL
- BARREL
- BEACON
- BEEHIVE
- BEE_NEST
- BLAST_FURNACE
- BREWING_STAND
- CAMPFIRE
- CARTOGRAPHY_TABLE
- CHEST
- CHEST_MINECART
- CHIPPED_ANVIL
- COMPOSTER
- CRAFTING_TABLE
- DAMAGED_ANVIL
- DISPENSER
- DROPPER
- ENCHANTING_TABLE
- ENDER_CHEST
- FURNACE
- FURNACE_MINECART
- FLETCHING_TABLE
- GRINDSTONE
- HOPPER
- HOPPER_MINECART
- ITEM_FRAME
- JUKEBOX
- LECTERN
- LOOM
- RESPAWN_ANCHOR
- SHULKER_BOX
- SMITHING_TABLE
- SMOKER
- SOUL_CAMPFIRE
- STONECUTTER
- TRAPPED_CHEST
- COMMAND_BLOCK
- STRUCTURE_BLOCK
- JIGSAW
# Comprobación de NoSlow basado en predicciones
# Grim compensa por código de red con errores aquí... difícil de sacar un falso positivo incluso haciendo spam del
# clic derecho y la mano secundaria.
+46 -3
View File
@@ -81,9 +81,52 @@ FarPlace:
PositionPlace:
cancelvl: 5
RotationPlace:
cancelvl: 5
LineOfSightPlace:
cancelvl: 1
# Determines whether to only run the LineOfSightPlace check on specific blocks
# Increases performance, mainly intended for mini-game servers which already prevent block placement
# And want to stop container or workstation access through walls
use-block-whitelist: false
block-whitelist:
- ANVIL
- BARREL
- BEACON
- BEEHIVE
- BEE_NEST
- BLAST_FURNACE
- BREWING_STAND
- CAMPFIRE
- CARTOGRAPHY_TABLE
- CHEST
- CHEST_MINECART
- CHIPPED_ANVIL
- COMPOSTER
- CRAFTING_TABLE
- DAMAGED_ANVIL
- DISPENSER
- DROPPER
- ENCHANTING_TABLE
- ENDER_CHEST
- FURNACE
- FURNACE_MINECART
- FLETCHING_TABLE
- GRINDSTONE
- HOPPER
- HOPPER_MINECART
- ITEM_FRAME
- JUKEBOX
- LECTERN
- LOOM
- RESPAWN_ANCHOR
- SHULKER_BOX
- SMITHING_TABLE
- SMOKER
- SOUL_CAMPFIRE
- STONECUTTER
- TRAPPED_CHEST
- COMMAND_BLOCK
- STRUCTURE_BLOCK
- JIGSAW
# Vérification de no-slow basée sur la prédiction.
# Grim tient compte des problèmes de netcode ici... difficile à tromper même en spamant le clic droit et le bouton de la main secondaire.
# Bien plus stable que les autres anti-triche, mais veuillez signaler toute fausse détection... J'ai corrigé de nombreux problèmes de netcode ici.
+46 -3
View File
@@ -78,9 +78,52 @@ PositionPlace:
# Livello di violazione per il piazzamento in una posizione specifica
cancelvl: 5
RotationPlace:
# Livello di violazione per il piazzamento con rotazione specifica
cancelvl: 5
LineOfSightPlace:
cancelvl: 1
# Determines whether to only run the LineOfSightPlace check on specific blocks
# Increases performance, mainly intended for mini-game servers which already prevent block placement
# And want to stop container or workstation access through walls
use-block-whitelist: false
block-whitelist:
- ANVIL
- BARREL
- BEACON
- BEEHIVE
- BEE_NEST
- BLAST_FURNACE
- BREWING_STAND
- CAMPFIRE
- CARTOGRAPHY_TABLE
- CHEST
- CHEST_MINECART
- CHIPPED_ANVIL
- COMPOSTER
- CRAFTING_TABLE
- DAMAGED_ANVIL
- DISPENSER
- DROPPER
- ENCHANTING_TABLE
- ENDER_CHEST
- FURNACE
- FURNACE_MINECART
- FLETCHING_TABLE
- GRINDSTONE
- HOPPER
- HOPPER_MINECART
- ITEM_FRAME
- JUKEBOX
- LECTERN
- LOOM
- RESPAWN_ANCHOR
- SHULKER_BOX
- SMITHING_TABLE
- SMOKER
- SOUL_CAMPFIRE
- STONECUTTER
- TRAPPED_CHEST
- COMMAND_BLOCK
- STRUCTURE_BLOCK
- JIGSAW
NoSlowA:
# Limite per rilevare l'uso scorretto dell'oggetto
+46 -3
View File
@@ -81,9 +81,52 @@ FarPlace:
PositionPlace:
cancelvl: 5
RotationPlace:
cancelvl: 5
LineOfSightPlace:
cancelvl: 1
# Determines whether to only run the LineOfSightPlace check on specific blocks
# Increases performance, mainly intended for mini-game servers which already prevent block placement
# And want to stop container or workstation access through walls
use-block-whitelist: false
block-whitelist:
- ANVIL
- BARREL
- BEACON
- BEEHIVE
- BEE_NEST
- BLAST_FURNACE
- BREWING_STAND
- CAMPFIRE
- CARTOGRAPHY_TABLE
- CHEST
- CHEST_MINECART
- CHIPPED_ANVIL
- COMPOSTER
- CRAFTING_TABLE
- DAMAGED_ANVIL
- DISPENSER
- DROPPER
- ENCHANTING_TABLE
- ENDER_CHEST
- FURNACE
- FURNACE_MINECART
- FLETCHING_TABLE
- GRINDSTONE
- HOPPER
- HOPPER_MINECART
- ITEM_FRAME
- JUKEBOX
- LECTERN
- LOOM
- RESPAWN_ANCHOR
- SHULKER_BOX
- SMITHING_TABLE
- SMOKER
- SOUL_CAMPFIRE
- STONECUTTER
- TRAPPED_CHEST
- COMMAND_BLOCK
- STRUCTURE_BLOCK
- JIGSAW
# Op voorspelling gebaseerde NoSlow-controle
# Grim is verantwoordelijk voor buggy netcode hier... moeilijk om vals te spelen, zelfs als je met rechts klikt en de offhand-knop spamt
# Veel stabieler dan andere anticheats, maar rapporteer alsjeblieft alle falses... Ik heb hier een heleboel netcode-problemen opgelost.
+91 -49
View File
@@ -10,16 +10,17 @@ alerts:
# Isso controla se/como os alertas são compartilhados entre os servidores conectados ao proxy.
# Deve-se ter 'bungee-plugin-message-channel' habilitado na configuração do Velocity se estiver usando.
proxy:
# Os alertas deveriam ser enviados para outros servidores conectados no mesmo proxy?
# Deviam os alertas ser enviados para outros servidores conectados no mesmo proxy?
send: false
# Os alertas deveriam ser recebidos de outros servidores conectados no mesmo proxy?
# Deviam os alertas ser recebidos de outros servidores conectados no mesmo proxy?
receive: false
verbose:
print-to-console: false
client-brand:
# Isso signifia que, caso o nome do cliente seja igual aos listados abaixo, não será notificados aos operadores.
# Isso signifia que, caso a marca do cliente seja igual às listadas abaixo, não será notificado aos
# administradores.
ignored-clients:
- "^vanilla$"
- "^fabric$"
@@ -27,7 +28,7 @@ client-brand:
- "^Feather Fabric$"
spectators:
# Esconde todos espectadores com a permissão 'grim.spectator' mesmo se não estiverem espectando ativamente.
# Esconde todos espectadores com a permissão 'grim.spectator' mesmo se não estiverem ativamente espectando.
hide-regardless: false
# Fará os espectadores serem escondidos apenas nos mundos listados abaixo, deixe em branco para habilitar em todos os mundos.
allowed-worlds:
@@ -36,35 +37,35 @@ spectators:
# Quanto tempo os jogadores tem até expulsarmos eles por inatividade? Padrão = 60 segundos.
max-transaction-time: 60
# Should the duplicate movement packet be cancelled?
# Mojang has fixed this issue in 1.21. This was their attempt to fix the "bucket desync". https://bugs.mojang.com/browse/MC-12363
# This setting only applies to 1.17-1.20.5 clients on 1.8 servers.
# Deve-se cancelar o pacote de movimento duplicado?
# A Mojang arrumou esse erro na 1.21, sendo sua tentativa de concertar a dessincronização do balde. https://bugs.mojang.com/browse/MC-12363
# Isso só se aplica à clientes da 1.17-1.20.5 em servidores da 1.8.
cancel-duplicate-packet: true
# Whether or not to ignore the rotation in duplicate packets
# Deve-se ignorar rotação em pacotes duplicados?
ignore-duplicate-packet-rotation: false
Simulation:
# Por quanto deveríamos multiplicar a vantagem caso o jogador não tenha falhado em nem uma verificação?
# Por quanto deve-se multiplicar a vantagem desde a última vez que falhou uma verificação?
# Isso é como a configuração padrão se parece (eixo X = segundos, eixo Y = 1/1000 blocos): https://www.desmos.com/calculator/d4ufgxrxer
setback-decay-multiplier: 0.999
# O quão longe o jogador deve estar da área simulada para adicionarmos violações?
# O quão longe o jogador deve estar da área simulada para registrar violações?
# Medido em blocos do último movimento legítmo.
# Aceitamos o Optifine trocando as tabelas trigonométricas, mas abaixar para 0.001 reduzirá o 'FastMath'
# de falhar, caso esta compensação não funcionar...
# Incluímos o Optifine trocando as tabelas trigonométricas; entretanto, abaixar para 0.001 reduzirá o 'FastMath'
# de falhar, caso essa compensação não funcionar.
threshold: 0.001
# O quão longe o jogador deve estar da área simulada para imediatamente recuá-lo?
# -1 desabilitará
immediate-setback-threshold: 0.1
# O quão longe da área simulada sobre todos os ticks para começarmos a recuar?
# Qual é a soma do desalinhamento da simulação em todos os ticks até começarmos a recuar?
# -1 desabilitará
max-advantage: 1
# Depois de 50 segundos, com a configuração padrão, o jogador irá de 4 blocos -> 1 bloco de vantagem.
# Depois de 50 segundos, com a configuração padrão, o jogador irá de 4 blocos a 1 bloco de vantagem.
# Isso serve para previnir o jogador de alcançar violações demais e nunca conseguir se livrar delas.
# Cela de vantagens padrão (eixo X = segundos, eixo Y = 1/1000 blocos): https://www.desmos.com/calculator/4lovswdarj
max-ceiling: 4
# Verifica se um jogador entrou em um bloco durante o movimento.
# Verifica se um jogador entrou em um bloco ao se mover.
Phase:
setbackvl: 1 # Entrar em blocos possibilita escalar paredes, além disso, essa verificação é relativamente estável.
decay: 0.005
@@ -81,33 +82,76 @@ FarPlace:
PositionPlace:
cancelvl: 5
RotationPlace:
cancelvl: 5
LineOfSightPlace:
cancelvl: 1
# Determines whether to only run the LineOfSightPlace check on specific blocks
# Increases performance, mainly intended for mini-game servers which already prevent block placement
# And want to stop container or workstation access through walls
use-block-whitelist: false
block-whitelist:
- ANVIL
- BARREL
- BEACON
- BEEHIVE
- BEE_NEST
- BLAST_FURNACE
- BREWING_STAND
- CAMPFIRE
- CARTOGRAPHY_TABLE
- CHEST
- CHEST_MINECART
- CHIPPED_ANVIL
- COMPOSTER
- CRAFTING_TABLE
- DAMAGED_ANVIL
- DISPENSER
- DROPPER
- ENCHANTING_TABLE
- ENDER_CHEST
- FURNACE
- FURNACE_MINECART
- FLETCHING_TABLE
- GRINDSTONE
- HOPPER
- HOPPER_MINECART
- ITEM_FRAME
- JUKEBOX
- LECTERN
- LOOM
- RESPAWN_ANCHOR
- SHULKER_BOX
- SMITHING_TABLE
- SMOKER
- SOUL_CAMPFIRE
- STONECUTTER
- TRAPPED_CHEST
- COMMAND_BLOCK
- STRUCTURE_BLOCK
- JIGSAW
# Verificação de NoSlow por simulação.
# Grim considera erros no netcode... difícil de gerar falsos positivos, até mesmo spamando o blotão direito e a offhand.
# Muito mais estável que outros anticheats, mas por favor, reporte quaisquer falsos positivos... eu já arrumei diversos
# Grim considera erros no netcode. Difícil de gerar falsos positivos, até mesmo spamando o blotão direito e a offhand.
# Muito mais estável que outros anticheats, mas por favor, reporte quaisquer falsos positivos. Eu já arrumei diversos
# erros de netcode.
NoSlowA:
# Quanto de desvio da simulação é considerado trapaça?
# Por padrão isso é menor que outros desvios.
# Acusa com 0.03-0.2 com consistência com o NoSlow ativo.
# Quanto de desalinhamento é considerado trapaça?
# Por padrão é menor que os outros.
# Em 0.03-0.2 detecta com consistência.
threshold: 0.001
# Recua rapidamente no primeiro item para remover quaisquer vantagens que o NoSlow dá.
# Recuo imediato para inibir as vantagens rapidamente.
setbackvl: 5
# Decai quando o jogador usa um item E é desasceletado por isso.
# Decai quando o jogador usa um item e é efetivamente desacelerado.
decay: 0.05
Knockback:
# Por quanto deveriamos multiplicar a vantagem caso o jogador não tenha falhado nessa verificação?
# Por quanto deve-se multiplicar a vantagem desde a última vez que falhou na verificação?
setback-decay-multiplier: 0.999
# O quão longe da área do recuo recebido o jogador deve estar para adicionarmos uma violação?
# O quão longe da área do recuo recebido o jogador deve estar para registrar violações?
# Medido em blocos do recuo recebido.
threshold: 0.001
# O quão longe o jogador deve estar da área simulada para imediatamente recuá-lo?
# -1 desabilitará.
immediate-setback-threshold: 0.1
# O quão longe da área simulada sobre todos os ticks deverá ser para começarmos a recuar?
# Qual é a soma do desalinhamento da simulação em todos os ticks até começarmos a recuar?
# -1 desabilitará.
max-advantage: 1
# Isso serve para previnir o jogador de alcançar violações demais e nunca conseguir se livrar delas.
@@ -120,16 +164,16 @@ Explosion:
TimerA:
setbackvl: 10
# Milissegundos que o jogador pode acumular para depois usar quando eles têm um atraso no ping.
# Pode permitir o uso de fast use / fast heal / fast bow na 1.8 caso o valor seja definido muito alto. 120ms parece
# um bom balanceamento.
# Pode ignorar o uso de fast use / fast heal / fast bow na 1.8 caso o valor seja definido muito alto.
# 120ms parece um bom balanceamento.
drift: 120
# Ping no qual a verificação vai começar a limitar o balançeamento do timer para previnir abuso.
# Pode causar recuos a jogadores legítmos, mas somente se estiverem acima deste ping.
# Pode causar recuos a jogadores legítmos, mas somente se estiverem acima desse ping.
# -1 desabilitará.
ping-abuse-limit-threshold: 1000
NegativeTimer:
# Número de milissegundos perdidos ao se mover antes de começarmos a adicionar violações.
# Número de milissegundos perdidos ao se mover antes de começarmos a registrar violações.
drift: 1200
# Mesmo método usado no TimerA, mas para veículos.
@@ -141,56 +185,54 @@ EntityControl:
setbackvl: 25
Reach:
# Por quanto devíamos expandir as caixas de colisão? 0.0005 deve detectar 3.0005+ de alcance.
# Por quanto deve-se expandir as caixas de colisão? 0.0005 deve detectar 3.0005+ de alcance.
#
# Há 0.03 de expansão forçada nas versões 1.9-1.18.1 (não a 1.18.2), ou algumas combinações de cliente/servidor por
# conta de mudanças no protocolo e limitações. Essa verificação é mais poderosa com clientes da 1.7/1.8 em servidores
# da 1.8.
threshold: 0.0005
# Deveríamos cancelar hits que sabemos que são impossíveis?
# O alcance de 3.00-3.03 pode acabar passando, mas continuarão adicionando violações, por conta de limitações na
# ordem de pacotes.
# Deve-se cancelar hits que sabemos que são impossíveis?
# O alcance de 3.00-3.03 pode acabar passando, por conta da ordem de pacotes, mas continuará registrando violações.
block-impossible-hits: true
# Isso enviará um pacote adicional no final de todos os ticks para aumentar a probabilidade de detectarmos trapaceiros.
# Isso injeta na lista de conecção do servidor para envar um pacote final logo antes do servidor recarregar.
# Habilitando isso, irá aumentar o uso de banda para todos os jogadores.
# Isso não irá interferir na performance do servidor.
# Habilitando isso, aumentará a probabilidade de detectarmos trapaceiros.
# Deixando-o desabilitado, continuará detectando cheaters e não irá causar falsos positivos.
# A menos que estiver em um servidor focado em PvP esse pacote adicional não é recomendado.
# Deixando-o desabilitado, continuará detectando trapaceiros e não irá causar falsos positivos.
# A menos que estiver em um servidor focado em PvP, esse pacote adicional não é recomendado.
enable-post-packet: false
exploit:
# Você pode ganhar altas velocidades enquanto pular correndo com uma elytra, isso previne o exploitação de ocorrer quando
# definido falso.
# Você pode ganhar altas velocidades se pular correndo com uma elytra, previne isso de ocorrer quando definido falso.
# A Mojang vacilou com o netcode por fazer a Elytra iniciar pelo lado do cliente.
# As Elytras levam 0.99 de fricção horizontal, então constantemente adicionando 0.2 de velocidade horizontal resulta
# em velocidades altíssimas.
allow-sprint-jumping-when-using-elytra: true
# Essa opção mitiga o posicionamento de blocos em blocos fantasmas, ressincronizando o jogador quando isso ocorrer.
# Essa opção mitiga posicionar blocos em blocos fantasma, ressincronizando o jogador quando isso ocorrer.
allow-building-on-ghostblocks: true
# Este ajuste, influenciado pelo booleano acima, define a distância para verificar blocos fantasmas.
# Seu intervalo válido é limitado de 2 a 4.
# Esse ajuste, influído pela opção acima, define a distância para verificar blocos fantasmas.
# O valor válido é limitado de 2 a 4.
distance-to-check-if-ghostblocks: 2
# Habilita registrar plugins que são injetados na netty ao entrar para depurar erros de compatibilidade.
# Registra plugins que são injetados na netty ao entrar para depurar erros de compatibilidade.
debug-pipeline-on-join: false
# Habilita verificações experimentais.
experimental-checks: false
# Grim às vezes cancela pacotes ilegais como com o timer, depois de X pacotes em um segundo que foram cancelados, quando
# devemos simplesmente expulsar o jogador? Isso é requirido já que alguns limitadores de pacotes não contam pacotes
# deve-se simplesmente expulsar o jogador? Isso é requirido já que alguns limitadores de pacotes não contam pacotes
# cancelados pelo Grim.
packet-spam-threshold: 100
# Grim é capaz de garantir que um jogador fora do estado de voo não possa ter mais de X milissegundos de ping.
# Grim pode impor que o jogador fora do estado de voo não possa ter mais de X milissegundos de ping.
# Isso ocorre porque o Grim atualmente não verifica os jogadores que estão voando.
# To disable, use -1
# -1 desabilitará.
max-ping-out-of-flying: 1000
# Maximum ping when a firework boost is removed from the player.
# This prevents high latency players from being able to use 1 firework boost with an elytra forever.
# Ping máximo quando o impulso do foguete acaba.
# Previne jogadores com ping alto de usarem um foguete ara voar indefinidamente.
max-ping-firework-boost: 1000
config-version: 9
+46 -3
View File
@@ -81,9 +81,52 @@ FarPlace:
PositionPlace:
cancelvl: 5
RotationPlace:
cancelvl: 5
LineOfSightPlace:
cancelvl: 1
# Determines whether to only run the LineOfSightPlace check on specific blocks
# Increases performance, mainly intended for mini-game servers which already prevent block placement
# And want to stop container or workstation access through walls
use-block-whitelist: false
block-whitelist:
- ANVIL
- BARREL
- BEACON
- BEEHIVE
- BEE_NEST
- BLAST_FURNACE
- BREWING_STAND
- CAMPFIRE
- CARTOGRAPHY_TABLE
- CHEST
- CHEST_MINECART
- CHIPPED_ANVIL
- COMPOSTER
- CRAFTING_TABLE
- DAMAGED_ANVIL
- DISPENSER
- DROPPER
- ENCHANTING_TABLE
- ENDER_CHEST
- FURNACE
- FURNACE_MINECART
- FLETCHING_TABLE
- GRINDSTONE
- HOPPER
- HOPPER_MINECART
- ITEM_FRAME
- JUKEBOX
- LECTERN
- LOOM
- RESPAWN_ANCHOR
- SHULKER_BOX
- SMITHING_TABLE
- SMOKER
- SOUL_CAMPFIRE
- STONECUTTER
- TRAPPED_CHEST
- COMMAND_BLOCK
- STRUCTURE_BLOCK
- JIGSAW
# Основанная на предсказаниях проверка на отсутствие замедления
# Grim учитывает баги неткода здесь... трудно ошибиться, даже если спамить правым кликом и кнопкой оффхенд.
# Намного стабильнее, чем другие античиты, но, пожалуйста, сообщайте о любых фейлах... Я исправил тонну проблем с неткодом.
+236
View File
@@ -0,0 +1,236 @@
# GrimAC ana konfigrasyon dosyası
# Kaynak kodu: https://github.com/MWHunter/Grim
# Telif Hakkı 2024 DefineOutside ve katkıda bulunanlar, GPLv3 altında lisanslanmıştır.
# Değiştirilmiş ikili dosyalar veya kopyalanmış grim kodlu eklentiler özel olmalıdır,
# veya ek ücret ödemeden alıcılara sunulan tam kaynak kodu ile.
alerts:
# Oyunculara uyarı yayınlamanın yanı sıra, uyarılar konsola da gönderilmeli mi?
print-to-console: true
# Bu, uyarıların proxy'nize bağlı sunucular arasında paylaşılıp paylaşılmayacağını/nasıl paylaşılacağını kontrol eder.
# Velocity kullanılıyorsa, Velocity yapılandırmanızda 'bungee-plugin-message-channel' etkinleştirilmiş olmalıdır.
proxy:
# Proxy'nize bağlı diğer sunuculara uyarılar gönderilmeli mi?
send: false
# Diğer sunuculardan alınan uyarılar bu sunucuya duyurulmalı mı?
receive: false
verbose:
print-to-console: false
client-brand:
# Bu, giriş yapılan clientin aşağıdaki regex'lerle eşleşmesi durumunda markalarını operatörlere yayınlamayacağı anlamına gelir
ignored-clients:
- "^vanilla$"
- "^fabric$"
- "^lunarclient:v\\d+\\.\\d+\\.\\d+-\\d{4}$"
- "^Feather Fabric$"
spectators:
# Seyirciler aktif olarak izliyor olsalar bile, grim.spectator iznine sahip tüm seyircileri gizle
hide-regardless: false
# Sadece bu dünyalarda seyircileri gizleyecek, tüm dünyalara izin vermek için boş bırakın
allowed-worlds:
- ""
# Oyuncuların zaman aşımından dolayı sunucudan atılmadan önce ne kadar süreleri olmalı? Varsayılan = 60 saniye
max-transaction-time: 60
# Kopya hareket paketinin iptal edilip edilmemesi gerektiği
# Mojang bu sorunu 1.21'de çözdü. Bu, "kova senkronizasyonu" sorununu düzeltme girişimiydi. https://bugs.mojang.com/browse/MC-12363
# Bu ayar yalnızca 1.17-1.20.5 istemcileri için 1.8 sunucularında geçerlidir.
cancel-duplicate-packet: true
# Eşya çoğaltma paketlerindeki rotayı göz ardı edip etmeyeceğimiz
ignore-duplicate-packet-rotation: false
Simulation:
# Oyuncu meşru olduğunda toplam avantajı ne kadar çarpmalıyız?
# Varsayılan yapılandırma bu şekilde görünür (x ekseni = saniye, y ekseni = 1/1000 blok): https://www.desmos.com/calculator/d4ufgxrxer
setback-decay-multiplier: 0.999
# Oyuncunun hareketinden ne kadar büyük bir sapma ihlal oluşturmalıyız?
# Mümkün olan hareketten blok cinsinden ölçülür
# Optifine için trig tablolarını değiştirerek hesaba katıyoruz, ancak bunu 0.001'e düşürmek FastMath'ı azaltır
# Bu telafi işe yaramazsa anticheat'i işaretle...
threshold: 0.001
# Bir tik içinde ne kadar büyük bir ihlal olmalı ki oyuncu hemen geri dönsün?
# Devre dışı bırakmak için -1 yazın
immediate-setback-threshold: 0.1
# Tüm tikler boyunca ne kadar büyük bir avantaja sahip olmalıyız ki geri dönüş yapmaya başlayalım?
# Devre dışı bırakmak için -1 yazın
max-advantage: 1
# Varsayılan config ile 50 saniye sonra, oyuncu 4 bloktan -> 1 bloğa kadar avantaj kazanır
# Bu, oyuncunun çok fazla ihlal biriktirmesini ve bunların hepsini temizleyememesini önlemek içindir
# Varsayılan avantaj tavanı (x ekseni = saniye, y ekseni = 1/1000 blok): https://www.desmos.com/calculator/4lovswdarj
max-ceiling: 4
# Bir oyuncunun bir hareket sırasında bir bloğa girip girmediğini kontrol eder
Phase:
setbackvl: 1 # Blokların içinde glitch yapmak duvara tırmanmaya izin verebilir, ayrıca bu kontrol nispeten stabildir
decay: 0.005
AirLiquidPlace:
cancelvl: 0
FabricatedPlace:
cancelvl: 5
FarPlace:
cancelvl: 5
PositionPlace:
cancelvl: 5
LineOfSightPlace:
cancelvl: 1
# Determines whether to only run the LineOfSightPlace check on specific blocks
# Increases performance, mainly intended for mini-game servers which already prevent block placement
# And want to stop container or workstation access through walls
use-block-whitelist: false
block-whitelist:
- ANVIL
- BARREL
- BEACON
- BEEHIVE
- BEE_NEST
- BLAST_FURNACE
- BREWING_STAND
- CAMPFIRE
- CARTOGRAPHY_TABLE
- CHEST
- CHEST_MINECART
- CHIPPED_ANVIL
- COMPOSTER
- CRAFTING_TABLE
- DAMAGED_ANVIL
- DISPENSER
- DROPPER
- ENCHANTING_TABLE
- ENDER_CHEST
- FURNACE
- FURNACE_MINECART
- FLETCHING_TABLE
- GRINDSTONE
- HOPPER
- HOPPER_MINECART
- ITEM_FRAME
- JUKEBOX
- LECTERN
- LOOM
- RESPAWN_ANCHOR
- SHULKER_BOX
- SMITHING_TABLE
- SMOKER
- SOUL_CAMPFIRE
- STONECUTTER
- TRAPPED_CHEST
- COMMAND_BLOCK
- STRUCTURE_BLOCK
- JIGSAW
# Tahmin tabanlı yavaşlama kontrolü
# Grim burada hatalı ağ kodunu dikkate alır... sağ tıklama ve el yan tuşunu spam yapsanız bile yanlış pozitiflik oluşturması zordur
# Diğer anti-hilelerden çok daha stabil, ancak herhangi bir yanlış pozitif rapor edin... burada birçok ağ kodu sorununu çözdüm.
NoSlowA:
# Ne kadar bir sapma "hile" olarak kabul edilir?
# Varsayılan olarak bu, diğer sapmalardan daha düşüktür
# NoSlow açıkken 0.03-0.2 aralığında sürekli olarak uyarı verir
threshold: 0.001
# NoSlow'un sağladığı avantajı ortadan kaldırmak için ilk itemi hızlı bir şekilde geri alın
setbackvl: 5
# Oyuncu bir eşya kullandığında VE onun tarafından yavaşlatıldığında beklenilen zaman
decay: 0.05
Knockback:
# Oyuncu legit olduğunda toplam avantajı ne kadarla çarpmalıyız
setback-decay-multiplier: 0.999
# Oyuncunun hızından ne kadar büyük bir sapma ihlal oluşturmalıyız?
# Mümkün olan hızdan blok cinsinden ölçülür
threshold: 0.001
# Bir tik içinde ne kadar büyük bir ihlal olmalı ki oyuncu hemen geri dönsün?
# Devre dışı bırakmak için -1 yazın
immediate-setback-threshold: 0.1
# Tüm tikler boyunca ne kadar büyük bir avantaja sahip olmalıyız ki geri dönüş yapmaya başlayalım?
# Devre dışı bırakmak için -1 yazın
max-advantage: 1
# Bu, oyuncunun çok fazla ihlal toplamasını ve asla hepsini temizleyememesini engellemek içindir
max-ceiling: 4
Explosion:
threshold: 0.001
setbackvl: 3
TimerA:
setbackvl: 10
# Oyuncunun geri kaldığında biriktirebileceği milisaniye miktarı
# Çok yüksek ayarlandığında 1.8 hızlı kullanma/hızlı iyileşme/hızlı yay atlama durumlarına izin verebilir, 120 ms iyi bir denge gibi görünüyor
drift: 120
# Kontrolün zamanlayıcı dengesini sınırlamaya başlayacağı ping değeri, kötüye kullanımı önlemek için.
# Bu, yalnızca bu ping eşiğini aşan meşru oyuncular için bazı geri dönüşlere neden olabilir.
# Devre dışı bırakmak için -1 yazın
ping-abuse-limit-threshold: 1000
NegativeTimer:
# Uyarı vermeye başlamadan önce hareket halindeyken kaybedilen milisaniye sayısı
drift: 1200
# TimerA ile aynı kontrol yöntemi, ancak araçlar için
TimerVehicle:
# Hedef 1.005 timer
setbackvl: 10
EntityControl:
setbackvl: 25
Reach:
# Hitbox'ları ne kadar genişletmeliyiz? 0.0005, 3.0005+ menzili tespit etmelidir
#
# 1.9-1.18.1 (1.18.2 değil) ile zorunlu olarak 0.03 genişletme vardır veya bazı istemci/sunucu kombinasyonları nedeniyle
# protokol değişiklikleri ve sınırlamaları nedeniyle. Bu kontrol, 1.7/1.8 istemcilerle 1.8 sunucularda en güçlüdür.
threshold: 0.0005
# İmkansız olduğunu bildiğimiz vuruşları iptal etmeli miyiz?
# Paket sırası sınırlamaları nedeniyle 3.00-3.03 arası vuruşlar gerçekleşebilir, ancak yine de işaretlenebilir
block-impossible-hits: true
# Bu, hileleri yakalama olasılığını artırmak için her tikin sonunda ek bir paket gönderecektir
# Bu, sunucunun bağlantı listesine enjekte edilerek sunucu boşaltmadan hemen önce son bir paket göndermek için yapılır
# Bunu etkinleştirmek, tüm oyuncular için bant genişliği kullanımını artıracaktır
# Bu, genel sunucu performansını düşürmez
# Bunu etkinleştirmek, daha fazla hilecinin yakalanmasını sağlar
# Bu özellik devre dışı bırakılsa bile hileciler yakalanır ve yanlış pozitifler oluşmaz
# 1.8 PvP odaklı bir sunucu değilseniz, bu ek paket önerilmez
enable-post-packet: false
exploit:
# Elytra ile sprint zıplarken yüksek hızlar kazanabilirsiniz, bu ayar false olarak ayarlandığında bu istismarı önler
# Mojang, elytra'nın istemci tarafında başlamasını ve sunucu tarafında bitmesini sağlayarak netcode'u bozdu
# Elytra'lar 0.99 yatay sürtünme alır, bu yüzden sürekli olarak 0.2 yatay hız eklemek çok yüksek hızlara yol açar
allow-sprint-jumping-when-using-elytra: true
# Bu seçenek, oyuncunun hayalet bloklar üzerindeki yerleşimini, gerçekleştiğinde oyuncuyu yeniden senkronize ederek hafifletir
allow-building-on-ghostblocks: true
# Bu ayar, yukarıdaki boolean ile etkilenen hayalet blokları kontrol etmek için mesafeyi tanımlar
# Geçerli aralığı 2 ile 4 arasında sınırlıdır
distance-to-check-if-ghostblocks: 2
# Netty'ye enjekte olan eklentileri giriş sırasında günlüklemeyi etkinleştirerek uyumluluk sorunlarını gider
debug-pipeline-on-join: false
# Deneysel kontrolleri etkinleştir
experimental-checks: false
# Grim bazen timer gibi yasa dışı paketleri iptal eder; bir saniyede X paket iptal edildikten sonra, ne zaman
# oyuncuyu basitçe atmalıyız? Bu, bazı paket sınırlayıcılarının Grim tarafından iptal edilen paketleri saymaması gerektiğindendir.
packet-spam-threshold: 100
# Grim, uçma durumundan çıkmış bir oyuncunun X milisaniyeden fazla ping'e sahip olamayacağını uygulayabilir
# Bu, Grim'in şu anda uçan oyuncuları kontrol etmemesindendir
# Devre dışı bırakmak için -1 yazın
max-ping-out-of-flying: 1000
# Bir havai fişek ivmesi oyuncudan çıkarıldığında maksimum ping.
# Bu, yüksek gecikmeli oyuncuların bir havai fişek ivmesini elytra ile sonsuza kadar kullanmalarını engeller.
max-ping-firework-boost: 1000
config-version: 9
# Translated by Kayera
# Have a nice day :)
+65 -17
View File
@@ -2,7 +2,7 @@
# 源代码链接: https://github.com/MWHunter/Grim
# 版权归 DefineOutside 和 贡献者所有,基于GPLv3开源协议。
# 修改过的二进制文件,或带有复制的Grim代码的插件,必须是私有仓库,不得公开售卖此插件。
# 中文翻译提供 nuym ,如有错误请提交 Issues 并@我
# 中文翻译提供 nuym/Dg32z_ ,如有错误请提交 Issues 并@我
alerts:
# 是否在控制台显示作弊消息?
@@ -27,21 +27,22 @@ client-brand:
- "^Feather Fabric$"
spectators:
# 把所有拥有 grim.spectartor 权限的人都隐藏起来,不管他是否观察玩家。
# 将拥有 grim.spectator 权限的人进行隐藏,不管他是否在观察玩家
hide-regardless: false
# 观察者这些世界不会隐藏
allowed-worlds:
- ""
# 在我们让他们超时之前,玩家应该有多长时间? 此处的60 指60s
# 在玩家无法接收 GrimAC的 transaction 数据包后,多少秒对玩家进行踢出
# 踢出原因为: disconnect.timeout
max-transaction-time: 60
# Should the duplicate movement packet be cancelled?
# Mojang has fixed this issue in 1.21. This was their attempt to fix the "bucket desync". https://bugs.mojang.com/browse/MC-12363
# This setting only applies to 1.17-1.20.5 clients on 1.8 servers.
# 是否对重复的移动数据包进行撤销?
# 这可以修复 "bucket desync". https://bugs.mojang.com/browse/MC-12363,不过Mojang已经在1.21修复了这个问题
# 这仅仅适用于1.8服务器中使用1.17-1.20.5版本的玩家
cancel-duplicate-packet: true
# Whether or not to ignore the rotation in duplicate packets
# 是否忽视重复的数据包中的玩家转头
ignore-duplicate-packet-rotation: false
Simulation:
@@ -81,10 +82,54 @@ FarPlace:
PositionPlace:
cancelvl: 5
RotationPlace:
cancelvl: 5
LineOfSightPlace:
cancelvl: 1
# Determines whether to only run the LineOfSightPlace check on specific blocks
# Increases performance, mainly intended for mini-game servers which already prevent block placement
# And want to stop container or workstation access through walls
use-block-whitelist: false
block-whitelist:
- ANVIL
- BARREL
- BEACON
- BEEHIVE
- BEE_NEST
- BLAST_FURNACE
- BREWING_STAND
- CAMPFIRE
- CARTOGRAPHY_TABLE
- CHEST
- CHEST_MINECART
- CHIPPED_ANVIL
- COMPOSTER
- CRAFTING_TABLE
- DAMAGED_ANVIL
- DISPENSER
- DROPPER
- ENCHANTING_TABLE
- ENDER_CHEST
- FURNACE
- FURNACE_MINECART
- FLETCHING_TABLE
- GRINDSTONE
- HOPPER
- HOPPER_MINECART
- ITEM_FRAME
- JUKEBOX
- LECTERN
- LOOM
- RESPAWN_ANCHOR
- SHULKER_BOX
- SMITHING_TABLE
- SMOKER
- SOUL_CAMPFIRE
- STONECUTTER
- TRAPPED_CHEST
- COMMAND_BLOCK
- STRUCTURE_BLOCK
- JIGSAW
# Prediction-based NoSlow 检查
# 使用模拟移动进行的NoSlow(使用物品时不会减速)检查
# 在这里解释错误的服务器代码......即使在发送垃圾数据包时右键单击和副手按钮也很分辨
# 比其他反作弊更稳定,但请报告任何错误......我在这里修复了大量的代码问题。
NoSlowA:
@@ -121,13 +166,13 @@ TimerA:
# 玩家卡顿时可以累积以供以后使用的毫秒数
# 如果设置得太高,可能会允许 1.8 快速使用/快速治疗/快速弓箭绕过,120 毫秒似乎是一个很好的平衡
drift: 120
# Ping at which the check will start to limit timer balance, to prevent abuse.
# Can cause some setbacks for legitimate players but only if they are over this ping threshold.
# -1 to disable
# 在检查玩家的延迟时对timer balance进行限制, 防止滥用
# 在合法玩家的延迟超过这个延迟阈值时可能会造成误拉回
# 填写-1则关闭
ping-abuse-limit-threshold: 1000
NegativeTimer:
# Number of milliseconds lost while moving before we should start flagging
# 在开始检查玩家时,玩家的移动丢失了多少毫秒数
drift: 1200
# 与 TimerA 相同的检查方法,但适用于坐骑
@@ -157,8 +202,10 @@ Reach:
enable-post-packet: false
exploit:
#是否允许玩家在使用鞘翅时候进行的疾跑跳跃
allow-sprint-jumping-when-using-elytra: true
# 该选项可在发生鬼块时重新同步玩家,从而减轻玩家在鬼块上的位置。
# 这可以在玩家遇到幽灵方块时进行同步,这可以减少玩家所碰到幽灵方块
allow-building-on-ghostblocks: true
distance-to-check-if-ghostblocks: 2
@@ -173,11 +220,12 @@ experimental-checks: false
packet-spam-threshold: 100
# Grim 能够强制执行一个规则:被设置为非飞行状态的玩家的 ping 值不能超过 X 毫秒。这是因为 Grim 目前不检查处于飞行状态的玩家。
# To disable, use -1
# 填写-1则关闭
max-ping-out-of-flying: 1000
# Maximum ping when a firework boost is removed from the player.
# This prevents high latency players from being able to use 1 firework boost with an elytra forever.
# 限制玩家在使用鞘翅飞行时使用烟花加速的延迟
# 这可以解决高延迟的玩家可以使用鞘翅时一直使用 1 个烟花就可以加速
# 填写-1则关闭
max-ping-firework-boost: 1000
config-version: 9
+1
View File
@@ -1,6 +1,7 @@
# Ob Discord Webhook aktiviert werden soll
enabled: false
webhook: ""
embed-title: "**Grim Alert**"
embed-color: "#00FFFF"
violation-content:
- "**Spieler**: %player%"
+1
View File
@@ -1,5 +1,6 @@
enabled: false
webhook: ""
embed-title: "**Grim Alert**"
embed-color: "#00FFFF"
violation-content:
- "**Player**: %player%"
+2
View File
@@ -7,6 +7,8 @@ enabled: false
# La URL del webhook.
webhook: ""
embed-title: "**Grim Alert**"
# El color del embed que se mandara por este webhook.
embed-color: "#00FFFF"
+1
View File
@@ -1,5 +1,6 @@
enabled: false
webhook: ""
embed-title: "**Grim Alert**"
embed-color: "#00FFFF"
violation-content:
- "**Joueur**: %player%"
+1
View File
@@ -1,5 +1,6 @@
enabled: false
webhook: ""
embed-title: "**Grim Alert**"
embed-color: "#00FFFF"
violation-content:
- "**Giocatore**: %player%"
+1
View File
@@ -1,5 +1,6 @@
enabled: false
webhook: ""
embed-title: "**Grim Alert**"
embed-color: "#00FFFF"
violation-content:
- "**Speler**: %player%"
+1
View File
@@ -1,5 +1,6 @@
enabled: false
webhook: ""
embed-title: "**Grim Alert**"
embed-color: "#00FFFF"
violation-content:
- "**Jogador**: %player%"
+1
View File
@@ -1,6 +1,7 @@
# Включать ли веб-крючок discord
enabled: false
webhook: ""
embed-title: "**Grim Alert**"
embed-color: "#00FFFF"
violation-content:
- "**Игрок**: %player%"
+11
View File
@@ -0,0 +1,11 @@
enabled: false
webhook: ""
embed-color: "#00FFFF"
violation-content:
- "**Oyuncu**: %player%"
- "**Kontrol**: %check%"
- "**İhlaller**: %violations%"
- "**Client Versiyonu**: %version%"
- "**Kullanılan Client**: %brand%"
- "**Gecikme**: %ping%"
- "**TPS**: %tps%"
+8 -7
View File
@@ -1,12 +1,13 @@
# 是否启用discord webhook
enabled: false
webhook: ""
embed-title: "**Grim Alert**"
embed-color: "#00FFFF"
violation-content:
- "**Player**: %player%"
- "**Check**: %check%"
- "**Violations**: %violations%"
- "**Client Version**: %version%"
- "**Brand**: %brand%"
- "**Ping**: %ping%"
- "**TPS**: %tps%"
- "**玩家名**: %player%"
- "**检查**: %check%"
- "**违规率**: %violations%"
- "**游戏版本**: %version%"
- "**客户端品牌**: %brand%"
- "**延迟**: %ping%"
- "**服务器卡顿程度**: %tps%"
+19 -22
View File
@@ -1,34 +1,31 @@
# Você pode usar códigos hexadecimais também, no formato #FFFFFF ou #ffffff.
# \u00BB é » (>> duplo), ANSI e UTF-8 interpretam isso diferentemente... você pode acabar vendo '?' por conta disso.
prefix: "&bGrim &8\u00BB"
alerts-enabled: "%prefix% &fAlertas do AntiCheat foram habilitados para você."
alerts-disabled: "%prefix% &fAlertas do AntiCheat foram desabilitados para você."
client-brand-format: "%prefix% &f%player% entrou usando: &c%brand%"
console-specify-target: "%prefix% &cVocê deve especificar o nome do alvo como o terminal!"
alerts-enabled: "%prefix% &fAlertas habilitados."
alerts-disabled: "%prefix% &fAlertas desabilitados."
client-brand-format: "%prefix% &f%player% entrou usando %brand%."
console-specify-target: "%prefix% &cVocê deve especificar o nome do alvo!"
player-not-found: "%prefix% &cJogador não encontrado!"
player-not-this-server: "%prefix% &cO jogador não está nesse servidor!"
player-not-this-server: "%prefix% &cO jogador não está no servidor!"
spectate-return: "\n%prefix% &fClique aqui para retornar a localização anterior\n"
cannot-spectate-return: "%prefix% &cVocê só pode fazer isso enquanto estiver espectando alguém."
cannot-run-on-self: "%prefix% &cVocê não pode usar esse comando em você mesmo!"
upload-log: "%prefix% &fLogs enviadas para: %url%"
upload-log-start: "%prefix% &fEnviando a log... Por favor, aguarde..."
upload-log-not-found: "%prefix% &cNão foi possível encontrar essa log."
upload-log-upload-failure: "%prefix% &cAlgo deu errado ao enviar essa log, veja o terminal para mais informações."
cannot-spectate-return: "%prefix% &cVocê só pode fazer isso após espectar um jogador."
cannot-run-on-self: "%prefix% &cVocê não pode usar esse comando em sí mesmo!"
upload-log: "%prefix% &fRegistro enviado para: %url%"
upload-log-start: "%prefix% &fEnviando o registro. Aguarde."
upload-log-not-found: "%prefix% &cNúmero do registro não encontrado."
upload-log-upload-failure: "%prefix% &cAlgo deu errado ao enviar o registro, veja o terminal para mais informações."
# Placeholders válidos:
# %prefix%
# %player%
# %check_name%
# %description%
# %experimental%
# %vl% - violações
# %verbose% - Informação extra da verificação, como os desvios, nem todas as verifiações terão essa informação.
# %verbose% - Informação extra da verificação, como os desalinhamentos, nem todas as verifiações terão essa informação.
alerts-format: "%prefix% &f%player% &bfalhou em &f%check_name%%experimental% &f(x&c%vl%&f) &7%verbose%"
alerts-format-proxy: "%prefix% &f[&cproxy&f] &f%player% &bfalhou em &f%check_name%%experimental% &f(x&c%vl%&f) &7%verbose%"
experimental-symbol: "*"
profile:
- "&7======================"
- "%prefix% &bPerfil de &f%player%"
@@ -41,13 +38,13 @@ profile:
- "&7======================"
help:
- "&7======================"
- "/grim alerts &f- &7Alterna entre &aon&f/&coff &7as notificações."
- "/grim alerts &f- &7Alterna noificações."
- "/grim profile <player> &f- &7Mosta as informações do jogador."
- "/grim help &f- &7Mostra essa mensagem de ajuda."
- "/grim debug <player> &f- &7Modo de depuração do plugin."
- "/grim perf &f- &7Informações de performance do plugin."
- "/grim reload &f- &7Recarregar configurações do AntiCheat."
- "/grim spectate <player> &f- &7Espectar um jogador."
- "/grim verbose &f- &7Mostra todos os flags para você sem os buffers."
- "/grim log [0-255] &f- &7Envie um log para visualização externa."
- "/grim debug <player> &f- &7Depura a simulação."
- "/grim perf &f- &7Depura a performance."
- "/grim reload &f- &7Recarrega as configurações."
- "/grim spectate <player> &f- &7Especta um jogador."
- "/grim verbose &f- &7Esconde as informações extra."
- "/grim log [0-255] &f- &7Envia o registro da simulação."
- "&7======================"
+56
View File
@@ -0,0 +1,56 @@
# Hex kodlarını #FFFFFF veya #ffffff formatında kullanabilirsiniz :)
# \u00BB eşittir » (çift >>), ANSI ve UTF-8 bunu farklı yorumlar. bu nedenle ? bile görebilirsiniz
prefix: "&bGrim &8\u00BB"
alerts-enabled: "%prefix% &fUyarılar aktif edildi."
alerts-disabled: "%prefix% &fUyarılar deaktif edildi."
client-brand-format: "%prefix% &f%player% adlı oyuncu %brand% kullanarak giriş yaptı."
console-specify-target: "%prefix% &cKonsol olarak bir hedef belirtmelisiniz!"
player-not-found: "%prefix% &cOyuncu bulunamadı veya aktif değil!"
player-not-this-server: "%prefix% &cBu oyuncu sunucuda değil!"
spectate-return: "\n%prefix% &fBir önceki konuma dönmek için buraya tıklayın.\n"
cannot-spectate-return: "%prefix% &cBunu yalnızca bir oyuncuyu izledikten sonra yapabilirsiniz."
cannot-run-on-self: "%prefix% &cBu komutu kendi üzerinizde kullanamazsınız!"
upload-log: "%prefix% &fDebug şu adrese yüklendi: %url%"
upload-log-start: "%prefix% &fLog yükleniyor... lütfen bekleyin."
upload-log-not-found: "%prefix% &cLog bulunamadı."
upload-log-upload-failure: "%prefix% &cLog yüklenirken bir problem oluştu, lütfen konsolu kontrol edin."
# Geçerli placeholderlar:
# %prefix%
# %player%
# %check_name%
# %description%
# %experimental%
# %vl% - ihlal uyarıları
# %verbose% - detaylı olarak verilen tüm uyarılar, checkler tüm bilgileri vermez
alerts-format: "%prefix% &b%player% &fadlı oyuncuda &f(x&c%vl%&f) &fadet &b%check_name%%experimental% &ftespit edildi: &7%verbose%"
alerts-format-proxy: "%prefix% &f[&cproxy&f] &b%player% &fadlı oyuncuda &f(x&c%vl%&f) &fadet &b%check_name%%experimental% &ftespit edildi: &7%verbose%"
experimental-symbol: "*"
profile:
- "&7======================"
- "%prefix% &f%player% &badlı oyuncunun profili:"
- "&bGecikme: &f%ping%"
- "&bVersiyon: &f%version%"
- "&bKullanılan Client: &f%brand%"
- "&bDikey Hassasiyet: &f%h_sensitivity%%"
- "&bYatay Hassasiyet: &f%v_sensitivity%%"
- "&bFastMath Ayarı: &f%fast_math%"
- "&7======================"
help:
- "&7======================"
- "/grim alerts &f- &7Uyarıları aktif/deaktif eder"
- "/grim profile (oyuncu) &f- &7Oyuncu profilini gösterir"
- "/grim help &f- &7Bu yardım mesajını gösterir"
- "/grim debug <player> &f- &7Geliştirici Debug'ını verir"
- "/grim perf &f- &7Geliştirici Performansını verir"
- "/grim reload &f- &7Configi yeniden yükler"
- "/grim spectate (oyuncu) &f- &7Bir oyuncuyu izlemenizi sağlar"
- "/grim verbose &f- &7Arabelleğe almadan tüm uyarıları görmenizi sağlar"
- "/grim log (0-255) &f- &7Uyarılar için bir debug logu çıktısı verir"
- "&7======================"
# Translated by Kayera
# Have a nice day ;)
+3 -3
View File
@@ -17,12 +17,12 @@ upload-log-start: "%prefix% &f上传中...请等待"
upload-log-not-found: "%prefix% &c找不到该日志."
upload-log-upload-failure: "%prefix% &c上载此日志时出错, 有关详细信息, 请参阅控制台"
# Valid placeholders:
# 变量:
# %prefix% - 前缀
# %player% - 玩家
# %check_name% - 检查名字
# %description%
# %experimental%
# %description% - 检查的介绍
# %experimental% - 检查是否是实验性
# %vl% - VL值
# %verbose% - 检查中的额外信息,例如偏移量,并非所有检查都会添加信息
alerts-format: "%prefix% &f%player% &b触发了 &f%check_name%%experimental% &f(x&c%vl%&f) &7%verbose%"
+1 -1
View File
@@ -1,5 +1,5 @@
# %check_name% - nome da verificação
# %description% - description of the check
# %description% - descrição da verificação
# %vl% - violações
# %verbose% - informação extra
# %player% - nome do jogador
+92
View File
@@ -0,0 +1,92 @@
# %check_name% - Kontrolün ismi
# %description% - Kontrolün açıklaması
# %vl% - Yapılan ihlaller
# %verbose% - Ekstra bilgilendirme
# %player% - Oyuncu ismi
# [alert] - Uyarılar için bunu kullanın
# [webhook] - Discord webhook için bunu kullanın
# [proxy] - Proxy'nize bağlı diğer sunuculara uyarı göndermek için bunu kullanın (BungeeCord/Velocity)
Punishments:
Simulation:
# Kaç saniye sonra ihlaller kaldırılsın?
remove-violations-after: 300
# Bu bölüm, aşağıdaki adlara sahip tüm kontrollerle eşleşecektir,
# Aksi takdirde eşleşecek bir denetimi hariç tutmak için, isminin önüne ünlem işareti koyun
# Örnek olarak, !BadPacketsN
checks:
- "Simulation"
- "GroundSpoof"
- "Timer"
- "NoFall"
# Eşik:Aralık Komutu
#
# Örnek: 100 ihlalde "yanlış hareket!" mesajıyla oyuncuyu atmak için, aralık komutu olmadan:
# - "100:0 kick %player% Yanlış hareket!"
# 0, tam olarak bir kez çalıştırılmasını ifade eder
# - "100:50 say %player% Hile yapıyor!"
# Kullanıcı 100 işaretine ulaştığında çalışır ve bundan sonra, 100'ün üzerindeki her 50. işarette çalışır
commands:
- "100:40 [alert]"
- "100:100 [webhook]"
- "100:100 [proxy]"
Knockback:
remove-violations-after: 300
checks:
- "Knockback"
- "Explosion"
commands:
- "5:5 [alert]"
- "20:20 [webhook]"
- "20:20 [proxy]"
Post:
remove-violations-after: 300
checks:
- "Post"
commands:
- "20:20 [alert]"
- "40:40 [webhook]"
- "40:40 [proxy]"
BadPackets:
remove-violations-after: 300
checks:
- "BadPackets"
- "Crash"
commands:
- "20:20 [alert]"
- "40:40 [webhook]"
- "40:40 [proxy]"
Reach:
remove-violations-after: 300
checks:
- "Reach"
commands:
- "5:5 [alert]"
- "5:5 [webhook]"
- "5:5 [proxy]"
Misc:
remove-violations-after: 300
checks:
- "EntityControl"
- "NoSlow"
- "Place"
- "Baritone"
- "FastBreak"
- "TransactionOrder"
commands:
- "10:5 [alert]"
- "20:10 [webhook]"
- "20:10 [proxy]"
Combat:
remove-violations-after: 300
checks:
- "Killaura"
- "Aim"
commands:
- "20:40 [alert]"
# 2.2.10 itibarıyla, AutoClicker denetimleri yoktur ve bu bir yer tutucudur. Grim, gelecekte AutoClicker denetimlerini dahil edecektir.
Autoclicker:
remove-violations-after: 300
checks:
- "Autoclicker"
commands:
- "20:40 [alert]"
+8 -8
View File
@@ -1,31 +1,31 @@
# %check_name% - 检查名称
# %description% - description of the check
# %description% - 检查的介绍
# %vl% - 违规
# %verbose% - 额外信息
# %player% - 玩家名字
# [alert] - 警告的特殊命令
# [webhook] - 警告discord webhook 的特殊命令
# [proxy] - special command to alert to other servers connected to your proxy (BungeeCord/Velocity)
# [proxy] - 用于使用(BungeeCord/Velocity)服务器时的警报
Punishments:
Simulation:
# 多少秒后重置VL
remove-violations-after: 300
# This section will match all checks with the name,
# To exclude a check that would otherwise be matched, put an exclamation mark in front of the name
# For example, !BadPacketsN
# 这个部分将会匹配检查的名字
# 要关闭掉一个检查就在检查前面加感叹号
# 例如, !BadPacketsN
checks:
- "Simulation"
- "GroundSpoof"
- "Timer"
- "NoFall"
# Threshold:Interval Command
# 最大vl:下一次执行需要的vl
#
# 这是个例子, 当到达100VL时为了以 "incorrect movement!" 为理由踢出玩家
# commands:
# - "100:0 kick %player% incorrect movement!"
# 0 means execute exactly once
# 后面的数如果使用0就代表只执行一次
# - "100:50 say %player% is cheating"
# Execute when the user hits flag 100, and after that, every 50th flag after 100
# 在100vl后执行say %player% is cheating,之后的每50vl都执行一次say %player% is cheating
#
commands:
- "100:40 [alert]"